X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..5c9f46613a83ebfc29a5b1f099448259e96a98f0:/bsd/netinet6/ah_input.c diff --git a/bsd/netinet6/ah_input.c b/bsd/netinet6/ah_input.c index 0b76ad007..fac8da28c 100644 --- a/bsd/netinet6/ah_input.c +++ b/bsd/netinet6/ah_input.c @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2008-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +/* $FreeBSD: src/sys/netinet6/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $ */ +/* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -31,15 +62,11 @@ * RFC1826/2402 authentication header. */ -#define _IP_VHL -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) -#include "opt_inet.h" -#endif - #include #include #include #include +#include #include #include #include @@ -49,9 +76,10 @@ #include #include +#include #include -#include #include +#include #include #include @@ -59,68 +87,75 @@ #include #include #include +#include +#if INET6 +#include +#endif #if INET6 #include #include +#include #include +#include #endif #include +#if INET6 +#include +#endif #include +#if INET6 +#include +#endif #include #include +#if IPSEC_DEBUG #include +#else +#define KEYDEBUG(lev,arg) +#endif +#include +#include +#include #include #define IPLEN_FLIPPED #if INET -extern struct protosw inetsw[]; -#if defined(__bsdi__) || defined(__NetBSD__) -extern u_char ip_protox[]; -#endif - void ah4_input(struct mbuf *m, int off) { struct ip *ip; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; size_t hlen; - int s; + size_t stripsiz = 0; + sa_family_t ifamily; -#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct newah)) { m = m_pullup(m, off + sizeof(struct newah)); if (!m) { ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" "dropping the packet for simplicity\n")); - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } } + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + ip = mtod(m, struct ip *); - ah = (struct ah *)(((caddr_t)ip) + off); -#else - ip = mtod(m, struct ip *); - IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n")); - ipsecstat.in_inval++; - goto fail; - } -#endif + ah = (struct ah *)(void *)(((caddr_t)ip) + off); nxt = ah->ah_nxt; #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; @@ -137,29 +172,30 @@ ah4_input(struct mbuf *m, int off) ipseclog((LOG_WARNING, "IPv4 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); - ipsecstat.in_nosa++; + IPSEC_STAT_INCREMENT(ipsecstat.in_nosa); goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input called to allocate SA:%p\n", sav)); + printf("DP ah4_input called to allocate SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { ipseclog((LOG_DEBUG, "IPv4 AH input: non-mature/dying SA found for spi %u\n", (u_int32_t)ntohl(spi))); - ipsecstat.in_badspi++; + IPSEC_STAT_INCREMENT(ipsecstat.in_badspi); goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv4 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); - ipsecstat.in_badspi++; + IPSEC_STAT_INCREMENT(ipsecstat.in_badspi); goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -171,36 +207,57 @@ ah4_input(struct mbuf *m, int off) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + /* + * Here, we do not do "siz1 == siz". This is because the way + * RFC240[34] section 2 is written. They do not require truncation + * to 96 bits. + * For example, Microsoft IPsec stack attaches 160 bits of + * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1, + * 32 bits of padding is attached. + * + * There are two downsides to this specification. + * They have no real harm, however, they leave us fuzzy feeling. + * - if we attach more than 96 bits of authentication data onto AH, + * we will never notice about possible modification by rogue + * intermediate nodes. + * Since extra bits in AH checksum is never used, this constitutes + * no real issue, however, it is wacky. + * - even if the peer attaches big authentication data, we will never + * notice the difference, since longer authentication data will just + * work. + * + * We may need some clarification in the spec. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input " + "(%lu, should be at least %lu): %s\n", + (u_int32_t)siz1, (u_int32_t)siz, + ipsec4_logpacketstr(ip, spi))); + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); + goto fail; + } if ((ah->ah_len << 2) - sizoff != siz1) { ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec4_logpacketstr(ip, spi))); - ipsecstat.in_inval++; + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_int32_t)siz1, + ipsec4_logpacketstr(ip, spi))); + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } -#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); if (!m) { ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } + /* Expect 32-bit aligned data ptr on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip = mtod(m, struct ip *); - ah = (struct ah *)(((caddr_t)ip) + off); - } -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, - sizeof(struct ah) + sizoff + siz1); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); - ipsecstat.in_inval++; - goto fail; + ah = (struct ah *)(void *)(((caddr_t)ip) + off); } -#endif } /* @@ -210,7 +267,7 @@ ah4_input(struct mbuf *m, int off) if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) ; /*okey*/ else { - ipsecstat.in_ahreplay++; + IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay); ipseclog((LOG_WARNING, "replay packet in IPv4 AH input: %s %s\n", ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); @@ -226,43 +283,27 @@ ah4_input(struct mbuf *m, int off) if (!cksum) { ipseclog((LOG_DEBUG, "IPv4 AH input: " "couldn't alloc temporary region for cksum\n")); - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } - { -#if 1 /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. */ -#ifndef __NetBSD__ ip->ip_len = htons(ip->ip_len + hlen); - ip->ip_id = htons(ip->ip_id); -#else - ip->ip_len = htons(ip->ip_len); -#endif ip->ip_off = htons(ip->ip_off); -#endif - if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) { - _FREE(cksum, M_TEMP); - ipsecstat.in_inval++; + if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } - ipsecstat.in_ahhist[sav->alg_auth]++; -#if 1 + IPSEC_STAT_INCREMENT(ipsecstat.in_ahhist[sav->alg_auth]); /* * flip them back. */ -#ifndef __NetBSD__ ip->ip_len = ntohs(ip->ip_len) - hlen; - ip->ip_id = ntohs(ip->ip_id); -#else - ip->ip_len = ntohs(ip->ip_len); -#endif ip->ip_off = ntohs(ip->ip_off); -#endif - } { caddr_t sumpos = NULL; @@ -279,13 +320,13 @@ ah4_input(struct mbuf *m, int off) ipseclog((LOG_WARNING, "checksum mismatch in IPv4 AH input: %s %s\n", ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); - _FREE(cksum, M_TEMP); - ipsecstat.in_ahauthfail++; + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail); goto fail; } } - _FREE(cksum, M_TEMP); + FREE(cksum, M_TEMP); m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; @@ -307,7 +348,7 @@ ah4_input(struct mbuf *m, int off) if (!m) { ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } } @@ -333,12 +374,12 @@ ah4_input(struct mbuf *m, int off) ipseclog((LOG_DEBUG, "IPv4 AH input: authentication succeess\n")); #endif - ipsecstat.in_ahauthsucc++; + IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthsucc); } else { ipseclog((LOG_WARNING, "authentication failed in IPv4 AH input: %s %s\n", ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); - ipsecstat.in_ahauthfail++; + IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail); goto fail; } @@ -347,13 +388,24 @@ ah4_input(struct mbuf *m, int off) */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { - ipsecstat.in_ahreplay++; + IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay); goto fail; } } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) { + ifaddr_t ifa; + struct sockaddr_storage addr; + struct sockaddr_in *ipaddr; + /* * strip off all the headers that precedes AH. * IP xx AH IP' payload -> IP' payload @@ -361,44 +413,47 @@ ah4_input(struct mbuf *m, int off) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; - u_int8_t tos; + u_int8_t tos, otos; + int sum; - tos = ip->ip_tos; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; + if (ifamily == AF_INET6) { + ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch " + "in IPv4 AH input: %s\n", ipsec_logsastr(sav))); + goto fail; } + tos = ip->ip_tos; m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) { - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } } ip = mtod(m, struct ip *); + otos = ip->ip_tos; /* ECN consideration. */ - ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); + if (ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos) == 0) { + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); + goto fail; + } + + if (otos != ip->ip_tos) { + sum = ~ntohs(ip->ip_sum) & 0xffff; + sum += (~otos & 0xffff) + ip->ip_tos; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); /* add carry */ + ip->ip_sum = htons(~sum & 0xffff); + } + if (!key_checktunnelsanity(sav, AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " "in IPv4 AH input: %s %s\n", ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); - ipsecstat.in_inval++; - goto fail; - } - -#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ - /* drop it if it does not match the default policy */ - if (ipsec4_in_reject(m, NULL)) { - ipsecstat.in_polvio++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } -#endif #if 1 /* @@ -425,72 +480,57 @@ ah4_input(struct mbuf *m, int off) #endif key_sa_recordxfer(sav, m); - - s = splimp(); - if (IF_QFULL(&ipintrq)) { - ipsecstat.in_inval++; + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) { + IPSEC_STAT_INCREMENT(ipsecstat.in_nomem); goto fail; } - IF_ENQUEUE(&ipintrq, m); - m = NULL; - schednetisr(NETISR_IP); /*can be skipped but to make sure*/ - splx(s); + + bzero(&addr, sizeof(addr)); + ipaddr = (__typeof__(ipaddr))&addr; + ipaddr->sin_family = AF_INET; + ipaddr->sin_len = sizeof(*ipaddr); + ipaddr->sin_addr = ip->ip_dst; + + // update the receiving interface address based on the inner address + ifa = ifa_ifwithaddr((struct sockaddr *)&addr); + if (ifa) { + m->m_pkthdr.rcvif = ifa->ifa_ifp; + IFA_REMREF(ifa); + } + + // Input via IPSec interface + if (sav->sah->ipsec_if != NULL) { + if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) { + m = NULL; + goto done; + } else { + goto fail; + } + } + + if (proto_input(PF_INET, m) != 0) + goto fail; nxt = IPPROTO_DONE; } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single external mbuf. */ - size_t stripsiz = 0; - - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } ip = mtod(m, struct ip *); -#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single external mbuf. + */ ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; -#else - /* - * even in m_pulldown case, we need to strip off AH so that - * we can compute checksum for multiple AH correctly. - */ - if (m->m_len >= stripsiz + off) { - ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; - } else { - /* - * this comes with no copy if the boundary is on - * cluster - */ - struct mbuf *n; - - n = m_split(m, off, M_DONTWAIT); - if (n == NULL) { - /* m is retained by m_split */ - goto fail; - } - m_adj(n, stripsiz); - m_cat(m, n); - /* m_cat does not update m_pkthdr.len */ - m->m_pkthdr.len += n->m_pkthdr.len; - } -#endif if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (m == NULL) { - ipsecstat.in_inval++; + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } } @@ -504,27 +544,56 @@ ah4_input(struct mbuf *m, int off) /* forget about IP hdr checksum, the check has already been passed */ key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + IPSEC_STAT_INCREMENT(ipsecstat.in_nomem); + goto fail; + } - if (nxt != IPPROTO_DONE) - (*ip_protox[nxt]->pr_input)(m, off); - else + DTRACE_IP6(receive, struct mbuf *, m, struct inpcb *, NULL, + struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif, + struct ip *, ip, struct ip6_hdr *, NULL); + + if (nxt != IPPROTO_DONE) { + // Input via IPSec interface + if (sav->sah->ipsec_if != NULL) { + ip->ip_len = htons(ip->ip_len + hlen); + ip->ip_off = htons(ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = ip_cksum_hdr_in(m, hlen); + if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) { + m = NULL; + goto done; + } else { + goto fail; + } + } + + if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + IPSEC_STAT_INCREMENT(ipsecstat.in_polvio); + goto fail; + } + ip_proto_dispatch_in(m, off, nxt, 0); + } else m_freem(m); m = NULL; } - +done: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input call free SA:%p\n", sav)); - key_freesav(sav); + printf("DP ah4_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); + key_freesav(sav, KEY_SADB_UNLOCKED); } - ipsecstat.in_success++; + IPSEC_STAT_INCREMENT(ipsecstat.in_success); return; fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input call free SA:%p\n", sav)); - key_freesav(sav); + printf("DP ah4_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); + key_freesav(sav, KEY_SADB_UNLOCKED); } if (m) m_freem(m); @@ -534,34 +603,28 @@ fail: #if INET6 int -ah6_input(mp, offp, proto) - struct mbuf **mp; - int *offp, proto; +ah6_input(struct mbuf **mp, int *offp, int proto) { +#pragma unused(proto) struct mbuf *m = *mp; int off = *offp; struct ip6_hdr *ip6; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; - int s; + size_t stripsiz = 0; + sa_family_t ifamily; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), {return IPPROTO_DONE;}); + ah = (struct ah *)(void *)(mtod(m, caddr_t) + off); + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); - ah = (struct ah *)(mtod(m, caddr_t) + off); -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); - ipsecstat.in_inval++; - return IPPROTO_DONE; - } -#endif ip6 = mtod(m, struct ip6_hdr *); nxt = ah->ah_nxt; @@ -571,7 +634,7 @@ ah6_input(mp, offp, proto) if (ntohs(ip6->ip6_plen) == 0) { ipseclog((LOG_ERR, "IPv6 AH input: " "AH with IPv6 jumbogram is not supported.\n")); - ipsec6stat.in_inval++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } @@ -581,29 +644,30 @@ ah6_input(mp, offp, proto) ipseclog((LOG_WARNING, "IPv6 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); - ipsec6stat.in_nosa++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_nosa); goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input called to allocate SA:%p\n", sav)); + printf("DP ah6_input called to allocate SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { ipseclog((LOG_DEBUG, "IPv6 AH input: non-mature/dying SA found for spi %u; ", (u_int32_t)ntohl(spi))); - ipsec6stat.in_badspi++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi); goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv6 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); - ipsec6stat.in_badspi++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi); goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -615,26 +679,28 @@ ah6_input(mp, offp, proto) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; - if ((ah->ah_len << 2) - sizoff != siz1) { - ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec6_logpacketstr(ip6, spi))); - ipsec6stat.in_inval++; + /* + * Here, we do not do "siz1 == siz". See ah4_input() for complete + * description. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input " + "(%lu, should be at least %lu): %s\n", + (u_int32_t)siz1, (u_int32_t)siz, + ipsec6_logpacketstr(ip6, spi))); + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE); -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, - sizeof(struct ah) + sizoff + siz1); - if (ah == NULL) { - ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); - ipsecstat.in_inval++; - m = NULL; + if ((ah->ah_len << 2) - sizoff != siz1) { + ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_int32_t)siz1, + ipsec6_logpacketstr(ip6, spi))); + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } -#endif + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, + {return IPPROTO_DONE;}); } /* @@ -644,7 +710,7 @@ ah6_input(mp, offp, proto) if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) ; /*okey*/ else { - ipsec6stat.in_ahreplay++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay); ipseclog((LOG_WARNING, "replay packet in IPv6 AH input: %s %s\n", ipsec6_logpacketstr(ip6, spi), @@ -661,16 +727,16 @@ ah6_input(mp, offp, proto) if (!cksum) { ipseclog((LOG_DEBUG, "IPv6 AH input: " "couldn't alloc temporary region for cksum\n")); - ipsec6stat.in_inval++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } - if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) { - _FREE(cksum, M_TEMP); - ipsec6stat.in_inval++; + if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } - ipsec6stat.in_ahhist[sav->alg_auth]++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahhist[sav->alg_auth]); { caddr_t sumpos = NULL; @@ -687,13 +753,13 @@ ah6_input(mp, offp, proto) ipseclog((LOG_WARNING, "checksum mismatch in IPv6 AH input: %s %s\n", ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); - _FREE(cksum, M_TEMP); - ipsec6stat.in_ahauthfail++; + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail); goto fail; } } - _FREE(cksum, M_TEMP); + FREE(cksum, M_TEMP); m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; @@ -710,7 +776,8 @@ ah6_input(mp, offp, proto) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1 - + sizeof(struct ip6_hdr), IPPROTO_DONE); + + sizeof(struct ip6_hdr), + {return IPPROTO_DONE;}); nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1); if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src) @@ -733,12 +800,12 @@ ah6_input(mp, offp, proto) ipseclog((LOG_DEBUG, "IPv6 AH input: authentication succeess\n")); #endif - ipsec6stat.in_ahauthsucc++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthsucc); } else { ipseclog((LOG_WARNING, "authentication failed in IPv6 AH input: %s %s\n", ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); - ipsec6stat.in_ahauthfail++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail); goto fail; } @@ -747,13 +814,23 @@ ah6_input(mp, offp, proto) */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { - ipsec6stat.in_ahreplay++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay); goto fail; } } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) { + ifaddr_t ifa; + struct sockaddr_storage addr; + struct sockaddr_in6 *ip6addr; /* * strip off all the headers that precedes AH. * IP6 xx AH IP6' payload -> IP6' payload @@ -761,17 +838,15 @@ ah6_input(mp, offp, proto) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; u_int32_t flowinfo; /*net endian*/ - flowinfo = ip6->ip6_flow; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; + if (ifamily == AF_INET) { + ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch " + "in IPv6 AH input: %s\n", ipsec_logsastr(sav))); + goto fail; } + + flowinfo = ip6->ip6_flow; m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip6)) { /* @@ -780,30 +855,25 @@ ah6_input(mp, offp, proto) */ m = m_pullup(m, sizeof(*ip6)); if (!m) { - ipsec6stat.in_inval++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } } ip6 = mtod(m, struct ip6_hdr *); /* ECN consideration. */ - ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); + if (ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow) == 0) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); + goto fail; + } if (!key_checktunnelsanity(sav, AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " "in IPv6 AH input: %s %s\n", ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); - ipsec6stat.in_inval++; - goto fail; - } - -#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ - /* drop it if it does not match the default policy */ - if (ipsec6_in_reject(m, NULL)) { - ipsec6stat.in_polvio++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } -#endif #if 1 /* @@ -815,24 +885,43 @@ ah6_input(mp, offp, proto) #endif key_sa_recordxfer(sav, m); - - s = splimp(); - if (IF_QFULL(&ip6intrq)) { - ipsec6stat.in_inval++; + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); goto fail; } - IF_ENQUEUE(&ip6intrq, m); - m = NULL; - schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ - splx(s); + + bzero(&addr, sizeof(addr)); + ip6addr = (__typeof__(ip6addr))&addr; + ip6addr->sin6_family = AF_INET6; + ip6addr->sin6_len = sizeof(*ip6addr); + ip6addr->sin6_addr = ip6->ip6_dst; + + // update the receiving interface address based on the inner address + ifa = ifa_ifwithaddr((struct sockaddr *)&addr); + if (ifa) { + m->m_pkthdr.rcvif = ifa->ifa_ifp; + IFA_REMREF(ifa); + } + + // Input via IPSec interface + if (sav->sah->ipsec_if != NULL) { + if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) { + m = NULL; + nxt = IPPROTO_DONE; + goto done; + } else { + goto fail; + } + } + + if (proto_input(PF_INET6, m) != 0) + goto fail; nxt = IPPROTO_DONE; } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single mbuf. */ - size_t stripsiz = 0; char *prvnxtp; /* @@ -843,74 +932,145 @@ ah6_input(mp, offp, proto) prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ *prvnxtp = nxt; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } - ip6 = mtod(m, struct ip6_hdr *); -#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single mbuf. + */ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; -#else - /* - * even in m_pulldown case, we need to strip off AH so that - * we can compute checksum for multiple AH correctly. - */ - if (m->m_len >= stripsiz + off) { - ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; - } else { - /* - * this comes with no copy if the boundary is on - * cluster - */ - struct mbuf *n; - - n = m_split(m, off, M_DONTWAIT); - if (n == NULL) { - /* m is retained by m_split */ - goto fail; - } - m_adj(n, stripsiz); - m_cat(m, n); - /* m_cat does not update m_pkthdr.len */ - m->m_pkthdr.len += n->m_pkthdr.len; - } -#endif ip6 = mtod(m, struct ip6_hdr *); /* XXX jumbogram */ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); + goto fail; + } + + // Input via IPSec interface + if (sav->sah->ipsec_if != NULL) { + if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) { + m = NULL; + nxt = IPPROTO_DONE; + goto done; + } else { + goto fail; + } + } } +done: *offp = off; *mp = m; - if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input call free SA:%p\n", sav)); - key_freesav(sav); + printf("DP ah6_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); + key_freesav(sav, KEY_SADB_UNLOCKED); } - ipsec6stat.in_success++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_success); return nxt; fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input call free SA:%p\n", sav)); - key_freesav(sav); + printf("DP ah6_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); + key_freesav(sav, KEY_SADB_UNLOCKED); } if (m) m_freem(m); return IPPROTO_DONE; } + +void +ah6_ctlinput(int cmd, struct sockaddr *sa, void *d) +{ + const struct newah *ahp; + struct newah ah; + struct secasvar *sav; + struct ip6_hdr *ip6; + struct mbuf *m; + struct ip6ctlparam *ip6cp = NULL; + int off = 0; + struct sockaddr_in6 *sa6_src, *sa6_dst; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if ((unsigned)cmd >= PRC_NCMDS) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + if (ip6) { + /* + * XXX: We assume that when ip6 is non NULL, + * M and OFF are valid. + */ + + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(ah)) + return; + + if (m->m_len < off + sizeof(ah)) { + /* + * this should be rare case, + * so we compromise on this copy... + */ + m_copydata(m, off, sizeof(ah), (caddr_t)&ah); + ahp = &ah; + } else + ahp = (struct newah *)(void *)(mtod(m, caddr_t) + off); + + if (cmd == PRC_MSGSIZE) { + int valid = 0; + + /* + * Check to see if we have a valid SA corresponding to + * the address in the ICMP message payload. + */ + sa6_src = ip6cp->ip6c_src; + sa6_dst = (struct sockaddr_in6 *)(void *)sa; + sav = key_allocsa(AF_INET6, + (caddr_t)&sa6_src->sin6_addr, + (caddr_t)&sa6_dst->sin6_addr, + IPPROTO_AH, ahp->ah_spi); + if (sav) { + if (sav->state == SADB_SASTATE_MATURE || + sav->state == SADB_SASTATE_DYING) + valid++; + key_freesav(sav, KEY_SADB_UNLOCKED); + } + + /* XXX Further validation? */ + + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + } + + /* we normally notify single pcb here */ + } else { + /* we normally notify any pcb here */ + } +} #endif /* INET6 */