/* * Insert IP options into preformed packet. Adjust IP destination as * required for IP source routing, as indicated by a non-zero in_addr at the * start of the options. * * XXX This routine assumes that the packet has no options in place. */ struct mbuf * ip_insertoptions(struct mbuf *m, struct mbuf *opt, int *phlen) { struct ipoption *p = mtod(opt, struct ipoption *); struct mbuf *n; struct ip *ip = mtod(m, struct ip *); unsigned optlen; optlen = opt->m_len - sizeof(p->ipopt_dst); if (optlen + ntohs(ip->ip_len) > IP_MAXPACKET) { *phlen = 0; return (m); /* XXX should fail */ } if (p->ipopt_dst.s_addr) ip->ip_dst = p->ipopt_dst; if (!M_WRITABLE(m) || M_LEADINGSPACE(m) < optlen) { n = m_gethdr(M_NOWAIT, MT_DATA); if (n == NULL) { *phlen = 0; return (m); } m_move_pkthdr(n, m); n->m_pkthdr.rcvif = NULL; n->m_pkthdr.len += optlen; m->m_len -= sizeof(struct ip); m->m_data += sizeof(struct ip); n->m_next = m; m = n; m->m_len = optlen + sizeof(struct ip); m->m_data += max_linkhdr; bcopy(ip, mtod(m, void *), sizeof(struct ip)); } else {
/* * Ensure len bytes of contiguous space at the beginning of the mbuf chain */ struct mbuf * m_prepend(struct mbuf *m, int len, int how) { struct mbuf *mn; if (len > MHLEN) panic("mbuf prepend length too big"); if (M_LEADINGSPACE(m) >= len) { m->m_data -= len; m->m_len += len; } else { MGET(mn, how, m->m_type); if (mn == NULL) { m_freem(m); return (NULL); } if (m->m_flags & M_PKTHDR) M_MOVE_PKTHDR(mn, m); mn->m_next = m; m = mn; MH_ALIGN(m, len); m->m_len = len; } if (m->m_flags & M_PKTHDR) m->m_pkthdr.len += len; return (m); }
/* * Inject a new mbuf chain of length siz in mbuf chain m0 at * position len0. Returns a pointer to the first injected mbuf, or * NULL on failure (m0 is left undisturbed). Note that if there is * enough space for an object of size siz in the appropriate position, * no memory will be allocated. Also, there will be no data movement in * the first len0 bytes (pointers to that will remain valid). * * XXX It is assumed that siz is less than the size of an mbuf at the moment. */ struct mbuf * m_inject(struct mbuf *m0, int len0, int siz, int wait) { struct mbuf *m, *n, *n2 = NULL, *n3; unsigned len = len0, remain; if ((siz >= MHLEN) || (len0 <= 0)) return (NULL); for (m = m0; m && len > m->m_len; m = m->m_next) len -= m->m_len; if (m == NULL) return (NULL); remain = m->m_len - len; if (remain == 0) { if ((m->m_next) && (M_LEADINGSPACE(m->m_next) >= siz)) { m->m_next->m_len += siz; if (m0->m_flags & M_PKTHDR) m0->m_pkthdr.len += siz; m->m_next->m_data -= siz; return m->m_next; } } else { n2 = m_copym2(m, len, remain, wait); if (n2 == NULL) return (NULL); } MGET(n, wait, MT_DATA); if (n == NULL) { if (n2) m_freem(n2); return (NULL); } n->m_len = siz; if (m0->m_flags & M_PKTHDR) m0->m_pkthdr.len += siz; m->m_len -= remain; /* Trim */ if (n2) { for (n3 = n; n3->m_next != NULL; n3 = n3->m_next) ; n3->m_next = n2; } else n3 = n; for (; n3->m_next != NULL; n3 = n3->m_next) ; n3->m_next = m->m_next; m->m_next = n; return n; }
/* * Queue a packet. Start transmission if not active. * Packet is placed in Information field of PPP frame. * Called at splnet as the if->if_output handler. * Called at splnet from pppwrite(). */ static int pppoutput_serialized(struct ifnet *ifp, struct ifaltq_subque *ifsq, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rtp) { struct ppp_softc *sc = &ppp_softc[ifp->if_dunit]; int protocol, address, control; u_char *cp; int error; #ifdef INET struct ip *ip; #endif struct ifqueue *ifq; enum NPmode mode; int len; struct mbuf *m; struct altq_pktattr pktattr; if (sc->sc_devp == NULL || (ifp->if_flags & IFF_RUNNING) == 0 || ((ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC)) { error = ENETDOWN; /* sort of */ goto bad; } ifq_classify(&ifp->if_snd, m0, dst->sa_family, &pktattr); /* * Compute PPP header. */ m0->m_flags &= ~M_HIGHPRI; switch (dst->sa_family) { #ifdef INET case AF_INET: address = PPP_ALLSTATIONS; control = PPP_UI; protocol = PPP_IP; mode = sc->sc_npmode[NP_IP]; /* * If this packet has the "low delay" bit set in the IP header, * put it on the fastq instead. */ ip = mtod(m0, struct ip *); if (ip->ip_tos & IPTOS_LOWDELAY) m0->m_flags |= M_HIGHPRI; break; #endif #ifdef IPX case AF_IPX: /* * This is pretty bogus.. We dont have an ipxcp module in pppd * yet to configure the link parameters. Sigh. I guess a * manual ifconfig would do.... -Peter */ address = PPP_ALLSTATIONS; control = PPP_UI; protocol = PPP_IPX; mode = NPMODE_PASS; break; #endif case AF_UNSPEC: address = PPP_ADDRESS(dst->sa_data); control = PPP_CONTROL(dst->sa_data); protocol = PPP_PROTOCOL(dst->sa_data); mode = NPMODE_PASS; break; default: kprintf("%s: af%d not supported\n", ifp->if_xname, dst->sa_family); error = EAFNOSUPPORT; goto bad; } /* * Drop this packet, or return an error, if necessary. */ if (mode == NPMODE_ERROR) { error = ENETDOWN; goto bad; } if (mode == NPMODE_DROP) { error = 0; goto bad; } /* * Add PPP header. If no space in first mbuf, allocate another. * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.) */ if (M_LEADINGSPACE(m0) < PPP_HDRLEN) { m0 = m_prepend(m0, PPP_HDRLEN, MB_DONTWAIT); if (m0 == NULL) { error = ENOBUFS; goto bad; } m0->m_len = 0; } else m0->m_data -= PPP_HDRLEN; cp = mtod(m0, u_char *); *cp++ = address; *cp++ = control; *cp++ = protocol >> 8; *cp++ = protocol & 0xff; m0->m_len += PPP_HDRLEN; len = 0; for (m = m0; m != NULL; m = m->m_next) len += m->m_len; if (sc->sc_flags & SC_LOG_OUTPKT) { kprintf("%s output: ", ifp->if_xname); pppdumpm(m0); } if ((protocol & 0x8000) == 0) { #ifdef PPP_FILTER /* * Apply the pass and active filters to the packet, * but only if it is a data packet. */ *mtod(m0, u_char *) = 1; /* indicates outbound */ if (sc->sc_pass_filt.bf_insns != NULL && bpf_filter(sc->sc_pass_filt.bf_insns, (u_char *) m0, len, 0) == 0) { error = 0; /* drop this packet */ goto bad; } /* * Update the time we sent the most recent packet. */ if (sc->sc_active_filt.bf_insns == NULL || bpf_filter(sc->sc_active_filt.bf_insns, (u_char *) m0, len, 0)) sc->sc_last_sent = time_uptime; *mtod(m0, u_char *) = address; #else /* * Update the time we sent the most recent data packet. */ sc->sc_last_sent = time_uptime; #endif /* PPP_FILTER */ } BPF_MTAP(ifp, m0); /* * Put the packet on the appropriate queue. */ crit_enter(); if (mode == NPMODE_QUEUE) { /* XXX we should limit the number of packets on this queue */ *sc->sc_npqtail = m0; m0->m_nextpkt = NULL; sc->sc_npqtail = &m0->m_nextpkt; } else { /* fastq and if_snd are emptied at spl[soft]net now */ if ((m0->m_flags & M_HIGHPRI) && !ifq_is_enabled(&sc->sc_if.if_snd)) { ifq = &sc->sc_fastq; if (IF_QFULL(ifq) && dst->sa_family != AF_UNSPEC) { IF_DROP(ifq); m_freem(m0); error = ENOBUFS; } else { IF_ENQUEUE(ifq, m0); error = 0; } } else { ASSERT_ALTQ_SQ_SERIALIZED_HW(ifsq); error = ifsq_enqueue(ifsq, m0, &pktattr); } if (error) { crit_exit(); IFNET_STAT_INC(&sc->sc_if, oerrors, 1); sc->sc_stats.ppp_oerrors++; return (error); } (*sc->sc_start)(sc); } getmicrotime(&ifp->if_lastchange); IFNET_STAT_INC(ifp, opackets, 1); IFNET_STAT_INC(ifp, obytes, len); crit_exit(); return (0); bad: m_freem(m0); return (error); }
/* * ensure that [off, off + len) is contiguous on the mbuf chain "m". * packet chain before "off" is kept untouched. * if offp == NULL, the target will start at <retval, 0> on resulting chain. * if offp != NULL, the target will start at <retval, *offp> on resulting chain. * * on error return (NULL return value), original "m" will be freed. * * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster) */ struct mbuf * m_pulldown(struct mbuf *m, int off, int len, int *offp) { struct mbuf *n, *o; int hlen, tlen, olen; int sharedcluster; #if defined(PULLDOWN_STAT) && defined(INET6) static struct mbuf *prev = NULL; int prevlen = 0, prevmlen = 0; #endif /* check invalid arguments. */ if (m == NULL) panic("m == NULL in m_pulldown()"); if (len > MCLBYTES) { m_freem(m); return NULL; /* impossible */ } #if defined(PULLDOWN_STAT) && defined(INET6) ip6stat.ip6s_pulldown++; #endif #if defined(PULLDOWN_STAT) && defined(INET6) /* statistics for m_pullup */ ip6stat.ip6s_pullup++; if (off + len > MHLEN) ip6stat.ip6s_pullup_fail++; else { int dlen, mlen; dlen = (prev == m) ? prevlen : m->m_len; mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m); if (dlen >= off + len) ip6stat.ip6s_pullup--; /* call will not be made! */ else if ((m->m_flags & M_EXT) != 0) { ip6stat.ip6s_pullup_alloc++; ip6stat.ip6s_pullup_copy++; } else { if (mlen >= off + len) ip6stat.ip6s_pullup_copy++; else { ip6stat.ip6s_pullup_alloc++; ip6stat.ip6s_pullup_copy++; } } prevlen = off + len; prevmlen = MHLEN; } /* statistics for m_pullup2 */ ip6stat.ip6s_pullup2++; if (off + len > MCLBYTES) ip6stat.ip6s_pullup2_fail++; else { int dlen, mlen; dlen = (prev == m) ? prevlen : m->m_len; mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m); prevlen = off + len; prevmlen = mlen; if (dlen >= off + len) ip6stat.ip6s_pullup2--; /* call will not be made! */ else if ((m->m_flags & M_EXT) != 0) { ip6stat.ip6s_pullup2_alloc++; ip6stat.ip6s_pullup2_copy++; prevmlen = (off + len > MHLEN) ? MCLBYTES : MHLEN; } else { if (mlen >= off + len) ip6stat.ip6s_pullup2_copy++; else { ip6stat.ip6s_pullup2_alloc++; ip6stat.ip6s_pullup2_copy++; prevmlen = (off + len > MHLEN) ? MCLBYTES : MHLEN; } } } prev = m; #endif #ifdef PULLDOWN_DEBUG { struct mbuf *t; printf("before:"); for (t = m; t; t = t->m_next) printf(" %d", t->m_len); printf("\n"); } #endif n = m; while (n != NULL && off > 0) { if (n->m_len > off) break; off -= n->m_len; n = n->m_next; } /* be sure to point non-empty mbuf */ while (n != NULL && n->m_len == 0) n = n->m_next; if (!n) { m_freem(m); return NULL; /* mbuf chain too short */ } /* * the target data is on <n, off>. * if we got enough data on the mbuf "n", we're done. */ if ((off == 0 || offp) && len <= n->m_len - off) goto ok; #if defined(PULLDOWN_STAT) && defined(INET6) ip6stat.ip6s_pulldown_copy++; #endif /* * when len < n->m_len - off and off != 0, it is a special case. * len bytes from <n, off> sits in single mbuf, but the caller does * not like the starting position (off). * chop the current mbuf into two pieces, set off to 0. */ if (len < n->m_len - off) { o = m_copym(n, off, n->m_len - off, M_DONTWAIT); if (o == NULL) { m_freem(m); return NULL; /* ENOBUFS */ } n->m_len = off; o->m_next = n->m_next; n->m_next = o; n = n->m_next; off = 0; goto ok; } /* * we need to take hlen from <n, off> and tlen from <n->m_next, 0>, * and construct contiguous mbuf with m_len == len. * note that hlen + tlen == len, and tlen > 0. */ hlen = n->m_len - off; tlen = len - hlen; /* * ensure that we have enough trailing data on mbuf chain. * if not, we can do nothing about the chain. */ olen = 0; for (o = n->m_next; o != NULL; o = o->m_next) olen += o->m_len; if (hlen + olen < len) { m_freem(m); return NULL; /* mbuf chain too short */ } /* * easy cases first. * we need to use m_copydata() to get data from <n->m_next, 0>. */ if ((n->m_flags & M_EXT) == 0) sharedcluster = 0; else { if (n->m_ext.ext_free) sharedcluster = 1; else if (m_mclhasreference(n)) sharedcluster = 1; else sharedcluster = 0; } if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen && !sharedcluster) { m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len); n->m_len += tlen; m_adj(n->m_next, tlen); goto ok; } if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen && !sharedcluster) { n->m_next->m_data -= hlen; n->m_next->m_len += hlen; bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen); n->m_len -= hlen; n = n->m_next; off = 0; goto ok; } /* * now, we need to do the hard way. don't m_copy as there's no room * on both end. */ #if defined(PULLDOWN_STAT) && defined(INET6) ip6stat.ip6s_pulldown_alloc++; #endif MGET(o, M_DONTWAIT, m->m_type); if (o == NULL) { m_freem(m); return NULL; /* ENOBUFS */ } if (len > MHLEN) { /* use MHLEN just for safety */ MCLGET(o, M_DONTWAIT); if ((o->m_flags & M_EXT) == 0) { m_freem(m); m_free(o); return NULL; /* ENOBUFS */ } } /* get hlen from <n, off> into <o, 0> */ o->m_len = hlen; bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen); n->m_len -= hlen; /* get tlen from <n->m_next, 0> into <o, hlen> */ m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len); o->m_len += tlen; m_adj(n->m_next, tlen); o->m_next = n->m_next; n->m_next = o; n = o; off = 0; ok: #ifdef PULLDOWN_DEBUG { struct mbuf *t; printf("after:"); for (t = m; t; t = t->m_next) printf("%c%d", t == n ? '*' : ' ', t->m_len); printf(" (off=%d)\n", off); } #endif if (offp) *offp = off; return n; }
/* * Modify the packet so that it includes the authentication data. * The mbuf passed must start with IPv4 header. * * assumes that the first mbuf contains IPv4 header + option only. * the function does not modify m. */ int ah4_output(struct mbuf *m, struct ipsecrequest *isr) { struct secasvar *sav = isr->sav; const struct ah_algorithm *algo; u_int32_t spi; u_char *ahdrpos; u_char *ahsumpos = NULL; size_t hlen = 0; /* IP header+option in bytes */ size_t plen = 0; /* AH payload size in bytes */ size_t ahlen = 0; /* plen + sizeof(ah) */ struct ip *ip; struct in_addr dst; struct in_addr *finaldst; int error; /* sanity checks */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { struct ip *ip; ip = mtod(m, struct ip *); ipseclog((LOG_DEBUG, "ah4_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++; m_freem(m); return EINVAL; } algo = ah_algorithm_lookup(sav->alg_auth); if (!algo) { ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: " "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; m_freem(m); return EINVAL; } spi = sav->spi; /* * determine the size to grow. */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */ ahlen = plen + sizeof(struct ah); } else { /* RFC 2402 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */ ahlen = plen + sizeof(struct newah); } /* * grow the mbuf to accomodate AH. */ ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif if (m->m_len != hlen) panic("ah4_output: assumption failed (first mbuf length)"); if (M_LEADINGSPACE(m->m_next) < ahlen) { struct mbuf *n; MGET(n, MB_DONTWAIT, MT_DATA); if (!n) { ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n", __LINE__)); m_freem(m); return ENOBUFS; } n->m_len = ahlen; n->m_next = m->m_next; m->m_next = n; m->m_pkthdr.len += ahlen; ahdrpos = mtod(n, u_char *); } 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, 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 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 {