summaryrefslogtreecommitdiff
path: root/sys/netinet6/ip6_divert.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/ip6_divert.c')
-rw-r--r--sys/netinet6/ip6_divert.c40
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);