this just makes the eyes hurt:
                *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,
and trust me, it's not just about the eyes.

anyway. here is a complete revamp of the checksum handling in our
kernel. instead of mucking with them all the time we move to a model
where we assume the hardware will do it at the end. if we run into a
case where we don't have such hardware we apply band-aid in form of
calling the software implementation to re-do the checksums. note that
we're talking ip as well as protocol (tcp, udp, ...) checksums here.

this has seen light testing but will need much much more. guess the
most interesting cases are forwarding boxes (routing as well as bridging,
the fooookin' bridge is very annoying here), especially with pf making
changes (e. g. all forms of NAT), and of course various tunnel
situations.

contains traces of v6 code written by krw (thanks again!). 99% from
iceland by yours truly. it is a net minus and would be a way bigger
net minus if ipvshit wasn't so retarded.

Index: net/if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.192
diff -u -p -r1.192 if_bridge.c
--- net/if_bridge.c     16 May 2011 20:06:28 -0000      1.192
+++ net/if_bridge.c     3 Jul 2011 19:16:05 -0000
@@ -1061,15 +1061,6 @@ bridge_output(struct ifnet *ifp, struct 
                        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);
 
                LIST_FOREACH(p, &sc->sc_iflist, next) {
@@ -2459,6 +2450,8 @@ bridge_ipsec(struct bridge_softc *sc, st
                                        m_freem(m);
                                        return (1);
                                }
+                               if (m)
+                                       in_proto_cksum_out(m, encif);
                                break;
 #endif /* INET */
 #ifdef INET6
@@ -2470,6 +2463,8 @@ bridge_ipsec(struct bridge_softc *sc, st
                                        m_freem(m);
                                        return (1);
                                }
+                               if (m)
+                                       in6_proto_cksum_out(m, encif);
                                break;
 #endif /* INET6 */
                        }
@@ -2627,6 +2622,7 @@ bridge_ip(struct bridge_softc *sc, int d
                        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)) {
@@ -2672,6 +2668,7 @@ bridge_ip(struct bridge_softc *sc, int d
                if (m == NULL)
                        return (NULL);
 #endif /* NPF > 0 */
+               in6_proto_cksum_out(m, ifp);
 
                break;
        }
Index: net/if_pflog.c
===================================================================
RCS file: /cvs/src/sys/net/if_pflog.c,v
retrieving revision 1.35
diff -u -p -r1.35 if_pflog.c
--- net/if_pflog.c      20 Jun 2011 19:03:41 -0000      1.35
+++ net/if_pflog.c      3 Jul 2011 19:16:05 -0000
@@ -347,7 +347,7 @@ pflog_bpfcopy(const void *src_arg, void 
 
        if ((pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
            pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
-           pfloghdr->dir))) {
+           pfloghdr->dir, mfake))) {
                m_copyback(mfake, off, min(mfake->m_len - off, hdrlen),
                    pd.hdr.any, M_NOWAIT);
                PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
Index: net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.754
diff -u -p -r1.754 pf.c
--- net/pf.c    3 Jul 2011 18:42:45 -0000       1.754
+++ net/pf.c    3 Jul 2011 19:16:07 -0000
@@ -2,7 +2,7 @@
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
- * Copyright (c) 2002 - 2010 Henning Brauer
+ * Copyright (c) 2002 - 2011 Henning Brauer
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -147,20 +147,17 @@ void                       pf_add_threshold(struct 
pf_thres
 int                     pf_check_threshold(struct pf_threshold *);
 
 void                    pf_change_ap(struct pf_addr *, u_int16_t *,
-                           u_int16_t *, struct pf_addr *, u_int16_t,
-                           u_int8_t, sa_family_t);
+                           struct pf_addr *, u_int16_t, sa_family_t);
 int                     pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *,
                            struct tcphdr *, struct pf_state_peer *);
 #ifdef INET6
-void                    pf_change_a6(struct pf_addr *, u_int16_t *,
-                           struct pf_addr *, u_int8_t);
+void                    pf_change_a6(struct pf_addr *, struct pf_addr *);
 #endif /* INET6 */
 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);
+                           sa_family_t);
 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,
@@ -1512,95 +1509,27 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw
        }
 }
 
