/* * ipsec_common_input() gets called when we receive an IPsec-protected packet * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate * transform. The callback takes care of further processing (like ingress * filtering). */ int ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto, int udpencap) { #define IPSEC_ISTAT(x,y,z) (sproto == IPPROTO_ESP ? (x)++ : \ sproto == IPPROTO_AH ? (y)++ : (z)++) union sockaddr_union dst_address; struct timeval tv; struct tdb *tdbp; struct ifnet *encif; u_int32_t spi; u_int16_t cpi; int s, error; IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input, ipcompstat.ipcomps_input); if (m == 0) { DPRINTF(("ipsec_common_input(): NULL packet received\n")); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); return EINVAL; } if ((sproto == IPPROTO_ESP && !esp_enable) || (sproto == IPPROTO_AH && !ah_enable) || #if NPF > 0 (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) || #endif (sproto == IPPROTO_IPCOMP && !ipcomp_enable)) { switch (af) { #ifdef INET case AF_INET: rip_input(m, skip, sproto); break; #endif /* INET */ #ifdef INET6 case AF_INET6: rip6_input(&m, &skip, sproto); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } return 0; } if ((sproto == IPPROTO_IPCOMP) && (m->m_flags & M_COMP)) { m_freem(m); ipcompstat.ipcomps_pdrops++; DPRINTF(("ipsec_common_input(): repeated decompression\n")); return EINVAL; } if (m->m_pkthdr.len - skip < 2 * sizeof(u_int32_t)) { m_freem(m); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); DPRINTF(("ipsec_common_input(): packet too small\n")); 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) { m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t), (caddr_t) &cpi); spi = ntohl(htons(cpi)); } /* * Find tunnel control block and (indirectly) call the appropriate * kernel crypto routine. The resulting mbuf chain is a valid * IP packet ready to go through input processing. */ memset(&dst_address, 0, 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)); in6_recoverscope(&dst_address.sin6, &dst_address.sin6.sin6_addr, NULL); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } s = splsoftnet(); tdbp = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid), spi, &dst_address, sproto); if (tdbp == NULL) { splx(s); DPRINTF(("ipsec_common_input(): could not find SA for " "packet to %s, spi %08x\n", ipsp_address(dst_address), ntohl(spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb, ipcompstat.ipcomps_notdb); return ENOENT; } if (tdbp->tdb_flags & TDBF_INVALID) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid, ipcompstat.ipcomps_invalid); return EINVAL; } if (udpencap && !(tdbp->tdb_flags & TDBF_UDPENCAP)) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use non-udpencap SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); espstat.esps_udpinval++; return EINVAL; } if (tdbp->tdb_xform == NULL) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform, ipcompstat.ipcomps_noxform); return ENXIO; } if (sproto != IPPROTO_IPCOMP) { if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) == NULL) { splx(s); DPRINTF(("ipsec_common_input(): " "no enc%u interface for SA %s/%08x/%u\n", tdbp->tdb_tap, ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops, ipcompstat.ipcomps_pdrops); return EACCES; } /* XXX This conflicts with the scoped nature of IPv6 */ m->m_pkthdr.rcvif = encif; } /* Register first use, setup expiration timer. */ if (tdbp->tdb_first_use == 0) { tdbp->tdb_first_use = time_second; tv.tv_usec = 0; tv.tv_sec = tdbp->tdb_exp_first_use + tdbp->tdb_first_use; if (tdbp->tdb_flags & TDBF_FIRSTUSE) timeout_add(&tdbp->tdb_first_tmo, hzto(&tv)); tv.tv_sec = tdbp->tdb_first_use + tdbp->tdb_soft_first_use; if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE) timeout_add(&tdbp->tdb_sfirst_tmo, hzto(&tv)); } /* * Call appropriate transform and return -- callback takes care of * everything else. */ error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff); splx(s); return error; }
int encap6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; struct ip6_hdr *ip6; struct sockaddr_in6 s, d; const struct ip6protosw *psw; struct encaptab *ep, *match; int prio, matchprio; /* Expect 32-bit aligned data pointer on strict-align platforms */ MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip6 = mtod(m, struct ip6_hdr *); bzero(&s, sizeof(s)); s.sin6_family = AF_INET6; s.sin6_len = sizeof(struct sockaddr_in6); s.sin6_addr = ip6->ip6_src; bzero(&d, sizeof(d)); d.sin6_family = AF_INET6; d.sin6_len = sizeof(struct sockaddr_in6); d.sin6_addr = ip6->ip6_dst; match = NULL; matchprio = 0; for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { if (ep->af != AF_INET6) continue; if (ep->proto >= 0 && ep->proto != proto) continue; if (ep->func) prio = (*ep->func)(m, *offp, proto, ep->arg); else { /* * it's inbound traffic, we need to match in reverse * order */ prio = mask_match(ep, (struct sockaddr *)&d, (struct sockaddr *)&s); } /* see encap4_input() for issues here */ if (prio <= 0) continue; if (prio > matchprio) { matchprio = prio; match = ep; } } if (match) { /* found a match */ psw = (const struct ip6protosw *)match->psw; if (psw && psw->pr_input) { encap_fillarg(m, match); return (*psw->pr_input)(mp, offp, proto); } else { m_freem(m); return IPPROTO_DONE; } } /* last resort: inject to raw socket */ return rip6_input(mp, offp, proto); }