Module Name: src Committed By: maxv Date: Fri Aug 31 14:16:07 UTC 2018
Modified Files: src/sys/net/npf: npf_ext_normalize.c npf_impl.h npf_inet.c Log Message: Introduce npf_set_mss(). When the MSS is not 16bit-aligned, it sets: 0 8 16 24 32 +------+-----------+-----------+------+ | data | MSS (low) | MSS (hig) | data | +------+-----------+-----------+------+ ^ ^ old[0] old[1] And sets new[0,1] accordingly with the new value. The MSS-clamping code then adjusts twice the checksum on a 16bit boundary: from old[0] to new[0] from old[1] to new[1] Fixes PR/53479, opened by myself. Tested with wireshark and kASan. To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.8 src/sys/net/npf/npf_ext_normalize.c cvs rdiff -u -r1.70 -r1.71 src/sys/net/npf/npf_impl.h cvs rdiff -u -r1.50 -r1.51 src/sys/net/npf/npf_inet.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/npf/npf_ext_normalize.c diff -u src/sys/net/npf/npf_ext_normalize.c:1.7 src/sys/net/npf/npf_ext_normalize.c:1.8 --- src/sys/net/npf/npf_ext_normalize.c:1.7 Sat Apr 7 09:20:25 2018 +++ src/sys/net/npf/npf_ext_normalize.c Fri Aug 31 14:16:06 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_ext_normalize.c,v 1.7 2018/04/07 09:20:25 maxv Exp $ */ +/* $NetBSD: npf_ext_normalize.c,v 1.8 2018/08/31 14:16:06 maxv Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -28,7 +28,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ext_normalize.c,v 1.7 2018/04/07 09:20:25 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ext_normalize.c,v 1.8 2018/08/31 14:16:06 maxv Exp $"); #include <sys/types.h> #include <sys/module.h> @@ -148,8 +148,10 @@ npf_normalize(npf_cache_t *npc, void *pa { npf_normalize_t *np = params; uint16_t cksum, mss, maxmss = np->n_maxmss; + uint16_t old[2], new[2]; struct tcphdr *th; int wscale; + bool mid; /* Skip, if already blocking. */ if (*decision == NPF_DECISION_BLOCK) { @@ -182,13 +184,22 @@ npf_normalize(npf_cache_t *npc, void *pa maxmss = htons(maxmss); /* - * Store new MSS, calculate TCP checksum and update it. + * Store new MSS, calculate TCP checksum and update it. The MSS may + * not be aligned and fall in the middle of two uint16_t's, so we + * need to take care of that when calculating the checksum. + * * WARNING: must re-fetch the TCP header after the modification. */ - if (npf_fetch_tcpopts(npc, &maxmss, &wscale) && + if (npf_set_mss(npc, maxmss, old, new, &mid) && !nbuf_cksum_barrier(npc->npc_nbuf, mi->mi_di)) { th = npc->npc_l4.tcp; - cksum = npf_fixup16_cksum(th->th_sum, mss, maxmss); + if (mid) { + cksum = th->th_sum; + cksum = npf_fixup16_cksum(cksum, old[0], new[0]); + cksum = npf_fixup16_cksum(cksum, old[1], new[1]); + } else { + cksum = npf_fixup16_cksum(th->th_sum, mss, maxmss); + } th->th_sum = cksum; } Index: src/sys/net/npf/npf_impl.h diff -u src/sys/net/npf/npf_impl.h:1.70 src/sys/net/npf/npf_impl.h:1.71 --- src/sys/net/npf/npf_impl.h:1.70 Sun Dec 10 01:18:21 2017 +++ src/sys/net/npf/npf_impl.h Fri Aug 31 14:16:06 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_impl.h,v 1.70 2017/12/10 01:18:21 rmind Exp $ */ +/* $NetBSD: npf_impl.h,v 1.71 2018/08/31 14:16:06 maxv Exp $ */ /*- * Copyright (c) 2009-2014 The NetBSD Foundation, Inc. @@ -283,6 +283,8 @@ void npf_addr_mask(const npf_addr_t *, int npf_tcpsaw(const npf_cache_t *, tcp_seq *, tcp_seq *, uint32_t *); bool npf_fetch_tcpopts(npf_cache_t *, uint16_t *, int *); +bool npf_set_mss(npf_cache_t *, uint16_t, uint16_t *, uint16_t *, + bool *); bool npf_return_block(npf_cache_t *, const int); /* BPF interface. */ Index: src/sys/net/npf/npf_inet.c diff -u src/sys/net/npf/npf_inet.c:1.50 src/sys/net/npf/npf_inet.c:1.51 --- src/sys/net/npf/npf_inet.c:1.50 Sun Apr 8 05:51:45 2018 +++ src/sys/net/npf/npf_inet.c Fri Aug 31 14:16:06 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_inet.c,v 1.50 2018/04/08 05:51:45 maxv Exp $ */ +/* $NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $ */ /*- * Copyright (c) 2009-2014 The NetBSD Foundation, Inc. @@ -40,7 +40,7 @@ #ifdef _KERNEL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.50 2018/04/08 05:51:45 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.51 2018/08/31 14:16:06 maxv Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -230,7 +230,6 @@ npf_fetch_tcpopts(npf_cache_t *npc, uint nbuf_t *nbuf = npc->npc_nbuf; const struct tcphdr *th = npc->npc_l4.tcp; int cnt, optlen = 0; - bool setmss = false; uint8_t *cp, opt; uint8_t val; bool ok; @@ -246,11 +245,6 @@ npf_fetch_tcpopts(npf_cache_t *npc, uint } KASSERT(cnt <= MAX_TCPOPTLEN); - /* Determine if we want to set or get the mss. */ - if (mss) { - setmss = (*mss != 0); - } - /* Fetch all the options at once. */ nbuf_reset(nbuf); const int step = npc->npc_hlen + sizeof(struct tcphdr); @@ -279,11 +273,7 @@ npf_fetch_tcpopts(npf_cache_t *npc, uint if (optlen != TCPOLEN_MAXSEG) continue; if (mss) { - if (setmss) { - memcpy(cp + 2, mss, sizeof(uint16_t)); - } else { - memcpy(mss, cp + 2, sizeof(uint16_t)); - } + memcpy(mss, cp + 2, sizeof(uint16_t)); } break; case TCPOPT_WINDOW: @@ -305,6 +295,82 @@ done: return ok; } +/* + * npf_set_mss: set the MSS. + */ +bool +npf_set_mss(npf_cache_t *npc, uint16_t mss, uint16_t *old, uint16_t *new, + bool *mid) +{ + nbuf_t *nbuf = npc->npc_nbuf; + const struct tcphdr *th = npc->npc_l4.tcp; + int cnt, optlen = 0; + uint8_t *cp, *base, opt; + bool ok; + + KASSERT(npf_iscached(npc, NPC_IP46)); + KASSERT(npf_iscached(npc, NPC_TCP)); + + /* Determine if there are any TCP options, get their length. */ + cnt = (th->th_off << 2) - sizeof(struct tcphdr); + if (cnt <= 0) { + /* No options. */ + return false; + } + KASSERT(cnt <= MAX_TCPOPTLEN); + + /* Fetch all the options at once. */ + nbuf_reset(nbuf); + const int step = npc->npc_hlen + sizeof(struct tcphdr); + if ((base = nbuf_advance(nbuf, step, cnt)) == NULL) { + ok = false; + goto done; + } + + /* Scan the options. */ + for (cp = base; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + if (cnt < 2) + break; + optlen = cp[1]; + if (optlen < 2 || optlen > cnt) + break; + } + + switch (opt) { + case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) + continue; + if (((cp + 2) - base) % sizeof(uint16_t) != 0) { + *mid = true; + memcpy(&old[0], cp + 1, sizeof(uint16_t)); + memcpy(&old[1], cp + 3, sizeof(uint16_t)); + memcpy(cp + 2, &mss, sizeof(uint16_t)); + memcpy(&new[0], cp + 1, sizeof(uint16_t)); + memcpy(&new[1], cp + 3, sizeof(uint16_t)); + } else { + *mid = false; + memcpy(cp + 2, &mss, sizeof(uint16_t)); + } + break; + default: + break; + } + } + + ok = true; +done: + if (nbuf_flag_p(nbuf, NBUF_DATAREF_RESET)) { + npf_recache(npc); + } + return ok; +} + static int npf_cache_ip(npf_cache_t *npc, nbuf_t *nbuf) {