-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)
+pf_change_ap(struct pf_addr *a, u_int16_t *p, struct pf_addr *an,
+    u_int16_t pn, sa_family_t af)
 {
-       struct pf_addr  ao;
-       u_int16_t       po = *p;
-
-       PF_ACPY(&ao, a, af);
        PF_ACPY(a, an, af);
        *p = pn;
-
-       switch (af) {
-#ifdef INET
-       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;
-#endif /* INET */
-#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),
-                   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;
-#endif /* INET6 */
-       }
 }
 
 
 /* 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(void *a, u_int32_t an)
 {
-       u_int32_t       ao;
-
-       memcpy(&ao, a, sizeof(ao));
        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_addr *a, struct pf_addr *an)
 {
-       struct pf_addr  ao;
-
-       PF_ACPY(&ao, a, AF_INET6);
        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 */
 
@@ -1791,8 +1720,7 @@ pf_icmp_mapping(struct pf_pdesc *pd, u_i
 
 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)
+    struct pf_addr *na, u_int16_t np, sa_family_t af)
 {
        struct pf_addr  oia, ooa;
 
@@ -1800,72 +1728,16 @@ pf_change_icmp(struct pf_addr *ia, u_int
        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 (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 */
-       }
 }
 
 
@@ -1902,12 +1774,12 @@ pf_modulate_sack(struct mbuf *m, int off
                                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(&sack.start,
                                            htonl(ntohl(sack.start) -
-                                           dst->seqdiff), 0);
-                                       pf_change_a(&sack.end, &th->th_sum,
+                                           dst->seqdiff));
+                                       pf_change_a(&sack.end,
                                            htonl(ntohl(sack.end) -
-                                           dst->seqdiff), 0);
+                                           dst->seqdiff));
                                        memcpy(&opt[i], &sack, sizeof(sack));
                                }
                                copyback = 1;
@@ -3021,7 +2893,7 @@ pf_test_rule(struct pf_rule **rm, struct
                        rewrite += pf_translate(pd,
                            &sk->addr[pd->sidx], sk->port[pd->sidx],
                            &sk->addr[pd->didx], sk->port[pd->didx],
-                           virtual_type, icmp_dir);
+                           virtual_type, icmp_dir, m);
                }
        } else {
                while ((ri = SLIST_FIRST(&rules))) {
@@ -3114,9 +2986,10 @@ pf_create_state(struct pf_rule *r, struc
                        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(&th->th_seq,
+                           htonl(s->src.seqlo + s->src.seqdiff));
                        *rewrite = 1;
+                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
                } else
                        s->src.seqdiff = 0;
                if (th->th_flags & TH_SYN) {
@@ -3253,7 +3126,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:
@@ -3268,28 +3141,28 @@ pf_translate(struct pf_pdesc *pd, struct
        switch (pd->proto) {
        case IPPROTO_TCP:
                if (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);
+                       pf_change_ap(pd->src, pd->sport, saddr, sport, pd->af);
                        rewrite = 1;
                }
                if (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);
+                       pf_change_ap(pd->dst, pd->dport, daddr, dport, pd->af);
                        rewrite = 1;
                }
+               if (rewrite)
+                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
                break;
 
        case IPPROTO_UDP:
                if (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);
+                       pf_change_ap(pd->src, pd->sport, saddr, sport, pd->af);
                        rewrite = 1;
                }
                if (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);
+                       pf_change_ap(pd->dst, pd->dport, daddr, dport, pd->af);
                        rewrite = 1;
                }
+               if (rewrite)
+                       m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
                break;
 
 #ifdef INET
@@ -3299,26 +3172,23 @@ pf_translate(struct pf_pdesc *pd, struct
                        return (0);
 
                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->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->dst->v4.s_addr, daddr->v4.s_addr);
                        rewrite = 1;
                }
                if (virtual_type == htons(ICMP_ECHO)) {
                        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;
                        }
                }
+               if (rewrite)
+                       m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
                break;
 #endif /* INET */
 
@@ -3329,15 +3199,15 @@ pf_translate(struct pf_pdesc *pd, struct
                        return (0);
 
                if (PF_ANEQ(saddr, pd->src, pd->af)) {
-                       pf_change_a6(pd->src, &pd->hdr.icmp6->icmp6_cksum,
-                           saddr, 0);
+                       pf_change_a6(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->dst, daddr);
                        rewrite = 1;
                }
+               if (rewrite)
+                       m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
                break;
 #endif /* INET6 */
 
@@ -3346,13 +3216,13 @@ pf_translate(struct pf_pdesc *pd, struct
 #ifdef INET
                case AF_INET:
                        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->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->dst->v4.s_addr,
+                                   daddr->v4.s_addr);
                                rewrite = 1;
                        }
                        break;
