169303Sbrian/*- 269303Sbrian * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org> 369303Sbrian * All rights reserved. 469303Sbrian * 569303Sbrian * Redistribution and use in source and binary forms, with or without 669303Sbrian * modification, are permitted provided that the following conditions 769303Sbrian * are met: 869303Sbrian * 1. Redistributions of source code must retain the above copyright 969303Sbrian * notice, this list of conditions and the following disclaimer. 1069303Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1169303Sbrian * notice, this list of conditions and the following disclaimer in the 1269303Sbrian * documentation and/or other materials provided with the distribution. 1369303Sbrian * 1469303Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1569303Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1669303Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1769303Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1869303Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1969303Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2069303Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2169303Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2269303Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2369303Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2469303Sbrian * SUCH DAMAGE. 2569303Sbrian * 2669303Sbrian * $FreeBSD$ 2769303Sbrian */ 2869303Sbrian 2969303Sbrian#include <sys/param.h> 3069303Sbrian 3178410Sbrian#include <sys/socket.h> 3278410Sbrian#include <net/route.h> 3369303Sbrian#include <netinet/in_systm.h> 3469303Sbrian#include <netinet/in.h> 3569303Sbrian#include <netinet/ip.h> 3669303Sbrian#include <netinet/tcp.h> 3769303Sbrian#include <sys/un.h> 3869303Sbrian 3969303Sbrian#include <termios.h> 4069303Sbrian 4169303Sbrian#include "layer.h" 4269303Sbrian#include "defs.h" 4369303Sbrian#include "log.h" 4469303Sbrian#include "timer.h" 4569303Sbrian#include "fsm.h" 4669303Sbrian#include "mbuf.h" 4769303Sbrian#include "throughput.h" 4869303Sbrian#include "lqr.h" 4969303Sbrian#include "hdlc.h" 5069303Sbrian#include "lcp.h" 5169303Sbrian#include "ccp.h" 5269303Sbrian#include "link.h" 5369303Sbrian#include "iplist.h" 5469303Sbrian#include "slcompress.h" 5581634Sbrian#include "ncpaddr.h" 5669303Sbrian#include "ipcp.h" 5769303Sbrian#include "filter.h" 5869303Sbrian#include "descriptor.h" 5969303Sbrian#include "mp.h" 6078410Sbrian#include "iface.h" 6169303Sbrian#ifndef NORADIUS 6269303Sbrian#include "radius.h" 6369303Sbrian#endif 6481634Sbrian#include "ipv6cp.h" 6581634Sbrian#include "ncp.h" 6669303Sbrian#include "bundle.h" 6769303Sbrian 6869303Sbrian 6969303Sbrian/*- 7069303Sbrian * We are in a liberal position about MSS 7169303Sbrian * (RFC 879, section 7). 7269303Sbrian */ 73131265Sbrian#define MAXMSS(mtu) ((mtu) - sizeof(struct ip) - sizeof(struct tcphdr) - 12) 7469303Sbrian 7569303Sbrian 7669303Sbrian/*- 7769303Sbrian * The following macro is used to update an 7869303Sbrian * internet checksum. "acc" is a 32-bit 7969303Sbrian * accumulation of all the changes to the 8069303Sbrian * checksum (adding in old 16-bit words and 8169303Sbrian * subtracting out new words), and "cksum" 8269303Sbrian * is the checksum value to be updated. 8369303Sbrian */ 8469303Sbrian#define ADJUST_CHECKSUM(acc, cksum) { \ 8569303Sbrian acc += cksum; \ 8669303Sbrian if (acc < 0) { \ 8769303Sbrian acc = -acc; \ 8869303Sbrian acc = (acc >> 16) + (acc & 0xffff); \ 8969303Sbrian acc += acc >> 16; \ 9069303Sbrian cksum = (u_short) ~acc; \ 9169303Sbrian } else { \ 9269303Sbrian acc = (acc >> 16) + (acc & 0xffff); \ 9369303Sbrian acc += acc >> 16; \ 9469303Sbrian cksum = (u_short) acc; \ 9569303Sbrian } \ 9669303Sbrian} 9769303Sbrian 9869303Sbrianstatic void 99134789SbrianMSSFixup(struct tcphdr *tc, size_t pktlen, u_int16_t maxmss) 10069303Sbrian{ 101134789Sbrian size_t hlen, olen, optlen; 10269303Sbrian u_char *opt; 10369303Sbrian u_int16_t *mss; 10469303Sbrian int accumulate; 10569303Sbrian 10669303Sbrian hlen = tc->th_off << 2; 10769303Sbrian 10869303Sbrian /* Invalid header length or header without options. */ 10969303Sbrian if (hlen <= sizeof(struct tcphdr) || hlen > pktlen) 11069303Sbrian return; 11169303Sbrian 11269303Sbrian /* MSS option only allowed within SYN packets. */ 11369303Sbrian if (!(tc->th_flags & TH_SYN)) 11469303Sbrian return; 11569303Sbrian 11669303Sbrian for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1); 11769303Sbrian olen > 0; olen -= optlen, opt += optlen) { 11869303Sbrian if (*opt == TCPOPT_EOL) 11969303Sbrian break; 12069303Sbrian else if (*opt == TCPOPT_NOP) 12169303Sbrian optlen = 1; 12269303Sbrian else { 12369303Sbrian optlen = *(opt + 1); 12469303Sbrian if (optlen <= 0 || optlen > olen) 12569303Sbrian break; 12669303Sbrian if (*opt == TCPOPT_MAXSEG) { 12769303Sbrian if (optlen != TCPOLEN_MAXSEG) 12869303Sbrian continue; 12969303Sbrian mss = (u_int16_t *)(opt + 2); 13069303Sbrian if (ntohs(*mss) > maxmss) { 13169303Sbrian log_Printf(LogDEBUG, "MSS: %u -> %u\n", 13269303Sbrian ntohs(*mss), maxmss); 13369303Sbrian accumulate = *mss; 13469303Sbrian *mss = htons(maxmss); 13569303Sbrian accumulate -= *mss; 13669303Sbrian ADJUST_CHECKSUM(accumulate, tc->th_sum); 13769303Sbrian } 13869303Sbrian } 13969303Sbrian } 14069303Sbrian } 14169303Sbrian} 14269303Sbrian 14379650Sbrianstatic struct mbuf * 14479650Sbriantcpmss_Check(struct bundle *bundle, struct mbuf *bp) 14579650Sbrian{ 14679650Sbrian struct ip *pip; 147134789Sbrian size_t hlen, plen; 14879650Sbrian 14979650Sbrian if (!Enabled(bundle, OPT_TCPMSSFIXUP)) 15079650Sbrian return bp; 15179650Sbrian 15279650Sbrian bp = m_pullup(bp); 15379650Sbrian plen = m_length(bp); 15479650Sbrian pip = (struct ip *)MBUF_CTOP(bp); 15579650Sbrian hlen = pip->ip_hl << 2; 15679650Sbrian 15779650Sbrian /* 15879650Sbrian * Check for MSS option only for TCP packets with zero fragment offsets 15979650Sbrian * and correct total and header lengths. 16079650Sbrian */ 16179650Sbrian if (pip->ip_p == IPPROTO_TCP && (ntohs(pip->ip_off) & IP_OFFMASK) == 0 && 16279650Sbrian ntohs(pip->ip_len) == plen && hlen <= plen && 163134789Sbrian plen >= sizeof(struct tcphdr) + hlen) 16479650Sbrian MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen, 16579650Sbrian MAXMSS(bundle->iface->mtu)); 16679650Sbrian 16779650Sbrian return bp; 16879650Sbrian} 16979650Sbrian 17079650Sbrianstatic struct mbuf * 171134789Sbriantcpmss_LayerPush(struct bundle *bundle, struct link *l __unused, 172134789Sbrian struct mbuf *bp, int pri __unused, u_short *proto __unused) 17379650Sbrian{ 17479650Sbrian return tcpmss_Check(bundle, bp); 17579650Sbrian} 17679650Sbrian 17779650Sbrianstatic struct mbuf * 178134789Sbriantcpmss_LayerPull(struct bundle *bundle, struct link *l __unused, 179134789Sbrian struct mbuf *bp, u_short *proto __unused) 18079650Sbrian{ 18179650Sbrian return tcpmss_Check(bundle, bp); 18279650Sbrian} 18379650Sbrian 18479650Sbrianstruct layer tcpmsslayer = 18579650Sbrian { LAYER_PROTO, "tcpmss", tcpmss_LayerPush, tcpmss_LayerPull }; 186