示例#1
0
void *
nbuf_ensure_writable(nbuf_t *nbuf, size_t len)
{
	struct mbuf *m = nbuf->nb_mbuf;
	const u_int off = (uintptr_t)nbuf->nb_nptr - mtod(m, uintptr_t);
	const int tlen = off + len;
	bool head_buf;

	KASSERT(off < m_length(nbuf->nb_mbuf0));

	if (!M_UNWRITABLE(m, tlen)) {
		return nbuf->nb_nptr;
	}
	head_buf = (nbuf->nb_mbuf0 == m);
	if (m_makewritable(&m, 0, tlen, M_NOWAIT)) {
		memset(nbuf, 0, sizeof(nbuf_t));
		return NULL;
	}
	if (head_buf) {
		KASSERT(m_flags_p(m, M_PKTHDR));
		KASSERT(off < m_length(m));
		nbuf->nb_mbuf0 = m;
	}
	nbuf->nb_mbuf = m;
	nbuf->nb_nptr = mtod(m, uint8_t *) + off;

	return nbuf->nb_nptr;
}
static int
ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir)
{
	struct ip *ip;
	int rv, hlen;

#if __NetBSD_Version__ >= 200080000
	/*
	 * ensure that mbufs are writable beforehand
	 * as it's assumed by ipf code.
	 * XXX inefficient
	 */
	int error = m_makewritable(mp, 0, M_COPYALL, M_DONTWAIT);

	if (error) {
		m_freem(*mp);
		*mp = NULL;
		return error;
	}
#endif
	ip = mtod(*mp, struct ip *);
	hlen = ip->ip_hl << 2;

#ifdef INET
#if defined(M_CSUM_TCPv4)
	/*
	 * If the packet is out-bound, we can't delay checksums
	 * here.  For in-bound, the checksum has already been
	 * validated.
	 */
	if (dir == PFIL_OUT) {
		if ((*mp)->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) {
			in_delayed_cksum(*mp);
			(*mp)->m_pkthdr.csum_flags &=
			    ~(M_CSUM_TCPv4|M_CSUM_UDPv4);
		}
	}
#endif /* M_CSUM_TCPv4 */
#endif /* INET */

	/*
	 * Note, we don't need to update the checksum, because
	 * it has already been verified.
	 */
	rv = ipf_check(&ipfmain, ip, hlen, ifp, (dir == PFIL_OUT), mp);

	return (rv);
}
/*
 * ESP input processing, called (eventually) through the protocol switch.
 */
