From owner-dev-commits-src-all@freebsd.org Wed Dec 23 12:03:45 2020 Return-Path: Delivered-To: dev-commits-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 4AB6B4BD5A2; Wed, 23 Dec 2020 12:03:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4D1Bgj1jFNz4m66; Wed, 23 Dec 2020 12:03:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 2DBAC1DE0C; Wed, 23 Dec 2020 12:03:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 0BNC3jnm097183; Wed, 23 Dec 2020 12:03:45 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 0BNC3jG0097182; Wed, 23 Dec 2020 12:03:45 GMT (envelope-from git) Date: Wed, 23 Dec 2020 12:03:45 GMT Message-Id: <202012231203.0BNC3jG0097182@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: c3f69af03ae7 - pf: Fix unaligned checksum updates MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: c3f69af03ae7acc167cc1151f0c1ecc5e014ce4e Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: "Commit messages for all branches of the src repository." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 23 Dec 2020 12:03:45 -0000 The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=c3f69af03ae7acc167cc1151f0c1ecc5e014ce4e commit c3f69af03ae7acc167cc1151f0c1ecc5e014ce4e Author: Kristof Provost AuthorDate: 2020-12-20 20:06:32 +0000 Commit: Kristof Provost CommitDate: 2020-12-23 11:03:20 +0000 pf: Fix unaligned checksum updates The algorithm we use to update checksums only works correctly if the updated data is aligned on 16-bit boundaries (relative to the start of the packet). Import the OpenBSD fix for this issue. PR: 240416 Obtained from: OpenBSD MFC after: 1 week Reviewed by: tuexen (previous version) Differential Revision: https://reviews.freebsd.org/D27696 --- sys/net/pfvar.h | 6 ++++ sys/netpfil/pf/pf.c | 81 +++++++++++++++++++++++++++++++++++++++--------- sys/netpfil/pf/pf_norm.c | 23 ++++++++++---- 3 files changed, 90 insertions(+), 20 deletions(-) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 5cc613a543e4..bd6a8c8bd7c4 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -330,6 +330,8 @@ extern struct sx pf_end_lock; (neg) \ ) +#define PF_ALGNMNT(off) (((off) % 2) == 0) + struct pf_rule_uid { uid_t uid[2]; u_int8_t op; @@ -1735,6 +1737,10 @@ void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); void pf_change_proto_a(struct mbuf *, void *, u_int16_t *, u_int32_t, u_int8_t); void pf_change_tcp_a(struct mbuf *, void *, u_int16_t *, u_int32_t); +void pf_patch_16_unaligned(struct mbuf *, u_int16_t *, void *, u_int16_t, + bool, u_int8_t); +void pf_patch_32_unaligned(struct mbuf *, u_int16_t *, void *, u_int32_t, + bool, u_int8_t); void pf_send_deferred_syn(struct pf_state *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 2908a5746b54..047ad7fc308d 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -288,6 +288,8 @@ static void pf_print_state_parts(struct pf_state *, struct pf_state_key *, struct pf_state_key *); static int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); +static void pf_patch_8(struct mbuf *, u_int16_t *, u_int8_t *, u_int8_t, + bool, u_int8_t); static struct pf_state *pf_find_state(struct pfi_kif *, struct pf_state_key_cmp *, u_int); static int pf_src_connlimit(struct pf_state **); @@ -2084,16 +2086,60 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) u_int16_t pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) { - u_int32_t l; - - if (udp && !cksum) - return (0x0000); - l = cksum + old - new; - l = (l >> 16) + (l & 65535); - l = l & 65535; - if (udp && !l) - return (0xFFFF); - return (l); + u_int32_t x; + + x = cksum + old - new; + x = (x + (x >> 16)) & 0xffff; + + /* optimise: eliminate a branch when not udp */ + if (udp && cksum == 0x0000) + return cksum; + if (udp && x == 0x0000) + x = 0xffff; + + return (u_int16_t)(x); +} + +static void +pf_patch_8(struct mbuf *m, u_int16_t *cksum, u_int8_t *f, u_int8_t v, bool hi, + u_int8_t udp) +{ + u_int16_t old = htons(hi ? (*f << 8) : *f); + u_int16_t new = htons(hi ? ( v << 8) : v); + + if (*f == v) + return; + + *f = v; + + if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) + return; + + *cksum = pf_cksum_fixup(*cksum, old, new, udp); +} + +void +pf_patch_16_unaligned(struct mbuf *m, u_int16_t *cksum, void *f, u_int16_t v, + bool hi, u_int8_t udp) +{ + u_int8_t *fb = (u_int8_t *)f; + u_int8_t *vb = (u_int8_t *)&v; + + pf_patch_8(m, cksum, fb++, *vb++, hi, udp); + pf_patch_8(m, cksum, fb++, *vb++, !hi, udp); +} + +void +pf_patch_32_unaligned(struct mbuf *m, u_int16_t *cksum, void *f, u_int32_t v, + bool hi, u_int8_t udp) +{ + u_int8_t *fb = (u_int8_t *)f; + u_int8_t *vb = (u_int8_t *)&v; + + pf_patch_8(m, cksum, fb++, *vb++, hi, udp); + pf_patch_8(m, cksum, fb++, *vb++, !hi, udp); + pf_patch_8(m, cksum, fb++, *vb++, hi, udp); + pf_patch_8(m, cksum, fb++, *vb++, !hi, udp); } u_int16_t @@ -2319,6 +2365,7 @@ pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, return 0; while (hlen >= TCPOLEN_SACKLEN) { + size_t startoff = opt - opts; olen = opt[1]; switch (*opt) { case TCPOPT_EOL: /* FALLTHROUGH */ @@ -2333,10 +2380,16 @@ pf_modulate_sack(struct mbuf *m, int off, struct pf_pdesc *pd, for (i = 2; i + TCPOLEN_SACK <= olen; i += TCPOLEN_SACK) { memcpy(&sack, &opt[i], sizeof(sack)); - pf_change_proto_a(m, &sack.start, &th->th_sum, - htonl(ntohl(sack.start) - dst->seqdiff), 0); - pf_change_proto_a(m, &sack.end, &th->th_sum, - htonl(ntohl(sack.end) - dst->seqdiff), 0); + pf_patch_32_unaligned(m, + &th->th_sum, &sack.start, + htonl(ntohl(sack.start) - dst->seqdiff), + PF_ALGNMNT(startoff), + 0); + pf_patch_32_unaligned(m, &th->th_sum, + &sack.end, + htonl(ntohl(sack.end) - dst->seqdiff), + PF_ALGNMNT(startoff), + 0); memcpy(&opt[i], &sack, sizeof(sack)); } copyback = 1; diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c index b3f867a997c6..1f99dafe2b27 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -1517,6 +1517,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, u_int8_t *opt; int copyback = 0; int got_ts = 0; + size_t startoff; KASSERT((src->scrub || dst->scrub), ("%s: src->scrub && dst->scrub!", __func__)); @@ -1560,6 +1561,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, opt = hdr + sizeof(struct tcphdr); hlen = (th->th_off << 2) - sizeof(struct tcphdr); while (hlen >= TCPOLEN_TIMESTAMP) { + startoff = opt - (hdr + sizeof(struct tcphdr)); switch (*opt) { case TCPOPT_EOL: /* FALLTHROUGH */ case TCPOPT_NOP: @@ -1589,10 +1591,12 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, (src->scrub->pfss_flags & PFSS_TIMESTAMP)) { tsval = ntohl(tsval); - pf_change_proto_a(m, &opt[2], + pf_patch_32_unaligned(m, &th->th_sum, + &opt[2], htonl(tsval + src->scrub->pfss_ts_mod), + PF_ALGNMNT(startoff), 0); copyback = 1; } @@ -1605,8 +1609,11 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, PFSS_TIMESTAMP)) { tsecr = ntohl(tsecr) - dst->scrub->pfss_ts_mod; - pf_change_proto_a(m, &opt[6], - &th->th_sum, htonl(tsecr), + pf_patch_32_unaligned(m, + &th->th_sum, + &opt[6], + htonl(tsecr), + PF_ALGNMNT(startoff), 0); copyback = 1; } @@ -1903,6 +1910,7 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, int rewrite = 0; u_char opts[TCP_MAXOLEN]; u_char *optp = opts; + size_t startoff; thoff = th->th_off << 2; cnt = thoff - sizeof(struct tcphdr); @@ -1912,6 +1920,7 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, return (rewrite); for (; cnt > 0; cnt -= optlen, optp += optlen) { + startoff = optp - opts; opt = optp[0]; if (opt == TCPOPT_EOL) break; @@ -1928,9 +1937,11 @@ pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, case TCPOPT_MAXSEG: mss = (u_int16_t *)(optp + 2); if ((ntohs(*mss)) > r->max_mss) { - th->th_sum = pf_proto_cksum_fixup(m, - th->th_sum, *mss, htons(r->max_mss), 0); - *mss = htons(r->max_mss); + pf_patch_16_unaligned(m, + &th->th_sum, + mss, htons(r->max_mss), + PF_ALGNMNT(startoff), + 0); rewrite = 1; } break;