123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- /* $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),
- * Angelos D. Keromytis (kermit@csd.uch.gr) and
- * Niels Provos (provos@physnet.uni-hamburg.de).
- *
- * This code was written by John Ioannidis for BSD/OS in Athens, Greece,
- * in November 1995.
- *
- * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
- * by Angelos D. Keromytis.
- *
- * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
- * and Niels Provos.
- *
- * Additional features in 1999 by Angelos D. Keromytis.
- *
- * 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
- * all copies of any software which is or includes a copy or
- * modification of this software.
- * You may use this code under the GNU public license if you so wish. Please
- * contribute changes back to the authors under this freer than GPL license
- * so that we may further the use of strong encryption without limitations to
- * all.
- *
- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
- * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
- * PURPOSE.
- */
- /*
- * IPsec input processing.
- */
- #include <sys/cdefs.h>
- #include "opt_inet.h"
- #include "opt_inet6.h"
- #include "opt_ipsec.h"
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/malloc.h>
- #include <sys/mbuf.h>
- #include <sys/domain.h>
- #include <sys/protosw.h>
- #include <sys/socket.h>
- #include <sys/errno.h>
- #include <sys/hhook.h>
- #include <sys/syslog.h>
- #include <net/if.h>
- #include <net/if_var.h>
- #include <net/if_enc.h>
- #include <net/if_private.h>
- #include <net/netisr.h>
- #include <net/vnet.h>
- #include <netinet/in.h>
- #include <netinet/in_pcb.h>
- #include <netinet/in_systm.h>
- #include <netinet/ip.h>
- #include <netinet/ip_var.h>
- #include <netinet/ip_icmp.h>
- #include <netinet/in_var.h>
- #include <netinet/tcp_var.h>
- #include <netinet/ip6.h>
- #ifdef INET6
- #include <netinet6/ip6_var.h>
- #endif
- #include <netinet/in_pcb.h>
- #ifdef INET6
- #include <netinet/icmp6.h>
- #endif
- #include <netipsec/ipsec.h>
- #ifdef INET6
- #include <netipsec/ipsec6.h>
- #endif
- #include <netipsec/ipsec_support.h>
- #include <netipsec/ah_var.h>
- #include <netipsec/esp.h>
- #include <netipsec/esp_var.h>
- #include <netipsec/ipcomp_var.h>
- #include <netipsec/ipsec_offload.h>
- #include <netipsec/key.h>
- #include <netipsec/keydb.h>
- #include <netipsec/key_debug.h>
- #include <netipsec/xform.h>
- #include <machine/in_cksum.h>
- #include <machine/stdarg.h>
- #define IPSEC_ISTAT(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)
- /*
- * 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).
- */
- static int
- ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
- {
- IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
- union sockaddr_union dst_address;
- struct secasvar *sav;
- uint32_t spi;
- int error;
- IPSEC_ISTAT(sproto, input);
- IPSEC_ASSERT(m != NULL, ("null packet"));
- IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
- sproto == IPPROTO_IPCOMP,
- ("unexpected security protocol %u", sproto));
- if ((sproto == IPPROTO_ESP && !V_esp_enable) ||
- (sproto == IPPROTO_AH && !V_ah_enable) ||
- (sproto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
- m_freem(m);
- IPSEC_ISTAT(sproto, pdrops);
- return EOPNOTSUPP;
- }
- if (m->m_pkthdr.len - skip < 2 * sizeof (u_int32_t)) {
- m_freem(m);
- IPSEC_ISTAT(sproto, hdrops);
- DPRINTF(("%s: packet too small\n", __func__));
- return EINVAL;
- }
- /* Retrieve the SPI from the relevant IPsec header */
- if (sproto == IPPROTO_ESP)
- m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
- else if (sproto == IPPROTO_AH)
- m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
- (caddr_t) &spi);
- else if (sproto == IPPROTO_IPCOMP) {
- u_int16_t cpi;
- m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
- (caddr_t) &cpi);
- spi = ntohl(htons(cpi));
- }
- /*
- * Find the SA and (indirectly) call the appropriate
- * kernel crypto routine. The resulting mbuf chain is a valid
- * IP packet ready to go through input processing.
- */
- bzero(&dst_address, sizeof (dst_address));
- dst_address.sa.sa_family = af;
- switch (af) {
- #ifdef INET
- case AF_INET:
- dst_address.sin.sin_len = sizeof(struct sockaddr_in);
- m_copydata(m, offsetof(struct ip, ip_dst),
- sizeof(struct in_addr),
- (caddr_t) &dst_address.sin.sin_addr);
- break;
- #endif /* INET */
- #ifdef INET6
- case AF_INET6:
- dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
- m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
- sizeof(struct in6_addr),
- (caddr_t) &dst_address.sin6.sin6_addr);
- /* We keep addresses in SADB without embedded scope id */
- if (IN6_IS_SCOPE_LINKLOCAL(&dst_address.sin6.sin6_addr)) {
- /* XXX: sa6_recoverscope() */
- dst_address.sin6.sin6_scope_id =
- ntohs(dst_address.sin6.sin6_addr.s6_addr16[1]);
- dst_address.sin6.sin6_addr.s6_addr16[1] = 0;
- }
- break;
- #endif /* INET6 */
- default:
- DPRINTF(("%s: unsupported protocol family %u\n", __func__, af));
- m_freem(m);
- IPSEC_ISTAT(sproto, nopf);
- return EPFNOSUPPORT;
- }
- /* NB: only pass dst since key_allocsa follows RFC2401 */
- 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)),
- (u_long) ntohl(spi), sproto));
- IPSEC_ISTAT(sproto, notdb);
- m_freem(m);
- return ENOENT;
- }
- if (sav->tdb_xform == NULL) {
- DPRINTF(("%s: attempted to use uninitialized SA %s/%08lx/%u\n",
- __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
- (u_long) ntohl(spi), sproto));
- IPSEC_ISTAT(sproto, noxform);
- key_freesav(&sav);
- m_freem(m);
- return ENXIO;
- }
- /*
- * Call appropriate transform and return -- callback takes care of
- * everything else.
- */
- error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
- return (error);
- }
- #ifdef INET
- /*
- * 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
- ipsec4_input(struct mbuf *m, int offset, int proto)
- {
- int error;
- error = ipsec_accel_input(m, offset, proto);
- if (error != ENXIO)
- return (error);
- 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.
- */
- switch (proto) {
- case IPPROTO_ICMP:
- case IPPROTO_IGMP:
- case IPPROTO_IPV4:
- case IPPROTO_IPV6:
- case IPPROTO_RSVP:
- case IPPROTO_GRE:
- case IPPROTO_MOBILE:
- case IPPROTO_ETHERIP:
- case IPPROTO_PIM:
- case IPPROTO_SCTP:
- break;
- default:
- return (0);
- }
- };
- /*
- * 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);
- }
- int
- ipsec4_ctlinput(ipsec_ctlinput_param_t param)
- {
- struct icmp *icp = param.icmp;
- struct ip *ip = &icp->icmp_ip;
- struct sockaddr_in icmpsrc = {
- .sin_len = sizeof(struct sockaddr_in),
- .sin_family = AF_INET,
- .sin_addr = ip->ip_dst,
- };
- struct in_conninfo inc;
- struct secasvar *sav;
- uint32_t pmtu, spi;
- uint32_t max_pmtu;
- uint8_t proto;
- pmtu = ntohs(icp->icmp_nextmtu);
- if (pmtu < V_ip4_ipsec_min_pmtu)
- return (EINVAL);
- proto = ip->ip_p;
- if (proto != IPPROTO_ESP && proto != IPPROTO_AH &&
- proto != IPPROTO_IPCOMP)
- return (EINVAL);
- memcpy(&spi, (caddr_t)ip + (ip->ip_hl << 2), sizeof(spi));
- sav = key_allocsa((union sockaddr_union *)&icmpsrc, proto, spi);
- if (sav == NULL)
- return (ENOENT);
- key_freesav(&sav);
- memset(&inc, 0, sizeof(inc));
- inc.inc_faddr = ip->ip_dst;
- /* Update pmtu only if its smaller than the current one. */
- max_pmtu = tcp_hc_getmtu(&inc);
- if (max_pmtu == 0)
- max_pmtu = tcp_maxmtu(&inc, NULL);
- if (pmtu < max_pmtu)
- tcp_hc_updatemtu(&inc, pmtu);
- return (0);
- }
- /*
- * IPsec input callback for INET protocols.
- * This routine is called as the transform callback.
- * Takes care of filtering and other sanity checks on
- * the processed packet.
- */
- int
- ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
- int protoff)
- {
- IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
- struct epoch_tracker et;
- struct ipsec_ctx_data ctx;
- struct xform_history *xh;
- struct secasindex *saidx;
- struct m_tag *mtag;
- struct ip *ip;
- int error, prot, af, sproto, isr_prot;
- IPSEC_ASSERT(sav != NULL, ("null SA"));
- IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
- saidx = &sav->sah->saidx;
- af = saidx->dst.sa.sa_family;
- IPSEC_ASSERT(af == AF_INET, ("unexpected af %u", af));
- sproto = saidx->proto;
- IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
- sproto == IPPROTO_IPCOMP,
- ("unexpected security protocol %u", sproto));
- if (skip != 0) {
- /*
- * Fix IPv4 header
- */
- if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
- DPRINTF(("%s: processing failed for SA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst,
- buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
- IPSEC_ISTAT(sproto, hdrops);
- error = ENOBUFS;
- goto bad_noepoch;
- }
- ip = mtod(m, struct ip *);
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_sum = 0;
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
- } else {
- 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);
- /*
- * Needed for ipsec_run_hooks and netisr_queue_src
- */
- NET_EPOCH_ENTER(et);
- IPSEC_INIT_CTX(&ctx, &m, NULL, sav, AF_INET, IPSEC_ENC_BEFORE);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
- goto bad;
- 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;
- goto bad;
- }
- /* enc0: strip outer IPv4 header */
- m_striphdr(m, 0, ip->ip_hl << 2);
- }
- #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;
- goto bad;
- }
- /* enc0: strip IPv4 header, keep IPv6 header only */
- m_striphdr(m, 0, ip->ip_hl << 2);
- }
- #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. Set prot to
- * correctly choose netisr.
- */
- prot = IPPROTO_IPIP;
- }
- /*
- * Record what we've done to the packet (under what SA it was
- * processed).
- */
- if (sproto != IPPROTO_IPCOMP) {
- mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
- sizeof(struct xform_history), M_NOWAIT);
- if (mtag == NULL) {
- DPRINTF(("%s: failed to get tag\n", __func__));
- IPSEC_ISTAT(sproto, hdrops);
- error = ENOMEM;
- goto bad;
- }
- 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); /* record data transfer */
- /*
- * In transport mode requeue decrypted mbuf back to IPv4 protocol
- * handler. This is necessary to correctly expose rcvif.
- */
- if (saidx->mode == IPSEC_MODE_TRANSPORT)
- prot = IPPROTO_IPIP;
- /*
- * Re-dispatch via software interrupt.
- */
- switch (prot) {
- case IPPROTO_IPIP:
- isr_prot = NETISR_IP;
- af = AF_INET;
- break;
- #ifdef INET6
- case IPPROTO_IPV6:
- isr_prot = NETISR_IPV6;
- af = AF_INET6;
- break;
- #endif
- default:
- DPRINTF(("%s: cannot handle inner ip proto %d\n",
- __func__, prot));
- IPSEC_ISTAT(sproto, nopf);
- error = EPFNOSUPPORT;
- goto bad;
- }
- IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
- goto bad;
- /* 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));
- }
- }
- NET_EPOCH_EXIT(et);
- key_freesav(&sav);
- return (error);
- bad:
- NET_EPOCH_EXIT(et);
- bad_noepoch:
- key_freesav(&sav);
- if (m != NULL)
- m_freem(m);
- return (error);
- }
- #endif /* INET */
- #ifdef INET6
- static bool
- ipsec6_lasthdr(int proto)
- {
- switch (proto) {
- case IPPROTO_IPV4:
- case IPPROTO_IPV6:
- case IPPROTO_GRE:
- case IPPROTO_ICMPV6:
- case IPPROTO_ETHERIP:
- case IPPROTO_PIM:
- case IPPROTO_SCTP:
- return (true);
- default:
- return (false);
- };
- }
- /*
- * 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_input(struct mbuf *m, int offset, int proto)
- {
- int error;
- error = ipsec_accel_input(m, offset, proto);
- if (error != ENXIO)
- return (error);
-
- 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 (!ipsec6_lasthdr(proto))
- 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);
- }
- return (0);
- }
- int
- ipsec6_ctlinput(ipsec_ctlinput_param_t param)
- {
- return (0);
- }
- extern ipproto_input_t *ip6_protox[];
- /*
- * IPsec input callback, called by the transform callback. Takes care of
- * filtering and other sanity checks on the processed packet.
- */
- int
- ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
- int protoff)
- {
- IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
- struct epoch_tracker et;
- struct ipsec_ctx_data ctx;
- struct xform_history *xh;
- struct secasindex *saidx;
- struct ip6_hdr *ip6;
- struct m_tag *mtag;
- int prot, af, sproto;
- int nxt, isr_prot;
- int error, nest;
- uint8_t nxt8;
- IPSEC_ASSERT(sav != NULL, ("null SA"));
- IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
- saidx = &sav->sah->saidx;
- af = saidx->dst.sa.sa_family;
- IPSEC_ASSERT(af == AF_INET6, ("unexpected af %u", af));
- sproto = saidx->proto;
- IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
- sproto == IPPROTO_IPCOMP,
- ("unexpected security protocol %u", sproto));
- NET_EPOCH_ENTER(et);
- /* Fix IPv6 header */
- if (m->m_len < sizeof(struct ip6_hdr) &&
- (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
- DPRINTF(("%s: processing failed for SA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst, buf,
- sizeof(buf)), (u_long) ntohl(sav->spi)));
- IPSEC_ISTAT(sproto, hdrops);
- error = EACCES;
- goto bad;
- }
- IPSEC_INIT_CTX(&ctx, &m, NULL, 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;
- /*
- * Check that we have NAT-T enabled and apply transport mode
- * decapsulation NAT procedure (RFC3948).
- * Do this before invoking into the PFIL.
- */
- if (sav->natt != NULL &&
- (prot == IPPROTO_UDP || prot == IPPROTO_TCP))
- udp_ipsec_adjust_cksum(m, sav, prot, skip);
- /* IPv6-in-IP encapsulation */
- if (prot == IPPROTO_IPV6 &&
- saidx->mode != IPSEC_MODE_TRANSPORT) {
- if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
- IPSEC_ISTAT(sproto, hdrops);
- error = EINVAL;
- goto bad;
- }
- /* ip6n will now contain the inner IPv6 header. */
- m_striphdr(m, 0, skip);
- skip = 0;
- }
- #ifdef INET
- /* IP-in-IP encapsulation */
- else if (prot == IPPROTO_IPIP &&
- saidx->mode != IPSEC_MODE_TRANSPORT) {
- if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
- IPSEC_ISTAT(sproto, hdrops);
- error = EINVAL;
- goto bad;
- }
- /* ipn will now contain the inner IPv4 header */
- m_striphdr(m, 0, skip);
- skip = 0;
- }
- #endif /* INET */
- else {
- prot = IPPROTO_IPV6; /* for correct BPF processing */
- }
- /*
- * Record what we've done to the packet (under what SA it was
- * processed).
- */
- if (sproto != IPPROTO_IPCOMP) {
- mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
- sizeof(struct xform_history), M_NOWAIT);
- if (mtag == NULL) {
- DPRINTF(("%s: failed to get tag\n", __func__));
- IPSEC_ISTAT(sproto, hdrops);
- error = ENOMEM;
- goto bad;
- }
- 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;
- else
- #endif
- af = AF_INET6;
- IPSEC_INIT_CTX(&ctx, &m, NULL, sav, af, IPSEC_ENC_AFTER);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
- goto bad;
- if (skip == 0) {
- /*
- * We stripped outer IPv6 header.
- * Now we should requeue decrypted packet via netisr.
- */
- switch (prot) {
- #ifdef INET
- case IPPROTO_IPIP:
- isr_prot = NETISR_IP;
- break;
- #endif
- case IPPROTO_IPV6:
- isr_prot = NETISR_IPV6;
- break;
- default:
- DPRINTF(("%s: cannot handle inner ip proto %d\n",
- __func__, prot));
- IPSEC_ISTAT(sproto, nopf);
- error = EPFNOSUPPORT;
- goto bad;
- }
- /* 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));
- }
- }
- NET_EPOCH_EXIT(et);
- key_freesav(&sav);
- return (error);
- }
- /*
- * See the end of ip6_input for this logic.
- * IPPROTO_IPV[46] case will be processed just like other ones
- */
- nest = 0;
- nxt = nxt8;
- while (nxt != IPPROTO_DONE) {
- if (V_ip6_hdrnestlimit && (++nest > V_ip6_hdrnestlimit)) {
- IP6STAT_INC(ip6s_toomanyhdr);
- error = EINVAL;
- goto bad;
- }
- /*
- * Protection against faulty packet - there should be
- * more sanity checks in header chain processing.
- */
- if (m->m_pkthdr.len < skip) {
- IP6STAT_INC(ip6s_tooshort);
- in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
- error = EINVAL;
- goto bad;
- }
- /*
- * Enforce IPsec policy checking if we are seeing last header.
- * note that we do not visit this with protocols with pcb layer
- * code - like udp/tcp/raw ip.
- */
- if (ipsec6_lasthdr(nxt) && ipsec6_in_reject(m, NULL)) {
- error = EINVAL;
- goto bad;
- }
- nxt = ip6_protox[nxt](&m, &skip, nxt);
- }
- NET_EPOCH_EXIT(et);
- key_freesav(&sav);
- return (0);
- bad:
- NET_EPOCH_EXIT(et);
- key_freesav(&sav);
- if (m)
- m_freem(m);
- return (error);
- }
- #endif /* INET6 */
|