diff options
| author | itojun <itojun@NetBSD.org> | 2000-03-21 23:53:30 +0000 |
|---|---|---|
| committer | itojun <itojun@NetBSD.org> | 2000-03-21 23:53:30 +0000 |
| commit | fadbd2b29a157d2a6e1a110c3eef3d445aab496b (patch) | |
| tree | 5826e7203e42497083fe99810a24ddf0eb187001 /sys | |
| parent | 3b3bcd2a107e669d64b473712b518bc826b9f8f0 (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.c | 662 | ||||
| -rw-r--r-- | sys/netinet6/ah_input.c | 51 | ||||
| -rw-r--r-- | sys/netinet6/ah_output.c | 14 | ||||
| -rw-r--r-- | sys/netinet6/ip6_input.c | 111 | ||||
| -rw-r--r-- | sys/netinet6/ip6_var.h | 6 | ||||
| -rw-r--r-- | sys/netinet6/ipsec.c | 103 |
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; |
