Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c +++ sys/netinet6/in6_proto.c @@ -161,7 +161,7 @@ .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = udp6_input, .pr_ctlinput = udp6_ctlinput, - .pr_ctloutput = ip6_ctloutput, + .pr_ctloutput = udp6_ctloutput, #ifndef INET /* Do not call initialization twice. */ .pr_init = udp_init, #endif Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c +++ sys/netinet6/udp6_usrreq.c @@ -78,6 +78,7 @@ #include "opt_rss.h" #include +#include #include #include #include @@ -154,6 +155,9 @@ INP_RLOCK(inp); return (in_pcbrele_rlocked(inp)); } + + off += sizeof(struct udphdr); + #if defined(IPSEC) || defined(IPSEC_SUPPORT) /* Check AH/ESP integrity. */ if (IPSEC_ENABLED(ipv6)) { @@ -162,6 +166,11 @@ return (0); } } + if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */ + if (IPSEC_ENABLED(ipv6) && + UDPENCAP_INPUT(n, off, AF_INET6) != 0) + return (0); /* Consumed. */ + } #endif /* IPSEC */ #ifdef MAC if (mac_inpcb_check_deliver(inp, n) != 0) { @@ -185,7 +194,7 @@ } } - m_adj(n, off + sizeof(struct udphdr)); + m_adj(n, off); so = inp->inp_socket; SOCKBUF_LOCK(&so->so_rcv); @@ -684,6 +693,78 @@ SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection"); +int +udp6_ctloutput(struct socket *so, struct sockopt *sopt) +{ + struct inpcb *inp; + int error; + + error = 0; + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("%s: inp == NULL", __func__)); + INP_WLOCK(inp); + if (sopt->sopt_level != so->so_proto->pr_protocol) { +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) { + INP_WUNLOCK(inp); + error = ip6_ctloutput(so, sopt); + } +#endif +#if defined(INET) && defined(INET6) + else +#endif +#ifdef INET + { + INP_WUNLOCK(inp); + error = ip_ctloutput(so, sopt); + } +#endif + return (error); + } + + switch (sopt->sopt_dir) { + case SOPT_SET: + switch (sopt->sopt_name) { +#if defined(IPSEC) || defined(IPSEC_SUPPORT) +#ifdef INET6 + case UDP_ENCAP: + if (!IPSEC_ENABLED(ipv6)) { + INP_WUNLOCK(inp); + return (ENOPROTOOPT); + } + error = UDPENCAP_PCBCTL(inp, sopt); + break; +#endif /* INET */ +#endif /* IPSEC */ + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + case SOPT_GET: + switch (sopt->sopt_name) { +#if defined(IPSEC) || defined(IPSEC_SUPPORT) +#ifdef INET6 + case UDP_ENCAP: + if (!IPSEC_ENABLED(ipv6)) { + INP_WUNLOCK(inp); + return (ENOPROTOOPT); + } + error = UDPENCAP_PCBCTL(inp, sopt); + break; +#endif /* INET6 */ +#endif /* IPSEC */ + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + } + return (error); +} + static int udp6_output(struct socket *so, int flags_arg, struct mbuf *m, struct sockaddr *addr6, struct mbuf *control, struct thread *td) Index: sys/netinet6/udp6_var.h =================================================================== --- sys/netinet6/udp6_var.h +++ sys/netinet6/udp6_var.h @@ -72,6 +72,7 @@ void udp6_ctlinput(int, struct sockaddr *, void *); void udplite6_ctlinput(int, struct sockaddr *, void *); +int udp6_ctloutput(struct socket *, struct sockopt *); int udp6_input(struct mbuf **, int *, int); #endif Index: sys/netipsec/ipsec_input.c =================================================================== --- sys/netipsec/ipsec_input.c +++ sys/netipsec/ipsec_input.c @@ -533,6 +533,15 @@ m_copydata(m, protoff, 1, &nxt8); prot = nxt8; + /* + * Check that we have NAT-T enabled and apply transport mode + * decapsulation NAT procedure (RFC3948). + * Do this before invoking into the PFIL. + */ + if (sav->natt != NULL && + (prot == IPPROTO_UDP || prot == IPPROTO_TCP)) + udp_ipsec_adjust_cksum(m, sav, prot, skip); + /* IPv6-in-IP encapsulation */ if (prot == IPPROTO_IPV6 && saidx->mode != IPSEC_MODE_TRANSPORT) { Index: sys/netipsec/ipsec_mod.c =================================================================== --- sys/netipsec/ipsec_mod.c +++ sys/netipsec/ipsec_mod.c @@ -85,6 +85,8 @@ .capability = ipsec6_capability, .check_policy = ipsec6_in_reject, .hdrsize = ipsec_hdrsiz_inpcb, + .udp_input = udp_ipsec_input, + .udp_pcbctl = udp_ipsec_pcbctl, }; #ifndef KLD_MODULE static const struct ipsec_support ipv6_ipsec = { Index: sys/netipsec/ipsec_output.c =================================================================== --- sys/netipsec/ipsec_output.c +++ sys/netipsec/ipsec_output.c @@ -775,7 +775,7 @@ } key_freesp(&sp), sp = NULL; /* Release reference to SP */ -#ifdef INET +#if defined(INET) || defined(INET6) /* * Do UDP encapsulation if SA requires it. */ @@ -784,7 +784,7 @@ if (error != 0) goto bad; } -#endif /* INET */ +#endif /* INET || INET6 */ /* * We're done with IPsec processing, transmit the packet using the * appropriate network protocol (IP or IPv6). Index: sys/netipsec/udpencap.c =================================================================== --- sys/netipsec/udpencap.c +++ sys/netipsec/udpencap.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include @@ -44,11 +45,14 @@ #include #include #include +#include #include #include #include #include +#include + #include #include @@ -117,6 +121,7 @@ { union sockaddr_union dst; struct secasvar *sav; + struct ip6_hdr *ip6; struct udphdr *udp; struct ip *ip; uint32_t spi; @@ -154,8 +159,13 @@ #endif #ifdef INET6 case AF_INET6: - /* Not yet */ - /* FALLTHROUGH */ + dst.sin6.sin6_len = sizeof(struct sockaddr_in6); + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_nxt = IPPROTO_ESP; + off = offsetof(struct ip6_hdr, ip6_nxt); + hlen = sizeof(struct ip6_hdr); + dst.sin6.sin6_addr = ip6->ip6_dst; + break; #endif default: ESPSTAT_INC(esps_nopf); @@ -198,8 +208,24 @@ * We cannot yet update the cksums so clear any h/w cksum flags * as they are no longer valid. */ - if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) - m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + switch (af) { +#ifdef INET + case AF_INET: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) + m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) + m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR); + break; +#endif /* INET6 */ + default: + ESPSTAT_INC(esps_nopf); + m_freem(m); + return (EPFNOSUPPORT); + } /* * We can update ip_len and ip_sum here, but ipsec4_input_cb() * will do this anyway, so don't touch them here. @@ -212,6 +238,7 @@ int udp_ipsec_output(struct mbuf *m, struct secasvar *sav) { + struct ip6_hdr *ip6; struct udphdr *udp; struct mbuf *n; struct ip *ip; @@ -219,11 +246,25 @@ IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required.")); - if (sav->sah->saidx.dst.sa.sa_family == AF_INET6) - return (EAFNOSUPPORT); + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + break; +#endif +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof(struct ip6_hdr); + break; +#endif + default: + ESPSTAT_INC(esps_nopf); + return (EAFNOSUPPORT); + } - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2; n = m_makespace(m, hlen, sizeof(*udp), &off); if (n == NULL) { DPRINTF(("%s: m_makespace for udphdr failed\n", __func__)); @@ -236,9 +277,30 @@ udp->uh_sum = 0; udp->uh_ulen = htons(m->m_pkthdr.len - hlen); - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); - ip->ip_p = IPPROTO_UDP; + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_p = IPPROTO_UDP; + break; +#endif +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len); + ip6->ip6_nxt = IPPROTO_UDP; + udp->uh_sum = in6_cksum_pseudo(ip6, m->m_pkthdr.len - hlen, ip6->ip6_nxt, 0); + m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; + m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + break; +#endif + default: + ESPSTAT_INC(esps_nopf); + return (EAFNOSUPPORT); + } + return (0); } @@ -246,6 +308,7 @@ udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto, int skip) { + struct ip6_hdr *ip6; struct ip *ip; uint16_t cksum, off; @@ -272,23 +335,59 @@ if (proto == IPPROTO_TCP) { /* Ignore for TCP. */ m->m_pkthdr.csum_data = 0xffff; - m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | - CSUM_PSEUDO_HDR); + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | + CSUM_PSEUDO_HDR); + break; +#endif +#ifdef INET6 + case AF_INET6: + m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID_IPV6 | + CSUM_PSEUDO_HDR); + break; +#endif + default: + break; + } return; } cksum = 0; /* Reset for UDP. */ } m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); } else { /* Fully recompute */ - ip = mtod(m, struct ip *); - cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, - htons(m->m_pkthdr.len - skip + proto)); - m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); - m->m_pkthdr.csum_flags = - (proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP; - m->m_pkthdr.csum_data = off; - in_delayed_cksum(m); - m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(m->m_pkthdr.len - skip + proto)); + m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); + m->m_pkthdr.csum_flags = + (proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP; + m->m_pkthdr.csum_data = off; + in_delayed_cksum(m); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + break; +#endif +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + cksum = in6_cksum_pseudo(ip6, m->m_pkthdr.len - skip, proto, 0); + m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); + m->m_pkthdr.csum_flags = + (proto == IPPROTO_UDP) ? CSUM_UDP_IPV6: CSUM_TCP_IPV6; + m->m_pkthdr.csum_data = off; + in6_delayed_cksum(m, m->m_pkthdr.len - sizeof(struct ip6_hdr), sizeof(struct ip6_hdr)); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; + break; +#endif + default: + break; + } } }