@@ -3360,11 +3230,11 @@ pf_translate(struct pf_pdesc *pd, struct
 #ifdef INET6
                case AF_INET6:
                        if (PF_ANEQ(saddr, pd->src, pd->af)) {
-                               pf_change_a6(pd->src, NULL, saddr, 0);
+                               pf_change_a6(pd->src, saddr);
                                rewrite = 1;
                        }
                        if (PF_ANEQ(daddr, pd->dst, pd->af)) {
-                               pf_change_a6(pd->dst, NULL, daddr, 0);
+                               pf_change_a6(pd->dst, daddr);
                                rewrite = 1;
                        }
                        break;
@@ -3501,9 +3371,8 @@ pf_tcp_track_full(struct pf_state_peer *
                        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(&th->th_seq, htonl(seq + src->seqdiff));
+                       pf_change_a(&th->th_ack, htonl(ack));
                        *copyback = 1;
                } else {
                        ack = ntohl(th->th_ack);
@@ -3553,9 +3422,8 @@ pf_tcp_track_full(struct pf_state_peer *
                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(&th->th_seq, htonl(seq + src->seqdiff));
+                       pf_change_a(&th->th_ack, htonl(ack));
                        *copyback = 1;
                }
                end = seq + pd->p_len;
@@ -4006,22 +3874,24 @@ pf_test_state_tcp(struct pf_state **stat
 
                if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
                    nk->port[pd->sidx] != th->th_sport)
-                       pf_change_ap(pd->src, &th->th_sport, &th->th_sum,
-                           &nk->addr[pd->sidx], nk->port[pd->sidx], 0, pd->af);
+                       pf_change_ap(pd->src, &th->th_sport,
+                           &nk->addr[pd->sidx], nk->port[pd->sidx], pd->af);
                if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
                    pd->rdomain != nk->rdomain)
                        pd->destchg = 1;
                if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
                    nk->port[pd->didx] != th->th_dport)
-                       pf_change_ap(pd->dst, &th->th_dport, &th->th_sum,
-                           &nk->addr[pd->didx], nk->port[pd->didx], 0, pd->af);
+                       pf_change_ap(pd->dst, &th->th_dport,
+                           &nk->addr[pd->didx], nk->port[pd->didx], pd->af);
                m->m_pkthdr.rdomain = nk->rdomain;
                copyback = 1;
        }
 
        /* Copyback sequence modulation or stateful scrub changes if needed */
-       if (copyback)
+       if (copyback) {
                m_copyback(m, off, sizeof(*th), th, M_NOWAIT);
+               m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
+       }
 
        return (PF_PASS);
 }
@@ -4078,17 +3948,18 @@ pf_test_state_udp(struct pf_state **stat
 
                if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
                    nk->port[pd->sidx] != uh->uh_sport)
-                       pf_change_ap(pd->src, &uh->uh_sport, &uh->uh_sum,
-                           &nk->addr[pd->sidx], nk->port[pd->sidx], 1, pd->af);
+                       pf_change_ap(pd->src, &uh->uh_sport,
+                           &nk->addr[pd->sidx], nk->port[pd->sidx], pd->af);
                if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
                    pd->rdomain != nk->rdomain)
                        pd->destchg = 1;
                if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
                    nk->port[pd->didx] != uh->uh_dport)
-                       pf_change_ap(pd->dst, &uh->uh_dport, &uh->uh_sum,
-                           &nk->addr[pd->didx], nk->port[pd->didx], 1, pd->af);
+                       pf_change_ap(pd->dst, &uh->uh_dport,
+                           &nk->addr[pd->didx], nk->port[pd->didx], pd->af);
                m->m_pkthdr.rdomain = nk->rdomain;
                m_copyback(m, off, sizeof(*uh), uh, M_NOWAIT);
+               m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
        }
 
        return (PF_PASS);
