Index: sys/net/if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.210
diff -u -p -r1.210 if_bridge.c
--- sys/net/if_bridge.c	28 Mar 2013 23:10:05 -0000	1.210
+++ sys/net/if_bridge.c	5 Jun 2013 02:36:12 -0000
@@ -2,6 +2,7 @@
 
 /*
  * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
+ * Copyright (c) 2003 - 2012 Henning Brauer <henning@openbsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1013,15 +1014,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);
 
 		TAILQ_FOREACH(p, &sc->sc_iflist, next) {
@@ -2353,6 +2345,12 @@ bridge_ipsec(struct bridge_softc *sc, st
 			}
 			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 +2503,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)) {
@@ -2550,6 +2549,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: sys/net/if_pflog.c
===================================================================
RCS file: /cvs/src/sys/net/if_pflog.c,v
retrieving revision 1.53
diff -u -p -r1.53 if_pflog.c
--- sys/net/if_pflog.c	26 Mar 2013 13:19:25 -0000	1.53
+++ sys/net/if_pflog.c	5 Jun 2013 02:36:12 -0000
@@ -443,7 +443,7 @@ pflog_bpfcopy(const void *src_arg, void 
 	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
Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.834
diff -u -p -r1.834 pf.c
--- sys/net/pf.c	5 Jun 2013 00:56:35 -0000	1.834
+++ sys/net/pf.c	5 Jun 2013 02:36:13 -0000
@@ -2,7 +2,7 @@
 
 /*
  * 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_thre
 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 *
 				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 *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,
-    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_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)
+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;
+	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(&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 (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 of
 	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, st
 				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
 		    !(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
 			    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
 		}
 	}
 
-	/* 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, str
 			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
 	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
 	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
 #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
 			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
 #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
 #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
 #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, s
 			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, s
 		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, struc
 
 		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, struc
 		}
 #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_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, 
 #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, 
 #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, 
 				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, 
 			}
 #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, 
 			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, 
 			/* 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, 
 				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, 
 				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, 
 				}
 				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, 
 						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, 
 				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, 
 				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, 
 					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, 
 				    &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, 
 
 				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, 
 				    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, 
 				    ((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, 
 
 				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, 
 
 				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, 
 
 				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, 
 					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
 #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
 			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);
 }
 
@@ -6903,6 +6715,41 @@ pf_check_congestion(struct ifqueue *ifq)
 		return (1);
 	else
 		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;
+	}
 }
 
 /*
Index: sys/net/pf_norm.c
===================================================================
RCS file: /cvs/src/sys/net/pf_norm.c,v
retrieving revision 1.157
diff -u -p -r1.157 pf_norm.c
--- sys/net/pf_norm.c	6 Nov 2012 12:32:41 -0000	1.157
+++ sys/net/pf_norm.c	5 Jun 2013 02:36:13 -0000
@@ -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_pdes
 					    (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_pdes
 					    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_
 	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_
 		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);
 			}
@@ -1436,8 +1440,6 @@ pf_normalize_mss(struct pf_pdesc *pd, u_
 			break;
 		}
 	}
-
-
 
 	return (0);
 }
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.383
diff -u -p -r1.383 pfvar.h
--- sys/net/pfvar.h	4 Jun 2013 19:03:12 -0000	1.383
+++ sys/net/pfvar.h	5 Jun 2013 02:36:14 -0000
@@ -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 pf
 				    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_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(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(
 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 *);
@@ -1980,6 +1985,8 @@ int			 pf_map_addr(sa_family_t, struct p
 			    struct pf_pool *, enum pf_sn_types);
 
 int			 pf_postprocess_addr(struct pf_state *);
+
+void			 pf_cksum(struct pf_pdesc *, struct mbuf *);
 
 #endif /* _KERNEL */
 
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.211
diff -u -p -r1.211 ip_input.c
--- sys/netinet/ip_input.c	17 May 2013 09:04:30 -0000	1.211
+++ sys/netinet/ip_input.c	5 Jun 2013 02:36:15 -0000
@@ -480,6 +480,9 @@ ipv4_input(struct mbuf *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: sys/netinet/ip_output.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.240
diff -u -p -r1.240 ip_output.c
--- sys/netinet/ip_output.c	5 Jun 2013 02:25:05 -0000	1.240
+++ sys/netinet/ip_output.c	5 Jun 2013 02:36:15 -0000
@@ -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) &&
Index: sys/netinet6/in6.h
===================================================================
RCS file: /cvs/src/sys/netinet6/in6.h,v
retrieving revision 1.64
diff -u -p -r1.64 in6.h
--- sys/netinet6/in6.h	28 Mar 2013 15:05:32 -0000	1.64
+++ sys/netinet6/in6.h	5 Jun 2013 02:36:15 -0000
@@ -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);
Index: sys/netinet6/ip6_divert.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_divert.c,v
retrieving revision 1.12
diff -u -p -r1.12 ip6_divert.c
--- sys/netinet6/ip6_divert.c	31 May 2013 15:04:24 -0000	1.12
+++ sys/netinet6/ip6_divert.c	5 Jun 2013 02:36:15 -0000
@@ -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;
Index: sys/netinet6/ip6_forward.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_forward.c,v
retrieving revision 1.58
diff -u -p -r1.58 ip6_forward.c
--- sys/netinet6/ip6_forward.c	31 May 2013 15:04:24 -0000	1.58
+++ sys/netinet6/ip6_forward.c	5 Jun 2013 02:36:15 -0000
@@ -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)) {
Index: sys/netinet6/ip6_input.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.112
diff -u -p -r1.112 ip6_input.c
--- sys/netinet6/ip6_input.c	24 Apr 2013 10:17:08 -0000	1.112
+++ sys/netinet6/ip6_input.c	5 Jun 2013 02:36:16 -0000
@@ -650,6 +650,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: sys/netinet6/ip6_output.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.140
diff -u -p -r1.140 ip6_output.c
--- sys/netinet6/ip6_output.c	31 May 2013 15:04:24 -0000	1.140
+++ sys/netinet6/ip6_output.c	5 Jun 2013 02:36:16 -0000
@@ -126,6 +126,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;
@@ -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.
@@ -3181,4 +3185,69 @@ 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;
+
+	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 */
+	}
 }
