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)
 {

Reply via email to