@@ -4158,7 +4029,8 @@ pf_test_state_icmp(struct pf_state **sta
        struct pf_addr  *saddr = pd->src, *daddr = pd->dst;
        u_int16_t        icmpid, *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) {
@@ -4215,28 +4087,22 @@ pf_test_state_icmp(struct pf_state **sta
                        case AF_INET:
                                if (PF_ANEQ(pd->src,
                                    &nk->addr[pd->sidx], AF_INET))
-                                       pf_change_a(&saddr->v4.s_addr, NULL,
-                                           nk->addr[pd->sidx].v4.s_addr, 0);
+                                       pf_change_a(&saddr->v4.s_addr,
+                                           nk->addr[pd->sidx].v4.s_addr);
 
                                if (PF_ANEQ(pd->dst, &nk->addr[pd->didx],
                                    AF_INET)) {
-                                       pf_change_a(&daddr->v4.s_addr, NULL,
-                                           nk->addr[pd->didx].v4.s_addr, 0);
+                                       pf_change_a(&daddr->v4.s_addr,
+                                           nk->addr[pd->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(m, off, ICMP_MINLEN,
                                    pd->hdr.icmp, M_NOWAIT);
+                               copyback = 1;
                                break;
 #endif /* INET */
 #ifdef INET6
@@ -4244,26 +4110,23 @@ pf_test_state_icmp(struct pf_state **sta
                                if (PF_ANEQ(pd->src,
                                    &nk->addr[pd->sidx], AF_INET6))
                                        pf_change_a6(saddr,
-                                           &pd->hdr.icmp6->icmp6_cksum,
-                                           &nk->addr[pd->sidx], 0);
+                                           &nk->addr[pd->sidx]);
 
                                if (PF_ANEQ(pd->dst,
                                    &nk->addr[pd->didx], AF_INET6)) {
                                        pf_change_a6(daddr,
-                                           &pd->hdr.icmp6->icmp6_cksum,
-                                           &nk->addr[pd->didx], 0);
+                                           &nk->addr[pd->didx]);
                                        pd->destchg = 1;
                                }
 
                                m_copyback(m, off,
                                    sizeof(struct icmp6_hdr),
                                    pd->hdr.icmp6, M_NOWAIT);
+                               copyback = 1;
                                break;
 #endif /* INET6 */
                        }
                }
-               return (PF_PASS);
-
        } else {
                /*
                 * ICMP error message in response to a TCP/UDP packet.
@@ -4377,7 +4240,6 @@ pf_test_state_icmp(struct pf_state **sta
                        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
@@ -4417,8 +4279,7 @@ pf_test_state_icmp(struct pf_state **sta
                        /* 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(&th.th_seq, htonl(seq));
                                copyback = 1;
                        }
 
@@ -4463,8 +4324,7 @@ pf_test_state_icmp(struct pf_state **sta
                                    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,
-                                           pd2.ip_sum, icmpsum, 0, pd2.af);
+                                           nk->port[pd2.sidx], pd2.af);
 
                                if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
                                    pd2.af) || pd2.rdomain != nk->rdomain)
@@ -4476,8 +4336,7 @@ pf_test_state_icmp(struct pf_state **sta
                                    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,
-                                           pd2.ip_sum, icmpsum, 0, pd2.af);
+                                           nk->port[pd2.didx], pd2.af);
                                copyback = 1;
                        }
 
@@ -4503,8 +4362,6 @@ pf_test_state_icmp(struct pf_state **sta
                                }
                                m_copyback(m, off2, 8, &th, M_NOWAIT);
                        }
-
-                       return (PF_PASS);
                        break;
                }
                case IPPROTO_UDP: {
@@ -4538,8 +4395,7 @@ pf_test_state_icmp(struct pf_state **sta
                                    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,
-                                           pd2.ip_sum, icmpsum, 1, pd2.af);
+                                           nk->port[pd2.sidx], pd2.af);
 
                                if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
                                    pd2.af) || pd2.rdomain != nk->rdomain)
@@ -4551,8 +4407,7 @@ pf_test_state_icmp(struct pf_state **sta
                                    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,
-                                           pd2.ip_sum, icmpsum, 1, pd2.af);
+                                           nk->port[pd2.didx], pd2.af);
 
                                switch (pd2.af) {
 #ifdef INET
@@ -4573,9 +4428,10 @@ pf_test_state_icmp(struct pf_state **sta
                                        break;
 #endif /* INET6 */
                                }
+                               uh.uh_sum = 0;
                                m_copyback(m, off2, sizeof(uh), &uh, M_NOWAIT);
+                               copyback = 1;
                        }
-                       return (PF_PASS);
                        break;
                }
 #ifdef INET
