/* * Modify the packet so that the payload is encrypted. * The mbuf (m) must start with IPv4 or IPv6 header. * On failure, free the given mbuf and return NULL. * * on invocation: * m nexthdrp md * v v v * IP ......... payload * during the encryption: * m nexthdrp mprev md * v v v v * IP ............... esp iv payload pad padlen nxthdr * <--><-><------><---------------> * esplen plen extendsiz * ivlen * <-----> esphlen * <-> hlen * <-----------------> espoff */ static int esp_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, struct ipsecrequest *isr, int af) { struct mbuf *n; struct mbuf *mprev; struct esp *esp; struct esptail *esptail; struct secasvar *sav = isr->sav; const struct esp_algorithm *algo; u_int32_t spi; u_int8_t nxt = 0; size_t plen; /* payload length to be encrypted */ size_t espoff; int ivlen; int afnumber; size_t extendsiz; int error = 0; struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; stat = &ipsec6stat; break; #endif default: ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af)); return 0; /* no change at all */ } /* some sanity check */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { switch (af) { #ifdef INET case AF_INET: { struct ip *ip; ip = mtod(m, struct ip *); ipseclog((LOG_DEBUG, "esp4_output: internal error: " "sav->replay is null: %x->%x, SPI=%u\n", (u_int32_t)ntohl(ip->ip_src.s_addr), (u_int32_t)ntohl(ip->ip_dst.s_addr), (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; break; } #endif /* INET */ #ifdef INET6 case AF_INET6: ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; break; #endif /* INET6 */ default: panic("esp_output: should not reach here"); } m_freem(m); return EINVAL; } algo = esp_algorithm_lookup(sav->alg_enc); if (!algo) { ipseclog((LOG_ERR, "esp_output: unsupported algorithm: " "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); m_freem(m); return EINVAL; } spi = sav->spi; ivlen = sav->ivlen; /* should be okey */ if (ivlen < 0) { panic("invalid ivlen"); } { /* * insert ESP header. * XXX inserts ESP header right after IPv4 header. should * chase the header chain. * XXX sequential number */ #ifdef INET struct ip *ip = NULL; #endif size_t esplen; /* sizeof(struct esp/newesp) */ size_t esphlen; /* sizeof(struct esp/newesp) + ivlen */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ esplen = sizeof(struct esp); } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) esplen = sizeof(struct esp); else esplen = sizeof(struct newesp); } esphlen = esplen + ivlen; for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (mprev == NULL || mprev->m_next != md) { ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n", afnumber)); m_freem(m); return EINVAL; } plen = 0; for (n = md; n; n = n->m_next) plen += n->m_len; switch (af) { #ifdef INET case AF_INET: ip = mtod(m, struct ip *); break; #endif #ifdef INET6 case AF_INET6: break; #endif } /* make the packet over-writable */ mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); error = ENOBUFS; goto fail; } mprev->m_next = md; espoff = m->m_pkthdr.len - plen; /* * grow the mbuf to accomodate ESP header. * before: IP ... payload * after: IP ... ESP IV payload */ if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT)) { MGET(n, M_NOWAIT, MT_DATA); if (!n) { m_freem(m); error = ENOBUFS; goto fail; } n->m_len = esphlen; mprev->m_next = n; n->m_next = md; m->m_pkthdr.len += esphlen; esp = mtod(n, struct esp *); } else {
/* * Modify the packet so that the payload is compressed. * The mbuf (m) must start with IPv4 or IPv6 header. * On failure, free the given mbuf and return non-zero. * * on invocation: * m nexthdrp md * v v v * IP ......... payload * during the encryption: * m nexthdrp mprev md * v v v v * IP ............... ipcomp payload * <-----><-----> * complen plen * <-> hlen * <-----------------> compoff */ static int ipcomp_output(struct mbuf *m, u_char *nexthdrp, struct mbuf *md, struct ipsecrequest *isr, int af) { struct mbuf *n; struct mbuf *md0; struct mbuf *mcopy; struct mbuf *mprev; struct ipcomp *ipcomp; struct secasvar *sav = isr->sav; const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ size_t plen0, plen; /* payload length to be compressed */ size_t compoff; int afnumber; int error = 0; struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; stat = &ipsec6stat; break; #endif default: ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af)); return 0; /* no change at all */ } /* grab parameters */ algo = ipcomp_algorithm_lookup(sav->alg_enc); if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) { stat->out_inval++; m_freem(m); return EINVAL; } if ((sav->flags & SADB_X_EXT_RAWCPI) == 0) cpi = sav->alg_enc; else cpi = ntohl(sav->spi) & 0xffff; /* compute original payload length */ plen = 0; for (n = md; n; n = n->m_next) plen += n->m_len; /* if the payload is short enough, we don't need to compress */ if (plen < algo->minplen) return 0; /* * retain the original packet for two purposes: * (1) we need to backout our changes when compression is not necessary. * (2) byte lifetime computation should use the original packet. * see RFC2401 page 23. * compromise two m_copym(). we will be going through every byte of * the payload during compression process anyways. */ mcopy = m_copym(m, 0, M_COPYALL, MB_DONTWAIT); if (mcopy == NULL) { error = ENOBUFS; return 0; } md0 = m_copym(md, 0, M_COPYALL, MB_DONTWAIT); if (md0 == NULL) { m_freem(mcopy); error = ENOBUFS; return 0; } plen0 = plen; /* make the packet over-writable */ for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (mprev == NULL || mprev->m_next != md) { ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n", afnumber)); stat->out_inval++; m_freem(m); m_freem(md0); m_freem(mcopy); return EINVAL; } mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); m_freem(md0); m_freem(mcopy); error = ENOBUFS; goto fail; } mprev->m_next = md; /* compress data part */ if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) { ipseclog((LOG_ERR, "packet compression failure\n")); m = NULL; m_freem(md0); m_freem(mcopy); stat->out_inval++; error = EINVAL; goto fail; } stat->out_comphist[sav->alg_enc]++; md = mprev->m_next; /* * if the packet became bigger, meaningless to use IPComp. * we've only wasted our cpu time. */ if (plen0 < plen) { m_freem(md); m_freem(mcopy); mprev->m_next = md0; return 0; } /* * no need to backout change beyond here. */ m_freem(md0); md0 = NULL; m->m_pkthdr.len -= plen0; m->m_pkthdr.len += plen; { /* * insert IPComp header. */ #ifdef INET struct ip *ip = NULL; #endif size_t complen = sizeof(struct ipcomp); switch (af) { #ifdef INET case AF_INET: ip = mtod(m, struct ip *); break; #endif #ifdef INET6 case AF_INET6: break; #endif } compoff = m->m_pkthdr.len - plen; /* * grow the mbuf to accomodate ipcomp header. * before: IP ... payload * after: IP ... ipcomp payload */ if (M_LEADINGSPACE(md) < complen) { MGET(n, MB_DONTWAIT, MT_DATA); if (!n) { m_freem(m); error = ENOBUFS; goto fail; } n->m_len = complen; mprev->m_next = n; n->m_next = md; m->m_pkthdr.len += complen; ipcomp = mtod(n, struct ipcomp *); } else {
/* * Modify the packet so that the payload is encrypted. * The mbuf (m) must start with IPv4 or IPv6 header. * On failure, free the given mbuf and return NULL. * * on invocation: * m nexthdrp md * v v v * IP ......... payload * during the encryption: * m nexthdrp mprev md * v v v v * IP ............... esp iv payload pad padlen nxthdr * <--><-><------><---------------> * esplen plen extendsiz * ivlen * <-----> esphlen * <-> hlen * <-----------------> espoff */ static int esp_output( struct mbuf *m, u_char *nexthdrp, struct mbuf *md, int af, struct secasvar *sav) { struct mbuf *n; struct mbuf *mprev; struct esp *esp; struct esptail *esptail; const struct esp_algorithm *algo; u_int32_t spi; u_int8_t nxt = 0; size_t plen; /*payload length to be encrypted*/ size_t espoff; size_t esphlen; /* sizeof(struct esp/newesp) + ivlen */ int ivlen; int afnumber; size_t extendsiz; int error = 0; struct ipsecstat *stat; struct udphdr *udp = NULL; int udp_encapsulate = (sav->flags & SADB_X_EXT_NATT && (af == AF_INET || af == AF_INET6) && (esp_udp_encap_port & 0xFFFF) != 0); KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_START, sav->ivlen,0,0,0,0); switch (af) { #if INET case AF_INET: afnumber = 4; stat = &ipsecstat; break; #endif #if INET6 case AF_INET6: afnumber = 6; stat = &ipsec6stat; break; #endif default: ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af)); KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 1,0,0,0,0); return 0; /* no change at all */ } /* some sanity check */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { switch (af) { #if INET case AF_INET: { struct ip *ip; ip = mtod(m, struct ip *); ipseclog((LOG_DEBUG, "esp4_output: internal error: " "sav->replay is null: %x->%x, SPI=%u\n", (u_int32_t)ntohl(ip->ip_src.s_addr), (u_int32_t)ntohl(ip->ip_dst.s_addr), (u_int32_t)ntohl(sav->spi))); IPSEC_STAT_INCREMENT(ipsecstat.out_inval); break; } #endif /*INET*/ #if INET6 case AF_INET6: ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", (u_int32_t)ntohl(sav->spi))); IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); break; #endif /*INET6*/ default: panic("esp_output: should not reach here"); } m_freem(m); KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 2,0,0,0,0); return EINVAL; } algo = esp_algorithm_lookup(sav->alg_enc); if (!algo) { ipseclog((LOG_ERR, "esp_output: unsupported algorithm: " "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); m_freem(m); KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 3,0,0,0,0); return EINVAL; } spi = sav->spi; ivlen = sav->ivlen; /* should be okey */ if (ivlen < 0) { panic("invalid ivlen"); } { /* * insert ESP header. * XXX inserts ESP header right after IPv4 header. should * chase the header chain. * XXX sequential number */ #if INET struct ip *ip = NULL; #endif #if INET6 struct ip6_hdr *ip6 = NULL; #endif size_t esplen; /* sizeof(struct esp/newesp) */ size_t hlen = 0; /* ip header len */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ esplen = sizeof(struct esp); } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) esplen = sizeof(struct esp); else esplen = sizeof(struct newesp); } esphlen = esplen + ivlen; for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (mprev == NULL || mprev->m_next != md) { ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n", afnumber)); m_freem(m); KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 4,0,0,0,0); return EINVAL; } plen = 0; for (n = md; n; n = n->m_next) plen += n->m_len; switch (af) { #if INET case AF_INET: ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif break; #endif #if INET6 case AF_INET6: ip6 = mtod(m, struct ip6_hdr *); hlen = sizeof(*ip6); break; #endif } /* make the packet over-writable */ mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); error = ENOBUFS; goto fail; } mprev->m_next = md; /* * Translate UDP source port back to its original value. * SADB_X_EXT_NATT_MULTIPLEUSERS is only set for transort mode. */ if ((sav->flags & SADB_X_EXT_NATT_MULTIPLEUSERS) != 0) { /* if not UDP - drop it */ if (ip->ip_p != IPPROTO_UDP) { IPSEC_STAT_INCREMENT(ipsecstat.out_inval); m_freem(m); error = EINVAL; goto fail; } udp = mtod(md, struct udphdr *); /* if src port not set in sav - find it */ if (sav->natt_encapsulated_src_port == 0) if (key_natt_get_translated_port(sav) == 0) { m_freem(m); error = EINVAL; goto fail; } if (sav->remote_ike_port == htons(udp->uh_dport)) { /* translate UDP port */ udp->uh_dport = sav->natt_encapsulated_src_port; udp->uh_sum = 0; /* don't need checksum with ESP auth */ } else { /* drop the packet - can't translate the port */ IPSEC_STAT_INCREMENT(ipsecstat.out_inval); m_freem(m); error = EINVAL; goto fail; } } espoff = m->m_pkthdr.len - plen; if (udp_encapsulate) { esphlen += sizeof(struct udphdr); espoff += sizeof(struct udphdr); } /* * grow the mbuf to accomodate ESP header. * before: IP ... payload * after: IP ... [UDP] ESP IV payload */ if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) { MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); error = ENOBUFS; goto fail; } n->m_len = esphlen; mprev->m_next = n; n->m_next = md; m->m_pkthdr.len += esphlen; if (udp_encapsulate) { udp = mtod(n, struct udphdr *); esp = (struct esp *)(void *)((caddr_t)udp + sizeof(struct udphdr)); } else { esp = mtod(n, struct esp *); } } else {