xref: /freebsd/usr.sbin/ppp/tcpmss.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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