tcpmss.c revision 78410
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: head/usr.sbin/ppp/tcpmss.c 78410 2001-06-18 14:59:36Z brian $ 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" 5569303Sbrian#include "ipcp.h" 5669303Sbrian#include "filter.h" 5769303Sbrian#include "descriptor.h" 5869303Sbrian#include "mp.h" 5978410Sbrian#include "iface.h" 6069303Sbrian#ifndef NORADIUS 6169303Sbrian#include "radius.h" 6269303Sbrian#endif 6369303Sbrian#include "bundle.h" 6469303Sbrian 6569303Sbrian 6669303Sbrian/*- 6769303Sbrian * We are in a liberal position about MSS 6869303Sbrian * (RFC 879, section 7). 6969303Sbrian */ 7069303Sbrian#define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr)) 7169303Sbrian 7269303Sbrian 7369303Sbrianstatic void MSSFixup(struct tcphdr *, ssize_t, u_int16_t); 7469303Sbrian 7569303Sbrian 7669303Sbrianstatic struct mbuf * 7769303Sbriantcpmss_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp, 7869303Sbrian int pri, u_short *proto) 7969303Sbrian{ 8069303Sbrian struct ip *pip; 8169303Sbrian int hlen, plen; 8269303Sbrian 8369303Sbrian if (!Enabled(bundle, OPT_TCPMSSFIXUP)) 8469303Sbrian return bp; 8569303Sbrian 8669303Sbrian bp = m_pullup(bp); 8769303Sbrian plen = m_length(bp); 8869303Sbrian pip = (struct ip *)MBUF_CTOP(bp); 8969303Sbrian hlen = pip->ip_hl << 2; 9069303Sbrian 9169303Sbrian /* 9269303Sbrian * Check for MSS option only for TCP packets with zero fragment offsets 9369303Sbrian * and correct total and header lengths. 9469303Sbrian */ 9569303Sbrian if (pip->ip_p == IPPROTO_TCP && (ntohs(pip->ip_off) & IP_OFFMASK) == 0 && 9669303Sbrian ntohs(pip->ip_len) == plen && hlen <= plen && 9769303Sbrian plen - hlen >= sizeof(struct tcphdr)) 9869303Sbrian MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen, 9978410Sbrian MAXMSS(bundle->iface->mtu)); 10069303Sbrian 10169303Sbrian return bp; 10269303Sbrian} 10369303Sbrian 10469303Sbrian/*- 10569303Sbrian * The following macro is used to update an 10669303Sbrian * internet checksum. "acc" is a 32-bit 10769303Sbrian * accumulation of all the changes to the 10869303Sbrian * checksum (adding in old 16-bit words and 10969303Sbrian * subtracting out new words), and "cksum" 11069303Sbrian * is the checksum value to be updated. 11169303Sbrian */ 11269303Sbrian#define ADJUST_CHECKSUM(acc, cksum) { \ 11369303Sbrian acc += cksum; \ 11469303Sbrian if (acc < 0) { \ 11569303Sbrian acc = -acc; \ 11669303Sbrian acc = (acc >> 16) + (acc & 0xffff); \ 11769303Sbrian acc += acc >> 16; \ 11869303Sbrian cksum = (u_short) ~acc; \ 11969303Sbrian } else { \ 12069303Sbrian acc = (acc >> 16) + (acc & 0xffff); \ 12169303Sbrian acc += acc >> 16; \ 12269303Sbrian cksum = (u_short) acc; \ 12369303Sbrian } \ 12469303Sbrian} 12569303Sbrian 12669303Sbrianstatic void 12769303SbrianMSSFixup(struct tcphdr *tc, ssize_t pktlen, u_int16_t maxmss) 12869303Sbrian{ 12969303Sbrian int hlen, olen, optlen; 13069303Sbrian u_char *opt; 13169303Sbrian u_int16_t *mss; 13269303Sbrian int accumulate; 13369303Sbrian 13469303Sbrian hlen = tc->th_off << 2; 13569303Sbrian 13669303Sbrian /* Invalid header length or header without options. */ 13769303Sbrian if (hlen <= sizeof(struct tcphdr) || hlen > pktlen) 13869303Sbrian return; 13969303Sbrian 14069303Sbrian /* MSS option only allowed within SYN packets. */ 14169303Sbrian if (!(tc->th_flags & TH_SYN)) 14269303Sbrian return; 14369303Sbrian 14469303Sbrian for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1); 14569303Sbrian olen > 0; olen -= optlen, opt += optlen) { 14669303Sbrian if (*opt == TCPOPT_EOL) 14769303Sbrian break; 14869303Sbrian else if (*opt == TCPOPT_NOP) 14969303Sbrian optlen = 1; 15069303Sbrian else { 15169303Sbrian optlen = *(opt + 1); 15269303Sbrian if (optlen <= 0 || optlen > olen) 15369303Sbrian break; 15469303Sbrian if (*opt == TCPOPT_MAXSEG) { 15569303Sbrian if (optlen != TCPOLEN_MAXSEG) 15669303Sbrian continue; 15769303Sbrian mss = (u_int16_t *)(opt + 2); 15869303Sbrian if (ntohs(*mss) > maxmss) { 15969303Sbrian log_Printf(LogDEBUG, "MSS: %u -> %u\n", 16069303Sbrian ntohs(*mss), maxmss); 16169303Sbrian accumulate = *mss; 16269303Sbrian *mss = htons(maxmss); 16369303Sbrian accumulate -= *mss; 16469303Sbrian ADJUST_CHECKSUM(accumulate, tc->th_sum); 16569303Sbrian } 16669303Sbrian } 16769303Sbrian } 16869303Sbrian } 16969303Sbrian} 17069303Sbrian 17169303Sbrianstruct layer tcpmsslayer = { LAYER_PROTO, "tcpmss", tcpmss_LayerPush, NULL }; 172