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