@@ -4614,8 +4470,7 @@ pf_test_state_icmp(struct pf_state **sta
                                            &iih.icmp_id : NULL,
                                            daddr, &nk->addr[pd2.sidx],
                                            (virtual_type == htons(ICMP_ECHO)) ?
-                                           nk->port[iidx] : 0, NULL,
-                                           pd2.ip_sum, 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)
@@ -4625,8 +4480,7 @@ pf_test_state_icmp(struct pf_state **sta
                                if (PF_ANEQ(pd2.dst,
                                    &nk->addr[pd2.didx], pd2.af))
                                        pf_change_icmp(pd2.dst, NULL, saddr,
-                                           &nk->addr[pd2.didx], 0, NULL,
-                                           pd2.ip_sum, icmpsum, 0, AF_INET);
+                                           &nk->addr[pd2.didx], 0, AF_INET);
 
                                m_copyback(m, off, ICMP_MINLEN, pd->hdr.icmp,
                                    M_NOWAIT);
@@ -4634,8 +4488,8 @@ pf_test_state_icmp(struct pf_state **sta
                                    M_NOWAIT);
                                m_copyback(m, off2, ICMP_MINLEN, &iih,
                                    M_NOWAIT);
+                               copyback = 1;
                        }
-                       return (PF_PASS);
                        break;
                }
 #endif /* INET */
@@ -4687,8 +4541,7 @@ pf_test_state_icmp(struct pf_state **sta
                                            daddr, &nk->addr[pd2.sidx],
                                            (virtual_type ==
                                            htons(ICMP6_ECHO_REQUEST))
-                                           ? nk->port[iidx] : 0, NULL,
-                                           pd2.ip_sum, 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)
@@ -4698,8 +4551,7 @@ pf_test_state_icmp(struct pf_state **sta
                                if (PF_ANEQ(pd2.dst,
                                    &nk->addr[pd2.didx], pd2.af))
                                        pf_change_icmp(pd2.dst, NULL, saddr,
-                                           &nk->addr[pd2.didx], 0, NULL,
-                                           pd2.ip_sum, icmpsum, 0, AF_INET6);
+                                           &nk->addr[pd2.didx], 0, AF_INET6);
 
                                m_copyback(m, off, sizeof(struct icmp6_hdr),
                                    pd->hdr.icmp6, M_NOWAIT);
@@ -4707,8 +4559,8 @@ pf_test_state_icmp(struct pf_state **sta
                                    M_NOWAIT);
                                m_copyback(m, off2, sizeof(struct icmp6_hdr),
                                    &iih, M_NOWAIT);
+                               copyback = 1;
                        }
-                       return (PF_PASS);
                        break;
                }
 #endif /* INET6 */
@@ -4731,8 +4583,7 @@ pf_test_state_icmp(struct pf_state **sta
                                if (PF_ANEQ(pd2.src,
                                    &nk->addr[pd2.sidx], pd2.af))
                                        pf_change_icmp(pd2.src, NULL, daddr,
-                                           &nk->addr[pd2.sidx], 0, NULL,
-                                           pd2.ip_sum, icmpsum, 0, pd2.af);
+                                           &nk->addr[pd2.sidx], 0, pd2.af);
 
                                if (PF_ANEQ(pd2.dst, &nk->addr[pd2.didx],
                                    pd2.af) || pd2.rdomain != nk->rdomain)
@@ -4742,8 +4593,7 @@ pf_test_state_icmp(struct pf_state **sta
                                if (PF_ANEQ(pd2.dst,
                                    &nk->addr[pd2.didx], pd2.af))
                                        pf_change_icmp(pd2.dst, NULL, saddr,
-                                           &nk->addr[pd2.didx], 0, NULL,
-                                           pd2.ip_sum, icmpsum, 0, pd2.af);
+                                           &nk->addr[pd2.didx], 0, pd2.af);
 
                                switch (pd2.af) {
 #ifdef INET
@@ -4764,12 +4614,15 @@ pf_test_state_icmp(struct pf_state **sta
                                        break;
 #endif /* INET6 */
                                }
+                               copyback = 1;
                        }
-                       return (PF_PASS);
                        break;
                }
                }
        }
+       if (copyback)
+               m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
+       return (PF_PASS);
 }
 
 int
@@ -4828,13 +4681,11 @@ pf_test_state_other(struct pf_state **st
 #ifdef INET
                case AF_INET:
                        if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET))
