summaryrefslogtreecommitdiff
path: root/sys/netinet6
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2012-11-01 07:55:57 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2012-11-01 07:55:57 +0000
commit3e894385f9270ed9b5e48e20e90bb2039e96480b (patch)
tree95d1f777b809073048e099140897afb666a2fe8f /sys/netinet6
parent8b9044e72499aee42f7fe82a72015b3e02b99652 (diff)
redo most of the protocol (tcp/udp/...) checksum handling
-assume we have hardware checksum offloading. stop mucking with the checksum in most of the stack -stop checksum mucking in pf, just set a "needs checksumming" flag if needed -in all output pathes, very late, if we figure out the outbound interface doesn't have hw cksum offloading, do the cksum in software. this especially makes the bridge path behave like a regular output path -little special casing for bridge still required until the broadcast path loses its disgusting shortcut hacks, but at least it's in one place now and not all over the stack in6_proto_cksum_out mostly written by krw@ started at k2k11 in iceland more than 1.5 years ago - yes it took that long, this stuff is everything but easy. this happens to fix the infamous pf rdr bug that made us turn off proto cksum offloading on almost all interface drivers. ok camield sthen claudio, testing by many, thanks!
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6.h3
-rw-r--r--sys/netinet6/ip6_divert.c5
-rw-r--r--sys/netinet6/ip6_forward.c5
-rw-r--r--sys/netinet6/ip6_input.c5
-rw-r--r--sys/netinet6/ip6_output.c65
5 files changed, 77 insertions, 6 deletions
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 9bbec7df68d..ad01de151cb 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6.h,v 1.59 2012/09/17 20:01:26 yasuoka Exp $ */
+/* $OpenBSD: in6.h,v 1.60 2012/11/01 07:55:56 henning Exp $ */
/* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */
/*
@@ -758,6 +758,7 @@ struct ip6_mtuinfo {
struct cmsghdr;
int in6_cksum(struct mbuf *, u_int8_t, u_int32_t, u_int32_t);
+extern void in6_proto_cksum_out(struct mbuf *, struct ifnet *);
int in6_localaddr(struct in6_addr *);
int in6_addrscope(struct in6_addr *);
struct in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *, u_int);
diff --git a/sys/netinet6/ip6_divert.c b/sys/netinet6/ip6_divert.c
index fbc6d9551f5..603c7995625 100644
--- a/sys/netinet6/ip6_divert.c
+++ b/sys/netinet6/ip6_divert.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_divert.c,v 1.6 2012/10/21 13:06:03 benno Exp $ */
+/* $OpenBSD: ip6_divert.c,v 1.7 2012/11/01 07:55:56 henning Exp $ */
/*
* Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
@@ -188,6 +188,9 @@ divert6_packet(struct mbuf *m, int dir)
break;
}
}
+ /* force checksum calculation */
+ if (dir == PF_OUT)
+ in6_proto_cksum_out(m, NULL);
if (inp != CIRCLEQ_END(&divb6table.inpt_queue)) {
sa = inp->inp_socket;
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index 3c63ac1c6df..f5e6754f4b4 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_forward.c,v 1.54 2012/07/16 18:05:36 markus Exp $ */
+/* $OpenBSD: ip6_forward.c,v 1.55 2012/11/01 07:55:56 henning Exp $ */
/* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */
/*
@@ -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)) {
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 6adf9bc17ce..3cbd344b3da 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_input.c,v 1.103 2012/04/03 15:03:08 mikeb Exp $ */
+/* $OpenBSD: ip6_input.c,v 1.104 2012/11/01 07:55:56 henning Exp $ */
/* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */
/*
@@ -670,6 +670,9 @@ ip6_input(struct mbuf *m)
return;
}
+ /* pf might have changed things */
+ in6_proto_cksum_out(m, NULL);
+
ip6 = mtod(m, struct ip6_hdr *);
/*
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index fb31a691399..f12ea0e3fa3 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_output.c,v 1.128 2012/10/16 08:09:09 bluhm Exp $ */
+/* $OpenBSD: ip6_output.c,v 1.129 2012/11/01 07:55:56 henning Exp $ */
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
/*
@@ -134,6 +134,8 @@ int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
struct ifnet *, struct in6_addr *, u_long *, int *);
int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
+void in6_delayed_cksum(struct mbuf *, u_int8_t);
+void in6_proto_cksum_out(struct mbuf *, struct ifnet *);
/* Context for non-repeating IDs */
struct idgen32_ctx ip6_id_ctx;
@@ -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)) {
@@ -3214,3 +3218,62 @@ 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_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 */
+ }
+}