summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authoritojun <itojun@NetBSD.org>2000-03-21 23:53:30 +0000
committeritojun <itojun@NetBSD.org>2000-03-21 23:53:30 +0000
commitfadbd2b29a157d2a6e1a110c3eef3d445aab496b (patch)
tree5826e7203e42497083fe99810a24ddf0eb187001 /sys
parent3b3bcd2a107e669d64b473712b518bc826b9f8f0 (diff)
cleanup AH/policy processing.
- parse IPv6 header by using common function, ip6_{last,next}hdr. - fix behaivior in multiple AH cases. make strict boundary checks on mbuf chasing. (sync with latest kame)
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet6/ah_core.c662
-rw-r--r--sys/netinet6/ah_input.c51
-rw-r--r--sys/netinet6/ah_output.c14
-rw-r--r--sys/netinet6/ip6_input.c111
-rw-r--r--sys/netinet6/ip6_var.h6
-rw-r--r--sys/netinet6/ipsec.c103
6 files changed, 521 insertions, 426 deletions
diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c
index 1711a6bfd90..fe696b6f959 100644
--- a/sys/netinet6/ah_core.c
+++ b/sys/netinet6/ah_core.c
@@ -1,4 +1,5 @@
-/* $NetBSD: ah_core.c,v 1.16 2000/02/06 12:49:40 itojun Exp $ */
+/* $NetBSD: ah_core.c,v 1.17 2000/03/21 23:53:30 itojun Exp $ */
+/* $KAME: ah_core.c,v 1.29 2000/03/11 09:20:21 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -92,11 +93,6 @@
#define HMACSIZE 16
-#ifdef INET6
-#define ZEROBUFLEN 256
-static char zerobuf[ZEROBUFLEN];
-#endif
-
static int ah_sumsiz_1216 __P((struct secasvar *));
static int ah_sumsiz_zero __P((struct secasvar *));
static int ah_none_mature __P((struct secasvar *));
@@ -129,8 +125,11 @@ static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, caddr_t,
size_t));
static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t));
+static void ah_update_mbuf __P((struct mbuf *, int, int, struct ah_algorithm *,
+ struct ah_algorithm_state *));
+
/* checksum algorithms */
-/* NOTE: The order depends on SADB_AALG_x in net/pfkeyv2.h.h */
+/* NOTE: The order depends on SADB_AALG_x in net/pfkeyv2.h */
struct ah_algorithm ah_algorithms[] = {
{ 0, 0, 0, 0, 0, 0, },
{ ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128,
@@ -655,27 +654,78 @@ ah_hmac_sha1_result(state, addr)
/*
* go generate the checksum.
*/
+static void
+ah_update_mbuf(m, off, len, algo, algos)
+ struct mbuf *m;
+ int off;
+ int len;
+ struct ah_algorithm *algo;
+ struct ah_algorithm_state *algos;
+{
+ struct mbuf *n;
+ int tlen;
+
+ /* easy case first */
+ if (off + len <= m->m_len) {
+ (algo->update)(algos, mtod(m, caddr_t) + off, len);
+ return;
+ }
+
+ for (n = m; n; n = n->m_next) {
+ if (off < n->m_len)
+ break;
+
+ off -= n->m_len;
+ }
+
+ if (!n)
+ panic("ah_update_mbuf: wrong offset specified");
+
+ for (/*nothing*/; n && len > 0; n = n->m_next) {
+ if (n->m_len == 0)
+ continue;
+ if (n->m_len - off < len)
+ tlen = n->m_len - off;
+ else
+ tlen = len;
+
+ (algo->update)(algos, mtod(n, caddr_t) + off, tlen);
+
+ len -= tlen;
+ off = 0;
+ }
+}
+
+/*
+ * Go generate the checksum. This function won't modify the mbuf chain
+ * except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
+ */
int
-ah4_calccksum(m0, ahdat, algo, sav)
- struct mbuf *m0;
+ah4_calccksum(m, ahdat, algo, sav)
+ struct mbuf *m;
caddr_t ahdat;
struct ah_algorithm *algo;
struct secasvar *sav;
{
- struct mbuf *m;
+ int off;
int hdrtype;
- u_char *p;
size_t advancewidth;
struct ah_algorithm_state algos;
- int tlen;
u_char sumbuf[AH_MAXSUMSIZE];
int error = 0;
+ int ahseen;
+ struct mbuf *n = NULL;
- hdrtype = -1; /*dummy, it is called IPPROTO_IP*/
+ if ((m->m_flags & M_PKTHDR) == 0)
+ return EINVAL;
- m = m0;
+ ahseen = 0;
+ hdrtype = -1; /*dummy, it is called IPPROTO_IP*/
- p = mtod(m, u_char *);
+ off = 0;
(algo->init)(&algos, sav);
@@ -684,17 +734,16 @@ ah4_calccksum(m0, ahdat, algo, sav)
again:
/* gory. */
switch (hdrtype) {
- case -1: /*first one*/
+ case -1: /*first one only*/
{
/*
* copy ip hdr, modify to fit the AH checksum rule,
* then take a checksum.
- * XXX need to care about source routing... jesus.
*/
struct ip iphdr;
size_t hlen;
- bcopy((caddr_t)p, (caddr_t)&iphdr, sizeof(struct ip));
+ m_copydata(m, off, sizeof(iphdr), (caddr_t)&iphdr);
#ifdef _IP_VHL
hlen = IP_VHL_HL(iphdr.ip_vhl) << 2;
#else
@@ -702,22 +751,38 @@ again:
#endif
iphdr.ip_ttl = 0;
iphdr.ip_sum = htons(0);
- if (ip4_ah_cleartos) iphdr.ip_tos = 0;
+ if (ip4_ah_cleartos)
+ iphdr.ip_tos = 0;
iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask);
(algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip));
if (hlen != sizeof(struct ip)) {
u_char *p;
- int i, j;
- int l, skip;
- u_char dummy[4];
+ int i, l, skip;
+
+ if (hlen > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && hlen > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, hlen, mtod(n, caddr_t));
/*
* IP options processing.
* See RFC2402 appendix A.
*/
- bzero(dummy, sizeof(dummy));
- p = mtod(m, u_char *);
+ p = mtod(n, u_char *);
i = sizeof(struct ip);
while (i < hlen) {
skip = 1;
@@ -746,19 +811,22 @@ again:
"(type=%02x len=%02x)\n",
p[i + IPOPT_OPTVAL],
p[i + IPOPT_OLEN]));
- break;
+ m_free(n);
+ n = NULL;
+ error = EINVAL;
+ goto fail;
}
- if (skip) {
- for (j = 0; j < l / sizeof(dummy); j++)
- (algo->update)(&algos, dummy, sizeof(dummy));
-
- (algo->update)(&algos, dummy, l % sizeof(dummy));
- } else
- (algo->update)(&algos, p + i, l);
+ if (skip)
+ bzero(p + i, l);
if (p[i + IPOPT_OPTVAL] == IPOPT_EOL)
break;
i += l;
}
+ p = mtod(n, u_char *) + sizeof(struct ip);
+ (algo->update)(&algos, p, hlen - sizeof(struct ip));
+
+ m_free(n);
+ n = NULL;
}
hdrtype = (iphdr.ip_p) & 0xff;
@@ -768,375 +836,293 @@ again:
case IPPROTO_AH:
{
- u_char dummy[4];
+ struct ah ah;
int siz;
int hdrsiz;
+ int totlen;
- hdrsiz = (sav->flags & SADB_X_EXT_OLD) ?
- sizeof(struct ah) : sizeof(struct newah);
-
- (algo->update)(&algos, p, hdrsiz);
-
- /* key data region. */
+ m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
+ hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+ ? sizeof(struct ah)
+ : sizeof(struct newah);
siz = (*algo->sumsiz)(sav);
- bzero(&dummy[0], sizeof(dummy));
- while (sizeof(dummy) <= siz) {
- (algo->update)(&algos, dummy, sizeof(dummy));
- siz -= sizeof(dummy);
- }
- /* can't happen, but just in case */
- if (siz)
- (algo->update)(&algos, dummy, siz);
-
- /* padding region, just in case */
- siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav);
- if ((sav->flags & SADB_X_EXT_OLD) == 0)
- siz -= 4; /* sequence number field */
- if (0 < siz) {
- /* RFC 1826 */
- (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav),
- siz);
- }
+ totlen = (ah.ah_len + 2) << 2;
- hdrtype = ((struct ah *)p)->ah_nxt;
- advancewidth = hdrsiz;
- advancewidth += ((struct ah *)p)->ah_len << 2;
- if ((sav->flags & SADB_X_EXT_OLD) == 0)
- advancewidth -= 4; /* sequence number field */
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (!ahseen) {
+ if (totlen > m->m_pkthdr.len - off ||
+ totlen > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && totlen > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, totlen, mtod(n, caddr_t));
+ n->m_len = totlen;
+ bzero(mtod(n, caddr_t) + hdrsiz, siz);
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ } else
+ ah_update_mbuf(m, off, totlen, algo, &algos);
+ ahseen++;
+
+ hdrtype = ah.ah_nxt;
+ advancewidth = totlen;
break;
}
default:
- ipseclog((LOG_DEBUG, "ah4_calccksum: unexpected hdrtype=%x; "
- "treating rest as payload\n", hdrtype));
- /*fall through*/
- case IPPROTO_ICMP:
- case IPPROTO_IGMP:
- case IPPROTO_IPIP:
-#ifdef INET6
- case IPPROTO_IPV6:
- case IPPROTO_ICMPV6:
-#endif
- case IPPROTO_UDP:
- case IPPROTO_TCP:
- case IPPROTO_ESP:
- case IPPROTO_IPCOMP:
- while (m) {
- tlen = m->m_len - (p - mtod(m, u_char *));
- (algo->update)(&algos, p, tlen);
- m = m->m_next;
- p = m ? mtod(m, u_char *) : NULL;
- }
-
- advancewidth = 0; /*loop finished*/
+ ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, &algos);
+ advancewidth = m->m_pkthdr.len - off;
break;
}
- if (advancewidth) {
- /* is it safe? */
- while (m && advancewidth) {
- tlen = m->m_len - (p - mtod(m, u_char *));
- if (advancewidth < tlen) {
- p += advancewidth;
- advancewidth = 0;
- } else {
- advancewidth -= tlen;
- m = m->m_next;
- if (m)
- p = mtod(m, u_char *);
- else {
- ipseclog((LOG_DEBUG,
- "hit the end-of-mbuf...\n"));
- p = NULL;
- }
- }
- }
+ off += advancewidth;
+ if (off < m->m_pkthdr.len)
+ goto again;
- if (m)
- goto again;
- }
-
- /* for HMAC algorithms... */
(algo->result)(&algos, &sumbuf[0]);
bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
+ if (n)
+ m_free(n);
+ return error;
+
+fail:
+ if (n)
+ m_free(n);
return error;
}
#ifdef INET6
/*
- * go generate the checksum. This function won't modify the mbuf chain
+ * Go generate the checksum. This function won't modify the mbuf chain
* except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
*/
int
-ah6_calccksum(m0, ahdat, algo, sav)
- struct mbuf *m0;
+ah6_calccksum(m, ahdat, algo, sav)
+ struct mbuf *m;
caddr_t ahdat;
struct ah_algorithm *algo;
struct secasvar *sav;
{
- struct mbuf *m;
- int hdrtype;
- u_char *p;
- size_t advancewidth;
+ int newoff, off;
+ int proto, nxt;
+ struct mbuf *n = NULL;
+ int error;
+ int ahseen;
struct ah_algorithm_state algos;
- int tlen;
- int error = 0;
u_char sumbuf[AH_MAXSUMSIZE];
- int nest;
-
- hdrtype = -1; /*dummy, it is called IPPROTO_IPV6 */
-
- m = m0;
- p = mtod(m, u_char *);
+ if ((m->m_flags & M_PKTHDR) == 0)
+ return EINVAL;
(algo->init)(&algos, sav);
- advancewidth = 0; /*safety*/
- nest = 0;
-
-again:
- if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
- ip6stat.ip6s_toomanyhdr++;
- error = EINVAL; /*XXX*/
- goto bad;
+ off = 0;
+ proto = IPPROTO_IPV6;
+ nxt = -1;
+ ahseen = 0;
+
+ again:
+ newoff = ip6_nexthdr(m, off, proto, &nxt);
+ if (newoff < 0)
+ newoff = m->m_pkthdr.len;
+ else if (newoff <= off) {
+ error = EINVAL;
+ goto fail;
}
- /* gory. */
- switch (hdrtype) {
- case -1: /*first one*/
- {
- struct ip6_hdr ip6copy;
-
- bcopy(p, &ip6copy, sizeof(struct ip6_hdr));
- /* RFC2402 */
- ip6copy.ip6_flow = 0;
- ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK;
- ip6copy.ip6_vfc |= IPV6_VERSION;
- ip6copy.ip6_hlim = 0;
- if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
- ip6copy.ip6_src.s6_addr16[1] = 0x0000;
- if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
- ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
- (algo->update)(&algos, (caddr_t)&ip6copy,
- sizeof(struct ip6_hdr));
- hdrtype = (((struct ip6_hdr *)p)->ip6_nxt) & 0xff;
- advancewidth = sizeof(struct ip6_hdr);
+ switch (proto) {
+ case IPPROTO_IPV6:
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (off == 0) {
+ struct ip6_hdr ip6copy;
+
+ if (newoff - off != sizeof(struct ip6_hdr)) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ m_copydata(m, off, newoff - off, (caddr_t)&ip6copy);
+ /* RFC2402 */
+ ip6copy.ip6_flow = 0;
+ ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6copy.ip6_vfc |= IPV6_VERSION;
+ ip6copy.ip6_hlim = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
+ ip6copy.ip6_src.s6_addr16[1] = 0x0000;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
+ ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
+ (algo->update)(&algos, (caddr_t)&ip6copy,
+ sizeof(struct ip6_hdr));
+ } else {
+ newoff = m->m_pkthdr.len;
+ ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo,
+ &algos);
+ }
break;
- }
case IPPROTO_AH:
{
- u_char dummy[4];
int siz;
int hdrsiz;
- hdrsiz = (sav->flags & SADB_X_EXT_OLD) ?
- sizeof(struct ah) : sizeof(struct newah);
-
- (algo->update)(&algos, p, hdrsiz);
-
- /* key data region. */
+ hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+ ? sizeof(struct ah)
+ : sizeof(struct newah);
siz = (*algo->sumsiz)(sav);
- bzero(&dummy[0], 4);
- while (4 <= siz) {
- (algo->update)(&algos, dummy, 4);
- siz -= 4;
- }
- /* can't happen, but just in case */
- if (siz)
- (algo->update)(&algos, dummy, siz);
-
- /* padding region, just in case */
- siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav);
- if ((sav->flags & SADB_X_EXT_OLD) == 0)
- siz -= 4; /* sequence number field */
- if (0 < siz) {
- (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav),
- siz);
- }
- hdrtype = ((struct ah *)p)->ah_nxt;
- advancewidth = hdrsiz;
- advancewidth += ((struct ah *)p)->ah_len << 2;
- if ((sav->flags & SADB_X_EXT_OLD) == 0)
- advancewidth -= 4; /* sequence number field */
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (!ahseen) {
+ if (newoff - off > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && newoff - off > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+ n->m_len = newoff - off;
+ bzero(mtod(n, caddr_t) + hdrsiz, siz);
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ } else
+ ah_update_mbuf(m, off, newoff - off, algo, &algos);
+ ahseen++;
break;
}
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
{
- int hdrlen, optlen;
- u_int8_t *optp, *lastp = p, *optend, opt;
+ struct ip6_ext *ip6e;
+ int hdrlen, optlen;
+ u_int8_t *p, *optend, *optp;
- tlen = m->m_len - (p - mtod(m, u_char *));
- /* We assume all the options is contained in a single mbuf */
- if (tlen < sizeof(struct ip6_ext)) {
- error = EINVAL;
- goto bad;
- }
- hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3;
- hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt;
- if (tlen < hdrlen) {
- error = EINVAL;
- goto bad;
- }
- optend = p + hdrlen;
-
- /*
- * ICV calculation for the options header including all
- * options. This part is a little tricky since there are
- * two type of options; mutable and immutable. Our approach
- * is to calculate ICV for a consecutive immutable options
- * at once. Here is an example. In the following figure,
- * suppose that we've calculated ICV from the top of the
- * header to MutableOpt1, which is a mutable option.
- * lastp points to the end of MutableOpt1. Some immutable
- * options follows MutableOpt1, and we encounter a new
- * mutable option; MutableOpt2. optp points to the head
- * of MutableOpt2. In this situation, uncalculated immutable
- * field is the field from lastp to optp+2 (note that the
- * type and the length fields are considered as immutable
- * even in a mutable option). So we first calculate ICV
- * for the field as immutable, then calculate from optp+2
- * to the end of MutableOpt2, whose length is optlen-2,
- * where optlen is the length of MutableOpt2. Finally,
- * lastp is updated to point to the end of MutableOpt2
- * for further calculation. The updated point is shown as
- * lastp' in the figure.
- * <------ optlen ----->
- * -----------+-------------------+---+---+-----------+
- * MutableOpt1|ImmutableOptions...|typ|len|MutableOpt2|
- * -----------+-------------------+---+---+-----------+
- * ^ ^ ^
- * lastp optp optp+2
- * <---- optp + 2 - lastp -----><-optlen-2->
- * ^
- * lastp'
- */
- for (optp = p + 2; optp < optend; optp += optlen) {
- opt = optp[0];
- if (opt == IP6OPT_PAD1) {
- optlen = 1;
- } else {
- if (optp + 2 > optend) {
- error = EINVAL; /* malformed option */
- goto bad;
- }
- optlen = optp[1] + 2;
- if (opt & IP6OPT_MUTABLE) {
- /*
- * ICV calc. for the (consecutive)
- * immutable field followd by the
- * option.
- */
- (algo->update)(&algos, lastp,
- optp + 2 - lastp);
- if (optlen - 2 > ZEROBUFLEN) {
- error = EINVAL; /* XXX */
- goto bad;
- }
- /*
- * ICV calc. for the mutable
- * option using an all-0 buffer.
- */
- (algo->update)(&algos, zerobuf,
- optlen - 2);
- lastp = optp + optlen;
- }
- }
- }
- /*
- * Wrap up the calulation; compute ICV for the consecutive
- * immutable options at the end of the header(if any).
- */
- (algo->update)(&algos, lastp, p + hdrlen - lastp);
- advancewidth = hdrlen;
- break;
- }
- case IPPROTO_ROUTING:
- {
- /*
- * For an input packet, we can just calculate `as is'.
- * For an output packet, we assume ip6_output have already
- * made packet how it will be received at the final destination.
- * So we'll only check if the header is malformed.
- */
- int hdrlen;
-
- tlen = m->m_len - (p - mtod(m, u_char *));
- /* We assume all the options is contained in a single mbuf */
- if (tlen < sizeof(struct ip6_ext)) {
- error = EINVAL;
- goto bad;
- }
- hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3;
- hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt;
- if (tlen < hdrlen) {
+ if (newoff - off > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && newoff - off > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+ n->m_len = newoff - off;
+
+ ip6e = mtod(n, struct ip6_ext *);
+ hdrlen = (ip6e->ip6e_len + 1) << 3;
+ if (newoff - off < hdrlen) {
error = EINVAL;
- goto bad;
- }
- advancewidth = hdrlen;
- (algo->update)(&algos, p, hdrlen);
- break;
- }
- default:
- ipseclog((LOG_DEBUG, "ah6_calccksum: unexpected hdrtype=%x; "
- "treating rest as payload\n", hdrtype));
- /*fall through*/
- case IPPROTO_ICMP:
- case IPPROTO_IGMP:
- case IPPROTO_IPIP:
- case IPPROTO_IPV6:
- case IPPROTO_ICMPV6:
- case IPPROTO_UDP:
- case IPPROTO_TCP:
- case IPPROTO_ESP:
- case IPPROTO_IPCOMP:
- while (m) {
- tlen = m->m_len - (p - mtod(m, u_char *));
- (algo->update)(&algos, p, tlen);
- m = m->m_next;
- p = m ? mtod(m, u_char *) : NULL;
+ m_free(n);
+ n = NULL;
+ goto fail;
}
-
- advancewidth = 0; /*loop finished*/
- break;
- }
+ p = mtod(n, u_int8_t *);
+ optend = p + hdrlen;
- if (advancewidth) {
- /* is it safe? */
- while (m && advancewidth) {
- tlen = m->m_len - (p - mtod(m, u_char *));
- if (advancewidth < tlen) {
- p += advancewidth;
- advancewidth = 0;
- } else {
- advancewidth -= tlen;
- m = m->m_next;
- if (m)
- p = mtod(m, u_char *);
- else {
- ipseclog((LOG_DEBUG,
- "hit the end-of-mbuf...\n"));
- p = NULL;
+ /*
+ * ICV calculation for the options header including all
+ * options. This part is a little tricky since there are
+ * two type of options; mutable and immutable. We try to
+ * null-out mutable ones here.
+ */
+ optp = p + 2;
+ while (optp < optend) {
+ if (optp[0] == IP6OPT_PAD1)
+ optlen = 1;
+ else {
+ if (optp + 2 > optend) {
+ error = EINVAL;
+ m_free(n);
+ n = NULL;
+ goto fail;
}
+ optlen = optp[1] + 2;
+
+ if (optp[0] & IP6OPT_MUTABLE)
+ bzero(optp + 2, optlen - 2);
}
+
+ optp += optlen;
}
- if (m)
- goto again;
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ break;
+ }
+
+ case IPPROTO_ROUTING:
+ /*
+ * For an input packet, we can just calculate `as is'.
+ * For an output packet, we assume ip6_output have already
+ * made packet how it will be received at the final
+ * destination.
+ */
+ /* FALLTHROUGH */
+
+ default:
+ ah_update_mbuf(m, off, newoff - off, algo, &algos);
+ break;
+ }
+
+ if (newoff < m->m_pkthdr.len) {
+ proto = nxt;
+ off = newoff;
+ goto again;
}
- /* for HMAC algorithms... */
(algo->result)(&algos, &sumbuf[0]);
bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
- return(0);
-
- bad:
- return(error);
+ /* just in case */
+ if (n)
+ m_free(n);
+ return 0;
+fail:
+ /* just in case */
+ if (n)
+ m_free(n);
+ return error;
}
#endif
diff --git a/sys/netinet6/ah_input.c b/sys/netinet6/ah_input.c
index 039f64378c7..15de6fc29b0 100644
--- a/sys/netinet6/ah_input.c
+++ b/sys/netinet6/ah_input.c
@@ -1,5 +1,5 @@
-/* $NetBSD: ah_input.c,v 1.11 2000/02/26 11:49:44 itojun Exp $ */
-/* $KAME: ah_input.c,v 1.21 2000/02/26 11:37:24 itojun Exp $ */
+/* $NetBSD: ah_input.c,v 1.12 2000/03/21 23:53:30 itojun Exp $ */
+/* $KAME: ah_input.c,v 1.22 2000/03/09 21:51:39 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -389,7 +389,7 @@ ah4_input(m, va_alist)
goto fail;
}
-#if 0 /* XXX should call ipfw rather than ipsec_inn_reject, shouldn't it ? */
+#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */
/* drop it if it does not match the default policy */
if (ipsec4_in_reject(m, NULL)) {
ipsecstat.in_polvio++;
@@ -457,9 +457,8 @@ ah4_input(m, va_alist)
m->m_pkthdr.len -= stripsiz;
#else
/*
- * in m_pulldown case, we don't really need to strip off AH.
- * however, upper-layer protocols (for example, ICMPv4)
- * chokes if we don't.
+ * even in m_pulldown case, we need to strip off AH so that
+ * we can compute checksum for multiple AH correctly.
*/
if (m->m_len >= stripsiz + off) {
ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
@@ -795,7 +794,7 @@ ah6_input(mp, offp, proto)
goto fail;
}
-#if 0 /* XXX should call ipfw rather than ipsec_inn_reject, shouldn't it ? */
+#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */
/* drop it if it does not match the default policy */
if (ipsec6_in_reject(m, NULL)) {
ipsec6stat.in_polvio++;
@@ -831,7 +830,6 @@ ah6_input(mp, offp, proto)
* the packet is placed in a single mbuf.
*/
size_t stripsiz = 0;
-#ifndef PULLDOWN_TEST
char *prvnxtp;
/*
@@ -841,7 +839,6 @@ ah6_input(mp, offp, proto)
*/
prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
*prvnxtp = nxt;
-#endif
if (sav->flags & SADB_X_EXT_OLD) {
/* RFC 1826 */
@@ -851,19 +848,43 @@ ah6_input(mp, offp, proto)
stripsiz = sizeof(struct newah) + siz1;
}
-#ifndef PULLDOWN_TEST
ip6 = mtod(m, struct ip6_hdr *);
- ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz),
- off);
+#ifndef PULLDOWN_TEST
+ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
m->m_data += stripsiz;
m->m_len -= stripsiz;
m->m_pkthdr.len -= stripsiz;
+#else
+ /*
+ * even in m_pulldown case, we need to strip off AH so that
+ * we can compute checksum for multiple AH correctly.
+ */
+ if (m->m_len >= stripsiz + off) {
+ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+ } else {
+ /*
+ * this comes with no copy if the boundary is on
+ * cluster
+ */
+ struct mbuf *n;
+ n = m_split(m, off, M_DONTWAIT);
+ if (n == NULL) {
+ /* m is retained by m_split */
+ goto fail;
+ }
+ m_adj(n, stripsiz);
+ m_cat(m, n);
+ /* m_cat does not update m_pkthdr.len */
+ m->m_pkthdr.len += n->m_pkthdr.len;
+ }
+#endif
ip6 = mtod(m, struct ip6_hdr *);
+ /* XXX jumbogram */
ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
-#else
- off += stripsiz;
-#endif
key_sa_recordxfer(sav, m);
}
diff --git a/sys/netinet6/ah_output.c b/sys/netinet6/ah_output.c
index ae3975d6b79..2741cedcff8 100644
--- a/sys/netinet6/ah_output.c
+++ b/sys/netinet6/ah_output.c
@@ -1,4 +1,5 @@
-/* $NetBSD: ah_output.c,v 1.7 2000/02/06 12:49:42 itojun Exp $ */
+/* $NetBSD: ah_output.c,v 1.8 2000/03/21 23:53:30 itojun Exp $ */
+/* $KAME: ah_output.c,v 1.17 2000/03/09 08:54:48 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -404,7 +405,8 @@ ah6_output(m, nexthdrp, md, isr)
"sav->replay is null: SPI=%u\n",
(u_int32_t)ntohl(sav->spi)));
ipsec6stat.out_inval++;
- return 0; /* no change at all */
+ m_freem(m);
+ return EINVAL;
}
algo = &ah_algorithms[sav->alg_auth];
@@ -459,12 +461,14 @@ ah6_output(m, nexthdrp, md, isr)
* and the algorithm specified.
*/
error = ah6_calccksum(m, (caddr_t)ahsumpos, algo, sav);
- if (error)
+ if (error) {
ipsec6stat.out_inval++;
- else
+ m_freem(m);
+ } else {
ipsec6stat.out_success++;
+ key_sa_recordxfer(sav, m);
+ }
ipsec6stat.out_ahhist[sav->alg_auth]++;
- key_sa_recordxfer(sav, m);
return(error);
}
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 4d1d3d5d07e..129433a8d73 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_input.c,v 1.17 2000/03/21 11:05:12 itojun Exp $ */
+/* $NetBSD: ip6_input.c,v 1.18 2000/03/21 23:53:30 itojun Exp $ */
/* $KAME: ip6_input.c,v 1.72 2000/03/21 09:23:19 itojun Exp $ */
/*
@@ -1185,6 +1185,115 @@ ip6_get_prevhdr(m, off)
}
/*
+ * get next header offset. m will be retained.
+ */
+int
+ip6_nexthdr(m, off, proto, nxtp)
+ struct mbuf *m;
+ int off;
+ int proto;
+ int *nxtp;
+{
+ struct ip6_hdr ip6;
+ struct ip6_ext ip6e;
+ struct ip6_frag fh;
+
+ /* just in case */
+ if (m == NULL)
+ panic("ip6_nexthdr: m == NULL");
+ if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off)
+ return -1;
+
+ switch (proto) {
+ case IPPROTO_IPV6:
+ if (m->m_pkthdr.len < off + sizeof(ip6))
+ return -1;
+ m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6);
+ if (nxtp)
+ *nxtp = ip6.ip6_nxt;
+ off += sizeof(ip6);
+ return off;
+
+ case IPPROTO_FRAGMENT:
+ /*
+ * terminate parsing if it is not the first fragment,
+ * it does not make sense to parse through it.
+ */
+ if (m->m_pkthdr.len < off + sizeof(fh))
+ return -1;
+ m_copydata(m, off, sizeof(fh), (caddr_t)&fh);
+ if ((ntohs(fh.ip6f_offlg) & IP6F_OFF_MASK) != 0)
+ return -1;
+ if (nxtp)
+ *nxtp = fh.ip6f_nxt;
+ off += sizeof(struct ip6_frag);
+ return off;
+
+ case IPPROTO_AH:
+ if (m->m_pkthdr.len < off + sizeof(ip6e))
+ return -1;
+ m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+ if (nxtp)
+ *nxtp = ip6e.ip6e_nxt;
+ off += (ip6e.ip6e_len + 2) << 2;
+ return off;
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_DSTOPTS:
+ if (m->m_pkthdr.len < off + sizeof(ip6e))
+ return -1;
+ m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+ if (nxtp)
+ *nxtp = ip6e.ip6e_nxt;
+ off += (ip6e.ip6e_len + 1) << 3;
+ return off;
+
+ case IPPROTO_NONE:
+ case IPPROTO_ESP:
+ case IPPROTO_IPCOMP:
+ /* give up */
+ return -1;
+
+ default:
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * get offset for the last header in the chain. m will be kept untainted.
+ */
+int
+ip6_lasthdr(m, off, proto, nxtp)
+ struct mbuf *m;
+ int off;
+ int proto;
+ int *nxtp;
+{
+ int newoff;
+ int nxt;
+
+ if (!nxtp) {
+ nxt = -1;
+ nxtp = &nxt;
+ }
+ while (1) {
+ newoff = ip6_nexthdr(m, off, proto, nxtp);
+ if (newoff < 0)
+ return off;
+ else if (newoff < off)
+ return -1; /* invalid */
+ else if (newoff == off)
+ return newoff;
+
+ off = newoff;
+ proto = *nxtp;
+ }
+}
+
+/*
* System control for IP6
*/
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 13d6a451f5f..ff20ad7d3ab 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -1,5 +1,5 @@
-/* $NetBSD: ip6_var.h,v 1.11 2000/02/26 09:09:18 itojun Exp $ */
-/* $KAME: ip6_var.h,v 1.27 2000/02/22 14:04:22 itojun Exp $ */
+/* $NetBSD: ip6_var.h,v 1.12 2000/03/21 23:53:31 itojun Exp $ */
+/* $KAME: ip6_var.h,v 1.28 2000/03/09 00:46:12 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -254,6 +254,8 @@ void ip6_input __P((struct mbuf *));
void ip6_freemoptions __P((struct ip6_moptions *));
int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int));
char * ip6_get_prevhdr __P((struct mbuf *, int));
+int ip6_nexthdr __P((struct mbuf *, int, int, int *));
+int ip6_lasthdr __P((struct mbuf *, int, int, int *));
int ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *));
int ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *,
u_int32_t *));
diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c
index 2b8ce879cfd..09bbbff7c28 100644
--- a/sys/netinet6/ipsec.c
+++ b/sys/netinet6/ipsec.c
@@ -1,5 +1,5 @@
-/* $NetBSD: ipsec.c,v 1.18 2000/03/01 12:49:47 itojun Exp $ */
-/* $KAME: ipsec.c,v 1.49 2000/02/23 08:52:52 jinmei Exp $ */
+/* $NetBSD: ipsec.c,v 1.19 2000/03/21 23:53:31 itojun Exp $ */
+/* $KAME: ipsec.c,v 1.53 2000/03/09 13:02:05 sakane Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -742,10 +742,7 @@ ipsec6_get_ulp(m, spidx)
struct mbuf *m;
struct secpolicyindex *spidx;
{
- struct ip6_hdr *ip6;
- struct ip6_ext *ip6e;
int off, nxt;
- int len;
/* sanity check */
if (m == NULL)
@@ -759,64 +756,36 @@ ipsec6_get_ulp(m, spidx)
_INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY;
_INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY;
- ip6 = mtod(m, struct ip6_hdr *);
- nxt = ip6->ip6_nxt;
- off = sizeof(struct ip6_hdr);
- len = m->m_len;
-
- while (off < len) {
- ip6e = (struct ip6_ext *)((caddr_t) ip6 + off);
- if (m->m_len < off + sizeof(*ip6e)) {
- ipseclog((LOG_DEBUG, "ipsec6_get_ulp: all exthdr are "
- "not in single mbuf.\n"));
- return;
- }
+ nxt = -1;
+ off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
+ if (off < 0 || m->m_pkthdr.len < off)
+ return;
- switch(nxt) {
- case IPPROTO_TCP:
- spidx->ul_proto = nxt;
- _INPORTBYSA(&spidx->src) =
- ((struct tcphdr *)((caddr_t)ip6 + off))->th_sport;
- _INPORTBYSA(&spidx->dst) =
- ((struct tcphdr *)((caddr_t)ip6 + off))->th_dport;
- return;
- case IPPROTO_UDP:
- spidx->ul_proto = nxt;
- _INPORTBYSA(&spidx->src) =
- ((struct udphdr *)((caddr_t)ip6 + off))->uh_sport;
- _INPORTBYSA(&spidx->dst) =
- ((struct udphdr *)((caddr_t)ip6 + off))->uh_dport;
- return;
- case IPPROTO_ICMPV6:
- spidx->ul_proto = nxt;
- return;
- case IPPROTO_FRAGMENT:
- off += sizeof(struct ip6_frag);
- break;
- case IPPROTO_AH:
- off += (ip6e->ip6e_len + 2) << 2;
- break;
- default:
- switch (nxt) {
- case IPPROTO_HOPOPTS:
- case IPPROTO_ROUTING:
- case IPPROTO_NONE:
- case IPPROTO_DSTOPTS:
- break;
- case IPPROTO_ESP:
- case IPPROTO_IPCOMP:
- /* give up */
- return;
- default:
- return; /* XXX */
- }
- off += (ip6e->ip6e_len + 1) << 3;
- break;
+ switch (nxt) {
+ case IPPROTO_TCP:
+ spidx->ul_proto = nxt;
+ if (off + sizeof(struct tcphdr) <= m->m_pkthdr.len) {
+ struct tcphdr th;
+ m_copydata(m, off, sizeof(th), (caddr_t)&th);
+ _INPORTBYSA(&spidx->src) = th.th_sport;
+ _INPORTBYSA(&spidx->dst) = th.th_dport;
}
- nxt = ip6e->ip6e_nxt;
+ break;
+ case IPPROTO_UDP:
+ spidx->ul_proto = nxt;
+ if (off + sizeof(struct udphdr) <= m->m_pkthdr.len) {
+ struct udphdr uh;
+ m_copydata(m, off, sizeof(uh), (caddr_t)&uh);
+ _INPORTBYSA(&spidx->src) = uh.uh_sport;
+ _INPORTBYSA(&spidx->dst) = uh.uh_dport;
+ }
+ break;
+ case IPPROTO_ICMPV6:
+ spidx->ul_proto = nxt;
+ break;
+ default:
+ break;
}
-
- return;
}
#endif
@@ -878,7 +847,7 @@ ipsec4_setspidx_ipaddr(m, spidx)
/* sanity check 1 for minimum ip header length */
if (m == NULL)
- panic("ipsec4_setspidx_in6pcb: m == 0 passed.\n");
+ panic("ipsec4_setspidx_ipaddr: m == 0 passed.\n");
if (m->m_pkthdr.len < sizeof(struct ip)) {
KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
@@ -1154,7 +1123,7 @@ ipsec_set_policy(pcb_sp, optname, request, len, priv)
int error;
/* sanity check. */
- if (pcb_sp == NULL || *pcb_sp == NULL || xpl == NULL)
+ if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
return EINVAL;
if (len < sizeof(*xpl))
return EINVAL;
@@ -1262,7 +1231,9 @@ ipsec4_get_policy(inp, request, len, mp)
/* sanity check. */
if (inp == NULL || request == NULL || mp == NULL)
return EINVAL;
- if (len != sizeof(*xpl))
+ if (inp->inp_sp == NULL)
+ panic("policy in PCB is NULL\n");
+ if (len < sizeof(*xpl))
return EINVAL;
xpl = (struct sadb_x_policy *)request;
@@ -1275,7 +1246,7 @@ ipsec4_get_policy(inp, request, len, mp)
pcb_sp = inp->inp_sp->sp_out;
break;
default:
- ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n",
+ ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n",
xpl->sadb_x_policy_dir));
return EINVAL;
}
@@ -1357,7 +1328,9 @@ ipsec6_get_policy(in6p, request, len, mp)
/* sanity check. */
if (in6p == NULL || request == NULL || mp == NULL)
return EINVAL;
- if (len != sizeof(*xpl))
+ if (in6p->in6p_sp == NULL)
+ panic("policy in PCB is NULL\n");
+ if (len < sizeof(*xpl))
return EINVAL;
xpl = (struct sadb_x_policy *)request;