diff options
author | Lawrence Teo <lteo@cvs.openbsd.org> | 2014-07-12 03:27:01 +0000 |
---|---|---|
committer | Lawrence Teo <lteo@cvs.openbsd.org> | 2014-07-12 03:27:01 +0000 |
commit | 1bf4d2923f8199c11151e9b627302e5f3b806421 (patch) | |
tree | 2574ed126facb2be29efdab298d839128f0e97af /sys/netinet6/ip6_divert.c | |
parent | ce3f9a0c5cc7cf1f9b41f8bba44ebb8cf0a8d06d (diff) |
Implement checksum offload for divert(4): simply set the checksum flag
and let the stack take care of the checksums for reinjected outbound
packets.
Reinjected inbound packets will continue to have their checksums
calculated manually but we can now take advantage of in_proto_cksum_out
and in6_proto_cksum_out to streamline the way their checksums are done.
help from florian@ and henning@, feedback from naddy@
ok florian@ henning@
Diffstat (limited to 'sys/netinet6/ip6_divert.c')
-rw-r--r-- | sys/netinet6/ip6_divert.c | 40 |
1 files changed, 21 insertions, 19 deletions
diff --git a/sys/netinet6/ip6_divert.c b/sys/netinet6/ip6_divert.c index 41902474d4b..c7ab5f86e74 100644 --- a/sys/netinet6/ip6_divert.c +++ b/sys/netinet6/ip6_divert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_divert.c,v 1.24 2014/07/10 03:17:59 lteo Exp $ */ +/* $OpenBSD: ip6_divert.c,v 1.25 2014/07/12 03:27:00 lteo Exp $ */ /* * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> @@ -89,10 +89,9 @@ divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, struct sockaddr_in6 *sin6; struct socket *so; struct ifaddr *ifa; - int s, error = 0, p_hdrlen = 0, nxt = 0, off; + int s, error = 0, p_hdrlen = 0, nxt = 0, off, dir; struct ip6_hdr *ip6; - u_int16_t csum = 0; - size_t p_off = 0; + u_int16_t csum_flag = 0; m->m_pkthdr.rcvif = NULL; m->m_nextpkt = NULL; @@ -125,39 +124,35 @@ divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); if (off < sizeof(struct ip6_hdr)) goto fail; + + dir = (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? PF_OUT : PF_IN); + switch (nxt) { case IPPROTO_TCP: p_hdrlen = sizeof(struct tcphdr); - p_off = offsetof(struct tcphdr, th_sum); + csum_flag = M_TCP_CSUM_OUT; break; case IPPROTO_UDP: p_hdrlen = sizeof(struct udphdr); - p_off = offsetof(struct udphdr, uh_sum); + csum_flag = M_UDP_CSUM_OUT; break; case IPPROTO_ICMPV6: p_hdrlen = sizeof(struct icmp6_hdr); - p_off = offsetof(struct icmp6_hdr, icmp6_cksum); + csum_flag = M_ICMP_CSUM_OUT; break; default: /* nothing */ break; } - if (p_hdrlen) { - if (m->m_pkthdr.len < off + p_hdrlen) - goto fail; + if (p_hdrlen && m->m_pkthdr.len < off + p_hdrlen) + goto fail; - if ((error = m_copyback(m, off + p_off, sizeof(csum), &csum, M_NOWAIT))) - goto fail; - csum = in6_cksum(m, nxt, off, m->m_pkthdr.len - off); - if (nxt == IPPROTO_UDP && csum == 0) - csum = 0xffff; - if ((error = m_copyback(m, off + p_off, sizeof(csum), &csum, M_NOWAIT))) - goto fail; - } + if (csum_flag) + m->m_pkthdr.csum_flags |= csum_flag; m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET; - if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if (dir == PF_IN) { ip6addr.sin6_addr = sin6->sin6_addr; ifa = ifa_ifwithaddr(sin6tosa(&ip6addr), m->m_pkthdr.ph_rtableid); @@ -169,6 +164,13 @@ divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam, inq = &ip6intrq; + /* + * Recalculate the protocol checksum for the inbound packet + * since the userspace application may have modified the packet + * prior to reinjection. + */ + in6_proto_cksum_out(m, NULL); + s = splnet(); IF_INPUT_ENQUEUE(inq, m); schednetisr(NETISR_IPV6); |