194d7be52SBrian Somers /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
494d7be52SBrian Somers * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
594d7be52SBrian Somers * All rights reserved.
694d7be52SBrian Somers *
794d7be52SBrian Somers * Redistribution and use in source and binary forms, with or without
894d7be52SBrian Somers * modification, are permitted provided that the following conditions
994d7be52SBrian Somers * are met:
1094d7be52SBrian Somers * 1. Redistributions of source code must retain the above copyright
1194d7be52SBrian Somers * notice, this list of conditions and the following disclaimer.
1294d7be52SBrian Somers * 2. Redistributions in binary form must reproduce the above copyright
1394d7be52SBrian Somers * notice, this list of conditions and the following disclaimer in the
1494d7be52SBrian Somers * documentation and/or other materials provided with the distribution.
1594d7be52SBrian Somers *
1694d7be52SBrian Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1794d7be52SBrian Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1894d7be52SBrian Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1994d7be52SBrian Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2094d7be52SBrian Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2194d7be52SBrian Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2294d7be52SBrian Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2394d7be52SBrian Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2494d7be52SBrian Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2594d7be52SBrian Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2694d7be52SBrian Somers * SUCH DAMAGE.
2794d7be52SBrian Somers */
2894d7be52SBrian Somers
2994d7be52SBrian Somers #include <sys/param.h>
3094d7be52SBrian Somers
31c8b9fb53SBrian Somers #include <sys/socket.h>
32c8b9fb53SBrian Somers #include <net/route.h>
3394d7be52SBrian Somers #include <netinet/in_systm.h>
3494d7be52SBrian Somers #include <netinet/in.h>
3594d7be52SBrian Somers #include <netinet/ip.h>
36cef3c4e0SMichael Tuexen #ifndef NOINET6
37cef3c4e0SMichael Tuexen #include <netinet/ip6.h>
38cef3c4e0SMichael Tuexen #endif
3994d7be52SBrian Somers #include <netinet/tcp.h>
4094d7be52SBrian Somers #include <sys/un.h>
4194d7be52SBrian Somers
4294d7be52SBrian Somers #include <termios.h>
4394d7be52SBrian Somers
4494d7be52SBrian Somers #include "layer.h"
4594d7be52SBrian Somers #include "defs.h"
4694d7be52SBrian Somers #include "log.h"
4794d7be52SBrian Somers #include "timer.h"
4894d7be52SBrian Somers #include "fsm.h"
4994d7be52SBrian Somers #include "mbuf.h"
5094d7be52SBrian Somers #include "throughput.h"
5194d7be52SBrian Somers #include "lqr.h"
5294d7be52SBrian Somers #include "hdlc.h"
5394d7be52SBrian Somers #include "lcp.h"
5494d7be52SBrian Somers #include "ccp.h"
5594d7be52SBrian Somers #include "link.h"
5694d7be52SBrian Somers #include "iplist.h"
5794d7be52SBrian Somers #include "slcompress.h"
5830949fd4SBrian Somers #include "ncpaddr.h"
5994d7be52SBrian Somers #include "ipcp.h"
6094d7be52SBrian Somers #include "filter.h"
6194d7be52SBrian Somers #include "descriptor.h"
6294d7be52SBrian Somers #include "mp.h"
63c8b9fb53SBrian Somers #include "iface.h"
6494d7be52SBrian Somers #ifndef NORADIUS
6594d7be52SBrian Somers #include "radius.h"
6694d7be52SBrian Somers #endif
6730949fd4SBrian Somers #include "ipv6cp.h"
6830949fd4SBrian Somers #include "ncp.h"
6994d7be52SBrian Somers #include "bundle.h"
7094d7be52SBrian Somers
7194d7be52SBrian Somers
7294d7be52SBrian Somers /*-
73cef3c4e0SMichael Tuexen * Compute the MSS as described in RFC 6691.
7494d7be52SBrian Somers */
75cef3c4e0SMichael Tuexen #define MAXMSS4(mtu) ((mtu) - sizeof(struct ip) - sizeof(struct tcphdr))
76cef3c4e0SMichael Tuexen #ifndef NOINET6
77cef3c4e0SMichael Tuexen #define MAXMSS6(mtu) ((mtu) - sizeof(struct ip6_hdr) - sizeof(struct tcphdr))
78cef3c4e0SMichael Tuexen #endif
7994d7be52SBrian Somers
8094d7be52SBrian Somers
8194d7be52SBrian Somers /*-
8294d7be52SBrian Somers * The following macro is used to update an
8394d7be52SBrian Somers * internet checksum. "acc" is a 32-bit
8494d7be52SBrian Somers * accumulation of all the changes to the
8594d7be52SBrian Somers * checksum (adding in old 16-bit words and
8694d7be52SBrian Somers * subtracting out new words), and "cksum"
8794d7be52SBrian Somers * is the checksum value to be updated.
8894d7be52SBrian Somers */
8994d7be52SBrian Somers #define ADJUST_CHECKSUM(acc, cksum) { \
9094d7be52SBrian Somers acc += cksum; \
9194d7be52SBrian Somers if (acc < 0) { \
9294d7be52SBrian Somers acc = -acc; \
9394d7be52SBrian Somers acc = (acc >> 16) + (acc & 0xffff); \
9494d7be52SBrian Somers acc += acc >> 16; \
9594d7be52SBrian Somers cksum = (u_short) ~acc; \
9694d7be52SBrian Somers } else { \
9794d7be52SBrian Somers acc = (acc >> 16) + (acc & 0xffff); \
9894d7be52SBrian Somers acc += acc >> 16; \
9994d7be52SBrian Somers cksum = (u_short) acc; \
10094d7be52SBrian Somers } \
10194d7be52SBrian Somers }
10294d7be52SBrian Somers
10394d7be52SBrian Somers static void
MSSFixup(struct tcphdr * tc,size_t pktlen,u_int16_t maxmss)104057f1760SBrian Somers MSSFixup(struct tcphdr *tc, size_t pktlen, u_int16_t maxmss)
10594d7be52SBrian Somers {
106057f1760SBrian Somers size_t hlen, olen, optlen;
10794d7be52SBrian Somers u_char *opt;
10894d7be52SBrian Somers u_int16_t *mss;
10994d7be52SBrian Somers int accumulate;
11094d7be52SBrian Somers
11194d7be52SBrian Somers hlen = tc->th_off << 2;
11294d7be52SBrian Somers
11394d7be52SBrian Somers /* Invalid header length or header without options. */
11494d7be52SBrian Somers if (hlen <= sizeof(struct tcphdr) || hlen > pktlen)
11594d7be52SBrian Somers return;
11694d7be52SBrian Somers
11794d7be52SBrian Somers /* MSS option only allowed within SYN packets. */
11894d7be52SBrian Somers if (!(tc->th_flags & TH_SYN))
11994d7be52SBrian Somers return;
12094d7be52SBrian Somers
12194d7be52SBrian Somers for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1);
12294d7be52SBrian Somers olen > 0; olen -= optlen, opt += optlen) {
12394d7be52SBrian Somers if (*opt == TCPOPT_EOL)
12494d7be52SBrian Somers break;
12594d7be52SBrian Somers else if (*opt == TCPOPT_NOP)
12694d7be52SBrian Somers optlen = 1;
12794d7be52SBrian Somers else {
12894d7be52SBrian Somers optlen = *(opt + 1);
12994d7be52SBrian Somers if (optlen <= 0 || optlen > olen)
13094d7be52SBrian Somers break;
13194d7be52SBrian Somers if (*opt == TCPOPT_MAXSEG) {
13294d7be52SBrian Somers if (optlen != TCPOLEN_MAXSEG)
13394d7be52SBrian Somers continue;
13494d7be52SBrian Somers mss = (u_int16_t *)(opt + 2);
13594d7be52SBrian Somers if (ntohs(*mss) > maxmss) {
13694d7be52SBrian Somers log_Printf(LogDEBUG, "MSS: %u -> %u\n",
13794d7be52SBrian Somers ntohs(*mss), maxmss);
13894d7be52SBrian Somers accumulate = *mss;
13994d7be52SBrian Somers *mss = htons(maxmss);
14094d7be52SBrian Somers accumulate -= *mss;
14194d7be52SBrian Somers ADJUST_CHECKSUM(accumulate, tc->th_sum);
14294d7be52SBrian Somers }
14394d7be52SBrian Somers }
14494d7be52SBrian Somers }
14594d7be52SBrian Somers }
14694d7be52SBrian Somers }
14794d7be52SBrian Somers
1486cee8a83SBrian Somers static struct mbuf *
tcpmss_Check(struct bundle * bundle,struct mbuf * bp)1496cee8a83SBrian Somers tcpmss_Check(struct bundle *bundle, struct mbuf *bp)
1506cee8a83SBrian Somers {
1516cee8a83SBrian Somers struct ip *pip;
152cef3c4e0SMichael Tuexen #ifndef NOINET6
153cef3c4e0SMichael Tuexen struct ip6_hdr *pip6;
154cef3c4e0SMichael Tuexen struct ip6_frag *pfrag;
155cef3c4e0SMichael Tuexen #endif
156057f1760SBrian Somers size_t hlen, plen;
1576cee8a83SBrian Somers
1586cee8a83SBrian Somers if (!Enabled(bundle, OPT_TCPMSSFIXUP))
1596cee8a83SBrian Somers return bp;
1606cee8a83SBrian Somers
1616cee8a83SBrian Somers bp = m_pullup(bp);
1626cee8a83SBrian Somers plen = m_length(bp);
163cef3c4e0SMichael Tuexen if (plen < sizeof(struct ip))
164cef3c4e0SMichael Tuexen return bp;
1656cee8a83SBrian Somers pip = (struct ip *)MBUF_CTOP(bp);
1666cee8a83SBrian Somers
167cef3c4e0SMichael Tuexen switch (pip->ip_v) {
168cef3c4e0SMichael Tuexen case IPVERSION:
1696cee8a83SBrian Somers /*
1706cee8a83SBrian Somers * Check for MSS option only for TCP packets with zero fragment offsets
1716cee8a83SBrian Somers * and correct total and header lengths.
1726cee8a83SBrian Somers */
173cef3c4e0SMichael Tuexen hlen = pip->ip_hl << 2;
1746cee8a83SBrian Somers if (pip->ip_p == IPPROTO_TCP && (ntohs(pip->ip_off) & IP_OFFMASK) == 0 &&
1756cee8a83SBrian Somers ntohs(pip->ip_len) == plen && hlen <= plen &&
176057f1760SBrian Somers plen >= sizeof(struct tcphdr) + hlen)
1776cee8a83SBrian Somers MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen,
178cef3c4e0SMichael Tuexen MAXMSS4(bundle->iface->mtu));
179cef3c4e0SMichael Tuexen break;
180cef3c4e0SMichael Tuexen #ifndef NOINET6
181cef3c4e0SMichael Tuexen case IPV6_VERSION >> 4:
182cef3c4e0SMichael Tuexen /*
183cef3c4e0SMichael Tuexen * Check for MSS option only for TCP packets with no extension headers
184cef3c4e0SMichael Tuexen * or a single extension header which is a fragmentation header with
185cef3c4e0SMichael Tuexen * offset 0. Furthermore require that the length field is correct.
186cef3c4e0SMichael Tuexen */
187cef3c4e0SMichael Tuexen if (plen < sizeof(struct ip6_hdr))
188cef3c4e0SMichael Tuexen break;
189cef3c4e0SMichael Tuexen pip6 = (struct ip6_hdr *)MBUF_CTOP(bp);
190cef3c4e0SMichael Tuexen if (ntohs(pip6->ip6_plen) + sizeof(struct ip6_hdr) != plen)
191cef3c4e0SMichael Tuexen break;
192cef3c4e0SMichael Tuexen hlen = 0;
193cef3c4e0SMichael Tuexen switch (pip6->ip6_nxt) {
194cef3c4e0SMichael Tuexen case IPPROTO_TCP:
195cef3c4e0SMichael Tuexen hlen = sizeof(struct ip6_hdr);
196cef3c4e0SMichael Tuexen break;
197cef3c4e0SMichael Tuexen case IPPROTO_FRAGMENT:
198cef3c4e0SMichael Tuexen if (plen >= sizeof(struct ip6_frag) + sizeof(struct ip6_hdr)) {
199cef3c4e0SMichael Tuexen pfrag = (struct ip6_frag *)(MBUF_CTOP(bp) + sizeof(struct ip6_hdr));
200cef3c4e0SMichael Tuexen if (pfrag->ip6f_nxt == IPPROTO_TCP &&
201cef3c4e0SMichael Tuexen ntohs(pfrag->ip6f_offlg & IP6F_OFF_MASK) == 0)
202cef3c4e0SMichael Tuexen hlen = sizeof(struct ip6_hdr)+ sizeof(struct ip6_frag);
203cef3c4e0SMichael Tuexen }
204cef3c4e0SMichael Tuexen break;
205cef3c4e0SMichael Tuexen }
206cef3c4e0SMichael Tuexen if (hlen > 0 && plen >= sizeof(struct tcphdr) + hlen)
207cef3c4e0SMichael Tuexen MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen,
208cef3c4e0SMichael Tuexen MAXMSS6(bundle->iface->mtu));
209cef3c4e0SMichael Tuexen break;
210cef3c4e0SMichael Tuexen #endif
211cef3c4e0SMichael Tuexen default:
212cef3c4e0SMichael Tuexen log_Printf(LogDEBUG, "tcpmss_Check: Unknown IP family %u\n", pip->ip_v);
213cef3c4e0SMichael Tuexen break;
214cef3c4e0SMichael Tuexen }
2156cee8a83SBrian Somers return bp;
2166cee8a83SBrian Somers }
2176cee8a83SBrian Somers
2186cee8a83SBrian Somers static struct mbuf *
tcpmss_LayerPush(struct bundle * bundle,struct link * l __unused,struct mbuf * bp,int pri __unused,u_short * proto __unused)219057f1760SBrian Somers tcpmss_LayerPush(struct bundle *bundle, struct link *l __unused,
220057f1760SBrian Somers struct mbuf *bp, int pri __unused, u_short *proto __unused)
2216cee8a83SBrian Somers {
2226cee8a83SBrian Somers return tcpmss_Check(bundle, bp);
2236cee8a83SBrian Somers }
2246cee8a83SBrian Somers
2256cee8a83SBrian Somers static struct mbuf *
tcpmss_LayerPull(struct bundle * bundle,struct link * l __unused,struct mbuf * bp,u_short * proto __unused)226057f1760SBrian Somers tcpmss_LayerPull(struct bundle *bundle, struct link *l __unused,
227057f1760SBrian Somers struct mbuf *bp, u_short *proto __unused)
2286cee8a83SBrian Somers {
2296cee8a83SBrian Somers return tcpmss_Check(bundle, bp);
2306cee8a83SBrian Somers }
2316cee8a83SBrian Somers
2326cee8a83SBrian Somers struct layer tcpmsslayer =
2336cee8a83SBrian Somers { LAYER_PROTO, "tcpmss", tcpmss_LayerPush, tcpmss_LayerPull };
234