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