-                               pf_change_a(&pd->src->v4.s_addr, NULL,
-                                   nk->addr[pd->sidx].v4.s_addr,
-                                   0);
+                               pf_change_a(&pd->src->v4.s_addr,
+                                   nk->addr[pd->sidx].v4.s_addr);
                        if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) {
-                               pf_change_a(&pd->dst->v4.s_addr, NULL,
-                                   nk->addr[pd->didx].v4.s_addr,
-                                   0);
+                               pf_change_a(&pd->dst->v4.s_addr,
+                                   nk->addr[pd->didx].v4.s_addr);
                                pd->destchg = 1;
                        }
                        break;
Index: net/pf_norm.c
===================================================================
RCS file: /cvs/src/sys/net/pf_norm.c,v
retrieving revision 1.136
diff -u -p -r1.136 pf_norm.c
--- net/pf_norm.c       3 Jul 2011 18:08:02 -0000       1.136
+++ net/pf_norm.c       3 Jul 2011 19:16:07 -0000
@@ -2,7 +2,7 @@
 
 /*
  * Copyright 2001 Niels Provos <[email protected]>
- * Copyright 2009 Henning Brauer <[email protected]>
+ * Copyright 2009 - 2011 Henning Brauer <[email protected]>
  * Copyright 2011 Alexander Bluhm <[email protected]>
  * All rights reserved.
  *
@@ -967,20 +967,20 @@ pf_normalize_tcp(int dir, struct mbuf *m
                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) {
                m_copyback(m, off, sizeof(*th), th, M_NOWAIT);
+               m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
+       }
 
        return (PF_PASS);
 
@@ -1169,10 +1169,8 @@ pf_normalize_tcp_stateful(struct mbuf *m
                                            PFSS_TIMESTAMP)) {
                                                tsval = ntohl(tsval);
                                                pf_change_a(&opt[2],
-                                                   &th->th_sum,
                                                    htonl(tsval +
-                                                   src->scrub->pfss_ts_mod),
-                                                   0);
+                                                   src->scrub->pfss_ts_mod));
                                                copyback = 1;
                                        }
 
@@ -1185,8 +1183,7 @@ pf_normalize_tcp_stateful(struct mbuf *m
                                                tsecr = ntohl(tsecr)
                                                    - dst->scrub->pfss_ts_mod;
                                                pf_change_a(&opt[6],
-                                                   &th->th_sum, htonl(tsecr),
-                                                   0);
+                                                   htonl(tsecr));
                                                copyback = 1;
                                        }
                                        got_ts = 1;
@@ -1204,6 +1201,7 @@ pf_normalize_tcp_stateful(struct mbuf *m
                        m_copyback(m, off + sizeof(struct tcphdr),
                            (th->th_off << 2) - sizeof(struct tcphdr), hdr +
                            sizeof(struct tcphdr), M_NOWAIT);
+                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
                }
        }
 
@@ -1516,21 +1514,18 @@ pf_normalize_mss(struct mbuf *m, int off
                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(m,
                                    off + sizeof(*th) + optp + 2 - opts,
                                    2, &mss, M_NOWAIT);
                                m_copyback(m, off, sizeof(*th), th, M_NOWAIT);
+                               m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
                        }
                        break;
                default:
                        break;
                }
        }
-
-
 
        return (0);
 }
Index: net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.334
diff -u -p -r1.334 pfvar.h
--- net/pfvar.h 21 Jun 2011 08:59:47 -0000      1.334
+++ net/pfvar.h 3 Jul 2011 19:16:07 -0000
@@ -1730,8 +1730,6 @@ extern struct pf_state            *pf_find_state_a
                                    u_int, int *);
 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;
@@ -1759,7 +1757,7 @@ void      pf_addr_inc(struct pf_addr *, sa_fa
 
 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(void *, u_int32_t);
 int    pflog_packet(struct pfi_kif *, struct mbuf *, u_int8_t,
            u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *,
            struct pf_pdesc *);
@@ -1798,7 +1796,7 @@ struct pf_state_key *pf_alloc_state_key(
 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 *);
 
 void   pfr_initialize(void);
 int    pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
Index: netinet/ip_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.192
diff -u -p -r1.192 ip_input.c
--- netinet/ip_input.c  15 Jun 2011 09:11:01 -0000      1.192
+++ netinet/ip_input.c  3 Jul 2011 19:16:07 -0000
@@ -501,6 +501,9 @@ ipv4_input(m)
        return;
 
 ours:
+       /* 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.
Index: netinet/ip_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.222
diff -u -p -r1.222 ip_output.c
--- netinet/ip_output.c 15 Jun 2011 09:11:01 -0000      1.222
+++ netinet/ip_output.c 3 Jul 2011 19:16:08 -0000
@@ -615,6 +615,7 @@ sendit:
                        splx(s);
                        goto done;
                }
+               in_proto_cksum_out(m, encif);
                ip = mtod(m, struct ip *);
                hlen = ip->ip_hl << 2;
                /*
@@ -698,8 +699,6 @@ sendit:
        }
 #endif /* IPSEC */
 
