diff options
Diffstat (limited to 'sys/netipsec/ipsec_input.c')
-rw-r--r-- | sys/netipsec/ipsec_input.c | 530 |
1 files changed, 151 insertions, 379 deletions
diff --git a/sys/netipsec/ipsec_input.c b/sys/netipsec/ipsec_input.c index 0ec07d8424ac9..c4acafecc2438 100644 --- a/sys/netipsec/ipsec_input.c +++ b/sys/netipsec/ipsec_input.c @@ -1,4 +1,3 @@ -/* $FreeBSD$ */ /* $OpenBSD: ipsec_input.c,v 1.63 2003/02/20 18:35:43 deraadt Exp $ */ /*- * The authors of this code are John Ioannidis (ji@tla.org), @@ -19,6 +18,7 @@ * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, * Angelos D. Keromytis and Niels Provos. * Copyright (c) 2001, Angelos D. Keromytis. + * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in @@ -40,6 +40,9 @@ * IPsec input processing. */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -87,6 +90,7 @@ #include <netipsec/key.h> #include <netipsec/keydb.h> +#include <netipsec/key_debug.h> #include <netipsec/xform.h> #include <netinet6/ip6protosw.h> @@ -104,29 +108,20 @@ IPCOMPSTAT_INC(ipcomps_##name); \ } while (0) -#ifdef INET -static void ipsec4_common_ctlinput(int, struct sockaddr *, void *, int); -#endif - /* * ipsec_common_input gets called when an IPsec-protected packet * is received by IPv4 or IPv6. Its job is to find the right SA * and call the appropriate transform. The transform callback * takes care of further processing (like ingress filtering). */ -int +static int ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) { - char buf[INET6_ADDRSTRLEN]; + char buf[IPSEC_ADDRSTRLEN]; union sockaddr_union dst_address; struct secasvar *sav; - u_int32_t spi; + uint32_t spi; int error; -#ifdef INET -#ifdef IPSEC_NAT_T - struct m_tag *tag; -#endif -#endif IPSEC_ISTAT(sproto, input); @@ -178,12 +173,6 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) m_copydata(m, offsetof(struct ip, ip_dst), sizeof(struct in_addr), (caddr_t) &dst_address.sin.sin_addr); -#ifdef IPSEC_NAT_T - /* Find the source port for NAT-T; see udp*_espdecap. */ - tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL); - if (tag != NULL) - dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1]; -#endif /* IPSEC_NAT_T */ break; #endif /* INET */ #ifdef INET6 @@ -209,7 +198,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) } /* NB: only pass dst since key_allocsa follows RFC2401 */ - sav = KEY_ALLOCSA(&dst_address, sproto, spi); + sav = key_allocsa(&dst_address, sproto, spi); if (sav == NULL) { DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n", __func__, ipsec_address(&dst_address, buf, sizeof(buf)), @@ -224,7 +213,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) __func__, ipsec_address(&dst_address, buf, sizeof(buf)), (u_long) ntohl(spi), sproto)); IPSEC_ISTAT(sproto, noxform); - KEY_FREESAV(&sav); + key_freesav(&sav); m_freem(m); return ENXIO; } @@ -234,69 +223,50 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) * everything else. */ error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff); - KEY_FREESAV(&sav); - return error; + if (error != 0) + key_freesav(&sav); + return (error); } #ifdef INET -int -ah4_input(struct mbuf **mp, int *offp, int proto) -{ - struct mbuf *m; - int off; - - m = *mp; - off = *offp; - *mp = NULL; - - ipsec_common_input(m, off, offsetof(struct ip, ip_p), - AF_INET, IPPROTO_AH); - return (IPPROTO_DONE); -} -void -ah4_ctlinput(int cmd, struct sockaddr *sa, void *v) -{ - if (sa->sa_family == AF_INET && - sa->sa_len == sizeof(struct sockaddr_in)) - ipsec4_common_ctlinput(cmd, sa, v, IPPROTO_AH); -} - -int -esp4_input(struct mbuf **mp, int *offp, int proto) -{ - struct mbuf *m; - int off; - - m = *mp; - off = *offp; - mp = NULL; - - ipsec_common_input(m, off, offsetof(struct ip, ip_p), - AF_INET, IPPROTO_ESP); - return (IPPROTO_DONE); -} - -void -esp4_ctlinput(int cmd, struct sockaddr *sa, void *v) -{ - if (sa->sa_family == AF_INET && - sa->sa_len == sizeof(struct sockaddr_in)) - ipsec4_common_ctlinput(cmd, sa, v, IPPROTO_ESP); -} +extern struct protosw inetsw[]; +/* + * IPSEC_INPUT() method implementation for IPv4. + * 0 - Permitted by inbound security policy for further processing. + * EACCES - Forbidden by inbound security policy. + * EINPROGRESS - consumed by IPsec. + */ int -ipcomp4_input(struct mbuf **mp, int *offp, int proto) +ipsec4_input(struct mbuf *m, int offset, int proto) { - struct mbuf *m; - int off; - m = *mp; - off = *offp; - mp = NULL; - - ipsec_common_input(m, off, offsetof(struct ip, ip_p), - AF_INET, IPPROTO_IPCOMP); - return (IPPROTO_DONE); + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_IPCOMP: + /* Do inbound IPsec processing for AH/ESP/IPCOMP */ + ipsec_common_input(m, offset, + offsetof(struct ip, ip_p), AF_INET, proto); + return (EINPROGRESS); /* mbuf consumed by IPsec */ + default: + /* + * Protocols with further headers get their IPsec treatment + * within the protocol specific processing. + */ + if ((inetsw[ip_protox[proto]].pr_flags & PR_LASTHDR) == 0) + return (0); + /* FALLTHROUGH */ + }; + /* + * Enforce IPsec policy checking if we are seeing last header. + */ + if (ipsec4_in_reject(m, NULL) != 0) { + /* Forbidden by inbound security policy */ + m_freem(m); + return (EACCES); + } + return (0); } /* @@ -309,21 +279,14 @@ int ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int protoff) { - char buf[INET6_ADDRSTRLEN]; + char buf[IPSEC_ADDRSTRLEN]; struct ipsec_ctx_data ctx; - int prot, af, sproto, isr_prot; - struct ip *ip; - struct m_tag *mtag; - struct tdb_ident *tdbi; + struct xform_history *xh; struct secasindex *saidx; - int error; -#ifdef INET6 -#ifdef notyet - char ip6buf[INET6_ADDRSTRLEN]; -#endif -#endif + struct m_tag *mtag; + struct ip *ip; + int error, prot, af, sproto, isr_prot; - IPSEC_ASSERT(m != NULL, ("null mbuf")); IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); saidx = &sav->sah->saidx; @@ -337,7 +300,6 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, if (skip != 0) { /* * Fix IPv4 header - * XXXGL: do we need this entire block? */ if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) { DPRINTF(("%s: processing failed for SA %s/%08lx\n", @@ -356,16 +318,23 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, ip = mtod(m, struct ip *); } prot = ip->ip_p; + /* + * 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); IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; - ip = mtod(m, struct ip *); + ip = mtod(m, struct ip *); /* update pointer */ /* IP-in-IP encapsulation */ if (prot == IPPROTO_IPIP && saidx->mode != IPSEC_MODE_TRANSPORT) { - if (m->m_pkthdr.len - skip < sizeof(struct ip)) { IPSEC_ISTAT(sproto, hdrops); error = EINVAL; @@ -373,40 +342,11 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, } /* enc0: strip outer IPv4 header */ m_striphdr(m, 0, ip->ip_hl << 2); - -#ifdef notyet - /* XXX PROXY address isn't recorded in SAH */ - /* - * Check that the inner source address is the same as - * the proxy address, if available. - */ - if ((saidx->proxy.sa.sa_family == AF_INET && - saidx->proxy.sin.sin_addr.s_addr != - INADDR_ANY && - ipn.ip_src.s_addr != - saidx->proxy.sin.sin_addr.s_addr) || - (saidx->proxy.sa.sa_family != AF_INET && - saidx->proxy.sa.sa_family != 0)) { - - DPRINTF(("%s: inner source address %s doesn't " - "correspond to expected proxy source %s, " - "SA %s/%08lx\n", __func__, - inet_ntoa4(ipn.ip_src), - ipsp_address(saidx->proxy), - ipsp_address(saidx->dst), - (u_long) ntohl(sav->spi))); - - IPSEC_ISTAT(sproto, pdrops); - error = EACCES; - goto bad; - } -#endif /* notyet */ } #ifdef INET6 /* IPv6-in-IP encapsulation. */ else if (prot == IPPROTO_IPV6 && saidx->mode != IPSEC_MODE_TRANSPORT) { - if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) { IPSEC_ISTAT(sproto, hdrops); error = EINVAL; @@ -414,39 +354,14 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, } /* enc0: strip IPv4 header, keep IPv6 header only */ m_striphdr(m, 0, ip->ip_hl << 2); -#ifdef notyet - /* - * Check that the inner source address is the same as - * the proxy address, if available. - */ - if ((saidx->proxy.sa.sa_family == AF_INET6 && - !IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) && - !IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src, - &saidx->proxy.sin6.sin6_addr)) || - (saidx->proxy.sa.sa_family != AF_INET6 && - saidx->proxy.sa.sa_family != 0)) { - - DPRINTF(("%s: inner source address %s doesn't " - "correspond to expected proxy source %s, " - "SA %s/%08lx\n", __func__, - ip6_sprintf(ip6buf, &ip6n.ip6_src), - ipsec_address(&saidx->proxy), - ipsec_address(&saidx->dst), - (u_long) ntohl(sav->spi))); - - IPSEC_ISTAT(sproto, pdrops); - error = EACCES; - goto bad; - } -#endif /* notyet */ } #endif /* INET6 */ else if (prot != IPPROTO_IPV6 && saidx->mode == IPSEC_MODE_ANY) { /* * When mode is wildcard, inner protocol is IPv6 and * we have no INET6 support - drop this packet a bit later. - * In other cases we assume transport mode and outer - * header was already stripped in xform_xxx_cb. + * In other cases we assume transport mode. Set prot to + * correctly choose netisr. */ prot = IPPROTO_IPIP; } @@ -457,7 +372,7 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, */ if (sproto != IPPROTO_IPCOMP) { mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE, - sizeof(struct tdb_ident), M_NOWAIT); + sizeof(struct xform_history), M_NOWAIT); if (mtag == NULL) { DPRINTF(("%s: failed to get tag\n", __func__)); IPSEC_ISTAT(sproto, hdrops); @@ -465,14 +380,11 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } - tdbi = (struct tdb_ident *)(mtag + 1); - bcopy(&saidx->dst, &tdbi->dst, saidx->dst.sa.sa_len); - tdbi->proto = sproto; - tdbi->spi = sav->spi; - /* Cache those two for enc(4) in xform_ipip. */ - tdbi->alg_auth = sav->alg_auth; - tdbi->alg_enc = sav->alg_enc; - + xh = (struct xform_history *)(mtag + 1); + bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len); + xh->spi = sav->spi; + xh->proto = sproto; + xh->mode = saidx->mode; m_tag_prepend(m, mtag); } @@ -509,69 +421,65 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_AFTER); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; - error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m); - if (error) { - IPSEC_ISTAT(sproto, qfull); - DPRINTF(("%s: queue full; proto %u packet dropped\n", - __func__, sproto)); - return error; + + /* Handle virtual tunneling interfaces */ + if (saidx->mode == IPSEC_MODE_TUNNEL) + error = ipsec_if_input(m, sav, af); + if (error == 0) { + error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m); + if (error) { + IPSEC_ISTAT(sproto, qfull); + DPRINTF(("%s: queue full; proto %u packet dropped\n", + __func__, sproto)); + } } - return 0; + key_freesav(&sav); + return (error); bad: - m_freem(m); - return error; -} - -void -ipsec4_common_ctlinput(int cmd, struct sockaddr *sa, void *v, int proto) -{ - /* XXX nothing just yet */ + key_freesav(&sav); + if (m != NULL) + m_freem(m); + return (error); } #endif /* INET */ #ifdef INET6 -/* IPv6 AH wrapper. */ +/* + * IPSEC_INPUT() method implementation for IPv6. + * 0 - Permitted by inbound security policy for further processing. + * EACCES - Forbidden by inbound security policy. + * EINPROGRESS - consumed by IPsec. + */ int -ipsec6_common_input(struct mbuf **mp, int *offp, int proto) +ipsec6_input(struct mbuf *m, int offset, int proto) { - int l = 0; - int protoff; - struct ip6_ext ip6e; - - if (*offp < sizeof(struct ip6_hdr)) { - DPRINTF(("%s: bad offset %u\n", __func__, *offp)); - return IPPROTO_DONE; - } else if (*offp == sizeof(struct ip6_hdr)) { - protoff = offsetof(struct ip6_hdr, ip6_nxt); - } else { - /* Chase down the header chain... */ - protoff = sizeof(struct ip6_hdr); - - do { - protoff += l; - m_copydata(*mp, protoff, sizeof(ip6e), - (caddr_t) &ip6e); - - if (ip6e.ip6e_nxt == IPPROTO_AH) - l = (ip6e.ip6e_len + 2) << 2; - else - l = (ip6e.ip6e_len + 1) << 3; - IPSEC_ASSERT(l > 0, ("l went zero or negative")); - } while (protoff + l < *offp); - - /* Malformed packet check */ - if (protoff + l != *offp) { - DPRINTF(("%s: bad packet header chain, protoff %u, " - "l %u, off %u\n", __func__, protoff, l, *offp)); - IPSEC_ISTAT(proto, hdrops); - m_freem(*mp); - *mp = NULL; - return IPPROTO_DONE; - } - protoff += offsetof(struct ip6_ext, ip6e_nxt); + + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + case IPPROTO_IPCOMP: + /* Do inbound IPsec processing for AH/ESP/IPCOMP */ + ipsec_common_input(m, offset, + offsetof(struct ip6_hdr, ip6_nxt), AF_INET6, proto); + return (EINPROGRESS); /* mbuf consumed by IPsec */ + default: + /* + * Protocols with further headers get their IPsec treatment + * within the protocol specific processing. + */ + if ((inet6sw[ip6_protox[proto]].pr_flags & PR_LASTHDR) == 0) + return (0); + /* FALLTHROUGH */ + }; + /* + * Enforce IPsec policy checking if we are seeing last header. + */ + if (ipsec6_in_reject(m, NULL) != 0) { + /* Forbidden by inbound security policy */ + m_freem(m); + return (EACCES); } - (void) ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto); - return IPPROTO_DONE; + return (0); } /* @@ -582,21 +490,17 @@ int ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int protoff) { - char buf[INET6_ADDRSTRLEN]; + char buf[IPSEC_ADDRSTRLEN]; struct ipsec_ctx_data ctx; - int prot, af, sproto; + struct xform_history *xh; + struct secasindex *saidx; struct ip6_hdr *ip6; struct m_tag *mtag; - struct tdb_ident *tdbi; - struct secasindex *saidx; + int prot, af, sproto; int nxt, isr_prot; - u_int8_t nxt8; int error, nest; -#ifdef notyet - char ip6buf[INET6_ADDRSTRLEN]; -#endif + uint8_t nxt8; - IPSEC_ASSERT(m != NULL, ("null mbuf")); IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); saidx = &sav->sah->saidx; @@ -620,12 +524,13 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); - IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_BEFORE); if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0) goto bad; + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); + /* Save protocol */ m_copydata(m, protoff, 1, &nxt8); prot = nxt8; @@ -641,31 +546,6 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, /* ip6n will now contain the inner IPv6 header. */ m_striphdr(m, 0, skip); skip = 0; -#ifdef notyet - /* - * Check that the inner source address is the same as - * the proxy address, if available. - */ - if ((saidx->proxy.sa.sa_family == AF_INET6 && - !IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) && - !IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src, - &saidx->proxy.sin6.sin6_addr)) || - (saidx->proxy.sa.sa_family != AF_INET6 && - saidx->proxy.sa.sa_family != 0)) { - - DPRINTF(("%s: inner source address %s doesn't " - "correspond to expected proxy source %s, " - "SA %s/%08lx\n", __func__, - ip6_sprintf(ip6buf, &ip6n.ip6_src), - ipsec_address(&saidx->proxy), - ipsec_address(&saidx->dst), - (u_long) ntohl(sav->spi))); - - IPSEC_ISTAT(sproto, pdrops); - error = EACCES; - goto bad; - } -#endif /* notyet */ } #ifdef INET /* IP-in-IP encapsulation */ @@ -677,32 +557,8 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } /* ipn will now contain the inner IPv4 header */ - m_striphdr(m, 0, skip); + m_striphdr(m, 0, skip); skip = 0; -#ifdef notyet - /* - * Check that the inner source address is the same as - * the proxy address, if available. - */ - if ((saidx->proxy.sa.sa_family == AF_INET && - saidx->proxy.sin.sin_addr.s_addr != INADDR_ANY && - ipn.ip_src.s_addr != saidx->proxy.sin.sin_addr.s_addr) || - (saidx->proxy.sa.sa_family != AF_INET && - saidx->proxy.sa.sa_family != 0)) { - - DPRINTF(("%s: inner source address %s doesn't " - "correspond to expected proxy source %s, " - "SA %s/%08lx\n", __func__, - inet_ntoa4(ipn.ip_src), - ipsec_address(&saidx->proxy), - ipsec_address(&saidx->dst), - (u_long) ntohl(sav->spi))); - - IPSEC_ISTAT(sproto, pdrops); - error = EACCES; - goto bad; - } -#endif /* notyet */ } #endif /* INET */ else { @@ -715,7 +571,7 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, */ if (sproto != IPPROTO_IPCOMP) { mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE, - sizeof(struct tdb_ident), M_NOWAIT); + sizeof(struct xform_history), M_NOWAIT); if (mtag == NULL) { DPRINTF(("%s: failed to get tag\n", __func__)); IPSEC_ISTAT(sproto, hdrops); @@ -723,20 +579,16 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, goto bad; } - tdbi = (struct tdb_ident *)(mtag + 1); - bcopy(&saidx->dst, &tdbi->dst, sizeof(union sockaddr_union)); - tdbi->proto = sproto; - tdbi->spi = sav->spi; - /* Cache those two for enc(4) in xform_ipip. */ - tdbi->alg_auth = sav->alg_auth; - tdbi->alg_enc = sav->alg_enc; - + xh = (struct xform_history *)(mtag + 1); + bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len); + xh->spi = sav->spi; + xh->proto = sproto; + xh->mode = saidx->mode; m_tag_prepend(m, mtag); } key_sa_recordxfer(sav, m); - #ifdef INET if (prot == IPPROTO_IPIP) af = AF_INET; @@ -767,12 +619,19 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, error = EPFNOSUPPORT; goto bad; } - error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m); - if (error) { - IPSEC_ISTAT(sproto, qfull); - DPRINTF(("%s: queue full; proto %u packet dropped\n", - __func__, sproto)); + /* Handle virtual tunneling interfaces */ + if (saidx->mode == IPSEC_MODE_TUNNEL) + error = ipsec_if_input(m, sav, af); + if (error == 0) { + error = netisr_queue_src(isr_prot, + (uintptr_t)sav->spi, m); + if (error) { + IPSEC_ISTAT(sproto, qfull); + DPRINTF(("%s: queue full; proto %u packet" + " dropped\n", __func__, sproto)); + } } + key_freesav(&sav); return (error); } /* @@ -810,99 +669,12 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, } nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &skip, nxt); } - return 0; + key_freesav(&sav); + return (0); bad: + key_freesav(&sav); if (m) m_freem(m); - return error; -} - -void -esp6_ctlinput(int cmd, struct sockaddr *sa, void *d) -{ - struct ip6ctlparam *ip6cp = NULL; - struct mbuf *m = NULL; - struct ip6_hdr *ip6; - int off; - - 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; - off = 0; /* calm gcc */ - } - - if (ip6 != NULL) { - - struct ip6ctlparam ip6cp1; - - /* - * Notify the error to all possible sockets via pfctlinput2. - * Since the upper layer information (such as protocol type, - * source and destination ports) is embedded in the encrypted - * data and might have been cut, we can't directly call - * an upper layer ctlinput function. However, the pcbnotify - * function will consider source and destination addresses - * as well as the flow info value, and may be able to find - * some PCB that should be notified. - * Although pfctlinput2 will call esp6_ctlinput(), there is - * no possibility of an infinite loop of function calls, - * because we don't pass the inner IPv6 header. - */ - bzero(&ip6cp1, sizeof(ip6cp1)); - ip6cp1.ip6c_src = ip6cp->ip6c_src; - pfctlinput2(cmd, sa, (void *)&ip6cp1); - - /* - * Then go to special cases that need ESP header information. - * XXX: We assume that when ip6 is non NULL, - * M and OFF are valid. - */ - - if (cmd == PRC_MSGSIZE) { - struct secasvar *sav; - u_int32_t spi; - int valid; - - /* check header length before using m_copydata */ - if (m->m_pkthdr.len < off + sizeof (struct esp)) - return; - m_copydata(m, off + offsetof(struct esp, esp_spi), - sizeof(u_int32_t), (caddr_t) &spi); - /* - * Check to see if we have a valid SA corresponding to - * the address in the ICMP message payload. - */ - sav = KEY_ALLOCSA((union sockaddr_union *)sa, - IPPROTO_ESP, spi); - valid = (sav != NULL); - if (sav) - KEY_FREESAV(&sav); - - /* XXX Further validation? */ - - /* - * Depending on whether the SA is "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(ip6cp, valid); - } - } else { - /* we normally notify any pcb here */ - } + return (error); } #endif /* INET6 */ |