diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2013-06-26 09:12:41 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2013-06-26 09:12:41 +0000 |
commit | 2092a5a508fea02619356ab86e8fac0b9d17f62c (patch) | |
tree | f80658ce533adb3cde6fbcae84d133ac88ef775e /sys | |
parent | feaa5c3eb6db1089559e6ecef2894b368b933037 (diff) |
put the cksum diff back, of course with the bug fixed where we could
under some circumstances repair broken checksums on the way.
ok ryan naddy mikeb
.
redo most of the protocol (tcp/udp/...) checksum handling
-assume we have hardware checksum offloading. stop mucking with the
checksum in most of the stack
-stop checksum mucking in pf, just set a "needs checksumming" flag if needed
-in all output pathes, very late, if we figure out the outbound interface
doesn't have hw cksum offloading, do the cksum in software. this especially
makes the bridge path behave like a regular output path
-little special casing for bridge still required until the broadcast path
loses its disgusting shortcut hacks, but at least it's in one place now
and not all over the stack
in6_proto_cksum_out mostly written by krw@
started at k2k11 in iceland more than 1.5 years ago - yes it took that
long, this stuff is everything but easy.
this happens to fix the infamous pf rdr bug that made us turn off proto
cksum offloading on almost all interface drivers.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if_bridge.c | 19 | ||||
-rw-r--r-- | sys/net/if_pflog.c | 4 | ||||
-rw-r--r-- | sys/net/pf.c | 615 | ||||
-rw-r--r-- | sys/net/pf_norm.c | 32 | ||||
-rw-r--r-- | sys/net/pfvar.h | 17 | ||||
-rw-r--r-- | sys/netinet/ip_input.c | 5 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 6 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 3 | ||||
-rw-r--r-- | sys/netinet6/ip6_divert.c | 5 | ||||
-rw-r--r-- | sys/netinet6/ip6_forward.c | 5 | ||||
-rw-r--r-- | sys/netinet6/ip6_input.c | 5 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 71 |
12 files changed, 361 insertions, 426 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index d5cb23d6841..41d7b678e82 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.210 2013/03/28 23:10:05 tedu Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.211 2013/06/26 09:12:39 henning Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -1013,15 +1013,6 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, return (0); } #endif /* IPSEC */ - - /* Catch packets that need TCP/UDP hardware checksumming */ - if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT || - m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) { - m_freem(m); - splx(s); - return (0); - } - bridge_span(sc, NULL, m); TAILQ_FOREACH(p, &sc->sc_iflist, next) { @@ -2353,6 +2344,12 @@ bridge_ipsec(struct bridge_softc *sc, struct ifnet *ifp, } if (m == NULL) return (1); + else if (af == AF_INET) + in_proto_cksum_out(m, encif); +#ifdef INET6 + else if (af == AF_INET6) + in6_proto_cksum_out(m, encif); +#endif /* INET6 */ #endif /* NPF */ ip = mtod(m, struct ip *); @@ -2505,6 +2502,7 @@ bridge_ip(struct bridge_softc *sc, int dir, struct ifnet *ifp, return (NULL); if (m->m_len < sizeof(struct ip)) goto dropit; + in_proto_cksum_out(m, ifp); ip = mtod(m, struct ip *); ip->ip_sum = 0; if (0 && (ifp->if_capabilities & IFCAP_CSUM_IPv4)) { @@ -2550,6 +2548,7 @@ bridge_ip(struct bridge_softc *sc, int dir, struct ifnet *ifp, if (m == NULL) return (NULL); #endif /* NPF > 0 */ + in6_proto_cksum_out(m, ifp); break; } diff --git a/sys/net/if_pflog.c b/sys/net/if_pflog.c index 5070fa5c4e6..44277972006 100644 --- a/sys/net/if_pflog.c +++ b/sys/net/if_pflog.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_pflog.c,v 1.53 2013/03/26 13:19:25 mpi Exp $ */ +/* $OpenBSD: if_pflog.c,v 1.54 2013/06/26 09:12:39 henning Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -443,7 +443,7 @@ pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len) if (pd.virtual_proto != PF_VPROTO_FRAGMENT && (pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr, pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0, - pfloghdr->dir))) { + pfloghdr->dir, pd.m))) { m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen), pd.hdr.any, M_NOWAIT); #if INET && INET6 diff --git a/sys/net/pf.c b/sys/net/pf.c index d44e455d52b..6f785502ade 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,8 +1,8 @@ -/* $OpenBSD: pf.c,v 1.836 2013/06/26 07:53:59 blambert Exp $ */ +/* $OpenBSD: pf.c,v 1.837 2013/06/26 09:12:39 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier - * Copyright (c) 2002 - 2010 Henning Brauer + * Copyright (c) 2002 - 2012 Henning Brauer <henning@openbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -147,19 +147,18 @@ void pf_init_threshold(struct pf_threshold *, u_int32_t, void pf_add_threshold(struct pf_threshold *); int pf_check_threshold(struct pf_threshold *); -void pf_change_ap(struct pf_addr *, u_int16_t *, +void pf_change_ap(struct pf_pdesc *, struct pf_addr *, u_int16_t *, struct pf_addr *, u_int16_t, - u_int8_t, sa_family_t, sa_family_t); + sa_family_t); int pf_modulate_sack(struct pf_pdesc *, struct pf_state_peer *); -void pf_change_a6(struct pf_addr *, u_int16_t *, - struct pf_addr *, u_int8_t); +void pf_change_a6(struct pf_pdesc *, struct pf_addr *a, + struct pf_addr *an); int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *, int *, u_int16_t *, u_int16_t *); -void pf_change_icmp(struct pf_addr *, u_int16_t *, - struct pf_addr *, struct pf_addr *, u_int16_t, - u_int16_t *, u_int16_t *, u_int16_t *, - u_int8_t, sa_family_t); +void pf_change_icmp(struct pf_pdesc *, struct pf_addr *, + u_int16_t *, struct pf_addr *, struct pf_addr *, + u_int16_t, sa_family_t); int pf_change_icmp_af(struct mbuf *, int, struct pf_pdesc *, struct pf_pdesc *, struct pf_addr *, struct pf_addr *, sa_family_t, @@ -213,8 +212,6 @@ u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, int, u_int16_t); void pf_set_rt_ifp(struct pf_state *, struct pf_addr *); -int pf_check_proto_cksum(struct mbuf *, int, int, - u_int8_t, sa_family_t); struct pf_divert *pf_get_divert(struct mbuf *); int pf_walk_option6(struct pf_pdesc *, struct ip6_hdr *, int, int, u_short *); @@ -1665,140 +1662,37 @@ 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); -} - void -pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *pc, - struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af, - sa_family_t naf) +pf_change_ap(struct pf_pdesc *pd, struct pf_addr *a, u_int16_t *p, + struct pf_addr *an, u_int16_t pn, sa_family_t naf) { - struct pf_addr ao; - u_int16_t po; - - PF_ACPY(&ao, a, af); - if (af == naf) + 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 == naf) PF_ACPY(a, an, naf); - - if (p == NULL) /* no port -> done. no cksum to worry about. */ - return; - po = *p; - *p = pn; - - switch (af) { -#ifdef INET - case AF_INET: - switch (naf) { - case AF_INET: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - po, pn, u); - break; -#ifdef INET6 - case AF_INET6: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - 0, an->addr16[2], u), - 0, an->addr16[3], u), - 0, an->addr16[4], u), - 0, an->addr16[5], u), - 0, an->addr16[6], u), - 0, an->addr16[7], u), - po, pn, u); - break; -#endif /* INET6 */ - } - break; -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - switch (naf) { -#ifdef INET - case AF_INET: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], 0, u), - ao.addr16[3], 0, u), - ao.addr16[4], 0, u), - ao.addr16[5], 0, u), - ao.addr16[6], 0, u), - ao.addr16[7], 0, u), - po, pn, u); - break; -#endif /* INET */ - case AF_INET6: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], an->addr16[2], u), - ao.addr16[3], an->addr16[3], u), - ao.addr16[4], an->addr16[4], u), - ao.addr16[5], an->addr16[5], u), - ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u), - po, pn, u); - break; - } - break; -#endif /* INET6 */ - } + if (p != NULL) + *p = pn; } /* Changes a u_int32_t. Uses a void * so there are no align restrictions */ void -pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) +pf_change_a(struct pf_pdesc *pd, void *a, u_int32_t an) { - u_int32_t ao; - - memcpy(&ao, a, sizeof(ao)); + 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)); - if (c != NULL) - *c = pf_cksum_fixup(pf_cksum_fixup(*c, ao / 65536, an / 65536, - u), ao % 65536, an % 65536, u); } #ifdef INET6 void -pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) +pf_change_a6(struct pf_pdesc *pd, struct pf_addr *a, struct pf_addr *an) { - struct pf_addr ao; - - PF_ACPY(&ao, a, AF_INET6); + if (pd->csum_status == PF_CSUM_UNKNOWN) + pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, + pd->proto, pd->af); PF_ACPY(a, an, AF_INET6); - - if (c) - *c = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*c, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], an->addr16[2], u), - ao.addr16[3], an->addr16[3], u), - ao.addr16[4], an->addr16[4], u), - ao.addr16[5], an->addr16[5], u), - ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u); } #endif /* INET6 */ @@ -1986,82 +1880,23 @@ pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type, int *icmp_dir, int *multi, } void -pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, - struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, - u_int16_t *ic, u_int8_t u, sa_family_t af) +pf_change_icmp(struct pf_pdesc *pd, struct pf_addr *ia, u_int16_t *ip, + struct pf_addr *oa, struct pf_addr *na, u_int16_t np, sa_family_t af) { - struct pf_addr oia, ooa; - - PF_ACPY(&oia, ia, af); - if (oa) - PF_ACPY(&ooa, oa, af); - - /* Change inner protocol port, fix inner protocol checksum. */ - if (ip != NULL) { - u_int16_t oip = *ip; - u_int32_t opc; + if (pd->csum_status == PF_CSUM_UNKNOWN) + pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, + pd->proto, pd->af); - if (pc != NULL) - opc = *pc; + /* Change inner protocol port */ + if (ip != NULL) *ip = np; - if (pc != NULL) - *pc = pf_cksum_fixup(*pc, oip, *ip, u); - *ic = pf_cksum_fixup(*ic, oip, *ip, 0); - if (pc != NULL) - *ic = pf_cksum_fixup(*ic, opc, *pc, 0); - } - /* Change inner ip address, fix inner ip and icmp checksums. */ + + /* Change inner ip address */ PF_ACPY(ia, na, af); - switch (af) { -#ifdef INET - case AF_INET: { - u_int32_t oh2c = *h2c; - - /* XXX just in_cksum() */ - *h2c = pf_cksum_fixup(pf_cksum_fixup(*h2c, - oia.addr16[0], ia->addr16[0], 0), - oia.addr16[1], ia->addr16[1], 0); - *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, - oia.addr16[0], ia->addr16[0], 0), - oia.addr16[1], ia->addr16[1], 0); - *ic = pf_cksum_fixup(*ic, oh2c, *h2c, 0); - break; - } -#endif /* INET */ -#ifdef INET6 - case AF_INET6: - *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*ic, - oia.addr16[0], ia->addr16[0], u), - oia.addr16[1], ia->addr16[1], u), - oia.addr16[2], ia->addr16[2], u), - oia.addr16[3], ia->addr16[3], u), - oia.addr16[4], ia->addr16[4], u), - oia.addr16[5], ia->addr16[5], u), - oia.addr16[6], ia->addr16[6], u), - oia.addr16[7], ia->addr16[7], u); - break; -#endif /* INET6 */ - } + /* Outer ip address, fix outer icmpv6 checksum, if necessary. */ - if (oa) { + if (oa) PF_ACPY(oa, na, af); -#ifdef INET6 - if (af == AF_INET6) - *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*ic, - ooa.addr16[0], oa->addr16[0], u), - ooa.addr16[1], oa->addr16[1], u), - ooa.addr16[2], oa->addr16[2], u), - ooa.addr16[3], oa->addr16[3], u), - ooa.addr16[4], oa->addr16[4], u), - ooa.addr16[5], oa->addr16[5], u), - ooa.addr16[6], oa->addr16[6], u), - ooa.addr16[7], oa->addr16[7], u); -#endif /* INET6 */ - } } #if INET && INET6 @@ -2074,6 +1909,10 @@ pf_translate_af(struct pf_pdesc *pd) 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); /* trim the old header */ @@ -2143,6 +1982,10 @@ pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, 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); + if (af == naf || (af != AF_INET && af != AF_INET6) || (naf != AF_INET && naf != AF_INET6)) return (-1); @@ -2305,28 +2148,12 @@ pf_translate_icmp_af(int af, void *arg) default: return (-1); } - if (icmp6->icmp6_type != type) { - icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, - icmp6->icmp6_type, type, 0); - icmp6->icmp6_type = type; - } - if (icmp6->icmp6_code != code) { - icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, - icmp6->icmp6_code, code, 0); - icmp6->icmp6_code = code; - } - if (icmp6->icmp6_mtu != htonl(mtu)) { - icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, - htons(ntohl(icmp6->icmp6_mtu)), htons(mtu), 0); - /* aligns well with a icmpv4 nextmtu */ - icmp6->icmp6_mtu = htonl(mtu); - } - if (ptr >= 0 && icmp6->icmp6_pptr != htonl(ptr)) { - icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, - htons(ntohl(icmp6->icmp6_pptr)), htons(ptr), 0); - /* icmpv4 pptr is a one most significant byte */ - icmp6->icmp6_pptr = htonl(ptr << 24); - } + icmp6->icmp6_type = type; + icmp6->icmp6_code = code; + /* aligns well with a icmpv4 nextmtu */ + icmp6->icmp6_mtu = htonl(mtu); + /* icmpv4 pptr is a one most significant byte */ + icmp6->icmp6_pptr = htonl(ptr << 24); break; case AF_INET6: icmp4 = arg; @@ -2415,26 +2242,10 @@ pf_translate_icmp_af(int af, void *arg) default: return (-1); } - if (icmp4->icmp_type != type) { - icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, - icmp4->icmp_type, type, 0); - icmp4->icmp_type = type; - } - if (icmp4->icmp_code != code) { - icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, - icmp4->icmp_code, code, 0); - icmp4->icmp_code = code; - } - if (icmp4->icmp_nextmtu != htons(mtu)) { - icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, - icmp4->icmp_nextmtu, htons(mtu), 0); - icmp4->icmp_nextmtu = htons(mtu); - } - if (ptr >= 0 && icmp4->icmp_void != ptr) { - icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, - htons(icmp4->icmp_pptr), htons(ptr), 0); - icmp4->icmp_void = htonl(ptr); - } + icmp4->icmp_type = type; + icmp4->icmp_code = code; + icmp4->icmp_nextmtu = htons(mtu); + icmp4->icmp_void = htonl(ptr); break; } @@ -2476,12 +2287,12 @@ pf_modulate_sack(struct pf_pdesc *pd, struct pf_state_peer *dst) for (i = 2; i + TCPOLEN_SACK <= olen; i += TCPOLEN_SACK) { memcpy(&sack, &opt[i], sizeof(sack)); - pf_change_a(&sack.start, &th->th_sum, + pf_change_a(pd, &sack.start, htonl(ntohl(sack.start) - - dst->seqdiff), 0); - pf_change_a(&sack.end, &th->th_sum, + dst->seqdiff)); + pf_change_a(pd, &sack.end, htonl(ntohl(sack.end) - - dst->seqdiff), 0); + dst->seqdiff)); memcpy(&opt[i], &sack, sizeof(sack)); } copyback = 1; @@ -3551,7 +3362,7 @@ 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->m, pd->off, + if (pf_check_proto_cksum(pd, pd->off, pd->tot_len - pd->off, IPPROTO_TCP, pd->af)) REASON_SET(&reason, PFRES_PROTCKSUM); else { @@ -3622,7 +3433,7 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm, sk->port[pd->af == pd->naf ? pd->sidx : pd->didx], &sk->addr[pd->af == pd->naf ? pd->didx : pd->sidx], sk->port[pd->af == pd->naf ? pd->didx : pd->sidx], - virtual_type, icmp_dir); + virtual_type, icmp_dir, pd->m); } } else { while ((ri = SLIST_FIRST(&rules))) { @@ -3631,9 +3442,11 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm, } } - /* copy back packet headers if we performed NAT operations */ - if (rewrite && pd->hdrlen) + /* 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); + } #if NPFSYNC > 0 if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) && @@ -3729,8 +3542,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(&th->th_seq, &th->th_sum, - htonl(s->src.seqlo + s->src.seqdiff), 0); + pf_change_a(pd, &th->th_seq, + htonl(s->src.seqlo + s->src.seqdiff)); *rewrite = 1; } else s->src.seqdiff = 0; @@ -3867,7 +3680,7 @@ csfailed: int pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, u_int16_t virtual_type, - int icmp_dir) + int icmp_dir, struct mbuf *m) { /* * when called from bpf_mtap_pflog, there are extra constraints: @@ -3884,14 +3697,14 @@ 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->src, pd->sport, &pd->hdr.tcp->th_sum, - saddr, sport, 0, pd->af, pd->naf); + pf_change_ap(pd, pd->src, pd->sport, saddr, sport, + pd->naf); rewrite = 1; } if (afto || PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) { - pf_change_ap(pd->dst, pd->dport, &pd->hdr.tcp->th_sum, - daddr, dport, 0, pd->af, pd->naf); + pf_change_ap(pd, pd->dst, pd->dport, daddr, dport, + pd->naf); rewrite = 1; } break; @@ -3899,14 +3712,14 @@ 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->src, pd->sport, &pd->hdr.udp->uh_sum, - saddr, sport, 1, pd->af, pd->naf); + pf_change_ap(pd, pd->src, pd->sport, saddr, sport, + pd->naf); rewrite = 1; } if (afto || PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) { - pf_change_ap(pd->dst, pd->dport, &pd->hdr.udp->uh_sum, - daddr, dport, 1, pd->af, pd->naf); + pf_change_ap(pd, pd->dst, pd->dport, daddr, dport, + pd->naf); rewrite = 1; } break; @@ -3926,13 +3739,13 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, #endif /* INET6 */ } else { if (PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a(&pd->src->v4.s_addr, NULL, - saddr->v4.s_addr, 0); + pf_change_a(pd, &pd->src->v4.s_addr, + saddr->v4.s_addr); rewrite = 1; } if (PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a(&pd->dst->v4.s_addr, NULL, - daddr->v4.s_addr, 0); + pf_change_a(pd, &pd->dst->v4.s_addr, + daddr->v4.s_addr); rewrite = 1; } } @@ -3940,9 +3753,6 @@ 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) { - pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, - pd->hdr.icmp->icmp_id, icmpid, 0); pd->hdr.icmp->icmp_id = icmpid; rewrite = 1; } @@ -3966,13 +3776,11 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, #endif /* INET */ } else { if (PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a6(pd->src, - &pd->hdr.icmp6->icmp6_cksum, saddr, 0); + pf_change_a6(pd, pd->src, saddr); rewrite = 1; } if (PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a6(pd->dst, - &pd->hdr.icmp6->icmp6_cksum, daddr, 0); + pf_change_a6(pd, pd->dst, daddr); rewrite = 1; } } @@ -3984,13 +3792,13 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, #ifdef INET case AF_INET: if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a(&pd->src->v4.s_addr, NULL, - saddr->v4.s_addr, 0); + pf_change_a(pd, &pd->src->v4.s_addr, + saddr->v4.s_addr); rewrite = 1; } if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a(&pd->dst->v4.s_addr, NULL, - daddr->v4.s_addr, 0); + pf_change_a(pd, &pd->dst->v4.s_addr, + daddr->v4.s_addr); rewrite = 1; } break; @@ -3998,11 +3806,11 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, #ifdef INET6 case AF_INET6: if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) { - pf_change_a6(pd->src, NULL, saddr, 0); + pf_change_a6(pd, pd->src, saddr); rewrite = 1; } if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a6(pd->dst, NULL, daddr, 0); + pf_change_a6(pd, pd->dst, daddr); rewrite = 1; } break; @@ -4053,9 +3861,8 @@ pf_tcp_track_full(struct pf_pdesc *pd, struct pf_state_peer *src, while ((src->seqdiff = arc4random() - seq) == 0) ; ack = ntohl(th->th_ack) - dst->seqdiff; - pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + - src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + pf_change_a(pd, &th->th_seq, htonl(seq + src->seqdiff)); + pf_change_a(pd, &th->th_ack, htonl(ack)); *copyback = 1; } else { ack = ntohl(th->th_ack); @@ -4105,9 +3912,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(&th->th_seq, &th->th_sum, htonl(seq + - src->seqdiff), 0); - pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + pf_change_a(pd, &th->th_seq, htonl(seq + src->seqdiff)); + pf_change_a(pd, &th->th_ack, htonl(ack)); *copyback = 1; } end = seq + pd->p_len; @@ -4613,19 +4419,17 @@ 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->src, pd->sport, pd->pcksum, - &nk->addr[sidx], nk->port[sidx], - pd->virtual_proto == IPPROTO_UDP, pd->af, nk->af); + pf_change_ap(pd, pd->src, pd->sport, + &nk->addr[sidx], nk->port[sidx], nk->af); if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || pd->rdomain != nk->rdomain) pd->destchg = 1; + if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || nk->port[didx] != pd->odport) - pf_change_ap(pd->dst, pd->dport, pd->pcksum, - &nk->addr[didx], nk->port[didx], - pd->virtual_proto == IPPROTO_UDP, pd->af, nk->af); - pd->m->m_pkthdr.rdomain = nk->rdomain; + pf_change_ap(pd, pd->dst, pd->dport, + &nk->addr[didx], nk->port[didx], nk->af); #if INET && INET6 if (afto) { @@ -4636,11 +4440,14 @@ pf_test_state(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) } #endif /* INET && INET6 */ + pd->m->m_pkthdr.rdomain = nk->rdomain; copyback = 1; } - if (copyback && pd->hdrlen > 0) + if (copyback && pd->hdrlen > 0) { + pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any, M_NOWAIT); + } return (action); } @@ -4702,7 +4509,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, struct pf_addr *saddr = pd->src, *daddr = pd->dst; u_int16_t *icmpsum, virtual_id, virtual_type; u_int8_t icmptype; - int icmp_dir, iidx, ret, multi; + int icmp_dir, iidx, ret, multi, copyback = 0; + struct pf_state_key_cmp key; switch (pd->proto) { @@ -4776,28 +4584,22 @@ 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(&saddr->v4.s_addr, NULL, - nk->addr[sidx].v4.s_addr, 0); + pf_change_a(pd, &saddr->v4.s_addr, + nk->addr[sidx].v4.s_addr); if (!afto && PF_ANEQ(pd->dst, &nk->addr[didx], AF_INET)) { - pf_change_a(&daddr->v4.s_addr, NULL, - nk->addr[didx].v4.s_addr, 0); + pf_change_a(pd, &daddr->v4.s_addr, + nk->addr[didx].v4.s_addr); pd->destchg = 1; } - if (nk->port[iidx] != - pd->hdr.icmp->icmp_id) { - pd->hdr.icmp->icmp_cksum = - pf_cksum_fixup( - pd->hdr.icmp->icmp_cksum, - pd->hdr.icmp->icmp_id, - nk->port[iidx], 0); + if (nk->port[iidx] != pd->hdr.icmp->icmp_id) pd->hdr.icmp->icmp_id = nk->port[iidx]; - } m_copyback(pd->m, pd->off, ICMP_MINLEN, pd->hdr.icmp, M_NOWAIT); + copyback = 1; break; #endif /* INET */ #ifdef INET6 @@ -4812,15 +4614,13 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, #endif /* INET */ if (!afto && PF_ANEQ(pd->src, &nk->addr[sidx], AF_INET6)) - pf_change_a6(saddr, - &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[sidx], 0); + pf_change_a6(pd, saddr, + &nk->addr[sidx]); if (!afto && PF_ANEQ(pd->dst, &nk->addr[didx], AF_INET6)) { - pf_change_a6(daddr, - &pd->hdr.icmp6->icmp6_cksum, - &nk->addr[didx], 0); + pf_change_a6(pd, daddr, + &nk->addr[didx]); pd->destchg = 1; } @@ -4831,6 +4631,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, M_NOWAIT); + copyback = 1; break; #endif /* INET6 */ } @@ -4843,8 +4644,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, } #endif /* INET && INET6 */ } - return (PF_PASS); - } else { /* * ICMP error message in response to a TCP/UDP packet. @@ -4931,7 +4730,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, u_int32_t seq; struct pf_state_peer *src, *dst; u_int8_t dws; - int copyback = 0; /* * Only the first 8 bytes of the TCP header can be @@ -4981,8 +4779,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(&th.th_seq, icmpsum, - htonl(seq), 0); + pf_change_a(pd, &th.th_seq, htonl(seq)); copyback = 1; } @@ -5064,10 +4861,10 @@ 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(pd2.src, &th.th_sport, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, - ipsum2, icmpsum, 0, pd2.af); + pf_change_icmp(pd, pd2.src, + &th.th_sport, daddr, + &nk->addr[pd2.sidx], + nk->port[pd2.sidx], pd2.af); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5077,10 +4874,10 @@ 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(pd2.dst, &th.th_dport, - saddr, &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, - ipsum2, icmpsum, 0, pd2.af); + pf_change_icmp(pd, pd2.dst, + &th.th_dport, saddr, + &nk->addr[pd2.didx], + nk->port[pd2.didx], pd2.af); copyback = 1; } @@ -5106,8 +4903,6 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, } m_copyback(pd2.m, pd2.off, 8, &th, M_NOWAIT); } - - return (PF_PASS); break; } case IPPROTO_UDP: { @@ -5161,12 +4956,12 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, pd->proto = IPPROTO_ICMP; else pd->proto = IPPROTO_ICMPV6; - pf_change_ap(pd2.src, &uh.uh_sport, + pf_change_ap(pd, pd2.src, &uh.uh_sum, &nk->addr[pd2.sidx], - nk->port[sidx], 1, pd->af, nk->af); - pf_change_ap(pd2.dst, &uh.uh_dport, + nk->port[sidx], nk->af); + pf_change_ap(pd, pd2.dst, &uh.uh_sum, &nk->addr[pd2.didx], - nk->port[didx], 1, pd->af, nk->af); + nk->port[didx], nk->af); m_copyback(pd2.m, pd2.off, sizeof(uh), &uh, M_NOWAIT); pd->m->m_pkthdr.rdomain = nk->rdomain; @@ -5183,10 +4978,10 @@ 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(pd2.src, &uh.uh_sport, - daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], &uh.uh_sum, - ipsum2, icmpsum, 1, pd2.af); + pf_change_icmp(pd, pd2.src, + &uh.uh_sport, daddr, + &nk->addr[pd2.sidx], + nk->port[pd2.sidx], pd2.af); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5196,10 +4991,10 @@ 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(pd2.dst, &uh.uh_dport, - saddr, &nk->addr[pd2.didx], - nk->port[pd2.didx], &uh.uh_sum, - ipsum2, icmpsum, 1, pd2.af); + pf_change_icmp(pd, pd2.dst, + &uh.uh_dport, saddr, + &nk->addr[pd2.didx], + nk->port[pd2.didx], pd2.af); switch (pd2.af) { #ifdef INET @@ -5220,10 +5015,11 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, break; #endif /* INET6 */ } + uh.uh_sum = 0; m_copyback(pd2.m, pd2.off, sizeof(uh), &uh, M_NOWAIT); + copyback = 1; } - return (PF_PASS); break; } #ifdef INET @@ -5305,13 +5101,12 @@ 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(pd2.src, + pf_change_icmp(pd, pd2.src, (virtual_type == htons(ICMP_ECHO)) ? &iih.icmp_id : NULL, daddr, &nk->addr[pd2.sidx], (virtual_type == htons(ICMP_ECHO)) ? - nk->port[iidx] : 0, NULL, - ipsum2, icmpsum, 0, AF_INET); + nk->port[iidx] : 0, AF_INET); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5320,9 +5115,9 @@ 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(pd2.dst, NULL, saddr, - &nk->addr[pd2.didx], 0, NULL, - ipsum2, icmpsum, 0, AF_INET); + pf_change_icmp(pd, pd2.dst, NULL, + saddr, &nk->addr[pd2.didx], 0, + AF_INET); m_copyback(pd->m, pd->off, ICMP_MINLEN, pd->hdr.icmp, M_NOWAIT); @@ -5330,8 +5125,8 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, M_NOWAIT); m_copyback(pd2.m, pd2.off, ICMP_MINLEN, &iih, M_NOWAIT); + copyback = 1; } - return (PF_PASS); break; } #endif /* INET */ @@ -5425,15 +5220,14 @@ 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(pd2.src, + pf_change_icmp(pd, pd2.src, (virtual_type == htons(ICMP6_ECHO_REQUEST)) ? &iih.icmp6_id : NULL, daddr, &nk->addr[pd2.sidx], (virtual_type == htons(ICMP6_ECHO_REQUEST)) - ? nk->port[iidx] : 0, NULL, - ipsum2, icmpsum, 0, AF_INET6); + ? nk->port[iidx] : 0, AF_INET6); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5442,19 +5236,17 @@ 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(pd2.dst, NULL, saddr, - &nk->addr[pd2.didx], 0, NULL, - ipsum2, icmpsum, 0, AF_INET6); + pf_change_icmp(pd, pd2.dst, NULL, + saddr, &nk->addr[pd2.didx], 0, + AF_INET6); m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, M_NOWAIT); - m_copyback(pd2.m, ipoff2, sizeof(h2_6), &h2_6, - M_NOWAIT); - m_copyback(pd2.m, pd2.off, + m_copyback(pd->m, pd2.off, sizeof(struct icmp6_hdr), &iih, M_NOWAIT); + copyback = 1; } - return (PF_PASS); break; } #endif /* INET6 */ @@ -5476,9 +5268,9 @@ 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(pd2.src, NULL, daddr, - &nk->addr[pd2.sidx], 0, NULL, - ipsum2, icmpsum, 0, pd2.af); + pf_change_icmp(pd, pd2.src, NULL, + daddr, &nk->addr[pd2.sidx], 0, + pd2.af); if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx], pd2.af) || pd2.rdomain != nk->rdomain) @@ -5487,9 +5279,9 @@ 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(pd2.dst, NULL, saddr, - &nk->addr[pd2.didx], 0, NULL, - ipsum2, icmpsum, 0, pd2.af); + pf_change_icmp(pd, pd2.dst, NULL, + saddr, &nk->addr[pd2.didx], 0, + pd2.af); switch (pd2.af) { #ifdef INET @@ -5510,12 +5302,16 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, break; #endif /* INET6 */ } + copyback = 1; } - return (PF_PASS); break; } } } + if (copyback) + pf_cksum(pd, pd->m); + + return (PF_PASS); } /* @@ -6006,12 +5802,17 @@ bad: * if the _OUT flag is set the checksum isn't done yet, consider these ok */ int -pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, +pf_check_proto_cksum(struct pf_pdesc *pd, int off, int len, u_int8_t p, 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; @@ -6027,47 +5828,55 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, #ifdef INET6 case IPPROTO_ICMPV6: #endif /* INET6 */ - flag_ok = flag_out = flag_bad = 0; + 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 (m->m_pkthdr.csum_flags & (flag_ok | flag_out)) + if (pd->m->m_pkthdr.csum_flags & (flag_ok | flag_out)) { + pd->csum_status = PF_CSUM_OK; return (0); - if (m->m_pkthdr.csum_flags & flag_bad) - return (1); - if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) - return (1); - if (m->m_pkthdr.len < off + len) + } + 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; return (1); + } switch (af) { #ifdef INET case AF_INET: if (p == IPPROTO_ICMP) { - if (m->m_len < off) + if (pd->m->m_len < off) { + pd->csum_status = PF_CSUM_BAD; return (1); - m->m_data += off; - m->m_len -= off; - sum = in_cksum(m, len); - m->m_data -= off; - m->m_len += off; + } + pd->m->m_data += off; + pd->m->m_len -= off; + sum = in_cksum(pd->m, len); + pd->m->m_data -= off; + pd->m->m_len += off; } else { - if (m->m_len < sizeof(struct ip)) + if (pd->m->m_len < sizeof(struct ip)) { + pd->csum_status = PF_CSUM_BAD; return (1); - sum = in4_cksum(m, p, off, len); + } + sum = in4_cksum(pd->m, p, off, len); } break; #endif /* INET */ #ifdef INET6 case AF_INET6: - if (m->m_len < sizeof(struct ip6_hdr)) + if (pd->m->m_len < sizeof(struct ip6_hdr)) { + pd->csum_status = PF_CSUM_BAD; return (1); - sum = in6_cksum(m, p, off, len); + } + sum = in6_cksum(pd->m, p, off, len); break; #endif /* INET6 */ } if (sum) { - m->m_pkthdr.csum_flags |= flag_bad; switch (p) { case IPPROTO_TCP: tcpstat.tcps_rcvbadsum++; @@ -6084,9 +5893,12 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, break; #endif /* INET6 */ } + pd->m->m_pkthdr.csum_flags |= flag_bad; + pd->csum_status = PF_CSUM_BAD; return (1); } - m->m_pkthdr.csum_flags |= flag_ok; + pd->m->m_pkthdr.csum_flags |= flag_ok; + pd->csum_status = PF_CSUM_OK; return (0); } @@ -6922,6 +6734,41 @@ pf_check_congestion(struct ifqueue *ifq) return (0); } +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; + if (pd->af == AF_INET) { + pd->hdr.tcp->th_sum = in_cksum_phdr(pd->src->v4.s_addr, + pd->dst->v4.s_addr, htons(pd->tot_len - + pd->off + IPPROTO_TCP)); + } + m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT; + break; + case IPPROTO_UDP: + pd->hdr.udp->uh_sum = 0; + if (pd->af == AF_INET) { + pd->hdr.udp->uh_sum = in_cksum_phdr(pd->src->v4.s_addr, + pd->dst->v4.s_addr, htons(pd->tot_len - + pd->off + IPPROTO_UDP)); + } + m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; + break; + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; + break; + default: + /* nothing */ + break; + } +} + /* * must be called whenever any addressing information such as * address, port, protocol has changed diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c index 8ba8357f352..30abff95f4e 100644 --- a/sys/net/pf_norm.c +++ b/sys/net/pf_norm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_norm.c,v 1.158 2013/06/17 19:50:06 bluhm Exp $ */ +/* $OpenBSD: pf_norm.c,v 1.159 2013/06/26 09:12:39 henning Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> @@ -840,6 +840,10 @@ 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 */ @@ -869,20 +873,20 @@ pf_normalize_tcp(struct pf_pdesc *pd) th->th_x2 = 0; nv = *(u_int16_t *)(&th->th_ack + 1); - th->th_sum = pf_cksum_fixup(th->th_sum, ov, nv, 0); rewrite = 1; } /* Remove urgent pointer, if TH_URG is not set */ if (!(flags & TH_URG) && th->th_urp) { - th->th_sum = pf_cksum_fixup(th->th_sum, th->th_urp, 0, 0); th->th_urp = 0; rewrite = 1; } /* copy back packet headers if we sanitized */ - if (rewrite) + if (rewrite) { + pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT); + } return (PF_PASS); @@ -1074,11 +1078,9 @@ pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason, (src->scrub->pfss_flags & PFSS_TIMESTAMP)) { tsval = ntohl(tsval); - pf_change_a(&opt[2], - &th->th_sum, + pf_change_a(pd, &opt[2], htonl(tsval + - src->scrub->pfss_ts_mod), - 0); + src->scrub->pfss_ts_mod)); copyback = 1; } @@ -1090,9 +1092,8 @@ pf_normalize_tcp_stateful(struct pf_pdesc *pd, u_short *reason, PFSS_TIMESTAMP)) { tsecr = ntohl(tsecr) - dst->scrub->pfss_ts_mod; - pf_change_a(&opt[6], - &th->th_sum, htonl(tsecr), - 0); + pf_change_a(pd, &opt[6], + htonl(tsecr)); copyback = 1; } got_ts = 1; @@ -1398,6 +1399,10 @@ pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss) 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); + thoff = th->th_off << 2; cnt = thoff - sizeof(struct tcphdr); @@ -1422,12 +1427,11 @@ pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss) case TCPOPT_MAXSEG: bcopy((caddr_t)(optp + 2), (caddr_t)&mss, 2); if (ntohs(mss) > maxmss) { - th->th_sum = pf_cksum_fixup(th->th_sum, - mss, htons(maxmss), 0); mss = htons(maxmss); m_copyback(pd->m, pd->off + sizeof(*th) + optp + 2 - opts, 2, &mss, M_NOWAIT); + pf_cksum(pd, pd->m); m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT); } @@ -1437,8 +1441,6 @@ pf_normalize_mss(struct pf_pdesc *pd, u_int16_t maxmss) } } - - return (0); } diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index c84634b1001..3d73bf591d7 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.383 2013/06/04 19:03:12 henning Exp $ */ +/* $OpenBSD: pfvar.h,v 1.384 2013/06/26 09:12:39 henning Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -1274,8 +1274,13 @@ 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 }; + /* flags for RDR options */ #define PF_DPORT_RANGE 0x01 /* Dest port uses range */ #define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ @@ -1772,8 +1777,6 @@ extern void pf_state_export(struct pfsync_state *, struct pf_state *); extern void pf_print_state(struct pf_state *); extern void pf_print_flags(u_int8_t); -extern u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, - u_int8_t); extern struct ifnet *sync_ifp; extern struct pf_rule pf_default_rule; @@ -1797,7 +1800,9 @@ 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(void *, u_int16_t *, u_int32_t, u_int8_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); int pflog_packet(struct pf_pdesc *, u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *); void pf_send_deferred_syn(struct pf_state *); @@ -1834,7 +1839,7 @@ struct pf_state_key *pf_alloc_state_key(int); void pf_pkt_addr_changed(struct mbuf *); int pf_state_key_attach(struct pf_state_key *, struct pf_state *, int); int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t, u_int16_t, int); + struct pf_addr *, u_int16_t, u_int16_t, int, struct mbuf *); int pf_translate_af(struct pf_pdesc *); void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *); @@ -1981,6 +1986,8 @@ 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 *); + #endif /* _KERNEL */ diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index e6d40b49c14..01cc664f354 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_input.c,v 1.212 2013/06/13 12:15:52 mpi Exp $ */ +/* $OpenBSD: ip_input.c,v 1.213 2013/06/26 09:12:40 henning Exp $ */ /* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */ /* @@ -506,6 +506,9 @@ ip_ours(struct mbuf *m) hlen = ip->ip_hl << 2; + /* pf might have modified stuff, might have to chksum */ + in_proto_cksum_out(m, NULL); + /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index ff34a3aa236..b59accf5569 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.241 2013/06/11 18:15:53 deraadt Exp $ */ +/* $OpenBSD: ip_output.c,v 1.242 2013/06/26 09:12:40 henning Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -613,6 +613,7 @@ sendit: * What's the behaviour? */ #endif + in_proto_cksum_out(m, encif); /* Check if we are allowed to fragment */ if (ip_mtudisc && (ip->ip_off & htons(IP_DF)) && tdb->tdb_mtu && @@ -683,8 +684,6 @@ sendit: } #endif /* IPSEC */ - in_proto_cksum_out(m, ifp); - /* * Packet filter */ @@ -710,6 +709,7 @@ sendit: goto reroute; } #endif + in_proto_cksum_out(m, ifp); #ifdef IPSEC if (ipsec_in_use && (flags & IP_FORWARDING) && (ipforwarding == 2) && diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index cc377ebd62a..6468dbe1ba9 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.h,v 1.64 2013/03/28 15:05:32 bluhm Exp $ */ +/* $OpenBSD: in6.h,v 1.65 2013/06/26 09:12:40 henning Exp $ */ /* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */ /* @@ -757,6 +757,7 @@ struct ip6_mtuinfo { struct cmsghdr; int in6_cksum(struct mbuf *, u_int8_t, u_int32_t, u_int32_t); +extern void in6_proto_cksum_out(struct mbuf *, struct ifnet *); int in6_localaddr(struct in6_addr *); int in6_addrscope(struct in6_addr *); struct in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *, u_int); diff --git a/sys/netinet6/ip6_divert.c b/sys/netinet6/ip6_divert.c index 0caa0ce92ef..afe58e3dc70 100644 --- a/sys/netinet6/ip6_divert.c +++ b/sys/netinet6/ip6_divert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_divert.c,v 1.12 2013/05/31 15:04:24 bluhm Exp $ */ +/* $OpenBSD: ip6_divert.c,v 1.13 2013/06/26 09:12:40 henning Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -246,6 +246,9 @@ divert6_packet(struct mbuf *m, int dir) break; } } + /* force checksum calculation */ + if (dir == PF_OUT) + in6_proto_cksum_out(m, NULL); if (inp != CIRCLEQ_END(&divb6table.inpt_queue)) { sa = inp->inp_socket; diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 60ab8caab96..6d7f9712840 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_forward.c,v 1.58 2013/05/31 15:04:24 bluhm Exp $ */ +/* $OpenBSD: ip6_forward.c,v 1.59 2013/06/26 09:12:40 henning Exp $ */ /* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */ /* @@ -370,6 +370,7 @@ reroute: * What's the behaviour? */ #endif + in6_proto_cksum_out(m, encif); m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ @@ -470,7 +471,6 @@ reroute: } if (m == NULL) goto senderr; - ip6 = mtod(m, struct ip6_hdr *); if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) == (PF_TAG_REROUTE | PF_TAG_GENERATED)) { @@ -483,6 +483,7 @@ reroute: goto reroute; } #endif + in6_proto_cksum_out(m, rt->rt_ifp); /* Check the size after pf_test to give pf a chance to refragment. */ if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index f730fc00164..7777b1604bd 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_input.c,v 1.113 2013/06/13 12:17:23 mpi Exp $ */ +/* $OpenBSD: ip6_input.c,v 1.114 2013/06/26 09:12:40 henning Exp $ */ /* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */ /* @@ -645,6 +645,9 @@ ip6_input(struct mbuf *m) return; } + /* pf might have changed things */ + in6_proto_cksum_out(m, NULL); + ip6 = mtod(m, struct ip6_hdr *); /* diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 6cc8f2ec004..f405b3162bf 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_output.c,v 1.140 2013/05/31 15:04:24 bluhm Exp $ */ +/* $OpenBSD: ip6_output.c,v 1.141 2013/06/26 09:12:40 henning Exp $ */ /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ /* @@ -126,6 +126,8 @@ int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); int ip6_getpmtu(struct route_in6 *, struct route_in6 *, struct ifnet *, struct in6_addr *, u_long *, int *); int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); +void in6_delayed_cksum(struct mbuf *, u_int8_t); +void in6_proto_cksum_out(struct mbuf *, struct ifnet *); /* Context for non-repeating IDs */ struct idgen32_ctx ip6_id_ctx; @@ -533,6 +535,7 @@ reroute: * What's the behaviour? */ #endif + in6_proto_cksum_out(m, encif); m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ @@ -808,6 +811,7 @@ reroute: goto reroute; } #endif + in6_proto_cksum_out(m, ifp); /* * Send the packet to the outgoing interface. @@ -3182,3 +3186,68 @@ ip6_randomid_init(void) { idgen32_init(&ip6_id_ctx); } + +/* + * Process a delayed payload checksum calculation. + */ +void +in6_delayed_cksum(struct mbuf *m, u_int8_t nxt) +{ + int nxtp, offset; + u_int16_t csum; + + offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxtp); + if (offset <= 0 || nxtp != nxt) + /* If the desired next protocol isn't found, punt. */ + return; + + if (nxt == IPPROTO_ICMPV6) { + struct icmp6_hdr *icmp6; + icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + offset); + icmp6->icmp6_cksum = 0; + } + + csum = (u_int16_t)(in6_cksum(m, nxt, offset, m->m_pkthdr.len - offset)); + + switch (nxt) { + case IPPROTO_TCP: + offset += offsetof(struct tcphdr, th_sum); + break; + + case IPPROTO_UDP: + offset += offsetof(struct udphdr, uh_sum); + if (csum == 0) + csum = 0xffff; + break; + + case IPPROTO_ICMPV6: + offset += offsetof(struct icmp6_hdr, icmp6_cksum); + break; + } + + if ((offset + sizeof(u_int16_t)) > m->m_len) + m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT); + else + *(u_int16_t *)(mtod(m, caddr_t) + offset) = csum; +} + +void +in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp) +{ + if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) { + if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) || + ifp->if_bridgeport != NULL) { + in6_delayed_cksum(m, IPPROTO_TCP); + m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */ + } + } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) { + if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) || + ifp->if_bridgeport != NULL) { + in6_delayed_cksum(m, IPPROTO_UDP); + m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */ + } + } else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) { + in6_delayed_cksum(m, IPPROTO_ICMPV6); + m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */ + } +} |