-       in_proto_cksum_out(m, ifp);
-
        /*
         * Packet filter
         */
@@ -711,6 +710,7 @@ sendit:
        }
        if (m == NULL)
                goto done;
+       in_proto_cksum_out(m, ifp);
        ip = mtod(m, struct ip *);
        hlen = ip->ip_hl << 2;
        if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
Index: netinet6/in6.h
===================================================================
RCS file: /cvs/src/sys/netinet6/in6.h,v
retrieving revision 1.53
diff -u -p -r1.53 in6.h
--- netinet6/in6.h      2 May 2011 13:48:38 -0000       1.53
+++ netinet6/in6.h      3 Jul 2011 19:16:08 -0000
@@ -834,6 +834,7 @@ extern int inet6_rth_add(void *, const s
 extern int inet6_rth_reverse(const void *, void *);
 extern int inet6_rth_segments(const void *);
 extern struct in6_addr *inet6_rth_getaddr(const void *, int);
+extern void in6_proto_cksum_out(struct mbuf *, struct ifnet *);
 __END_DECLS
 
 #endif /* !_NETINET6_IN6_H_ */
Index: netinet6/ip6_forward.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_forward.c,v
retrieving revision 1.52
diff -u -p -r1.52 ip6_forward.c
--- netinet6/ip6_forward.c      24 Mar 2011 20:09:45 -0000      1.52
+++ netinet6/ip6_forward.c      3 Jul 2011 19:16:08 -0000
@@ -361,6 +361,7 @@ reroute:
                        splx(s);
                        goto senderr;
                }
+               in6_proto_cksum_out(m, encif);
                ip6 = mtod(m, struct ip6_hdr *);
                /*
                 * PF_TAG_REROUTE handling or not...
@@ -470,7 +471,7 @@ reroute:
        }
        if (m == NULL)
                goto senderr;
-
+       in6_proto_cksum_out(m, rt->rt_ifp);
        ip6 = mtod(m, struct ip6_hdr *);
        if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
            (PF_TAG_REROUTE | PF_TAG_GENERATED)) {
Index: netinet6/ip6_input.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.99
diff -u -p -r1.99 ip6_input.c
--- netinet6/ip6_input.c        3 Apr 2011 13:56:05 -0000       1.99
+++ netinet6/ip6_input.c        3 Jul 2011 19:16:09 -0000
@@ -669,6 +669,9 @@ ip6_input(struct mbuf *m)
                return;
        }       
 
+       /* pf might have changed things */
+       in6_proto_cksum_out(m, NULL);
+
        ip6 = mtod(m, struct ip6_hdr *);
 
        /*
Index: netinet6/ip6_output.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.121
diff -u -p -r1.121 ip6_output.c
--- netinet6/ip6_output.c       2 May 2011 22:17:28 -0000       1.121
+++ netinet6/ip6_output.c       3 Jul 2011 19:16:09 -0000
@@ -134,6 +134,8 @@ int ip6_splithdr(struct mbuf *, struct i
 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;
@@ -532,6 +534,7 @@ reroute:
                        splx(s);
                        goto done;
                }
+               in6_proto_cksum_out(m, encif);
                ip6 = mtod(m, struct ip6_hdr *);
                /*
                 * PF_TAG_REROUTE handling or not...
@@ -803,6 +806,7 @@ reroute:
        }
        if (m == NULL)
                goto done;
+       in6_proto_cksum_out(m, ifp);
        ip6 = mtod(m, struct ip6_hdr *);
        if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) ==
            (PF_TAG_REROUTE | PF_TAG_GENERATED)) {
@@ -3204,4 +3208,63 @@ void
 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;
+
+       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_TCPv4) ||
+                   ifp->if_bridge != 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_UDPv4) ||
+                   ifp->if_bridge != 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 */
+       }
 }

Reply via email to