/* * RFC 3173 p 2.2. Non-Expansion Policy: * If the total size of a compressed payload and the IPComp header, as * defined in section 3, is not smaller than the size of the original * payload, the IP datagram MUST be sent in the original non-compressed * form. * * When we use IPComp in tunnel mode, for small packets we will receive * encapsulated IP-IP datagrams without any compression and without IPComp * header. */ static int ipcomp_encapcheck(union sockaddr_union *src, union sockaddr_union *dst) { struct secasvar *sav; sav = key_allocsa_tunnel(src, dst, IPPROTO_IPCOMP); if (sav == NULL) return (0); key_freesav(&sav); if (src->sa.sa_family == AF_INET) return (sizeof(struct in_addr) << 4); else return (sizeof(struct in6_addr) << 4); }
/* * ESP input processing, called (eventually) through the protocol switch. */ static int esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) { IPSEC_DEBUG_DECLARE(char buf[128]); const struct auth_hash *esph; const struct enc_xform *espx; struct xform_data *xd; struct cryptodesc *crde; struct cryptop *crp; struct newesp *esp; uint8_t *ivp; uint64_t cryptoid; int alen, error, hlen, plen; IPSEC_ASSERT(sav != NULL, ("null SA")); IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform")); error = EINVAL; /* Valid IP Packet length ? */ if ( (skip&3) || (m->m_pkthdr.len&3) ){ DPRINTF(("%s: misaligned packet, skip %u pkt len %u", __func__, skip, m->m_pkthdr.len)); ESPSTAT_INC(esps_badilen); goto bad; } /* XXX don't pullup, just copy header */ IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp)); esph = sav->tdb_authalgxform; espx = sav->tdb_encalgxform; /* Determine the ESP header and auth length */ if (sav->flags & SADB_X_EXT_OLD) hlen = sizeof (struct esp) + sav->ivlen; else hlen = sizeof (struct newesp) + sav->ivlen; alen = xform_ah_authsize(esph); /* * Verify payload length is multiple of encryption algorithm * block size. * * NB: This works for the null algorithm because the blocksize * is 4 and all packets must be 4-byte aligned regardless * of the algorithm. */ plen = m->m_pkthdr.len - (skip + hlen + alen); if ((plen & (espx->blocksize - 1)) || (plen <= 0)) { DPRINTF(("%s: payload of %d octets not a multiple of %d octets," " SA %s/%08lx\n", __func__, plen, espx->blocksize, ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), (u_long)ntohl(sav->spi))); ESPSTAT_INC(esps_badilen); goto bad; } /* * Check sequence number. */ SECASVAR_LOCK(sav); if (esph != NULL && sav->replay != NULL && sav->replay->wsize != 0) { if (ipsec_chkreplay(ntohl(esp->esp_seq), sav) == 0) { SECASVAR_UNLOCK(sav); DPRINTF(("%s: packet replay check for %s\n", __func__, ipsec_sa2str(sav, buf, sizeof(buf)))); ESPSTAT_INC(esps_replay); error = EACCES; goto bad; } } cryptoid = sav->tdb_cryptoid; SECASVAR_UNLOCK(sav); /* Update the counters */ ESPSTAT_ADD(esps_ibytes, m->m_pkthdr.len - (skip + hlen + alen)); /* Get crypto descriptors */ crp = crypto_getreq(esph && espx ? 2 : 1); if (crp == NULL) { DPRINTF(("%s: failed to acquire crypto descriptors\n", __func__)); ESPSTAT_INC(esps_crypto); error = ENOBUFS; goto bad; } /* Get IPsec-specific opaque pointer */ xd = malloc(sizeof(*xd) + alen, M_XDATA, M_NOWAIT | M_ZERO); if (xd == NULL) { DPRINTF(("%s: failed to allocate xform_data\n", __func__)); ESPSTAT_INC(esps_crypto); crypto_freereq(crp); error = ENOBUFS; goto bad; } if (esph != NULL) { struct cryptodesc *crda = crp->crp_desc; IPSEC_ASSERT(crda != NULL, ("null ah crypto descriptor")); /* Authentication descriptor */ crda->crd_skip = skip; if (SAV_ISGCM(sav)) crda->crd_len = 8; /* RFC4106 5, SPI + SN */ else crda->crd_len = m->m_pkthdr.len - (skip + alen); crda->crd_inject = m->m_pkthdr.len - alen; crda->crd_alg = esph->type; /* Copy the authenticator */ m_copydata(m, m->m_pkthdr.len - alen, alen, (caddr_t) (xd + 1)); /* Chain authentication request */ crde = crda->crd_next; } else { crde = crp->crp_desc; } /* Crypto operation descriptor */ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC; crp->crp_buf = (caddr_t) m; crp->crp_callback = esp_input_cb; crp->crp_sid = cryptoid; crp->crp_opaque = (caddr_t) xd; /* These are passed as-is to the callback */ xd->sav = sav; xd->protoff = protoff; xd->skip = skip; xd->cryptoid = cryptoid; /* Decryption descriptor */ IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor")); crde->crd_skip = skip + hlen; crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen); crde->crd_inject = skip + hlen - sav->ivlen; if (SAV_ISCTRORGCM(sav)) { ivp = &crde->crd_iv[0]; /* GCM IV Format: RFC4106 4 */ /* CTR IV Format: RFC3686 4 */ /* Salt is last four bytes of key, RFC4106 8.1 */ /* Nonce is last four bytes of key, RFC3686 5.1 */ memcpy(ivp, sav->key_enc->key_data + _KEYLEN(sav->key_enc) - 4, 4); if (SAV_ISCTR(sav)) { /* Initial block counter is 1, RFC3686 4 */ be32enc(&ivp[sav->ivlen + 4], 1); } m_copydata(m, skip + hlen - sav->ivlen, sav->ivlen, &ivp[4]); crde->crd_flags |= CRD_F_IV_EXPLICIT; } crde->crd_alg = espx->type; return (crypto_dispatch(crp)); bad: m_freem(m); key_freesav(&sav); 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 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); (*sav->tdb_xform->xf_input)(m, sav, hlen, off); return (EINPROGRESS); /* Consumed by IPsec. */ }