static int
esp_input(struct mbuf *m, const struct secasvar *sav, int skip, int protoff)
{
    const struct auth_hash *esph;
    const struct enc_xform *espx;
    struct tdb_ident *tdbi;
    struct tdb_crypto *tc;
    int plen, alen, hlen, error;
    struct m_tag *mtag;
    struct newesp *esp;

    struct cryptodesc *crde;
    struct cryptop *crp;

    IPSEC_SPLASSERT_SOFTNET("esp_input");

    IPSEC_ASSERT(sav != NULL, ("esp_input: null SA"));
    IPSEC_ASSERT(sav->tdb_encalgxform != NULL,
                 ("esp_input: null encoding xform"));
    IPSEC_ASSERT((skip&3) == 0 && (m->m_pkthdr.len&3) == 0,
                 ("esp_input: misaligned packet, skip %u pkt len %u",
                  skip, m->m_pkthdr.len));

    /* 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 length */
    if (sav->flags & SADB_X_EXT_OLD)
        hlen = sizeof (struct esp) + sav->ivlen;
    else
        hlen = sizeof (struct newesp) + sav->ivlen;
    /* Authenticator hash size */
    alen = esph ? esph->authsize : 0;

    /*
     * 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(("esp_input: "
                 "payload of %d octets not a multiple of %d octets,"
                 "  SA %s/%08lx\n",
                 plen, espx->blocksize,
                 ipsec_address(&sav->sah->saidx.dst),
                 (u_long) ntohl(sav->spi)));
        ESP_STATINC(ESP_STAT_BADILEN);
        m_freem(m);
        return EINVAL;
    }

    /*
     * Check sequence number.
     */
    if (esph && sav->replay && !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
        DPRINTF(("esp_input: packet replay check for %s\n",
                 ipsec_logsastr(sav)));	/*XXX*/
        ESP_STATINC(ESP_STAT_REPLAY);
        m_freem(m);
        return ENOBUFS;		/*XXX*/
    }

    /* Update the counters */
    ESP_STATADD(ESP_STAT_IBYTES, m->m_pkthdr.len - skip - hlen - alen);

    /* Find out if we've already done crypto */
    for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
            mtag != NULL;
            mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) {
        tdbi = (struct tdb_ident *) (mtag + 1);
        if (tdbi->proto == sav->sah->saidx.proto &&
                tdbi->spi == sav->spi &&
                !memcmp(&tdbi->dst, &sav->sah->saidx.dst,
                        sizeof(union sockaddr_union)))
            break;
    }

    /* Get crypto descriptors */
    crp = crypto_getreq(esph && espx ? 2 : 1);
    if (crp == NULL) {
        DPRINTF(("esp_input: failed to acquire crypto descriptors\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        m_freem(m);
        return ENOBUFS;
    }

    /* Get IPsec-specific opaque pointer */
    if (esph == NULL || mtag != NULL)
        tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
                                          M_XDATA, M_NOWAIT|M_ZERO);
    else
        tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
                                          M_XDATA, M_NOWAIT|M_ZERO);
    if (tc == NULL) {
        crypto_freereq(crp);
        DPRINTF(("esp_input: failed to allocate tdb_crypto\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        m_freem(m);
        return ENOBUFS;
    }

    error = m_makewritable(&m, 0, m->m_pkthdr.len, M_NOWAIT);
    if (error) {
        m_freem(m);
        free(tc, M_XDATA);
        crypto_freereq(crp);
        DPRINTF(("esp_input: m_makewritable failed\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        return error;
    }

    tc->tc_ptr = mtag;

    if (esph) {
        struct cryptodesc *crda = crp->crp_desc;

        IPSEC_ASSERT(crda != NULL, ("esp_input: null ah crypto descriptor"));

        /* Authentication descriptor */
        crda->crd_skip = skip;
        if (espx && espx->type == CRYPTO_AES_GCM_16)
            crda->crd_len = hlen - sav->ivlen;
        else
            crda->crd_len = m->m_pkthdr.len - (skip + alen);
        crda->crd_inject = m->m_pkthdr.len - alen;

        crda->crd_alg = esph->type;
        if (espx && (espx->type == CRYPTO_AES_GCM_16 ||
                     espx->type == CRYPTO_AES_GMAC)) {
            crda->crd_key = _KEYBUF(sav->key_enc);
            crda->crd_klen = _KEYBITS(sav->key_enc);
        } else {
            crda->crd_key = _KEYBUF(sav->key_auth);
            crda->crd_klen = _KEYBITS(sav->key_auth);
        }

        /* Copy the authenticator */
        if (mtag == NULL)
            m_copydata(m, m->m_pkthdr.len - alen, alen,
                       (tc + 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;
    crp->crp_buf = m;
    crp->crp_callback = esp_input_cb;
    crp->crp_sid = sav->tdb_cryptoid;
    crp->crp_opaque = tc;

    /* These are passed as-is to the callback */
    tc->tc_spi = sav->spi;
    tc->tc_dst = sav->sah->saidx.dst;
    tc->tc_proto = sav->sah->saidx.proto;
    tc->tc_protoff = protoff;
    tc->tc_skip = skip;

    /* Decryption descriptor */
    if (espx) {
        IPSEC_ASSERT(crde != NULL, ("esp_input: null esp crypto descriptor"));
        crde->crd_skip = skip + hlen;
        if (espx->type == CRYPTO_AES_GMAC)
            crde->crd_len = 0;
        else
            crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
        crde->crd_inject = skip + hlen - sav->ivlen;

        crde->crd_alg = espx->type;
        crde->crd_key = _KEYBUF(sav->key_enc);
        crde->crd_klen = _KEYBITS(sav->key_enc);
        /* XXX Rounds ? */
    }

    if (mtag == NULL)
        return crypto_dispatch(crp);
    else
        return esp_input_cb(crp);
}