Index: projects/ipsec/sys/netipsec/ipsec_mbuf.c =================================================================== --- projects/ipsec/sys/netipsec/ipsec_mbuf.c (revision 310709) +++ projects/ipsec/sys/netipsec/ipsec_mbuf.c (revision 310710) @@ -1,323 +1,337 @@ /*- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * IPsec-specific mbuf routines. */ #include #include #include #include #include #include #include #include /* * Make space for a new header of length hlen at skip bytes * into the packet. When doing this we allocate new mbufs only * when absolutely necessary. The mbuf where the new header * is to go is returned together with an offset into the mbuf. * If NULL is returned then the mbuf chain may have been modified; * the caller is assumed to always free the chain. */ struct mbuf * m_makespace(struct mbuf *m0, int skip, int hlen, int *off) { struct mbuf *m; unsigned remain; IPSEC_ASSERT(m0 != NULL, ("null mbuf")); IPSEC_ASSERT(hlen < MHLEN, ("hlen too big: %u", hlen)); for (m = m0; m && skip > m->m_len; m = m->m_next) skip -= m->m_len; if (m == NULL) return (NULL); /* * At this point skip is the offset into the mbuf m * where the new header should be placed. Figure out * if there's space to insert the new header. If so, * and copying the remainder makes sense then do so. * Otherwise insert a new mbuf in the chain, splitting * the contents of m as needed. */ remain = m->m_len - skip; /* data to move */ - if (hlen > M_TRAILINGSPACE(m)) { + if (remain > skip && + hlen + max_linkhdr < M_LEADINGSPACE(m)) { + /* + * mbuf has enough free space at the beginning. + * XXX: which operation is the most heavy - copying of + * possible several hundred of bytes or allocation + * of new mbuf? We can remove max_linkhdr check + * here, but it is possible that this will lead + * to allocation of new mbuf in Layer 2 code. + */ + m->m_data -= hlen; + bcopy(mtodo(m, hlen), mtod(m, caddr_t), skip); + m->m_len += hlen; + *off = skip; + } else if (hlen > M_TRAILINGSPACE(m)) { struct mbuf *n0, *n, **np; int todo, len, done, alloc; n0 = NULL; np = &n0; alloc = 0; done = 0; todo = remain; while (todo > 0) { if (todo > MHLEN) { n = m_getcl(M_NOWAIT, m->m_type, 0); len = MCLBYTES; } else { n = m_get(M_NOWAIT, m->m_type); len = MHLEN; } if (n == NULL) { m_freem(n0); return NULL; } *np = n; np = &n->m_next; alloc++; len = min(todo, len); memcpy(n->m_data, mtod(m, char *) + skip + done, len); n->m_len = len; done += len; todo -= len; } if (hlen <= M_TRAILINGSPACE(m) + remain) { m->m_len = skip + hlen; *off = skip; if (n0 != NULL) { *np = m->m_next; m->m_next = n0; } } else { n = m_get(M_NOWAIT, m->m_type); if (n == NULL) { m_freem(n0); return NULL; } alloc++; if ((n->m_next = n0) == NULL) np = &n->m_next; n0 = n; *np = m->m_next; m->m_next = n0; n->m_len = hlen; m->m_len = skip; m = n; /* header is at front ... */ *off = 0; /* ... of new mbuf */ } IPSECSTAT_INC(ips_mbinserted); } else { /* * Copy the remainder to the back of the mbuf * so there's space to write the new header. */ bcopy(mtod(m, caddr_t) + skip, - mtod(m, caddr_t) + skip + hlen, remain); + mtod(m, caddr_t) + skip + hlen, remain); m->m_len += hlen; *off = skip; } m0->m_pkthdr.len += hlen; /* adjust packet length */ return m; } /* * m_pad(m, n) pads with bytes at the end. The packet header * length is updated, and a pointer to the first byte of the padding * (which is guaranteed to be all in one mbuf) is returned. */ caddr_t m_pad(struct mbuf *m, int n) { register struct mbuf *m0, *m1; register int len, pad; caddr_t retval; if (n <= 0) { /* No stupid arguments. */ DPRINTF(("%s: pad length invalid (%d)\n", __func__, n)); m_freem(m); return NULL; } len = m->m_pkthdr.len; pad = n; m0 = m; while (m0->m_len < len) { len -= m0->m_len; m0 = m0->m_next; } if (m0->m_len != len) { DPRINTF(("%s: length mismatch (should be %d instead of %d)\n", __func__, m->m_pkthdr.len, m->m_pkthdr.len + m0->m_len - len)); m_freem(m); return NULL; } /* Check for zero-length trailing mbufs, and find the last one. */ for (m1 = m0; m1->m_next; m1 = m1->m_next) { if (m1->m_next->m_len != 0) { DPRINTF(("%s: length mismatch (should be %d instead " "of %d)\n", __func__, m->m_pkthdr.len, m->m_pkthdr.len + m1->m_next->m_len)); m_freem(m); return NULL; } m0 = m1->m_next; } if (pad > M_TRAILINGSPACE(m0)) { /* Add an mbuf to the chain. */ MGET(m1, M_NOWAIT, MT_DATA); if (m1 == NULL) { m_freem(m0); DPRINTF(("%s: unable to get extra mbuf\n", __func__)); return NULL; } m0->m_next = m1; m0 = m1; m0->m_len = 0; } retval = m0->m_data + m0->m_len; m0->m_len += pad; m->m_pkthdr.len += pad; return retval; } /* * Remove hlen data at offset skip in the packet. This is used by * the protocols strip protocol headers and associated data (e.g. IV, * authenticator) on input. */ int m_striphdr(struct mbuf *m, int skip, int hlen) { struct mbuf *m1; int roff; /* Find beginning of header */ m1 = m_getptr(m, skip, &roff); if (m1 == NULL) return (EINVAL); /* Remove the header and associated data from the mbuf. */ if (roff == 0) { /* The header was at the beginning of the mbuf */ IPSECSTAT_INC(ips_input_front); m_adj(m1, hlen); if ((m1->m_flags & M_PKTHDR) == 0) m->m_pkthdr.len -= hlen; } else if (roff + hlen >= m1->m_len) { struct mbuf *mo; /* * Part or all of the header is at the end of this mbuf, * so first let's remove the remainder of the header from * the beginning of the remainder of the mbuf chain, if any. */ IPSECSTAT_INC(ips_input_end); if (roff + hlen > m1->m_len) { /* Adjust the next mbuf by the remainder */ m_adj(m1->m_next, roff + hlen - m1->m_len); /* The second mbuf is guaranteed not to have a pkthdr... */ m->m_pkthdr.len -= (roff + hlen - m1->m_len); } /* Now, let's unlink the mbuf chain for a second...*/ mo = m1->m_next; m1->m_next = NULL; /* ...and trim the end of the first part of the chain...sick */ m_adj(m1, -(m1->m_len - roff)); if ((m1->m_flags & M_PKTHDR) == 0) m->m_pkthdr.len -= (m1->m_len - roff); /* Finally, let's relink */ m1->m_next = mo; } else { /* * The header lies in the "middle" of the mbuf; copy * the remainder of the mbuf down over the header. */ IPSECSTAT_INC(ips_input_middle); bcopy(mtod(m1, u_char *) + roff + hlen, mtod(m1, u_char *) + roff, m1->m_len - (roff + hlen)); m1->m_len -= hlen; m->m_pkthdr.len -= hlen; } return (0); } /* * Diagnostic routine to check mbuf alignment as required by the * crypto device drivers (that use DMA). */ void m_checkalignment(const char* where, struct mbuf *m0, int off, int len) { int roff; struct mbuf *m = m_getptr(m0, off, &roff); caddr_t addr; if (m == NULL) return; printf("%s (off %u len %u): ", where, off, len); addr = mtod(m, caddr_t) + roff; do { int mlen; if (((uintptr_t) addr) & 3) { printf("addr misaligned %p,", addr); break; } mlen = m->m_len; if (mlen > len) mlen = len; len -= mlen; if (len && (mlen & 3)) { printf("len mismatch %u,", mlen); break; } m = m->m_next; addr = m ? mtod(m, caddr_t) : NULL; } while (m && len > 0); for (m = m0; m; m = m->m_next) printf(" [%p:%u]", mtod(m, caddr_t), m->m_len); printf("\n"); } Index: projects/ipsec/sys/netipsec/ipsec_output.c =================================================================== --- projects/ipsec/sys/netipsec/ipsec_output.c (revision 310709) +++ projects/ipsec/sys/netipsec/ipsec_output.c (revision 310710) @@ -1,950 +1,999 @@ /*- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting * Copyright (c) 2016 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * IPsec output processing. */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_sctp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #include #ifdef INET6 #include #include #endif #include #ifdef INET6 #include #endif #ifdef SCTP #include #endif +#include +#include +#include #include #ifdef INET6 #include #endif #include #include #include #include #include #include #include #include #define IPSEC_OSTAT_INC(proto, name) do { \ if ((proto) == IPPROTO_ESP) \ ESPSTAT_INC(esps_##name); \ else if ((proto) == IPPROTO_AH)\ AHSTAT_INC(ahs_##name); \ else \ IPCOMPSTAT_INC(ipcomps_##name); \ } while (0) static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx); #ifdef INET static struct secasvar * ipsec4_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error) { struct secasindex *saidx, tmpsaidx; struct ipsecrequest *isr; struct sockaddr_in *sin; struct secasvar *sav; struct ip *ip; /* * Check system global policy controls. */ next: isr = sp->req[*pidx]; if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { DPRINTF(("%s: IPsec outbound packet dropped due" " to policy (check your sysctls)\n", __func__)); IPSEC_OSTAT_INC(isr->saidx.proto, pdrops); *error = EHOSTUNREACH; return (NULL); } /* * Craft SA index to search for proper SA. Note that * we only initialize unspecified SA peers for transport * mode; for tunnel mode they must already be filled in. */ if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { saidx = &tmpsaidx; *saidx = isr->saidx; ip = mtod(m, struct ip *); if (saidx->src.sa.sa_len == 0) { sin = &saidx->src.sin; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_port = IPSEC_PORT_ANY; sin->sin_addr = ip->ip_src; } if (saidx->dst.sa.sa_len == 0) { sin = &saidx->dst.sin; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_port = IPSEC_PORT_ANY; sin->sin_addr = ip->ip_dst; } } else saidx = &sp->req[*pidx]->saidx; /* * Lookup SA and validate it. */ sav = key_allocsa_policy(sp, saidx, error); if (sav == NULL) { IPSECSTAT_INC(ips_out_nosa); if (*error != 0) return (NULL); if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) { /* * We have no SA and policy that doesn't require * this IPsec transform, thus we can continue w/o * IPsec processing, i.e. return EJUSTRETURN. * But first check if there is some bundled transform. */ if (sp->tcount > (*pidx)) { (*pidx)++; goto next; } *error = EJUSTRETURN; } return (NULL); } IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform")); return (sav); } /* * IPsec output logic for IPv4. */ static int ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) { char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN]; struct ipsec_ctx_data ctx; union sockaddr_union *dst; struct secasvar *sav; struct ip *ip; int error, i, off; IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx)); /* * We hold the reference to SP. Content of SP couldn't be changed. * Craft secasindex and do lookup for suitable SA. * Then do encapsulation if needed and call xform's output. * We need to store SP in the xform callback parameters. * In xform callback we will extract SP and it can be used to * determine next transform. At the end of transform we can * release reference to SP. */ sav = ipsec4_allocsa(m, sp, &idx, &error); if (sav == NULL) { if (error == EJUSTRETURN) { /* No IPsec required */ key_freesp(&sp); return (error); } goto bad; } /* * XXXAE: most likely ip_sum at this point is wrong. */ IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; ip = mtod(m, struct ip *); dst = &sav->sah->saidx.dst; /* Do the appropriate encapsulation, if necessary */ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ dst->sa.sa_family != AF_INET || /* PF mismatch */ (dst->sa.sa_family == AF_INET && /* Proxy */ dst->sin.sin_addr.s_addr != INADDR_ANY && dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { /* Fix IPv4 header checksum and length */ ip->ip_len = htons(m->m_pkthdr.len); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, ip->ip_hl << 2); error = ipsec_encap(&m, &sav->sah->saidx); if (error != 0) { DPRINTF(("%s: encapsulation for SA %s->%s " "SPI 0x%08x failed with error %d\n", __func__, ipsec_address(&sav->sah->saidx.src, sbuf, sizeof(sbuf)), ipsec_address(&sav->sah->saidx.dst, dbuf, sizeof(dbuf)), ntohl(sav->spi), error)); /* XXXAE: IPSEC_OSTAT_INC(tunnel); */ goto bad; } } IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; /* * Dispatch to the appropriate IPsec transform logic. The * packet will be returned for transmission after crypto * processing, etc. are completed. * * NB: m & sav are ``passed to caller'' who's responsible for * reclaiming their resources. */ switch(dst->sa.sa_family) { case AF_INET: ip = mtod(m, struct ip *); i = ip->ip_hl << 2; off = offsetof(struct ip, ip_p); break; #ifdef INET6 case AF_INET6: i = sizeof(struct ip6_hdr); off = offsetof(struct ip6_hdr, ip6_nxt); break; #endif /* INET6 */ default: DPRINTF(("%s: unsupported protocol family %u\n", __func__, dst->sa.sa_family)); error = EPFNOSUPPORT; IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf); goto bad; } error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off); if (error != 0) { key_freesav(&sav); key_freesp(&sp); } return (error); bad: IPSECSTAT_INC(ips_out_inval); if (m != NULL) m_freem(m); if (sav != NULL) key_freesav(&sav); key_freesp(&sp); return (error); } int ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp, struct inpcb *inp) { return (ipsec4_perform_request(m, sp, 0)); } static int ipsec4_common_output(struct mbuf *m, struct inpcb *inp, int forwarding) { struct secpolicy *sp; int error, idx; /* Lookup for the corresponding outbound security policy */ sp = ipsec4_checkpolicy(m, inp, &error); if (sp == NULL) { if (error == -EINVAL) { /* Discarded by policy. */ m_freem(m); return (EACCES); } return (0); /* No IPsec required. */ } if (forwarding) { /* * Check that SP has tunnel mode IPsec transform. * We can't use transport mode when forwarding. */ for (idx = 0; idx < sp->tcount; idx++) { if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL) break; } if (idx == sp->tcount) { IPSECSTAT_INC(ips_out_inval); key_freesp(&sp); m_freem(m); return (EACCES); } } else { /* * Do delayed checksums now because we send before * this is done in the normal processing path. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP) { struct ip *ip = mtod(m, struct ip *); sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2)); m->m_pkthdr.csum_flags &= ~CSUM_SCTP; } #endif } /* NB: callee frees mbuf and releases reference to SP */ error = ipsec4_process_packet(m, sp, inp); if (error == EJUSTRETURN) { /* * We had a SP with a level of 'use' and no SA. We * will just continue to process the packet without * IPsec processing and return without error. */ return (0); } if (error == 0) return (EINPROGRESS); /* consumed by IPsec */ return (error); } /* * IPSEC_OUTPUT() method implementation for IPv4. * 0 - no IPsec handling needed * other values - mbuf consumed by IPsec. */ int ipsec4_output(struct mbuf *m, struct inpcb *inp) { /* * If the packet is resubmitted to ip_output (e.g. after * AH, ESP, etc. processing), there will be a tag to bypass * the lookup and related policy checking. */ if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) return (0); return (ipsec4_common_output(m, inp, 0)); } /* * IPSEC_FORWARD() method implementation for IPv4. * 0 - no IPsec handling needed * other values - mbuf consumed by IPsec. */ int ipsec4_forward(struct mbuf *m) { /* * Check if this packet has an active inbound SP and needs to be * dropped instead of forwarded. */ if (ipsec4_in_reject(m, NULL) != 0) { m_freem(m); return (EACCES); } return (ipsec4_common_output(m, NULL, 1)); } #endif #ifdef INET6 static int in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) { struct in6_addr ia2; if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) { memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); ia2.s6_addr16[1] = htons(sa->sin6_scope_id); return (IN6_ARE_ADDR_EQUAL(ia, &ia2)); } return (IN6_ARE_ADDR_EQUAL(&sa->sin6_addr, ia)); } static struct secasvar * ipsec6_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error) { struct secasindex *saidx, tmpsaidx; struct ipsecrequest *isr; struct sockaddr_in6 *sin6; struct secasvar *sav; struct ip6_hdr *ip6; /* * Check system global policy controls. */ next: isr = sp->req[*pidx]; if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { DPRINTF(("%s: IPsec outbound packet dropped due" " to policy (check your sysctls)\n", __func__)); IPSEC_OSTAT_INC(isr->saidx.proto, pdrops); *error = EHOSTUNREACH; return (NULL); } /* * Craft SA index to search for proper SA. Note that * we only fillin unspecified SA peers for transport * mode; for tunnel mode they must already be filled in. */ if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { saidx = &tmpsaidx; *saidx = isr->saidx; ip6 = mtod(m, struct ip6_hdr *); if (saidx->src.sin6.sin6_len == 0) { sin6 = (struct sockaddr_in6 *)&saidx->src; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = IPSEC_PORT_ANY; sin6->sin6_addr = ip6->ip6_src; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { /* fix scope id for comparing SPD */ sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); } } if (saidx->dst.sin6.sin6_len == 0) { sin6 = (struct sockaddr_in6 *)&saidx->dst; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; sin6->sin6_port = IPSEC_PORT_ANY; sin6->sin6_addr = ip6->ip6_dst; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { /* fix scope id for comparing SPD */ sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); } } } else saidx = &sp->req[*pidx]->saidx; /* * Lookup SA and validate it. */ sav = key_allocsa_policy(sp, saidx, error); if (sav == NULL) { IPSEC6STAT_INC(ips_out_nosa); if (*error != 0) return (NULL); if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) { /* * We have no SA and policy that doesn't require * this IPsec transform, thus we can continue w/o * IPsec processing, i.e. return EJUSTRETURN. * But first check if there is some bundled transform. */ if (sp->tcount > (*pidx)) { (*pidx)++; goto next; } *error = EJUSTRETURN; } return (NULL); } IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform")); return (sav); } /* * IPsec output logic for IPv6. */ static int ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx) { char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN]; struct ipsec_ctx_data ctx; union sockaddr_union *dst; struct secasvar *sav; struct ip6_hdr *ip6; int error, i, off; IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx)); sav = ipsec6_allocsa(m, sp, &idx, &error); if (sav == NULL) { if (error == EJUSTRETURN) { /* No IPsec required */ key_freesp(&sp); return (error); } goto bad; } IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET6, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); dst = &sav->sah->saidx.dst; /* Do the appropriate encapsulation, if necessary */ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ dst->sa.sa_family != AF_INET6 || /* PF mismatch */ ((dst->sa.sa_family == AF_INET6) && (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) { if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { /* No jumbogram support. */ error = ENXIO; /*XXX*/ goto bad; } error = ipsec_encap(&m, &sav->sah->saidx); if (error != 0) { DPRINTF(("%s: encapsulation for SA %s->%s " "SPI 0x%08x failed with error %d\n", __func__, ipsec_address(&sav->sah->saidx.src, sbuf, sizeof(sbuf)), ipsec_address(&sav->sah->saidx.dst, dbuf, sizeof(dbuf)), ntohl(sav->spi), error)); /* XXXAE: IPSEC_OSTAT_INC(tunnel); */ goto bad; } } IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0) goto bad; switch(dst->sa.sa_family) { #ifdef INET case AF_INET: { struct ip *ip; ip = mtod(m, struct ip *); i = ip->ip_hl << 2; off = offsetof(struct ip, ip_p); } break; #endif /* AF_INET */ case AF_INET6: i = sizeof(struct ip6_hdr); off = offsetof(struct ip6_hdr, ip6_nxt); break; default: DPRINTF(("%s: unsupported protocol family %u\n", __func__, dst->sa.sa_family)); error = EPFNOSUPPORT; IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf); goto bad; } error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off); if (error != 0) { key_freesav(&sav); key_freesp(&sp); } return (error); bad: IPSEC6STAT_INC(ips_out_inval); if (m != NULL) m_freem(m); if (sav != NULL) key_freesav(&sav); key_freesp(&sp); return (error); } int ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp, struct inpcb *inp) { return (ipsec6_perform_request(m, sp, 0)); } static int ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding) { struct secpolicy *sp; int error, idx; /* Lookup for the corresponding outbound security policy */ sp = ipsec6_checkpolicy(m, inp, &error); if (sp == NULL) { if (error == -EINVAL) { /* Discarded by policy. */ m_freem(m); return (EACCES); } return (0); /* No IPsec required. */ } if (forwarding) { /* * Check that SP has tunnel mode IPsec transform. * We can't use transport mode when forwarding. * * RFC2473 says: * "A tunnel IPv6 packet resulting from the encapsulation of * an original packet is considered an IPv6 packet originating * from the tunnel entry-point node." * So, we don't need MTU checking, after IPsec processing * we will just fragment it if needed. */ for (idx = 0; idx < sp->tcount; idx++) { if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL) break; } if (idx == sp->tcount) { IPSEC6STAT_INC(ips_out_inval); key_freesp(&sp); m_freem(m); return (EACCES); } } else { /* * Do delayed checksums now because we send before * this is done in the normal processing path. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { 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; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) { sctp_delayed_cksum(m, sizeof(struct ip6_hdr)); m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6; } #endif } /* NB: callee frees mbuf and releases reference to SP */ error = ipsec6_process_packet(m, sp, inp); if (error == EJUSTRETURN) { /* * We had a SP with a level of 'use' and no SA. We * will just continue to process the packet without * IPsec processing and return without error. */ return (0); } if (error == 0) return (EINPROGRESS); /* consumed by IPsec */ return (error); } /* * IPSEC_OUTPUT() method implementation for IPv6. * 0 - no IPsec handling needed * other values - mbuf consumed by IPsec. */ int ipsec6_output(struct mbuf *m, struct inpcb *inp) { /* * If the packet is resubmitted to ip_output (e.g. after * AH, ESP, etc. processing), there will be a tag to bypass * the lookup and related policy checking. */ if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) return (0); return (ipsec6_common_output(m, inp, 0)); } /* * IPSEC_FORWARD() method implementation for IPv6. * 0 - no IPsec handling needed * other values - mbuf consumed by IPsec. */ int ipsec6_forward(struct mbuf *m) { /* * Check if this packet has an active inbound SP and needs to be * dropped instead of forwarded. */ if (ipsec6_in_reject(m, NULL) != 0) { m_freem(m); return (EACCES); } return (ipsec6_common_output(m, NULL, 1)); } #endif /* INET6 */ int ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav, u_int idx) { struct xform_history *xh; struct secasindex *saidx; struct m_tag *mtag; int error; saidx = &sav->sah->saidx; switch (saidx->dst.sa.sa_family) { #ifdef INET case AF_INET: /* Fix the header length, for AH processing. */ mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); break; #endif /* INET */ #ifdef INET6 case AF_INET6: /* Fix the header length, for AH processing. */ if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { error = ENXIO; goto bad; } if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { /* No jumbogram support. */ error = ENXIO; /*?*/ goto bad; } mtod(m, struct ip6_hdr *)->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); break; #endif /* INET6 */ default: DPRINTF(("%s: unknown protocol family %u\n", __func__, saidx->dst.sa.sa_family)); error = ENXIO; goto bad; } /* * Add a record of what we've done to the packet. */ mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(*xh), M_NOWAIT); if (mtag == NULL) { DPRINTF(("%s: could not get packet tag\n", __func__)); error = ENOMEM; goto bad; } xh = (struct xform_history *)(mtag + 1); xh->dst = saidx->dst; xh->proto = saidx->proto; xh->mode = saidx->mode; xh->spi = sav->spi; m_tag_prepend(m, mtag); key_sa_recordxfer(sav, m); /* record data transfer */ /* * If there's another (bundled) SA to apply, do so. * Note that this puts a burden on the kernel stack size. * If this is a problem we'll need to introduce a queue * to set the packet on so we can unwind the stack before * doing further processing. */ if (++idx < sp->tcount) { switch (saidx->dst.sa.sa_family) { #ifdef INET case AF_INET: key_freesav(&sav); IPSECSTAT_INC(ips_out_bundlesa); return (ipsec4_perform_request(m, sp, idx)); /* NOTREACHED */ #endif #ifdef INET6 case AF_INET6: key_freesav(&sav); IPSEC6STAT_INC(ips_out_bundlesa); return (ipsec6_perform_request(m, sp, idx)); /* NOTREACHED */ #endif /* INET6 */ default: DPRINTF(("%s: unknown protocol family %u\n", __func__, saidx->dst.sa.sa_family)); error = EPFNOSUPPORT; goto bad; } } key_freesp(&sp), sp = NULL; /* Release reference to SP */ /* * Do UDP encapsulation if SA requires it. */ if (sav->natt != NULL) { error = udp_ipsec_output(m, sav); if (error != 0) goto bad; } /* * We're done with IPsec processing, transmit the packet using the * appropriate network protocol (IP or IPv6). */ switch (saidx->dst.sa.sa_family) { #ifdef INET case AF_INET: key_freesav(&sav); return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); #endif /* INET */ #ifdef INET6 case AF_INET6: key_freesav(&sav); return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); #endif /* INET6 */ } panic("ipsec_process_done"); bad: m_freem(m); key_freesav(&sav); if (sp != NULL) key_freesp(&sp); return (error); } +/* + * ipsec_prepend() is optimized version of M_PREPEND(). + * ipsec_encap() is called by IPsec output routine for tunnel mode SA. + * It is expected that after IP encapsulation some IPsec transform will + * be performed. Each IPsec transform inserts its variable length header + * just after outer IP header using m_makespace(). If given mbuf has not + * enough free space at the beginning, we allocate new mbuf and reserve + * some space at the beginning and at the end. + * This helps avoid allocating of new mbuf and data copying in m_makespace(), + * we place outer header in the middle of mbuf's data with reserved leading + * and trailing space: + * [ LEADINGSPACE ][ Outer IP header ][ TRAILINGSPACE ] + * LEADINGSPACE will be used to add ethernet header, TRAILINGSPACE will + * be used to inject AH/ESP/IPCOMP header. + */ +#define IPSEC_TRAILINGSPACE (sizeof(struct udphdr) +/* NAT-T */ \ + max(sizeof(struct newesp) + EALG_MAX_BLOCK_LEN, /* ESP + IV */ \ + sizeof(struct newah) + HASH_MAX_LEN /* AH + ICV */)) +static struct mbuf * +ipsec_prepend(struct mbuf *m, int len, int how) +{ + struct mbuf *n; + + M_ASSERTPKTHDR(m); + IPSEC_ASSERT(len < MHLEN, ("wrong length")); + if (M_LEADINGSPACE(m) >= len) { + /* No need to allocate new mbuf. */ + m->m_data -= len; + m->m_len += len; + m->m_pkthdr.len += len; + return (m); + } + n = m_gethdr(how, m->m_type); + if (n == NULL) { + m_freem(m); + return (NULL); + } + m_move_pkthdr(n, m); + n->m_next = m; + if (len + IPSEC_TRAILINGSPACE < M_SIZE(n)) + m_align(n, len + IPSEC_TRAILINGSPACE); + n->m_len = len; + n->m_pkthdr.len += len; + return (n); +} + static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx) { #ifdef INET6 struct ip6_hdr *ip6; #endif struct ip *ip; int setdf; uint8_t itos, proto; ip = mtod(*mp, struct ip *); switch (ip->ip_v) { #ifdef INET case IPVERSION: proto = IPPROTO_IPIP; /* * Collect IP_DF state from the inner header * and honor system-wide control of how to handle it. */ switch (V_ip4_ipsec_dfbit) { case 0: /* clear in outer header */ case 1: /* set in outer header */ setdf = V_ip4_ipsec_dfbit; break; default:/* propagate to outer header */ setdf = (ip->ip_off & htons(IP_DF)) != 0; } itos = ip->ip_tos; break; #endif #ifdef INET6 case (IPV6_VERSION >> 4): proto = IPPROTO_IPV6; ip6 = mtod(*mp, struct ip6_hdr *); itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; setdf = V_ip4_ipsec_dfbit ? 1: 0; /* scoped address handling */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); break; #endif default: return (EAFNOSUPPORT); } switch (saidx->dst.sa.sa_family) { #ifdef INET case AF_INET: if (saidx->src.sa.sa_family != AF_INET || saidx->src.sin.sin_addr.s_addr == INADDR_ANY || saidx->dst.sin.sin_addr.s_addr == INADDR_ANY) return (EINVAL); - M_PREPEND(*mp, sizeof(struct ip), M_NOWAIT); + *mp = ipsec_prepend(*mp, sizeof(struct ip), M_NOWAIT); if (*mp == NULL) return (ENOBUFS); ip = mtod(*mp, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_p = proto; ip->ip_len = htons((*mp)->m_pkthdr.len); ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; ip->ip_off = setdf ? htons(IP_DF): 0; ip->ip_src = saidx->src.sin.sin_addr; ip->ip_dst = saidx->dst.sin.sin_addr; ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos); ip_fillid(ip); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (saidx->src.sa.sa_family != AF_INET6 || IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr)) return (EINVAL); - M_PREPEND(*mp, sizeof(struct ip6_hdr), M_NOWAIT); + *mp = ipsec_prepend(*mp, sizeof(struct ip6_hdr), M_NOWAIT); if (*mp == NULL) return (ENOBUFS); ip6 = mtod(*mp, struct ip6_hdr *); ip6->ip6_flow = 0; ip6->ip6_vfc = IPV6_VERSION; ip6->ip6_hlim = V_ip6_defhlim; ip6->ip6_nxt = proto; ip6->ip6_dst = saidx->dst.sin6.sin6_addr; /* For link-local address embed scope zone id */ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) ip6->ip6_dst.s6_addr16[1] = htons(saidx->dst.sin6.sin6_scope_id & 0xffff); ip6->ip6_src = saidx->src.sin6.sin6_addr; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = htons(saidx->src.sin6.sin6_scope_id & 0xffff); ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6)); ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos); ip6->ip6_flow |= htonl((uint32_t)proto << 20); break; #endif /* INET6 */ default: return (EAFNOSUPPORT); } (*mp)->m_flags &= ~(M_BCAST | M_MCAST); return (0); } Index: projects/ipsec/sys/netipsec/udpencap.c =================================================================== --- projects/ipsec/sys/netipsec/udpencap.c (revision 310709) +++ projects/ipsec/sys/netipsec/udpencap.c (revision 310710) @@ -1,296 +1,293 @@ /*- * Copyright (c) 2016 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Handle UDP_ENCAP socket option. Always return with released INP_WLOCK. */ int udp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt) { struct udpcb *up; int error, optval; INP_WLOCK_ASSERT(inp); if (sopt->sopt_name != UDP_ENCAP) { INP_WUNLOCK(inp); return (ENOPROTOOPT); } up = intoudpcb(inp); if (sopt->sopt_dir == SOPT_GET) { if (up->u_flags & UF_ESPINUDP) optval = UDP_ENCAP_ESPINUDP; else optval = 0; INP_WUNLOCK(inp); return (sooptcopyout(sopt, &optval, sizeof(optval))); } INP_WUNLOCK(inp); error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); if (error != 0) return (error); INP_WLOCK(inp); switch (optval) { case 0: up->u_flags &= ~UF_ESPINUDP; break; case UDP_ENCAP_ESPINUDP: up->u_flags |= UF_ESPINUDP; break; default: error = EINVAL; } INP_WUNLOCK(inp); return (error); } /* * Potentially decap ESP in UDP frame. Check for an ESP header. * If present, strip the UDP header and push the result through IPSec. * * Returns error if mbuf consumed and/or processed, otherwise 0. */ int 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 error, hlen; /* * Just return if packet doesn't have enough data. * We need at least [IP header + UDP header + ESP header]. * NAT-Keepalive packet has only one byte of payload, so it * by default will not be processed. */ if (m->m_pkthdr.len < off + sizeof(struct esp)) return (0); m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi); if (spi == 0) /* Non-ESP marker. */ return (0); /* * Find SA and check that it is configured for UDP * encapsulation. */ bzero(&dst, sizeof(dst)); dst.sa.sa_family = af; switch (af) { #ifdef INET case AF_INET: dst.sin.sin_len = sizeof(struct sockaddr_in); ip = mtod(m, struct ip *); ip->ip_p = IPPROTO_ESP; off = offsetof(struct ip, ip_p); hlen = ip->ip_hl << 2; dst.sin.sin_addr = ip->ip_dst; break; #endif #ifdef INET6 case AF_INET6: /* Not yet */ /* FALLTHROUGH */ #endif default: ESPSTAT_INC(esps_nopf); m_freem(m); return (EPFNOSUPPORT); } sav = key_allocsa(&dst, IPPROTO_ESP, spi); if (sav == NULL) { ESPSTAT_INC(esps_notdb); m_freem(m); return (ENOENT); } udp = mtodo(m, hlen); if (sav->natt == NULL || sav->natt->sport != udp->uh_sport || sav->natt->dport != udp->uh_dport) { /* XXXAE: should we check source address? */ ESPSTAT_INC(esps_notdb); key_freesav(&sav); m_freem(m); return (ENOENT); } /* * Remove the UDP header * Before: * <--- off ---> * +----+------+-----+ * | IP | UDP | ESP | * +----+------+-----+ * <-skip-> * After: * +----+-----+ * | IP | ESP | * +----+-----+ * <-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); /* * We can update ip_len and ip_sum here, but ipsec4_input_cb() * will do this anyway, so don't touch them here. */ ESPSTAT_INC(esps_input); error = (*sav->tdb_xform->xf_input)(m, sav, hlen, off); if (error != 0) key_freesav(&sav); return (EINPROGRESS); /* Consumed by IPsec. */ } int 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) 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__)); return (ENOBUFS); } - /* - * IP header should stay in the same place of mbuf m. - * m_makespace() can add new mbuf or move the remaining data into - * the end of mbuf space. Thus mtod() isn't required for ip pointer. - */ + udp = mtodo(n, off); udp->uh_dport = sav->natt->dport; udp->uh_sport = sav->natt->sport; 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; - return (0); } 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")); IPSEC_ASSERT(proto == IPPROTO_UDP || proto == IPPROTO_TCP, ("unexpected protocol %u", proto)); if (proto == IPPROTO_UDP) off = offsetof(struct udphdr, uh_sum); else off = offsetof(struct tcphdr, th_sum); switch (V_natt_cksum_policy) { case 0: /* Incrementally recompute. */ if (sav->natt->cksum == 0) /* No OA from IKEd */ return; m_copydata(m, skip + off, sizeof(cksum), (caddr_t)&cksum); cksum = in_addword(cksum, sav->natt->cksum); break; case 1: /* 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; return; default:/* Reset for UDP, ignore for TCP */ if (proto == IPPROTO_UDP) { cksum = 0; break; } m->m_pkthdr.csum_data = 0xffff; m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); return; } m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); }