diff options
author | procter <procter@cvs.openbsd.org> | 2016-08-17 03:24:13 +0000 |
---|---|---|
committer | procter <procter@cvs.openbsd.org> | 2016-08-17 03:24:13 +0000 |
commit | ddee3cf55a4efa0d2c34a7fe5275cbd883471257 (patch) | |
tree | b8aad5e0414862b8a859e743d45ec64351a5c838 /sys | |
parent | 992a22e386971caad5421a7914a2b0c135954424 (diff) |
Reintroduce 5.3-style checksum modification to preserve end-to-end checksums
when fiddling with packets but without the mess that motivated Henning to
remove it. Affects only this one aspect of Henning's checksum work. Also tweak
the basic algorithm and supply a correctness argument.
OK dlg@ deraadt@ sthen@; no objection henning@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/pf.c | 805 | ||||
-rw-r--r-- | sys/net/pf_norm.c | 71 | ||||
-rw-r--r-- | sys/net/pfvar.h | 19 |
3 files changed, 559 insertions, 336 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 3b56da2847e..657793aaf01 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.979 2016/07/18 13:17:44 bluhm Exp $ */ +/* $OpenBSD: pf.c,v 1.980 2016/08/17 03:24:11 procter Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -150,21 +150,28 @@ void pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t); void pf_add_threshold(struct pf_threshold *); int pf_check_threshold(struct pf_threshold *); - -void pf_change_ap(struct pf_pdesc *, struct pf_addr *, +int pf_check_tcp_cksum(struct mbuf *, int, int, + sa_family_t); +static __inline void pf_cksum_fixup(u_int16_t *, u_int16_t, u_int16_t, + u_int8_t); +void pf_translate_ap(struct pf_pdesc *, struct pf_addr *, u_int16_t *, struct pf_addr *, u_int16_t); +void pf_cksum_fixup_a(u_int16_t *, const struct pf_addr *, + const struct pf_addr *, sa_family_t, u_int8_t); int pf_modulate_sack(struct pf_pdesc *, struct pf_state_peer *); int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *, u_int16_t *, u_int16_t *); -void pf_change_icmp(struct pf_pdesc *, struct pf_addr *, - u_int16_t *, struct pf_addr *, struct pf_addr *, - u_int16_t); int pf_change_icmp_af(struct mbuf *, int, struct pf_pdesc *, struct pf_pdesc *, struct pf_addr *, struct pf_addr *, sa_family_t, sa_family_t); -int pf_translate_icmp_af(int, void *); +void pf_translate_a(struct pf_pdesc *, struct pf_addr *, + struct pf_addr *); +void pf_translate_icmp(struct pf_pdesc *, struct pf_addr *, + u_int16_t *, struct pf_addr *, struct pf_addr *, + u_int16_t); +int pf_translate_icmp_af(struct pf_pdesc*, int, void *); void pf_send_tcp(const struct pf_rule *, sa_family_t, const struct pf_addr *, const struct pf_addr *, u_int16_t, u_int16_t, u_int32_t, u_int32_t, @@ -293,6 +300,8 @@ static __inline int pf_state_compare_key(struct pf_state_key *, struct pf_state_key *); static __inline int pf_state_compare_id(struct pf_state *, struct pf_state *); +static __inline void pf_cksum_uncover(u_int16_t *, u_int16_t, u_int8_t); +static __inline void pf_cksum_cover(u_int16_t *, u_int16_t, u_int8_t); struct pf_src_tree tree_src_tracking; @@ -1684,27 +1693,247 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) } } +/* This algorithm computes 'a + b - c' in ones-complement using a trick to + * emulate at most one ones-complement subtraction. This thereby limits net + * carries/borrows to at most one, eliminating a reduction step and saving one + * each of +, >>, & and ~. + * + * def. x mod y = x - (x//y)*y for integer x,y + * def. sum = x mod 2^16 + * def. accumulator = (x >> 16) mod 2^16 + * + * The trick works as follows: subtracting exactly one u_int16_t from the + * u_int32_t x incurs at most one underflow, wrapping its upper 16-bits, the + * accumulator, to 2^16 - 1. Adding this to the 16-bit sum preserves the + * ones-complement borrow: + * + * (sum + accumulator) mod 2^16 + * = { assume underflow: accumulator := 2^16 - 1 } + * (sum + 2^16 - 1) mod 2^16 + * = { mod } + * (sum - 1) mod 2^16 + * + * Although this breaks for sum = 0, giving 0xffff, which is ones-complement's + * other zero, not -1, that cannot occur: the 16-bit sum cannot be underflown + * to zero as that requires subtraction of at least 2^16, which exceeds a + * single u_int16_t's range. + * + * We use the following theorem to derive the implementation: + * + * th. (x + (y mod z)) mod z = (x + y) mod z (0) + * proof. + * (x + (y mod z)) mod z + * = { def mod } + * (x + y - (y//z)*z) mod z + * = { (a + b*c) mod c = a mod c } + * (x + y) mod z [end of proof] + * + * ... and thereby obtain: + * + * (sum + accumulator) mod 2^16 + * = { def. accumulator, def. sum } + * (x mod 2^16 + (x >> 16) mod 2^16) mod 2^16 + * = { (0), twice } + * (x + (x >> 16)) mod 2^16 + * = { x mod 2^n = x & (2^n - 1) } + * (x + (x >> 16)) & 0xffff + * + * Note: this serves also as a reduction step for at most one add (as the + * trailing mod 2^16 prevents further reductions by destroying carries). + */ +static __inline void +pf_cksum_fixup(u_int16_t *cksum, u_int16_t was, u_int16_t now, + u_int8_t proto) +{ + u_int32_t x; + const int udp = proto == IPPROTO_UDP; + + x = *cksum + was - now; + x = (x + (x >> 16)) & 0xffff; + + /* optimise: eliminate a branch when not udp */ + if (udp && *cksum == 0x0000) + return; + if (udp && x == 0x0000) + x = 0xffff; + + *cksum = (u_int16_t)(x); +} + +/* pre: coverage(cksum) is superset of coverage(covered_cksum) */ +static __inline void +pf_cksum_uncover(u_int16_t *cksum, u_int16_t covered_cksum, u_int8_t proto) +{ + pf_cksum_fixup(cksum, ~covered_cksum, 0x0, proto); +} + +/* pre: disjoint(coverage(cksum), coverage(uncovered_cksum)) */ +static __inline void +pf_cksum_cover(u_int16_t *cksum, u_int16_t uncovered_cksum, u_int8_t proto) +{ + pf_cksum_fixup(cksum, 0x0, ~uncovered_cksum, proto); +} + +/* pre: *a is 16-bit aligned within its packet + * + * This algorithm emulates 16-bit ones-complement sums on a twos-complement + * machine by conserving ones-complement's otherwise discarded carries in the + * upper bits of x. These accumulated carries when added to the lower 16-bits + * over at least zero 'reduction' steps then complete the ones-complement sum. + * + * def. sum = x mod 2^16 + * def. accumulator = (x >> 16) + * + * At most two reduction steps + * + * x := sum + accumulator + * = { def sum, def accumulator } + * x := x mod 2^16 + (x >> 16) + * = { x mod 2^n = x & (2^n - 1) } + * x := (x & 0xffff) + (x >> 16) + * + * are necessary to incorporate the accumulated carries (at most one per add) + * i.e. to reduce x < 2^16 from at most 16 carries in the upper 16 bits. + * + * The function is also invariant over the endian of the host. Why? + * + * Define the unary transpose operator ~ on a bitstring in python slice + * notation as lambda m: m[P:] + m[:P] , for some constant pivot P. + * + * th. ~ distributes over ones-complement addition, denoted by +_1, i.e. + * + * ~m +_1 ~n = ~(m +_1 n) (for all bitstrings m,n of equal length) + * + * proof. Regard the bitstrings in m +_1 n as split at P, forming at most two + * 'half-adds'. Under ones-complement addition, each half-add carries to the + * other, so the sum of each half-add is unaffected by their relative + * order. Therefore: + * + * ~m +_1 ~n + * = { half-adds invariant under transposition } + * ~s + * = { substitute } + * ~(m +_1 n) [end of proof] + * + * th. Summing two in-memory ones-complement 16-bit variables m,n on a machine + * with the converse endian does not alter the result. + * + * proof. + * { converse machine endian: load/store transposes, P := 8 } + * ~(~m +_1 ~n) + * = { ~ over +_1 } + * ~~m +_1 ~~n + * = { ~ is an involution } + * m +_1 n [end of proof] + * + */ +#define NEG(x) ((u_int16_t)~(x)) +void +pf_cksum_fixup_a(u_int16_t *cksum, const struct pf_addr *a, + const struct pf_addr *an, sa_family_t af, u_int8_t proto) +{ + u_int32_t x; + const u_int16_t *n = an->addr16; + const u_int16_t *o = a->addr16; + const int udp = proto == IPPROTO_UDP; + + switch (af) { + case AF_INET: + x = *cksum + o[0] + NEG(n[0]) + o[1] + NEG(n[1]); + break; +#ifdef INET6 + case AF_INET6: + x = *cksum + o[0] + NEG(n[0]) + o[1] + NEG(n[1]) +\ + o[2] + NEG(n[2]) + o[3] + NEG(n[3]) +\ + o[4] + NEG(n[4]) + o[5] + NEG(n[5]) +\ + o[6] + NEG(n[6]) + o[7] + NEG(n[7]); + break; +#endif /* INET6 */ + default: + unhandled_af(af); + } + + x = (x & 0xffff) + (x >> 16); + x = (x & 0xffff) + (x >> 16); + + /* optimise: eliminate a branch when not udp */ + if (udp && *cksum == 0x0000) + return; + if (udp && x == 0x0000) + x = 0xffff; + + *cksum = (u_int16_t)(x); +} + +void +pf_patch_8(struct pf_pdesc *pd, u_int8_t *f, u_int8_t v, bool hi) +{ + u_int16_t new = htons(hi ? ( v << 8) : v); + u_int16_t old = htons(hi ? (*f << 8) : *f); + + pf_cksum_fixup(pd->pcksum, old, new, pd->proto); + *f = v; +} + +/* pre: *f is 16-bit aligned within its packet */ +void +pf_patch_16(struct pf_pdesc *pd, u_int16_t *f, u_int16_t v) +{ + pf_cksum_fixup(pd->pcksum, *f, v, pd->proto); + *f = v; +} + +void +pf_patch_16_unaligned(struct pf_pdesc *pd, void *f, u_int16_t v, bool hi) +{ + u_int8_t *fb = (u_int8_t*)f; + u_int8_t *vb = (u_int8_t*)&v; + + if (hi && ALIGNED_POINTER(f, u_int16_t)) { + pf_patch_16(pd, f, v); /* optimise */ + return; + } + + pf_patch_8(pd, fb++, *vb++, hi); + pf_patch_8(pd, fb++, *vb++,!hi); +} + +/* pre: *f is 16-bit aligned within its packet */ void -pf_change_ap(struct pf_pdesc *pd, struct pf_addr *a, u_int16_t *p, +pf_patch_32(struct pf_pdesc *pd, u_int32_t *f, u_int32_t v) +{ + u_int16_t *pc = pd->pcksum; + + pf_cksum_fixup(pc, *f / (1 << 16), v / (1 << 16), pd->proto); + pf_cksum_fixup(pc, *f % (1 << 16), v % (1 << 16), pd->proto); + *f = v; +} + +void +pf_translate_ap(struct pf_pdesc *pd, struct pf_addr *a, u_int16_t *p, struct pf_addr *an, u_int16_t pn) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); if (pd->af == pd->naf) - PF_ACPY(a, an, pd->naf); + pf_translate_a(pd, a, an); if (p != NULL) - *p = pn; + pf_patch_16(pd, p, pn); } -/* Changes a u_int32_t. Uses a void * so there are no align restrictions */ void -pf_change_a(struct pf_pdesc *pd, void *a, u_int32_t an) +pf_patch_32_unaligned(struct pf_pdesc *pd, void *f, u_int32_t v, bool hi) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); - memcpy(a, &an, sizeof(u_int32_t)); + u_int8_t *fb = (u_int8_t*)f; + u_int8_t *vb = (u_int8_t*)&v; + + if (hi && ALIGNED_POINTER(f, u_int32_t)) { + pf_patch_32(pd, f, v); /* optimise */ + return; + } + + pf_patch_8(pd, fb++, *vb++, hi); + pf_patch_8(pd, fb++, *vb++,!hi); + pf_patch_8(pd, fb++, *vb++, hi); + pf_patch_8(pd, fb++, *vb++,!hi); } int @@ -1898,50 +2127,96 @@ pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type, int *icmp_dir, } void -pf_change_icmp(struct pf_pdesc *pd, struct pf_addr *ia, u_int16_t *ip, +pf_translate_icmp(struct pf_pdesc *pd, struct pf_addr *qa, u_int16_t *qp, struct pf_addr *oa, struct pf_addr *na, u_int16_t np) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); + /* note: doesn't trouble to fixup quoted checksums, if any */ - /* Change inner protocol port */ - if (ip != NULL) - *ip = np; + /* change quoted protocol port */ + if (qp != NULL) + pf_patch_16(pd, qp, np); - /* Change inner ip address */ - PF_ACPY(ia, na, pd->af); + /* change quoted ip address */ + pf_cksum_fixup_a(pd->pcksum, qa, na, pd->af, pd->proto); + PF_ACPY(qa, na, pd->af); - /* Outer ip address, fix outer icmpv6 checksum, if necessary. */ + /* change network-header's ip address */ if (oa) - PF_ACPY(oa, na, pd->af); + pf_translate_a(pd, oa, na); +} + +/* pre: *a is 16-bit aligned within its packet */ +/* *a is a network header src/dst address */ +void +pf_translate_a(struct pf_pdesc *pd, struct pf_addr *a, struct pf_addr *an) +{ + /* fixup transport pseudo-header, if any */ + switch (pd->proto) { + case IPPROTO_TCP: /* FALLTHROUGH */ + case IPPROTO_UDP: /* FALLTHROUGH */ + case IPPROTO_ICMPV6: + pf_cksum_fixup_a(pd->pcksum, a, an, pd->af, pd->proto); + break; + default: + break; /* assume no pseudo-header */ + } + + PF_ACPY(a, an, pd->af); } #if INET6 int pf_translate_af(struct pf_pdesc *pd) { - struct mbuf *mp; - struct ip *ip4; - struct ip6_hdr *ip6; - struct icmp6_hdr *icmp; - int hlen; - - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); - - hlen = pd->naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + static const struct pf_addr zero; + struct ip *ip4; + struct ip6_hdr *ip6; + int copyback = 0; + u_int hlen, ohlen, dlen; + u_int16_t *pc; + u_int8_t af_proto, naf_proto; + + hlen = (pd->naf == AF_INET) ? sizeof(*ip4) : sizeof(*ip6); + ohlen = pd->off; + dlen = pd->tot_len - pd->off; + pc = pd->pcksum; + + af_proto = naf_proto = pd->proto; + if (naf_proto == IPPROTO_ICMP) + af_proto = IPPROTO_ICMPV6; + if (naf_proto == IPPROTO_ICMPV6) + af_proto = IPPROTO_ICMP; + + /* uncover stale pseudo-header */ + switch (af_proto) { + case IPPROTO_ICMPV6: + /* optimise: unchanged for TCP/UDP */ + pf_cksum_fixup(pc, htons(af_proto), 0x0, af_proto); + pf_cksum_fixup(pc, htons(dlen), 0x0, af_proto); + /* FALLTHROUGH */ + case IPPROTO_UDP: /* FALLTHROUGH */ + case IPPROTO_TCP: + pf_cksum_fixup_a(pc, pd->src, &zero, pd->af, af_proto); + pf_cksum_fixup_a(pc, pd->dst, &zero, pd->af, af_proto); + copyback = 1; + break; + default: + break; /* assume no pseudo-header */ + } - /* trim the old header */ + /* replace the network header */ m_adj(pd->m, pd->off); + pd->src = NULL; + pd->dst = NULL; - /* prepend a new one */ if ((M_PREPEND(pd->m, hlen, M_DONTWAIT)) == NULL) { pd->m = NULL; return (-1); } + pd->off = hlen; + pd->tot_len += hlen - ohlen; + switch (pd->naf) { case AF_INET: ip4 = mtod(pd->m, struct ip *); @@ -1949,7 +2224,7 @@ pf_translate_af(struct pf_pdesc *pd) ip4->ip_v = IPVERSION; ip4->ip_hl = hlen >> 2; ip4->ip_tos = pd->tos; - ip4->ip_len = htons(hlen + (pd->tot_len - pd->off)); + ip4->ip_len = htons(hlen + dlen); ip4->ip_id = htons(ip_randomid()); ip4->ip_off = htons(IP_DF); ip4->ip_ttl = pd->ttl; @@ -1962,7 +2237,7 @@ pf_translate_af(struct pf_pdesc *pd) bzero(ip6, hlen); ip6->ip6_vfc = IPV6_VERSION; ip6->ip6_flow |= htonl((u_int32_t)pd->tos << 20); - ip6->ip6_plen = htons(pd->tot_len - pd->off); + ip6->ip6_plen = htons(dlen); ip6->ip6_nxt = pd->proto; if (!pd->ttl || pd->ttl > IPV6_DEFHLIM) ip6->ip6_hlim = IPV6_DEFHLIM; @@ -1972,70 +2247,77 @@ pf_translate_af(struct pf_pdesc *pd) ip6->ip6_dst = pd->ndaddr.v6; break; default: - return (-1); + unhandled_af(pd->naf); } - /* recalculate icmp/icmp6 checksums */ - if (pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6) { - int off; - if ((mp = m_pulldown(pd->m, hlen, sizeof(*icmp), &off)) == - NULL) { - pd->m = NULL; - return (-1); - } - icmp = (struct icmp6_hdr *)(mp->m_data + off); - icmp->icmp6_cksum = 0; - icmp->icmp6_cksum = pd->naf == AF_INET ? - in4_cksum(pd->m, 0, hlen, ntohs(ip4->ip_len) - hlen) : - in6_cksum(pd->m, IPPROTO_ICMPV6, hlen, - ntohs(ip6->ip6_plen)); + /* UDP over IPv6 must be checksummed per rfc2460 p27 */ + if (naf_proto == IPPROTO_UDP && *pc == 0x0000 && + pd->naf == AF_INET6) { + pd->m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; + } + + /* cover fresh pseudo-header */ + switch (naf_proto) { + case IPPROTO_ICMPV6: + /* optimise: unchanged for TCP/UDP */ + pf_cksum_fixup(pc, 0x0, htons(naf_proto), naf_proto); + pf_cksum_fixup(pc, 0x0, htons(dlen), naf_proto); + /* FALLTHROUGH */ + case IPPROTO_UDP: /* FALLTHROUGH */ + case IPPROTO_TCP: + pf_cksum_fixup_a(pc, &zero, &pd->nsaddr, pd->naf, naf_proto); + pf_cksum_fixup_a(pc, &zero, &pd->ndaddr, pd->naf, naf_proto); + copyback = 1; + break; + default: + break; /* assume no pseudo-header */ } + /* flush pd->pcksum */ + if (copyback) + m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any, M_NOWAIT); + return (0); } int -pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, +pf_change_icmp_af(struct mbuf *m, int ipoff2, struct pf_pdesc *pd, struct pf_pdesc *pd2, struct pf_addr *src, struct pf_addr *dst, sa_family_t af, sa_family_t naf) { struct mbuf *n = NULL; struct ip *ip4; struct ip6_hdr *ip6; - int hlen, olen, mlen; - - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); + u_int hlen, ohlen, d; if (af == naf || (af != AF_INET && af != AF_INET6) || (naf != AF_INET && naf != AF_INET6)) return (-1); - /* split the mbuf chain on the inner ip/ip6 header boundary */ - if ((n = m_split(m, off, M_DONTWAIT)) == NULL) + /* split the mbuf chain on the quoted ip/ip6 header boundary */ + if ((n = m_split(m, ipoff2, M_DONTWAIT)) == NULL) return (-1); - /* old header */ - olen = pd2->off - off; - /* new header */ + /* new quoted header */ hlen = naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + /* old quoted header */ + ohlen = pd2->off - ipoff2; - /* trim old header */ - m_adj(n, olen); + /* trim old quoted header */ + pf_cksum_uncover(pd->pcksum, in_cksum(n, ohlen), pd->proto); + m_adj(n, ohlen); - /* prepend a new one */ + /* prepend a new, translated, quoted header */ if ((M_PREPEND(n, hlen, M_DONTWAIT)) == NULL) return (-1); - /* translate inner ip/ip6 header */ switch (naf) { case AF_INET: ip4 = mtod(n, struct ip *); bzero(ip4, sizeof(*ip4)); ip4->ip_v = IPVERSION; ip4->ip_hl = sizeof(*ip4) >> 2; - ip4->ip_len = htons(sizeof(*ip4) + pd2->tot_len - olen); + ip4->ip_len = htons(sizeof(*ip4) + pd2->tot_len - ohlen); ip4->ip_id = htons(ip_randomid()); ip4->ip_off = htons(IP_DF); ip4->ip_ttl = pd2->ttl; @@ -2051,7 +2333,7 @@ pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, ip6 = mtod(n, struct ip6_hdr *); bzero(ip6, sizeof(*ip6)); ip6->ip6_vfc = IPV6_VERSION; - ip6->ip6_plen = htons(pd2->tot_len - olen); + ip6->ip6_plen = htons(pd2->tot_len - ohlen); if (pd2->proto == IPPROTO_ICMP) ip6->ip6_nxt = IPPROTO_ICMPV6; else @@ -2065,14 +2347,35 @@ pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, break; } - /* adjust payload offset and total packet length */ - pd2->off += hlen - olen; - pd->tot_len += hlen - olen; + /* cover new quoted header */ + /* optimise: any new AF_INET header of ours sums to zero */ + if (naf != AF_INET) { + pf_cksum_cover(pd->pcksum, in_cksum(n, hlen), pd->proto); + } + + /* reattach modified quoted packet to outer header */ + { + int nlen = n->m_pkthdr.len; + m_cat(m, n); + m->m_pkthdr.len += nlen; + } - /* merge modified inner packet with the original header */ - mlen = n->m_pkthdr.len; - m_cat(m, n); - m->m_pkthdr.len += mlen; + /* account for altered length */ + d = hlen - ohlen; + + if (pd->proto == IPPROTO_ICMPV6) { + /* fixup pseudo-header */ + int dlen = pd->tot_len - pd->off; + pf_cksum_fixup(pd->pcksum, + htons(dlen), htons(dlen + d), pd->proto); + } + + pd->tot_len += d; + pd2->tot_len += d; + pd2->off += d; + + /* note: not bothering to update network headers as + these due for rewrite by pf_translate_af() */ return (0); } @@ -2082,7 +2385,7 @@ pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, #define PTR_IP6(field) (offsetof(struct ip6_hdr, field)) int -pf_translate_icmp_af(int af, void *arg) +pf_translate_icmp_af(struct pf_pdesc *pd, int af, void *arg) { struct icmp *icmp4; struct icmp6_hdr *icmp6; @@ -2170,13 +2473,16 @@ pf_translate_icmp_af(int af, void *arg) default: return (-1); } - icmp6->icmp6_type = type; - icmp6->icmp6_code = code; + + pf_patch_8(pd, &icmp6->icmp6_type, type, PF_HI); + pf_patch_8(pd, &icmp6->icmp6_code, code, PF_LO); + /* aligns well with a icmpv4 nextmtu */ - icmp6->icmp6_mtu = htonl(mtu); + pf_patch_32(pd, &icmp6->icmp6_mtu, htonl(mtu)); + /* icmpv4 pptr is a one most significant byte */ if (ptr >= 0) - icmp6->icmp6_pptr = htonl(ptr << 24); + pf_patch_32(pd, &icmp6->icmp6_pptr, htonl(ptr << 24)); break; case AF_INET6: icmp4 = arg; @@ -2265,11 +2571,12 @@ pf_translate_icmp_af(int af, void *arg) default: return (-1); } - icmp4->icmp_type = type; - icmp4->icmp_code = code; - icmp4->icmp_nextmtu = htons(mtu); + + pf_patch_8(pd, &icmp4->icmp_type, type, PF_HI); + pf_patch_8(pd, &icmp4->icmp_code, code, PF_LO); + pf_patch_16(pd, &icmp4->icmp_nextmtu, htons(mtu)); if (ptr >= 0) - icmp4->icmp_void = htonl(ptr); + pf_patch_32(pd, &icmp4->icmp_void, htonl(ptr)); break; } @@ -2310,13 +2617,17 @@ pf_modulate_sack(struct pf_pdesc *pd, struct pf_state_peer *dst) if (olen >= TCPOLEN_SACKLEN) { for (i = 2; i + TCPOLEN_SACK <= olen; i += TCPOLEN_SACK) { + size_t startoff = (opt + i) - opts; memcpy(&sack, &opt[i], sizeof(sack)); - pf_change_a(pd, &sack.start, + pf_patch_32_unaligned(pd, &sack.start, htonl(ntohl(sack.start) - - dst->seqdiff)); - pf_change_a(pd, &sack.end, + dst->seqdiff), + PF_ALGNMNT(startoff)); + pf_patch_32_unaligned(pd, &sack.end, htonl(ntohl(sack.end) - - dst->seqdiff)); + dst->seqdiff), + PF_ALGNMNT(startoff + + sizeof(sack.start))); memcpy(&opt[i], &sack, sizeof(sack)); } copyback = 1; @@ -3357,8 +3668,8 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm, !(th->th_flags & TH_RST)) { u_int32_t ack = ntohl(th->th_seq) + pd->p_len; - if (pf_check_proto_cksum(pd, pd->off, - pd->tot_len - pd->off, IPPROTO_TCP, pd->af)) + if (pf_check_tcp_cksum(pd->m, pd->off, + pd->tot_len - pd->off, pd->af)) REASON_SET(reason, PFRES_PROTCKSUM); else { if (th->th_flags & TH_SYN) @@ -3453,7 +3764,6 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm, /* copy back packet headers if needed */ if (rewrite && pd->hdrlen) { - pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any, M_NOWAIT); } @@ -3539,8 +3849,8 @@ pf_create_state(struct pf_pdesc *pd, struct pf_rule *r, struct pf_rule *a, if ((s->src.seqdiff = pf_tcp_iss(pd) - s->src.seqlo) == 0) s->src.seqdiff = 1; - pf_change_a(pd, &th->th_seq, - htonl(s->src.seqlo + s->src.seqdiff)); + pf_patch_32(pd, + &th->th_seq, htonl(s->src.seqlo + s->src.seqdiff)); *rewrite = 1; } else s->src.seqdiff = 0; @@ -3699,12 +4009,12 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, case IPPROTO_TCP: if (afto || PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) { - pf_change_ap(pd, pd->src, pd->sport, saddr, sport); + pf_translate_ap(pd, pd->src, pd->sport, saddr, sport); rewrite = 1; } if (afto || PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) { - pf_change_ap(pd, pd->dst, pd->dport, daddr, dport); + pf_translate_ap(pd, pd->dst, pd->dport, daddr, dport); rewrite = 1; } break; @@ -3712,12 +4022,12 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, case IPPROTO_UDP: if (afto || PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) { - pf_change_ap(pd, pd->src, pd->sport, saddr, sport); + pf_translate_ap(pd, pd->src, pd->sport, saddr, sport); rewrite = 1; } if (afto || PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) { - pf_change_ap(pd, pd->dst, pd->dport, daddr, dport); + pf_translate_ap(pd, pd->dst, pd->dport, daddr, dport); rewrite = 1; } break; @@ -3729,20 +4039,18 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, if (afto) { #ifdef INET6 - if (pf_translate_icmp_af(AF_INET6, pd->hdr.icmp)) + if (pf_translate_icmp_af(pd, AF_INET6, pd->hdr.icmp)) return (0); pd->proto = IPPROTO_ICMPV6; rewrite = 1; #endif /* INET6 */ } else { if (PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a(pd, &pd->src->v4.s_addr, - saddr->v4.s_addr); + pf_translate_a(pd, pd->src, saddr); rewrite = 1; } if (PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a(pd, &pd->dst->v4.s_addr, - daddr->v4.s_addr); + pf_translate_a(pd, pd->dst, daddr); rewrite = 1; } } @@ -3750,11 +4058,8 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport; if (icmpid != pd->hdr.icmp->icmp_id) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, - pd->tot_len - pd->off, pd->proto, - pd->af); - pd->hdr.icmp->icmp_id = icmpid; + pf_patch_16(pd, + &pd->hdr.icmp->icmp_id, icmpid); rewrite = 1; } } @@ -3767,18 +4072,17 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, return (0); if (afto) { - /* ip_sum will be recalculated in pf_translate_af */ - if (pf_translate_icmp_af(AF_INET, pd->hdr.icmp6)) + if (pf_translate_icmp_af(pd, AF_INET, pd->hdr.icmp6)) return (0); pd->proto = IPPROTO_ICMP; rewrite = 1; } else { if (PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_ap(pd, pd->src, NULL, saddr, 0); + pf_translate_ap(pd, pd->src, NULL, saddr, 0); rewrite = 1; } if (PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_ap(pd, pd->dst, NULL, daddr, 0); + pf_translate_ap(pd, pd->dst, NULL, daddr, 0); rewrite = 1; } } @@ -3786,11 +4090,8 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport; if (icmpid != pd->hdr.icmp6->icmp6_id) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, - pd->tot_len - pd->off, pd->proto, - pd->af); - pd->hdr.icmp6->icmp6_id = icmpid; + pf_patch_16(pd, + &pd->hdr.icmp6->icmp6_id, icmpid); rewrite = 1; } } @@ -3801,24 +4102,22 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, switch (pd->af) { case AF_INET: if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a(pd, &pd->src->v4.s_addr, - saddr->v4.s_addr); + pf_translate_a(pd, pd->src, saddr); rewrite = 1; } if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a(pd, &pd->dst->v4.s_addr, - daddr->v4.s_addr); + pf_translate_a(pd, pd->dst, daddr); rewrite = 1; } break; #ifdef INET6 case AF_INET6: if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_ap(pd, pd->src, NULL, saddr, 0); + pf_translate_ap(pd, pd->src, NULL, saddr, 0); rewrite = 1; } if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_ap(pd, pd->dst, NULL, daddr, 0); + pf_translate_ap(pd, pd->dst, NULL, daddr, 0); rewrite = 1; } break; @@ -3869,8 +4168,8 @@ pf_tcp_track_full(struct pf_pdesc *pd, struct pf_state_peer *src, while ((src->seqdiff = arc4random() - seq) == 0) continue; ack = ntohl(th->th_ack) - dst->seqdiff; - pf_change_a(pd, &th->th_seq, htonl(seq + src->seqdiff)); - pf_change_a(pd, &th->th_ack, htonl(ack)); + pf_patch_32(pd, &th->th_seq, htonl(seq + src->seqdiff)); + pf_patch_32(pd, &th->th_ack, htonl(ack)); *copyback = 1; } else { ack = ntohl(th->th_ack); @@ -3921,8 +4220,8 @@ pf_tcp_track_full(struct pf_pdesc *pd, struct pf_state_peer *src, ack = ntohl(th->th_ack) - dst->seqdiff; if (src->seqdiff) { /* Modulate sequence numbers */ - pf_change_a(pd, &th->th_seq, htonl(seq + src->seqdiff)); - pf_change_a(pd, &th->th_ack, htonl(ack)); + pf_patch_32(pd, &th->th_seq, htonl(seq + src->seqdiff)); + pf_patch_32(pd, &th->th_ack, htonl(ack)); *copyback = 1; } end = seq + pd->p_len; @@ -4440,7 +4739,7 @@ pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) || nk->port[sidx] != pd->osport) - pf_change_ap(pd, pd->src, pd->sport, + pf_translate_ap(pd, pd->src, pd->sport, &nk->addr[sidx], nk->port[sidx]); if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || @@ -4449,7 +4748,7 @@ pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || nk->port[didx] != pd->odport) - pf_change_ap(pd, pd->dst, pd->dport, + pf_translate_ap(pd, pd->dst, pd->dport, &nk->addr[didx], nk->port[didx]); pd->m->m_pkthdr.ph_rtableid = nk->rdomain; @@ -4457,7 +4756,6 @@ pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) } if (copyback && pd->hdrlen > 0) { - pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any, M_NOWAIT); } @@ -4518,7 +4816,6 @@ int pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) { - struct pf_addr *saddr = pd->src, *daddr = pd->dst; u_int16_t virtual_id, virtual_type; u_int8_t icmptype; int icmp_dir, iidx, ret, copyback = 0; @@ -4580,7 +4877,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, case AF_INET: #ifdef INET6 if (afto) { - if (pf_translate_icmp_af(AF_INET6, + if (pf_translate_icmp_af(pd, AF_INET6, pd->hdr.icmp)) return (PF_DROP); pd->proto = IPPROTO_ICMPV6; @@ -4588,22 +4885,19 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, #endif /* INET6 */ if (!afto && PF_ANEQ(pd->src, &nk->addr[sidx], AF_INET)) - pf_change_a(pd, &saddr->v4.s_addr, - nk->addr[sidx].v4.s_addr); + pf_translate_a(pd, + pd->src, &nk->addr[sidx]); if (!afto && PF_ANEQ(pd->dst, &nk->addr[didx], AF_INET)) { - pf_change_a(pd, &daddr->v4.s_addr, - nk->addr[didx].v4.s_addr); + pf_translate_a(pd, + pd->dst, &nk->addr[didx]); pd->destchg = 1; } if (nk->port[iidx] != pd->hdr.icmp->icmp_id) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, - pd->off, pd->tot_len - - pd->off, pd->proto, pd->af); - pd->hdr.icmp->icmp_id = nk->port[iidx]; + pf_patch_16(pd, &pd->hdr.icmp->icmp_id, + nk->port[iidx]); } m_copyback(pd->m, pd->off, ICMP_MINLEN, @@ -4613,30 +4907,27 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, #ifdef INET6 case AF_INET6: if (afto) { - if (pf_translate_icmp_af(AF_INET, + if (pf_translate_icmp_af(pd, AF_INET, pd->hdr.icmp6)) return (PF_DROP); pd->proto = IPPROTO_ICMP; } if (!afto && PF_ANEQ(pd->src, &nk->addr[sidx], AF_INET6)) - pf_change_ap(pd, saddr, NULL, + pf_translate_ap(pd, pd->src, NULL, &nk->addr[sidx], 0); if (!afto && PF_ANEQ(pd->dst, &nk->addr[didx], AF_INET6)) { - pf_change_ap(pd, daddr, NULL, + pf_translate_ap(pd, pd->dst, NULL, &nk->addr[didx], 0); pd->destchg = 1; } if (nk->port[iidx] != pd->hdr.icmp6->icmp6_id) { - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, - pd->off, pd->tot_len - - pd->off, pd->proto, pd->af); - pd->hdr.icmp6->icmp6_id = - nk->port[iidx]; + pf_patch_16(pd, + &pd->hdr.icmp6->icmp6_id, + nk->port[iidx]); } m_copyback(pd->m, pd->off, @@ -4785,7 +5076,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, /* Demodulate sequence number */ seq = ntohl(th.th_seq) - src->seqdiff; if (src->seqdiff) { - pf_change_a(pd, &th.th_seq, htonl(seq)); + pf_patch_32(pd, &th.th_seq, htonl(seq)); copyback = 1; } @@ -4836,7 +5127,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, #ifdef INET6 if (afto) { - if (pf_translate_icmp_af(nk->af, + if (pf_translate_icmp_af(pd, nk->af, pd->hdr.icmp)) return (PF_DROP); m_copyback(pd->m, pd->off, @@ -4859,10 +5150,12 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, pd, &pd2, &nk->addr[sidx], &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); - pf_change_ap(pd, pd2.src, &th.th_sport, + pf_translate_ap(pd, + pd2.src, &th.th_sport, &nk->addr[pd2.sidx], nk->port[sidx]); - pf_change_ap(pd, pd2.dst, &th.th_dport, + pf_translate_ap(pd, + pd2.dst, &th.th_dport, &nk->addr[pd2.didx], nk->port[didx]); m_copyback(pd2.m, pd2.off, 8, &th, @@ -4873,8 +5166,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || nk->port[pd2.sidx] != th.th_sport) - pf_change_icmp(pd, pd2.src, - &th.th_sport, daddr, + pf_translate_icmp(pd, pd2.src, + &th.th_sport, pd->dst, &nk->addr[pd2.sidx], nk->port[pd2.sidx]); @@ -4886,8 +5179,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || nk->port[pd2.didx] != th.th_dport) - pf_change_icmp(pd, pd2.dst, - &th.th_dport, saddr, + pf_translate_icmp(pd, pd2.dst, + &th.th_dport, pd->src, &nk->addr[pd2.didx], nk->port[pd2.didx]); copyback = 1; @@ -4952,7 +5245,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, #ifdef INET6 if (afto) { - if (pf_translate_icmp_af(nk->af, + if (pf_translate_icmp_af(pd, nk->af, pd->hdr.icmp)) return (PF_DROP); m_copyback(pd->m, pd->off, @@ -4975,10 +5268,12 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, pd, &pd2, &nk->addr[sidx], &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); - pf_change_ap(pd, pd2.src, &uh.uh_sport, + pf_translate_ap(pd, + pd2.src, &uh.uh_sport, &nk->addr[pd2.sidx], nk->port[sidx]); - pf_change_ap(pd, pd2.dst, &uh.uh_dport, + pf_translate_ap(pd, + pd2.dst, &uh.uh_dport, &nk->addr[pd2.didx], nk->port[didx]); m_copyback(pd2.m, pd2.off, sizeof(uh), @@ -4990,8 +5285,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || nk->port[pd2.sidx] != uh.uh_sport) - pf_change_icmp(pd, pd2.src, - &uh.uh_sport, daddr, + pf_translate_icmp(pd, pd2.src, + &uh.uh_sport, pd->dst, &nk->addr[pd2.sidx], nk->port[pd2.sidx]); @@ -5003,8 +5298,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || nk->port[pd2.didx] != uh.uh_dport) - pf_change_icmp(pd, pd2.dst, - &uh.uh_dport, saddr, + pf_translate_icmp(pd, pd2.dst, + &uh.uh_dport, pd->src, &nk->addr[pd2.didx], nk->port[pd2.didx]); @@ -5025,7 +5320,10 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, break; #endif /* INET6 */ } - uh.uh_sum = 0; + /* Avoid recomputing quoted UDP checksum. + * note: udp6 0 csum invalid per rfc2460 p27. + * but presumed nothing cares in this context */ + pf_patch_16(pd, &uh.uh_sum, 0); m_copyback(pd2.m, pd2.off, sizeof(uh), &uh, M_NOWAIT); copyback = 1; @@ -5076,7 +5374,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (afto) { if (nk->af != AF_INET6) return (PF_DROP); - if (pf_translate_icmp_af(nk->af, + if (pf_translate_icmp_af(pd, nk->af, pd->hdr.icmp)) return (PF_DROP); m_copyback(pd->m, pd->off, @@ -5087,11 +5385,13 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); pd->proto = IPPROTO_ICMPV6; - if (pf_translate_icmp_af(nk->af, &iih)) + if (pf_translate_icmp_af(pd, + nk->af, &iih)) return (PF_DROP); if (virtual_type == htons(ICMP_ECHO) && nk->port[iidx] != iih.icmp_id) - iih.icmp_id = nk->port[iidx]; + pf_patch_16(pd, &iih.icmp_id, + nk->port[iidx]); m_copyback(pd2.m, pd2.off, ICMP_MINLEN, &iih, M_NOWAIT); pd->m->m_pkthdr.ph_rtableid = @@ -5110,10 +5410,10 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, &nk->addr[pd2.sidx], pd2.af) || (virtual_type == htons(ICMP_ECHO) && nk->port[iidx] != iih.icmp_id)) - pf_change_icmp(pd, pd2.src, + pf_translate_icmp(pd, pd2.src, (virtual_type == htons(ICMP_ECHO)) ? &iih.icmp_id : NULL, - daddr, &nk->addr[pd2.sidx], + pd->dst, &nk->addr[pd2.sidx], (virtual_type == htons(ICMP_ECHO)) ? nk->port[iidx] : 0); @@ -5124,8 +5424,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af)) - pf_change_icmp(pd, pd2.dst, NULL, - saddr, &nk->addr[pd2.didx], 0); + pf_translate_icmp(pd, pd2.dst, NULL, + pd->src, &nk->addr[pd2.didx], 0); m_copyback(pd->m, pd->off, ICMP_MINLEN, pd->hdr.icmp, M_NOWAIT); @@ -5186,7 +5486,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (afto) { if (nk->af != AF_INET) return (PF_DROP); - if (pf_translate_icmp_af(nk->af, + if (pf_translate_icmp_af(pd, nk->af, pd->hdr.icmp)) return (PF_DROP); m_copyback(pd->m, pd->off, @@ -5197,12 +5497,14 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, &nk->addr[didx], pd->af, nk->af)) return (PF_DROP); pd->proto = IPPROTO_ICMP; - if (pf_translate_icmp_af(nk->af, &iih)) + if (pf_translate_icmp_af(pd, + nk->af, &iih)) return (PF_DROP); if (virtual_type == htons(ICMP6_ECHO_REQUEST) && nk->port[iidx] != iih.icmp6_id) - iih.icmp6_id = nk->port[iidx]; + pf_patch_16(pd, &iih.icmp6_id, + nk->port[iidx]); m_copyback(pd2.m, pd2.off, sizeof(struct icmp6_hdr), &iih, M_NOWAIT); @@ -5222,11 +5524,11 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, ((virtual_type == htons(ICMP6_ECHO_REQUEST)) && nk->port[pd2.sidx] != iih.icmp6_id)) - pf_change_icmp(pd, pd2.src, + pf_translate_icmp(pd, pd2.src, (virtual_type == htons(ICMP6_ECHO_REQUEST)) ? &iih.icmp6_id : NULL, - daddr, &nk->addr[pd2.sidx], + pd->dst, &nk->addr[pd2.sidx], (virtual_type == htons(ICMP6_ECHO_REQUEST)) ? nk->port[iidx] : 0); @@ -5238,8 +5540,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af)) - pf_change_icmp(pd, pd2.dst, NULL, - saddr, &nk->addr[pd2.didx], 0); + pf_translate_icmp(pd, pd2.dst, NULL, + pd->src, &nk->addr[pd2.didx], 0); m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, @@ -5271,8 +5573,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af)) - pf_change_icmp(pd, pd2.src, NULL, - daddr, &nk->addr[pd2.sidx], 0); + pf_translate_icmp(pd, pd2.src, NULL, + pd->dst, &nk->addr[pd2.sidx], 0); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5281,8 +5583,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af)) - pf_change_icmp(pd, pd2.dst, NULL, - saddr, &nk->addr[pd2.didx], 0); + pf_translate_icmp(pd, pd2.dst, NULL, + pd->src, &nk->addr[pd2.didx], 0); switch (pd2.af) { case AF_INET: @@ -5308,7 +5610,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, } } if (copyback) { - pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any, M_NOWAIT); } @@ -5781,105 +6082,55 @@ bad: /* - * check protocol (tcp/udp/icmp/icmp6) checksum and set mbuf flag + * check TCP checksum and set mbuf flag * off is the offset where the protocol header starts * len is the total length of protocol header plus payload * returns 0 when the checksum is valid, otherwise returns 1. * if the _OUT flag is set the checksum isn't done yet, consider these ok */ int -pf_check_proto_cksum(struct pf_pdesc *pd, int off, int len, u_int8_t p, - sa_family_t af) +pf_check_tcp_cksum(struct mbuf *m, int off, int len, sa_family_t af) { - u_int16_t flag_ok, flag_bad, flag_out; u_int16_t sum; - if (pd->csum_status == PF_CSUM_OK) - return (0); - if (pd->csum_status == PF_CSUM_BAD) - return (1); - - switch (p) { - case IPPROTO_TCP: - flag_ok = M_TCP_CSUM_IN_OK; - flag_out = M_TCP_CSUM_OUT; - flag_bad = M_TCP_CSUM_IN_BAD; - break; - case IPPROTO_UDP: - flag_ok = M_UDP_CSUM_IN_OK; - flag_out = M_UDP_CSUM_OUT; - flag_bad = M_UDP_CSUM_IN_BAD; - break; - case IPPROTO_ICMP: -#ifdef INET6 - case IPPROTO_ICMPV6: -#endif /* INET6 */ - flag_ok = M_ICMP_CSUM_IN_OK; - flag_out = M_ICMP_CSUM_OUT; - flag_bad = M_ICMP_CSUM_IN_BAD; - break; - default: - return (1); - } - if (pd->m->m_pkthdr.csum_flags & (flag_ok | flag_out)) { - pd->csum_status = PF_CSUM_OK; + if (m->m_pkthdr.csum_flags & + (M_TCP_CSUM_IN_OK | M_TCP_CSUM_OUT)) { return (0); } - if (pd->m->m_pkthdr.csum_flags & flag_bad || off < sizeof(struct ip) || - pd->m->m_pkthdr.len < off + len) { - pd->csum_status = PF_CSUM_BAD; + if (m->m_pkthdr.csum_flags & M_TCP_CSUM_IN_BAD || + off < sizeof(struct ip) || + m->m_pkthdr.len < off + len) { return (1); } /* need to do it in software */ - if (p == IPPROTO_TCP) - tcpstat.tcps_inswcsum++; - else if (p == IPPROTO_UDP) - udpstat.udps_inswcsum++; + tcpstat.tcps_inswcsum++; switch (af) { case AF_INET: - if (pd->m->m_len < sizeof(struct ip)) { - pd->csum_status = PF_CSUM_BAD; + if (m->m_len < sizeof(struct ip)) return (1); - } - sum = in4_cksum(pd->m, (p == IPPROTO_ICMP ? 0 : p), off, len); + + sum = in4_cksum(m, IPPROTO_TCP, off, len); break; #ifdef INET6 case AF_INET6: - if (pd->m->m_len < sizeof(struct ip6_hdr)) { - pd->csum_status = PF_CSUM_BAD; + if (m->m_len < sizeof(struct ip6_hdr)) return (1); - } - sum = in6_cksum(pd->m, p, off, len); + + sum = in6_cksum(m, IPPROTO_TCP, off, len); break; #endif /* INET6 */ default: unhandled_af(af); } if (sum) { - switch (p) { - case IPPROTO_TCP: - tcpstat.tcps_rcvbadsum++; - break; - case IPPROTO_UDP: - udpstat.udps_badsum++; - break; - case IPPROTO_ICMP: - icmpstat.icps_checksum++; - break; -#ifdef INET6 - case IPPROTO_ICMPV6: - icmp6stat.icp6s_checksum++; - break; -#endif /* INET6 */ - } - pd->m->m_pkthdr.csum_flags |= flag_bad; - pd->csum_status = PF_CSUM_BAD; + tcpstat.tcps_rcvbadsum++; + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_BAD; return (1); } - pd->m->m_pkthdr.csum_flags |= flag_ok; - pd->csum_status = PF_CSUM_OK; + + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK; return (0); } @@ -6274,6 +6525,7 @@ pf_setup_pdesc(struct pf_pdesc *pd, void *pdhdrs, sa_family_t af, int dir, REASON_SET(reason, PFRES_SHORT); return (PF_DROP); } + pd->pcksum = &pd->hdr.icmp6->icmp6_cksum; break; } #endif /* INET6 */ @@ -6730,37 +6982,6 @@ done: return (action); } -void -pf_cksum(struct pf_pdesc *pd, struct mbuf *m) -{ - if (pd->csum_status != PF_CSUM_OK) - return; /* don't fix broken cksums */ - - switch (pd->proto) { - case IPPROTO_TCP: - pd->hdr.tcp->th_sum = 0; - m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT; - break; - case IPPROTO_UDP: - pd->hdr.udp->uh_sum = 0; - m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; - break; - case IPPROTO_ICMP: - pd->hdr.icmp->icmp_cksum = 0; - m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; - break; -#ifdef INET6 - case IPPROTO_ICMPV6: - pd->hdr.icmp6->icmp6_cksum = 0; - m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; - break; -#endif /* INET6 */ - default: - /* nothing */ - break; - } -} - int pf_ouraddr(struct mbuf *m) { diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c index 2b78bebe94f..9c999829de8 100644 --- a/sys/net/pf_norm.c +++ b/sys/net/pf_norm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_norm.c,v 1.188 2016/06/15 11:49:34 mpi Exp $ */ +/* $OpenBSD: pf_norm.c,v 1.189 2016/08/17 03:24:12 procter Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> @@ -855,10 +855,6 @@ pf_normalize_tcp(struct pf_pdesc *pd) u_int8_t flags; u_int rewrite = 0; - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); - flags = th->th_flags; if (flags & TH_SYN) { /* Illegal packet */ @@ -880,21 +876,23 @@ pf_normalize_tcp(struct pf_pdesc *pd) } /* If flags changed, or reserved data set, then adjust */ - if (flags != th->th_flags || th->th_x2 != 0) { - th->th_flags = flags; - th->th_x2 = 0; - rewrite = 1; - } + if (flags != th->th_flags || th->th_x2 != 0) { + /* hack: set 4-bit th_x2 = 0 */ + u_int8_t *th_off = (u_int8_t*)(&th->th_ack+1); + pf_patch_8(pd, th_off, th->th_off << 4, PF_HI); + + pf_patch_8(pd, &th->th_flags, flags, PF_LO); + rewrite = 1; + } /* Remove urgent pointer, if TH_URG is not set */ if (!(flags & TH_URG) && th->th_urp) { - th->th_urp = 0; + pf_patch_16(pd, &th->th_urp, 0); rewrite = 1; } /* copy back packet headers if we sanitized */ if (rewrite) { - pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT); } @@ -1008,7 +1006,7 @@ pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason, u_int32_t tsval, tsecr; u_int tsval_from_last; u_int8_t hdr[60]; - u_int8_t *opt; + u_int8_t *opts, *opt; int copyback = 0; int got_ts = 0; @@ -1049,7 +1047,7 @@ pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason, pd->af)) { /* Diddle with TCP options */ int hlen; - opt = hdr + sizeof(struct tcphdr); + opt = opts = hdr + sizeof(struct tcphdr); hlen = (th->th_off << 2) - sizeof(struct tcphdr); while (hlen >= TCPOLEN_TIMESTAMP) { switch (*opt) { @@ -1077,28 +1075,35 @@ pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason, return (PF_DROP); } if (opt[1] >= TCPOLEN_TIMESTAMP) { - memcpy(&tsval, &opt[2], - sizeof(u_int32_t)); + u_int8_t *ts = opt + 2; + u_int8_t *tsr = opt + 6; + + memcpy(&tsval, ts, sizeof(u_int32_t)); + memcpy(&tsecr, tsr, sizeof(u_int32_t)); + + /* modulate TS */ if (tsval && src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) { + /* note: tsval used further on */ tsval = ntohl(tsval); - pf_change_a(pd, &opt[2], + pf_patch_32_unaligned(pd, ts, htonl(tsval + - src->scrub->pfss_ts_mod)); + src->scrub->pfss_ts_mod), + PF_ALGNMNT(ts - opts)); copyback = 1; } - /* Modulate TS reply iff valid (!0) */ - memcpy(&tsecr, &opt[6], - sizeof(u_int32_t)); + /* modulate TS reply if any (!0) */ if (tsecr && dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP)) { + /* note: tsecr used further on */ tsecr = ntohl(tsecr) - dst->scrub->pfss_ts_mod; - pf_change_a(pd, &opt[6], - htonl(tsecr)); + pf_patch_32_unaligned(pd, tsr, + htonl(tsecr), + PF_ALGNMNT(tsr - opts)); copyback = 1; } got_ts = 1; @@ -1391,12 +1396,8 @@ pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss) u_int16_t mss; int thoff; int opt, cnt, optlen = 0; - u_char opts[MAX_TCPOPTLEN]; - u_char *optp = opts; - - if (pd->csum_status == PF_CSUM_UNKNOWN) - pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, - pd->proto, pd->af); + u_int8_t opts[MAX_TCPOPTLEN]; + u_int8_t *optp = opts; thoff = th->th_off << 2; cnt = thoff - sizeof(struct tcphdr); @@ -1419,13 +1420,15 @@ pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss) break; } if (opt == TCPOPT_MAXSEG) { - memcpy(&mss, (optp + 2), 2); + u_int8_t *mssp = optp + 2; + memcpy(&mss, mssp, sizeof(mss)); if (ntohs(mss) > maxmss) { - mss = htons(maxmss); + size_t mssoffopts = mssp - opts; + pf_patch_16_unaligned(pd, &mss, + htons(maxmss), PF_ALGNMNT(mssoffopts)); m_copyback(pd->m, - pd->off + sizeof(*th) + optp + 2 - opts, - 2, &mss, M_NOWAIT); - pf_cksum(pd, pd->m); + pd->off + sizeof(*th) + mssoffopts, + sizeof(mss), &mss, M_NOWAIT); m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT); } diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index b2243e5545b..43c91df13b8 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.434 2016/07/19 13:30:51 henning Exp $ */ +/* $OpenBSD: pfvar.h,v 1.435 2016/08/17 03:24:12 procter Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -1205,10 +1205,6 @@ struct pf_pdesc { u_int8_t didx; /* key index for destination */ u_int8_t destchg; /* flag set when destination changed */ u_int8_t pflog; /* flags for packet logging */ - u_int8_t csum_status; /* proto cksum ok/bad/unchecked */ -#define PF_CSUM_UNKNOWN 0 -#define PF_CSUM_BAD 1 -#define PF_CSUM_OK 2 }; @@ -1715,9 +1711,14 @@ void pf_addr_inc(struct pf_addr *, sa_family_t); void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, sa_family_t); -void pf_change_a(struct pf_pdesc *, void *, u_int32_t); -int pf_check_proto_cksum(struct pf_pdesc *, int, int, u_int8_t, - sa_family_t); +#define PF_HI (true) +#define PF_LO (!PF_HI) +#define PF_ALGNMNT(off) (((off) % 2) == 0 ? PF_HI : PF_LO) +void pf_patch_8(struct pf_pdesc *, u_int8_t *, u_int8_t, bool); +void pf_patch_16(struct pf_pdesc *, u_int16_t *, u_int16_t); +void pf_patch_16_unaligned(struct pf_pdesc *, void *, u_int16_t, bool); +void pf_patch_32(struct pf_pdesc *, u_int32_t *, u_int32_t); +void pf_patch_32_unaligned(struct pf_pdesc *, void *, u_int32_t, bool); int pflog_packet(struct pf_pdesc *, u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *, struct pf_rule *); void pf_send_deferred_syn(struct pf_state *); @@ -1906,8 +1907,6 @@ int pf_map_addr(sa_family_t, struct pf_rule *, int pf_postprocess_addr(struct pf_state *); -void pf_cksum(struct pf_pdesc *, struct mbuf *); - struct pf_state_key *pf_state_key_ref(struct pf_state_key *); void pf_state_key_unref(struct pf_state_key *); int pf_state_key_isvalid(struct pf_state_key *); |