The branch stable/12 has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=21745738a2b5662dfbe730b6338aa38e829cb0eb

commit 21745738a2b5662dfbe730b6338aa38e829cb0eb
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2020-12-20 20:06:32 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2021-01-03 22:11:08 +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
    
    (cherry picked from commit c3f69af03ae7acc167cc1151f0c1ecc5e014ce4e)
---
 sys/net/pfvar.h          |  5 +++
 sys/netpfil/pf/pf.c      | 81 +++++++++++++++++++++++++++++++++++++++---------
 sys/netpfil/pf/pf_norm.c | 23 ++++++++++----
 3 files changed, 89 insertions(+), 20 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index d0eb226ee41d..24faee5d45c6 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -330,6 +330,7 @@ extern struct sx pf_end_lock;
                (neg)                                                   \
        )
 
+#define PF_ALGNMNT(off) (((off) % 2) == 0)
 
 struct pf_rule_uid {
        uid_t            uid[2];
@@ -1727,6 +1728,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 693e45504745..84133039eb45 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -289,6 +289,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 **);
@@ -2091,16 +2093,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
@@ -2327,6 +2373,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 */
@@ -2341,10 +2388,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 7234c1def914..4bfdcd7898fe 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -1371,6 +1371,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__));
@@ -1414,6 +1415,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:
@@ -1443,10 +1445,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;
                                        }
@@ -1459,8 +1463,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;
                                        }
@@ -1761,6 +1768,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);
@@ -1770,6 +1778,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;
@@ -1786,9 +1795,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;
_______________________________________________
dev-commits-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "dev-commits-src-all-unsubscr...@freebsd.org"

Reply via email to