Add UDP encapsulation of ESP in IPv6

This patch provides UDP encapsulation of ESP packets over IPv6.
Ports the IPv4 code to IPv6 and adds support for IPv6 in udpencap.c
As required by the RFC and unlike in IPv4 encapsulation,
UDP checksums are calculated.

Co-authored-by:	Aurelien Cazuc <aurelien.cazuc.external@stormshield.eu>
Sponsored-by:	Stormshield
Sponsored-by:	Wiktel
Sponsored-by:	Klara, Inc.
This commit is contained in:
Xavier Beaudouin 2024-01-16 20:44:34 +00:00 committed by Allan Jude
parent 38f5f2a4af
commit 80044c785c
9 changed files with 322 additions and 91 deletions

View File

@ -4429,7 +4429,7 @@ netipsec/keysock.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/subr_ipsec.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/udpencap.c optional ipsec inet
netipsec/udpencap.c optional ipsec inet | ipsec inet6
netipsec/xform_ah.c optional ipsec inet | ipsec inet6
netipsec/xform_esp.c optional ipsec inet | ipsec inet6
netipsec/xform_ipcomp.c optional ipsec inet | ipsec inet6

View File

@ -267,7 +267,7 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
}
if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */
if (IPSEC_ENABLED(ipv4) &&
UDPENCAP_INPUT(n, off, AF_INET) != 0)
UDPENCAP_INPUT(ipv4, n, off, AF_INET) != 0)
return (0); /* Consumed. */
}
#endif /* IPSEC */
@ -893,19 +893,32 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
case SOPT_SET:
switch (sopt->sopt_name) {
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
#ifdef INET
#if defined(INET) || defined(INET6)
case UDP_ENCAP:
if (!INP_CHECK_SOCKAF(so, AF_INET)) {
INP_WUNLOCK(inp);
return (EINVAL);
#ifdef INET
if (INP_SOCKAF(so) == AF_INET) {
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(ipv4, inp, sopt);
break;
}
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(inp, sopt);
break;
#endif /* INET */
#ifdef INET6
if (INP_SOCKAF(so) == AF_INET6) {
if (!IPSEC_ENABLED(ipv6)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(ipv6, inp, sopt);
break;
}
#endif /* INET6 */
INP_WUNLOCK(inp);
return (EINVAL);
#endif /* INET || INET6 */
#endif /* IPSEC */
case UDPLITE_SEND_CSCOV:
case UDPLITE_RECV_CSCOV:
@ -944,19 +957,32 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
case SOPT_GET:
switch (sopt->sopt_name) {
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
#ifdef INET
#if defined(INET) || defined(INET6)
case UDP_ENCAP:
if (!INP_CHECK_SOCKAF(so, AF_INET)) {
INP_WUNLOCK(inp);
return (EINVAL);
#ifdef INET
if (INP_SOCKAF(so) == AF_INET) {
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(ipv4, inp, sopt);
break;
}
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(inp, sopt);
break;
#endif /* INET */
#ifdef INET6
if (INP_SOCKAF(so) == AF_INET6) {
if (!IPSEC_ENABLED(ipv6)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(ipv6, inp, sopt);
break;
}
#endif /* INET6 */
INP_WUNLOCK(inp);
return (EINVAL);
#endif /* INET || INET6 */
#endif /* IPSEC */
case UDPLITE_SEND_CSCOV:
case UDPLITE_RECV_CSCOV:

View File

@ -75,6 +75,7 @@
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/domain.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@ -158,6 +159,9 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
if (filtered)
return (in_pcbrele_rlocked(inp));
}
off += sizeof(struct udphdr);
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* Check AH/ESP integrity. */
if (IPSEC_ENABLED(ipv6)) {
@ -165,6 +169,12 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
m_freem(n);
return (0);
}
/* IPSec UDP encaps. */
if ((up->u_flags & UF_ESPINUDP) != 0 &&
UDPENCAP_INPUT(ipv6, n, off, AF_INET6) != 0) {
return (0); /* Consumed. */
}
}
#endif /* IPSEC */
#ifdef MAC
@ -189,7 +199,7 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
opts = tmp_opts;
}
}
m_adj(n, off + sizeof(struct udphdr));
m_adj(n, off);
so = inp->inp_socket;
SOCKBUF_LOCK(&so->so_rcv);

View File

@ -628,6 +628,15 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
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) {

View File

@ -84,6 +84,8 @@ static const struct ipsec_methods ipv6_methods = {
.check_policy = ipsec6_in_reject,
.ctlinput = ipsec6_ctlinput,
.hdrsize = ipsec_hdrsiz_inpcb,
.udp_input = udp_ipsec_input,
.udp_pcbctl = udp_ipsec_pcbctl,
};
#ifndef KLD_MODULE
static const struct ipsec_support ipv6_ipsec = {

View File

@ -935,7 +935,7 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
}
key_freesp(&sp), sp = NULL; /* Release reference to SP */
#ifdef INET
#if defined(INET) || defined(INET6)
/*
* Do UDP encapsulation if SA requires it.
*/
@ -944,7 +944,7 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
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).

View File

@ -164,10 +164,10 @@ extern const struct ipsec_support * const ipv6_ipsec_support;
#define IPSEC_CTLINPUT(proto, param) \
(*(proto ## _ipsec_support)->methods->ctlinput)(param)
#define UDPENCAP_INPUT(m, ...) \
(*ipv4_ipsec_support->methods->udp_input)(m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(inp, sopt) \
(*ipv4_ipsec_support->methods->udp_pcbctl)(inp, sopt)
#define UDPENCAP_INPUT(proto, m, ...) \
(*(proto ## _ipsec_support)->methods->udp_input)(m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(proto, inp, sopt) \
(*(proto ## _ipsec_support)->methods->udp_pcbctl)(inp, sopt)
#elif defined(IPSEC_SUPPORT)
struct ipsec_support {
@ -196,10 +196,10 @@ int ipsec_kmod_udp_input(struct ipsec_support * const, struct mbuf *, int, int);
int ipsec_kmod_udp_pcbctl(struct ipsec_support * const, struct inpcb *,
struct sockopt *);
#define UDPENCAP_INPUT(m, ...) \
ipsec_kmod_udp_input(ipv4_ipsec_support, m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(inp, sopt) \
ipsec_kmod_udp_pcbctl(ipv4_ipsec_support, inp, sopt)
#define UDPENCAP_INPUT(proto, m, ...) \
ipsec_kmod_udp_input(proto ## _ipsec_support, m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(proto, inp, sopt) \
ipsec_kmod_udp_pcbctl(proto ## _ipsec_support, inp, sopt)
#define IPSEC_INPUT(proto, ...) \
ipsec_kmod_input(proto ## _ipsec_support, __VA_ARGS__)

View File

@ -5752,6 +5752,7 @@ key_setnatt(struct secasvar *sav, const struct sadb_msghdr *mhp)
struct sockaddr *sa;
uint32_t addr;
uint16_t cksum;
int i;
IPSEC_ASSERT(sav->natt == NULL, ("natt is already initialized"));
/*
@ -5838,52 +5839,117 @@ key_setnatt(struct secasvar *sav, const struct sadb_msghdr *mhp)
if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL) {
cksum = 0;
if (oai != NULL) {
/* Currently we support only AF_INET */
sa = (struct sockaddr *)(oai + 1);
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in)) {
switch (sa->sa_family) {
#ifdef AF_INET
case AF_INET:
if (sa->sa_len != sizeof(struct sockaddr_in)) {
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAi header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
sav->sah->saidx.src.sin.sin_addr.s_addr) {
bcopy(sa, &sav->natt->oai.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAI;
/* Calculate checksum delta */
addr = sav->sah->saidx.src.sin.sin_addr.s_addr;
cksum = in_addword(cksum, ~addr >> 16);
cksum = in_addword(cksum, ~addr & 0xffff);
addr = sav->natt->oai.sin.sin_addr.s_addr;
cksum = in_addword(cksum, addr >> 16);
cksum = in_addword(cksum, addr & 0xffff);
}
break;
#endif
#ifdef AF_INET6
case AF_INET6:
if (sa->sa_len != sizeof(struct sockaddr_in6)) {
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAi header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
&sav->sah->saidx.src.sin6.sin6_addr.s6_addr,
sizeof(struct in6_addr)) != 0) {
bcopy(sa, &sav->natt->oai.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAI;
/* Calculate checksum delta */
for (i = 0; i < 8; i++) {
cksum = in_addword(cksum,
~sav->sah->saidx.src.sin6.sin6_addr.s6_addr16[i]);
cksum = in_addword(cksum,
sav->natt->oai.sin6.sin6_addr.s6_addr16[i]);
}
}
break;
#endif
default:
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAi header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
sav->sah->saidx.src.sin.sin_addr.s_addr) {
bcopy(sa, &sav->natt->oai.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAI;
/* Calculate checksum delta */
addr = sav->sah->saidx.src.sin.sin_addr.s_addr;
cksum = in_addword(cksum, ~addr >> 16);
cksum = in_addword(cksum, ~addr & 0xffff);
addr = sav->natt->oai.sin.sin_addr.s_addr;
cksum = in_addword(cksum, addr >> 16);
cksum = in_addword(cksum, addr & 0xffff);
}
}
if (oar != NULL) {
/* Currently we support only AF_INET */
sa = (struct sockaddr *)(oar + 1);
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in)) {
switch (sa->sa_family) {
#ifdef AF_INET
case AF_INET:
if (sa->sa_len != sizeof(struct sockaddr_in)) {
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAr header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
sav->sah->saidx.dst.sin.sin_addr.s_addr) {
bcopy(sa, &sav->natt->oar.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAR;
/* Calculate checksum delta */
addr = sav->sah->saidx.dst.sin.sin_addr.s_addr;
cksum = in_addword(cksum, ~addr >> 16);
cksum = in_addword(cksum, ~addr & 0xffff);
addr = sav->natt->oar.sin.sin_addr.s_addr;
cksum = in_addword(cksum, addr >> 16);
cksum = in_addword(cksum, addr & 0xffff);
}
break;
#endif
#ifdef AF_INET6
case AF_INET6:
if (sa->sa_len != sizeof(struct sockaddr_in6)) {
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAr header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
&sav->sah->saidx.dst.sin6.sin6_addr.s6_addr, 16) != 0) {
bcopy(sa, &sav->natt->oar.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAR;
/* Calculate checksum delta */
for (i = 0; i < 8; i++) {
cksum = in_addword(cksum,
~sav->sah->saidx.dst.sin6.sin6_addr.s6_addr16[i]);
cksum = in_addword(cksum,
sav->natt->oar.sin6.sin6_addr.s6_addr16[i]);
}
}
break;
#endif
default:
ipseclog((LOG_DEBUG,
"%s: wrong NAT-OAr header.\n",
__func__));
return (EINVAL);
}
/* Ignore address if it the same */
if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
sav->sah->saidx.dst.sin.sin_addr.s_addr) {
bcopy(sa, &sav->natt->oar.sa, sa->sa_len);
sav->natt->flags |= IPSEC_NATT_F_OAR;
/* Calculate checksum delta */
addr = sav->sah->saidx.dst.sin.sin_addr.s_addr;
cksum = in_addword(cksum, ~addr >> 16);
cksum = in_addword(cksum, ~addr & 0xffff);
addr = sav->natt->oar.sin.sin_addr.s_addr;
cksum = in_addword(cksum, addr >> 16);
cksum = in_addword(cksum, addr & 0xffff);
}
}
sav->natt->cksum = cksum;
}

View File

@ -26,6 +26,7 @@
#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/param.h>
@ -42,11 +43,14 @@
#include <netinet/in_pcb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet6/ip6_var.h>
#include <net/vnet.h>
#include <netipsec/ipsec.h>
@ -116,7 +120,6 @@ udp_ipsec_input(struct mbuf *m, int off, int af)
union sockaddr_union dst;
struct secasvar *sav;
struct udphdr *udp;
struct ip *ip;
uint32_t spi;
int hlen;
@ -141,7 +144,9 @@ udp_ipsec_input(struct mbuf *m, int off, int af)
dst.sa.sa_family = af;
switch (af) {
#ifdef INET
case AF_INET:
case AF_INET: {
struct ip *ip;
dst.sin.sin_len = sizeof(struct sockaddr_in);
ip = mtod(m, struct ip *);
ip->ip_p = IPPROTO_ESP;
@ -149,11 +154,20 @@ udp_ipsec_input(struct mbuf *m, int off, int af)
hlen = ip->ip_hl << 2;
dst.sin.sin_addr = ip->ip_dst;
break;
}
#endif
#ifdef INET6
case AF_INET6:
/* Not yet */
/* FALLTHROUGH */
case AF_INET6: {
struct ip6_hdr *ip6;
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);
@ -192,12 +206,30 @@ udp_ipsec_input(struct mbuf *m, int off, int af)
* <-skip->
*/
m_striphdr(m, hlen, sizeof(*udp));
/*
* 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,16 +244,31 @@ udp_ipsec_output(struct mbuf *m, struct secasvar *sav)
{
struct udphdr *udp;
struct mbuf *n;
struct ip *ip;
int hlen, off;
IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required."));
if (sav->sah->saidx.dst.sa.sa_family == AF_INET6)
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET: {
struct ip *ip;
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
break;
}
#endif
#ifdef INET6
case AF_INET6:
KASSERT(ip6->ip6_nxt == IPPROTO_ESP,
("unexpected next header type %d", ip->ip6_nxt));
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__));
@ -234,9 +281,36 @@ udp_ipsec_output(struct mbuf *m, struct secasvar *sav)
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: {
struct ip *ip;
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: {
struct ip6_hdr *ip6;
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);
}
@ -244,7 +318,6 @@ void
udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto,
int skip)
{
struct ip *ip;
uint16_t cksum, off;
IPSEC_ASSERT(sav->natt != NULL, ("NAT-T isn't required"));
@ -270,22 +343,67 @@ udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto,
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: {
struct ip *ip;
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: {
struct ip6_hdr *ip6;
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;
}
}
}