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