Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/net/npf Introduce npf_set_mss(). When the MSS is not 16b...



details:   https://anonhg.NetBSD.org/src/rev/814892dae96f
branches:  trunk
changeset: 366518:814892dae96f
user:      maxv <maxv%NetBSD.org@localhost>
date:      Fri Aug 31 14:16:06 2018 +0000

description:
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.

diffstat:

 sys/net/npf/npf_ext_normalize.c |  21 +++++++--
 sys/net/npf/npf_impl.h          |   4 +-
 sys/net/npf/npf_inet.c          |  92 +++++++++++++++++++++++++++++++++++-----
 3 files changed, 98 insertions(+), 19 deletions(-)

diffs (207 lines):

diff -r 2f25efa74642 -r 814892dae96f sys/net/npf/npf_ext_normalize.c
--- a/sys/net/npf/npf_ext_normalize.c   Fri Aug 31 11:21:00 2018 +0000
+++ b/sys/net/npf/npf_ext_normalize.c   Fri Aug 31 14:16:06 2018 +0000
@@ -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_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 @@
        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;
        }
 
diff -r 2f25efa74642 -r 814892dae96f sys/net/npf/npf_impl.h
--- a/sys/net/npf/npf_impl.h    Fri Aug 31 11:21:00 2018 +0000
+++ b/sys/net/npf/npf_impl.h    Fri Aug 31 14:16:06 2018 +0000
@@ -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 @@
 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. */
diff -r 2f25efa74642 -r 814892dae96f sys/net/npf/npf_inet.c
--- a/sys/net/npf/npf_inet.c    Fri Aug 31 11:21:00 2018 +0000
+++ b/sys/net/npf/npf_inet.c    Fri Aug 31 14:16:06 2018 +0000
@@ -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 @@
        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 @@
        }
        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 @@
                        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 @@
        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)
 {



Home | Main Index | Thread Index | Old Index