diff options
author | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
---|---|---|
committer | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
commit | 5f4e854de19331a53788d6100bbcd42845056bc1 (patch) | |
tree | 3ff8c876a5868b103fb8713055d83e29a3fa38d5 /sys/netinet6 | |
parent | bdc16885232d771a99d7dfc247cd27a44cd061f9 (diff) | |
download | FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.zip FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.tar.gz |
sync with kame tree as of july00. tons of bug fixes/improvements.
API changes:
- additional IPv6 ioctls
- IPsec PF_KEY API was changed, it is mandatory to upgrade setkey(8).
(also syntax change)
Diffstat (limited to 'sys/netinet6')
65 files changed, 11365 insertions, 6148 deletions
diff --git a/sys/netinet6/README b/sys/netinet6/README new file mode 100644 index 0000000..f144f63 --- /dev/null +++ b/sys/netinet6/README @@ -0,0 +1,28 @@ +a note to committers about KAME tree +$FreeBSD$ +KAME project + + +FreeBSD IPv6/IPsec tree is from KAMEproject (http://www.kame.net/). +To synchronize KAME tree and FreeBSD better today and in the future, +please understand the following: + +- DO NOT MAKE COSTMETIC CHANGES. + "Cosmetic changes" here includes tabify, untabify, removal of space at EOL, + minor KNF items, and whatever adds more output lines on "diff freebsd kame". + To make future synchronization easier. it is critical to preserve certain + statements in the code. Also, as KAME tree supports all 4 BSDs (Free, Open, + Net, BSD/OS) in single shared tree, it is not always possible to backport + FreeBSD changes into KAME tree. So again, please do not make cosmetic + changes. Even if you think it a right thing, that will bite KAME guys badly + during upgrade attempts, and prevent us from synchronizing two trees. + (you don't usually make cosmetic changes against third-party code, do you?) + +- REPORT CHANGES/BUGS TO KAME GUYS. + It is not always possible for KAME guys to watch all the freebsd mailing + list traffic, as the traffic is HUGE. So if possible, please, inform + kame guys of changes you made in IPv6/IPsec related portion. Contact + path would be snap-users@kame.net or KAME PR database on www.kame.net. + (or to core@kame.net if it is necessary to make it confidential) + +Thank you for your cooperation and have a happy IPv6 life! diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h index 1b7e602..3c7d0a6 100644 --- a/sys/netinet6/ah.h +++ b/sys/netinet6/ah.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,7 +35,7 @@ */ #ifndef _NETINET6_AH_H_ -#define _NETINET6_AH_H_ +#define _NETINET6_AH_H_ struct secasvar; @@ -65,9 +66,9 @@ struct ah_algorithm { int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ - void (*init) __P((struct ah_algorithm_state *, struct secasvar *)); - void (*update) __P((struct ah_algorithm_state *, const caddr_t, - size_t)); + const char *name; + int (*init) __P((struct ah_algorithm_state *, struct secasvar *)); + void (*update) __P((struct ah_algorithm_state *, caddr_t, size_t)); void (*result) __P((struct ah_algorithm_state *, caddr_t)); }; @@ -80,10 +81,10 @@ extern struct ah_algorithm ah_algorithms[]; extern int ah_hdrlen __P((struct secasvar *)); extern size_t ah_hdrsiz __P((struct ipsecrequest *)); -extern void ah4_input __P((struct mbuf *, int, int)); +extern void ah4_input __P((struct mbuf *, ...)); extern int ah4_output __P((struct mbuf *, struct ipsecrequest *)); -extern int ah4_calccksum __P((struct mbuf *, caddr_t, - struct ah_algorithm *, struct secasvar *)); -#endif +extern int ah4_calccksum __P((struct mbuf *, caddr_t, size_t, + struct ah_algorithm *, struct secasvar *)); +#endif /*_KERNEL*/ #endif /*_NETINET6_AH_H_*/ diff --git a/sys/netinet6/ah6.h b/sys/netinet6/ah6.h index b3448f4..4010f13 100644 --- a/sys/netinet6/ah6.h +++ b/sys/netinet6/ah6.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,7 +35,7 @@ */ #ifndef _NETINET6_AH6_H_ -#define _NETINET6_AH6_H_ +#define _NETINET6_AH6_H_ #ifdef _KERNEL struct secasvar; @@ -42,8 +43,8 @@ struct secasvar; extern int ah6_input __P((struct mbuf **, int *, int)); extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); -extern int ah6_calccksum __P((struct mbuf *, caddr_t, - struct ah_algorithm *, struct secasvar *)); +extern int ah6_calccksum __P((struct mbuf *, caddr_t, size_t, + struct ah_algorithm *, struct secasvar *)); #endif #endif /*_NETINET6_AH6_H_*/ diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c index 8ef0f39..6dbaee0 100644 --- a/sys/netinet6/ah_core.c +++ b/sys/netinet6/ah_core.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ah_core.c,v 1.35 2000/06/14 11:14:03 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,14 +28,13 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ +#include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -46,6 +48,7 @@ #include <sys/socketvar.h> #include <sys/errno.h> #include <sys/time.h> +#include <sys/syslog.h> #include <net/if.h> #include <net/route.h> @@ -54,18 +57,19 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_var.h> -#include <netinet/in_pcb.h> #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif #ifdef IPSEC_ESP @@ -75,7 +79,6 @@ #endif #endif #include <net/pfkeyv2.h> -#include <netkey/key_var.h> #include <netkey/keydb.h> #include <sys/md5.h> #include <crypto/sha1.h> @@ -84,57 +87,54 @@ #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 *)); -static void ah_none_init __P((struct ah_algorithm_state *, +static int ah_none_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_none_loop __P((struct ah_algorithm_state *, const caddr_t, - size_t)); +static void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_none_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_keyed_md5_mature __P((struct secasvar *)); -static void ah_keyed_md5_init __P((struct ah_algorithm_state *, +static int ah_keyed_md5_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_keyed_sha1_mature __P((struct secasvar *)); -static void ah_keyed_sha1_init __P((struct ah_algorithm_state *, +static int ah_keyed_sha1_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_hmac_md5_mature __P((struct secasvar *)); -static void ah_hmac_md5_init __P((struct ah_algorithm_state *, +static int ah_hmac_md5_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_hmac_sha1_mature __P((struct secasvar *)); -static void ah_hmac_sha1_init __P((struct ah_algorithm_state *, +static int ah_hmac_sha1_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, const caddr_t, +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 netkey/keyv2.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, + { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5", ah_hmac_md5_init, ah_hmac_md5_loop, ah_hmac_md5_result, }, - { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, + { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1", ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, }, - { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, + { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5", ah_keyed_md5_init, ah_keyed_md5_loop, ah_keyed_md5_result, }, - { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, + { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1", ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, }, - { ah_sumsiz_zero, ah_none_mature, 0, 2048, + { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", ah_none_init, ah_none_loop, ah_none_result, }, }; @@ -164,24 +164,26 @@ ah_none_mature(sav) struct secasvar *sav; { if (sav->sah->saidx.proto == IPPROTO_AH) { - printf("ah_none_mature: protocol and algorithm mismatch.\n"); + ipseclog((LOG_ERR, + "ah_none_mature: protocol and algorithm mismatch.\n")); return 1; } return 0; } -static void +static int ah_none_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { state->foo = NULL; + return 0; } static void ah_none_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { } @@ -201,34 +203,34 @@ ah_keyed_md5_mature(sav) return 0; } -static void +static int ah_keyed_md5_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; + if (!state) panic("ah_keyed_md5_init: what?"); state->sav = sav; state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT); if (state->foo == NULL) - panic("ah_keyed_md5_init: what?"); + return ENOBUFS; + MD5Init((MD5_CTX *)state->foo); if (state->sav) { MD5Update((MD5_CTX *)state->foo, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); - { /* * Pad after the key. * We cannot simply use md5_pad() since the function * won't update the total length. */ - size_t padlen; - size_t keybitlen; - u_int8_t buf[32]; - if (_KEYLEN(state->sav->key_auth) < 56) padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); else @@ -254,14 +256,15 @@ ah_keyed_md5_init(state, sav) buf[2] = (keybitlen >> 16) & 0xff; buf[3] = (keybitlen >> 24) & 0xff; MD5Update((MD5_CTX *)state->foo, buf, 8); - } } + + return 0; } static void ah_keyed_md5_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { if (!state) @@ -297,26 +300,30 @@ ah_keyed_sha1_mature(sav) struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_keyed_sha1_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_keyed_sha1_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_keyed_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_keyed_sha1_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { SHA1_CTX *ctxt; + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; if (!state) panic("ah_keyed_sha1_init: what?"); @@ -324,7 +331,7 @@ ah_keyed_sha1_init(state, sav) state->sav = sav; state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_keyed_sha1_init: what?"); + return ENOBUFS; ctxt = (SHA1_CTX *)state->foo; SHA1Init(ctxt); @@ -333,14 +340,9 @@ ah_keyed_sha1_init(state, sav) SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); - { /* * Pad after the key. */ - size_t padlen; - size_t keybitlen; - u_int8_t buf[32]; - if (_KEYLEN(state->sav->key_auth) < 56) padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); else @@ -366,14 +368,15 @@ ah_keyed_sha1_init(state, sav) buf[2] = (keybitlen >> 16) & 0xff; buf[3] = (keybitlen >> 24) & 0xff; SHA1Update(ctxt, buf, 8); - } } + + return 0; } static void ah_keyed_sha1_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { SHA1_CTX *ctxt; @@ -382,7 +385,7 @@ ah_keyed_sha1_loop(state, addr, len) panic("ah_keyed_sha1_loop: what?"); ctxt = (SHA1_CTX *)state->foo; - sha1_loop(ctxt, (caddr_t)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void @@ -414,21 +417,22 @@ ah_hmac_md5_mature(sav) struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_hmac_md5_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_hmac_md5_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_hmac_md5_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_hmac_md5_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; @@ -447,7 +451,7 @@ ah_hmac_md5_init(state, sav) state->sav = sav; state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_hmac_md5_init: what?"); + return ENOBUFS; ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); @@ -477,12 +481,14 @@ ah_hmac_md5_init(state, sav) MD5Init(ctxt); MD5Update(ctxt, ipad, 64); + + return 0; } static void ah_hmac_md5_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { MD5_CTX *ctxt; @@ -529,21 +535,22 @@ ah_hmac_sha1_mature(sav) struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_hmac_sha1_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_hmac_sha1_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_hmac_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_hmac_sha1_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; @@ -563,7 +570,7 @@ ah_hmac_sha1_init(state, sav) state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_hmac_sha1_init: what?"); + return ENOBUFS; ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); @@ -593,12 +600,14 @@ ah_hmac_sha1_init(state, sav) SHA1Init(ctxt); SHA1Update(ctxt, ipad, 64); + + return 0; } static void ah_hmac_sha1_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { SHA1_CTX *ctxt; @@ -644,46 +653,99 @@ 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, len, algo, sav) + struct mbuf *m; caddr_t ahdat; + size_t len; 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); + error = (algo->init)(&algos, sav); + if (error) + return error; advancewidth = 0; /*safety*/ 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 @@ -691,22 +753,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; @@ -730,23 +808,27 @@ again: break; } if (l <= 0 || hlen - i < l) { - printf("ah4_input: invalid IP option " - "(type=%02x len=%02x)\n", - p[i + IPOPT_OPTVAL], - p[i + IPOPT_OLEN]); - break; + ipseclog((LOG_ERR, + "ah4_calccksum: invalid IP option " + "(type=%02x len=%02x)\n", + p[i + IPOPT_OPTVAL], + p[i + IPOPT_OLEN])); + 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; @@ -756,370 +838,306 @@ 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: - printf("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: - 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 { - printf("ERR: hit the end-of-mbuf...\n"); - p = NULL; - } - } - } + off += advancewidth; + if (off < m->m_pkthdr.len) + goto again; - if (m) - goto again; + if (len < (*algo->sumsiz)(sav)) { + error = EINVAL; + goto fail; } - /* 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, len, algo, sav) + struct mbuf *m; caddr_t ahdat; + size_t len; 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 *); - (algo->init)(&algos, sav); + if ((m->m_flags & M_PKTHDR) == 0) + return EINVAL; + + error = (algo->init)(&algos, sav); + if (error) + return error; + + 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; + } - advancewidth = 0; /*safety*/ - nest = 0; + switch (proto) { + case IPPROTO_IPV6: + /* + * special treatment is necessary for the first one, not others + */ + if (off == 0) { + struct ip6_hdr ip6copy; -again: - if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { - ip6stat.ip6s_toomanyhdr++; - error = EINVAL; /*XXX*/ - goto bad; - } + if (newoff - off != sizeof(struct ip6_hdr)) { + 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; - 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); + 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) { + 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; - } - 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; + m_free(n); + n = NULL; + goto fail; + } + p = mtod(n, u_int8_t *); + 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. 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; + } + + (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. - * 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) { - error = EINVAL; - goto bad; - } - advancewidth = hdrlen; - (algo->update)(&algos, p, hdrlen); - break; - } - default: - printf("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: - 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; - } + /* + * 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 */ - advancewidth = 0; /*loop finished*/ + default: + ah_update_mbuf(m, off, newoff - off, algo, &algos); 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 { - printf("ERR: hit the end-of-mbuf...\n"); - p = NULL; - } - } - } + if (newoff < m->m_pkthdr.len) { + proto = nxt; + off = newoff; + goto again; + } - if (m) - goto again; + if (len < (*algo->sumsiz)(sav)) { + error = EINVAL; + goto fail; } - /* 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 e8aa77e..1f59bf9 100644 --- a/sys/netinet6/ah_input.c +++ b/sys/netinet6/ah_input.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ah_input.c,v 1.29 2000/05/29 08:33:53 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -35,7 +36,6 @@ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -64,15 +64,17 @@ #endif #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif #include <netkey/key.h> @@ -83,19 +85,24 @@ #define KEYDEBUG(lev,arg) #endif -#include <netinet/ipprotosw.h> - #include <machine/stdarg.h> #include <net/net_osdep.h> +#define IPLEN_FLIPPED + #ifdef INET +#include <netinet/ipprotosw.h> extern struct ipprotosw inetsw[]; void -ah4_input(m, off, proto) +#if __STDC__ +ah4_input(struct mbuf *m, ...) +#else +ah4_input(m, va_alist) struct mbuf *m; - int off, proto; + va_dcl +#endif { struct ip *ip; struct ah *ah; @@ -108,12 +115,20 @@ ah4_input(m, off, proto) u_int16_t nxt; size_t hlen; int s; + int off, proto; + va_list ap; + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + +#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct newah)) { m = m_pullup(m, off + sizeof(struct newah)); if (!m) { - printf("IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n")); ipsecstat.in_inval++; goto fail; } @@ -121,6 +136,16 @@ ah4_input(m, off, proto) ip = mtod(m, struct ip *); ah = (struct ah *)(((caddr_t)ip) + off); +#else + ip = mtod(m, struct ip *); + IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n")); + ipsecstat.in_inval++; + goto fail; + } +#endif nxt = ah->ah_nxt; #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; @@ -134,9 +159,9 @@ ah4_input(m, off, proto) if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, IPPROTO_AH, spi)) == 0) { - printf("IPv4 AH input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv4 AH input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_nosa++; goto fail; } @@ -144,17 +169,16 @@ ah4_input(m, off, proto) printf("DP ah4_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv4 AH input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv4 AH input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } if (sav->alg_auth == SADB_AALG_NONE) { - printf("IPv4 AH input: unspecified authentication algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv4 AH input: " + "unspecified authentication algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } @@ -172,20 +196,49 @@ ah4_input(m, off, proto) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + /* + * Here, we do not do "siz1 == siz". This is because the way + * RFC240[34] section 2 is written. They do not require truncation + * to 96 bits. + * For example, Microsoft IPsec stack attaches 160 bits of + * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1, + * 32 bits of padding is attached. + * + * There are two downsides to this specification. + * They have no real harm, however, they leave us fuzzy feeling. + * - if we attach more than 96 bits of authentication data onto AH, + * we will never notice about possible modification by rogue + * intermediate nodes. + * Since extra bits in AH checksum is never used, this constitutes + * no real issue, however, it is wacky. + * - even if the peer attaches big authentication data, we will never + * notice the difference, since longer authentication data will just + * work. + * + * We may need some clarification in the spec. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input " + "(%lu, should be at least %lu): %s\n", + (u_long)siz1, (u_long)siz, + ipsec4_logpacketstr(ip, spi))); + ipsecstat.in_inval++; + goto fail; + } if ((ah->ah_len << 2) - sizoff != siz1) { - log(LOG_NOTICE, "sum length mismatch in IPv4 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec4_logpacketstr(ip, spi)); + ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_long)siz1, + ipsec4_logpacketstr(ip, spi))); ipsecstat.in_inval++; goto fail; } +#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); if (!m) { - printf("IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); ipsecstat.in_inval++; goto fail; } @@ -193,6 +246,15 @@ ah4_input(m, off, proto) ip = mtod(m, struct ip *); ah = (struct ah *)(((caddr_t)ip) + off); } +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, + sizeof(struct ah) + sizoff + siz1); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); + ipsecstat.in_inval++; + goto fail; + } +#endif } /* @@ -203,9 +265,9 @@ ah4_input(m, off, proto) ; /*okey*/ else { ipsecstat.in_ahreplay++; - log(LOG_AUTH, "replay packet in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); goto fail; } } @@ -216,12 +278,14 @@ ah4_input(m, off, proto) */ cksum = malloc(siz1, M_TEMP, M_NOWAIT); if (!cksum) { - printf("IPv4 AH input: couldn't alloc temporary region for cksum\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: " + "couldn't alloc temporary region for cksum\n")); ipsecstat.in_inval++; goto fail; } - + { +#if 1 /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. @@ -229,18 +293,21 @@ ah4_input(m, off, proto) ip->ip_len = htons(ip->ip_len + hlen); ip->ip_id = htons(ip->ip_id); ip->ip_off = htons(ip->ip_off); - if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) { +#endif + if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { free(cksum, M_TEMP); ipsecstat.in_inval++; goto fail; } ipsecstat.in_ahhist[sav->alg_auth]++; +#if 1 /* * flip them back. */ ip->ip_len = ntohs(ip->ip_len) - hlen; ip->ip_id = ntohs(ip->ip_id); ip->ip_off = ntohs(ip->ip_off); +#endif } { @@ -255,9 +322,9 @@ ah4_input(m, off, proto) } if (bcmp(sumpos, cksum, siz) != 0) { - log(LOG_AUTH, "checksum mismatch in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "checksum mismatch in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); free(cksum, M_TEMP); ipsecstat.in_ahauthfail++; goto fail; @@ -269,23 +336,66 @@ ah4_input(m, off, proto) m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; - /* M_AUTH related flags might be cleared here in the future */ +#if 0 + /* + * looks okey, but we need more sanity check. + * XXX should elaborate. + */ + if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) { + struct ip *nip; + size_t sizoff; + + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) { + m = m_pullup(m, off + sizeof(struct ah) + + sizoff + siz1 + hlen); + if (!m) { + ipseclog((LOG_DEBUG, + "IPv4 AH input: can't pullup\n")); + ipsecstat.in_inval++; + goto fail; + } + } + + nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1); + if (nip->ip_src.s_addr != ip->ip_src.s_addr + || nip->ip_dst.s_addr != ip->ip_dst.s_addr) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } + } +#ifdef INET6 + else if (ah->ah_nxt == IPPROTO_IPV6) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } +#endif /*INET6*/ +#endif /*0*/ if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) { +#if 0 + ipseclog((LOG_DEBUG, + "IPv4 AH input: authentication succeess\n")); +#endif ipsecstat.in_ahauthsucc++; } else { - log(LOG_AUTH, "authentication failed in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "authentication failed in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_ahauthfail++; + goto fail; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + ipsecstat.in_ahreplay++; + goto fail; + } } /* was it transmitted over the IPsec tunnel SA? */ @@ -321,13 +431,22 @@ ah4_input(m, off, proto) ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); if (!key_checktunnelsanity(sav, AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " + "in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto fail; } +#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++; + goto fail; + } +#endif + +#if 1 /* * Should the inner packet be considered authentic? * My current answer is: NO. @@ -349,6 +468,7 @@ ah4_input(m, off, proto) */ m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; +#endif key_sa_recordxfer(sav, m); @@ -365,8 +485,6 @@ ah4_input(m, off, proto) } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single external mbuf. */ size_t stripsiz = 0; @@ -379,17 +497,62 @@ ah4_input(m, off, proto) } ip = mtod(m, struct ip *); +#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single external mbuf. + */ ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + 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)ip, ((caddr_t)ip) + 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 + + if (m->m_len < sizeof(*ip)) { + m = m_pullup(m, sizeof(*ip)); + if (m == NULL) { + ipsecstat.in_inval++; + goto fail; + } + } ip = mtod(m, struct ip *); - /*ip_len is in host endian*/ +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - stripsiz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz); +#endif ip->ip_p = nxt; /* forget about IP hdr checksum, the check has already been passed */ + key_sa_recordxfer(sav, m); + if (nxt != IPPROTO_DONE) (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); else @@ -436,18 +599,26 @@ ah6_input(mp, offp, proto) u_int16_t nxt; int s; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); - + ah = (struct ah *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); + ipsecstat.in_inval++; + return IPPROTO_DONE; + } +#endif ip6 = mtod(m, struct ip6_hdr *); - ah = (struct ah *)(((caddr_t)ip6) + off); - nxt = ah->ah_nxt; /* find the sassoc. */ spi = ah->ah_spi; if (ntohs(ip6->ip6_plen) == 0) { - printf("IPv6 AH input: AH with IPv6 jumbogram is not supported.\n"); + ipseclog((LOG_ERR, "IPv6 AH input: " + "AH with IPv6 jumbogram is not supported.\n")); ipsec6stat.in_inval++; goto fail; } @@ -455,9 +626,9 @@ ah6_input(mp, offp, proto) if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, IPPROTO_AH, spi)) == 0) { - printf("IPv6 AH input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv6 AH input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_nosa++; goto fail; } @@ -465,17 +636,16 @@ ah6_input(mp, offp, proto) printf("DP ah6_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv6 AH input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv6 AH input: non-mature/dying SA found for spi %u; ", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } if (sav->alg_auth == SADB_AALG_NONE) { - printf("IPv6 AH input: unspecified authentication algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv6 AH input: " + "unspecified authentication algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } @@ -493,15 +663,38 @@ ah6_input(mp, offp, proto) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + /* + * Here, we do not do "siz1 == siz". See ah4_input() for complete + * description. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input " + "(%lu, should be at least %lu): %s\n", + (u_long)siz1, (u_long)siz, + ipsec6_logpacketstr(ip6, spi))); + ipsec6stat.in_inval++; + goto fail; + } if ((ah->ah_len << 2) - sizoff != siz1) { - log(LOG_NOTICE, "sum length mismatch in IPv6 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec6_logpacketstr(ip6, spi)); + ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_long)siz1, + ipsec6_logpacketstr(ip6, spi))); ipsec6stat.in_inval++; goto fail; } +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE); +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, + sizeof(struct ah) + sizoff + siz1); + if (ah == NULL) { + ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); + ipsecstat.in_inval++; + m = NULL; + goto fail; + } +#endif } /* @@ -512,9 +705,10 @@ ah6_input(mp, offp, proto) ; /*okey*/ else { ipsec6stat.in_ahreplay++; - log(LOG_AUTH, "replay packet in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); goto fail; } } @@ -525,12 +719,13 @@ ah6_input(mp, offp, proto) */ cksum = malloc(siz1, M_TEMP, M_NOWAIT); if (!cksum) { - printf("IPv6 AH input: couldn't alloc temporary region for cksum\n"); + ipseclog((LOG_DEBUG, "IPv6 AH input: " + "couldn't alloc temporary region for cksum\n")); ipsec6stat.in_inval++; goto fail; } - - if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) { + + if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { free(cksum, M_TEMP); ipsec6stat.in_inval++; goto fail; @@ -549,9 +744,9 @@ ah6_input(mp, offp, proto) } if (bcmp(sumpos, cksum, siz) != 0) { - log(LOG_AUTH, "checksum mismatch in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "checksum mismatch in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); free(cksum, M_TEMP); ipsec6stat.in_ahauthfail++; goto fail; @@ -563,23 +758,58 @@ ah6_input(mp, offp, proto) m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; - /* M_AUTH related flags might be cleared here in the future */ +#if 0 + /* + * looks okey, but we need more sanity check. + * XXX should elaborate. + */ + if (ah->ah_nxt == IPPROTO_IPV6) { + struct ip6_hdr *nip6; + size_t sizoff; + + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1 + + sizeof(struct ip6_hdr), IPPROTO_DONE); + + nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1); + if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src) + || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } + } else if (ah->ah_nxt == IPPROTO_IPIP) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } else if (ah->ah_nxt == IPPROTO_IP) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } +#endif if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) { +#if 0 + ipseclog((LOG_DEBUG, + "IPv6 AH input: authentication succeess\n")); +#endif ipsec6stat.in_ahauthsucc++; } else { - log(LOG_AUTH, "authentication failed in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "authentication failed in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_ahauthfail++; + goto fail; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + ipsec6stat.in_ahreplay++; + goto fail; + } } /* was it transmitted over the IPsec tunnel SA? */ @@ -619,19 +849,30 @@ ah6_input(mp, offp, proto) ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); if (!key_checktunnelsanity(sav, AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " + "in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto fail; } +#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++; + goto fail; + } +#endif + +#if 1 /* * should the inner packet be considered authentic? * see comment in ah4_input(). */ m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; +#endif key_sa_recordxfer(sav, m); @@ -648,8 +889,6 @@ ah6_input(mp, offp, proto) } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single mbuf. */ size_t stripsiz = 0; char *prvnxtp; @@ -671,13 +910,45 @@ ah6_input(mp, offp, proto) } ip6 = mtod(m, struct ip6_hdr *); - ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), - off); +#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single mbuf. + */ + 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); key_sa_recordxfer(sav, m); diff --git a/sys/netinet6/ah_output.c b/sys/netinet6/ah_output.c index c33dfe8..477c589 100644 --- a/sys/netinet6/ah_output.c +++ b/sys/netinet6/ah_output.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ah_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,18 +28,14 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ +#include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" - -#define ahdprintf(x) printf x #include <sys/param.h> #include <sys/systm.h> @@ -57,27 +56,23 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_var.h> -#include <netinet/in_pcb.h> #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <net/net_osdep.h> @@ -103,15 +98,15 @@ ah_hdrsiz(isr) panic("unsupported mode passed to ah_hdrsiz"); if (isr->sav == NULL) - goto contrive; + goto estimate; if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) - goto contrive; + goto estimate; /* we need transport mode AH. */ algo = &ah_algorithms[isr->sav->alg_auth]; if (!algo) - goto contrive; + goto estimate; /* * XXX @@ -128,7 +123,7 @@ ah_hdrsiz(isr) return hdrsiz; - contrive: + estimate: /* ASSUMING: * sizeof(struct newah) > sizeof(struct ah). * 16 = (16 + 3) & ~(4 - 1). @@ -166,12 +161,11 @@ ah4_output(m, isr) struct ip *ip; ip = mtod(m, struct ip *); - printf("ah4_output: internal error: " - "sav->replay is null: " - "%x->%x, SPI=%u\n", + 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)); + (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; m_freem(m); return EINVAL; @@ -209,7 +203,8 @@ ah4_output(m, isr) struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); if (!n) { - printf("ENOBUFS in ah4_output %d\n", __LINE__); + ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n", + __LINE__)); m_freem(m); return ENOBUFS; } @@ -249,6 +244,17 @@ ah4_output(m, isr) ahdr->ah_nxt = ip->ip_p; ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is @@ -265,7 +271,7 @@ ah4_output(m, isr) if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + ahlen); else { - printf("IPv4 AH output: size exceeds limit\n"); + ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); return EMSGSIZE; @@ -288,9 +294,11 @@ ah4_output(m, isr) * calcurate the checksum, based on security association * and the algorithm specified. */ - error = ah4_calccksum(m, (caddr_t)ahsumpos, algo, sav); + error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); if (error) { - printf("error after ah4_calccksum, called from ah4_output"); + ipseclog((LOG_ERR, + "error after ah4_calccksum, called from ah4_output")); + m_freem(m); m = NULL; ipsecstat.out_inval++; return error; @@ -314,7 +322,7 @@ ah_hdrlen(sav) { struct ah_algorithm *algo; int plen, ahlen; - + algo = &ah_algorithms[sav->alg_auth]; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ @@ -352,7 +360,7 @@ ah6_output(m, nexthdrp, md, isr) struct ip6_hdr *ip6; if (m->m_len < sizeof(struct ip6_hdr)) { - printf("ah6_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n")); m_freem(m); return EINVAL; } @@ -364,7 +372,7 @@ ah6_output(m, nexthdrp, md, isr) for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (!mprev || mprev->m_next != md) { - printf("ah6_output: md is not in chain\n"); + ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n")); m_freem(m); return EINVAL; } @@ -389,7 +397,8 @@ ah6_output(m, nexthdrp, md, isr) /* fix plen */ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) { - printf("ip6_output: AH with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, + "ip6_output: AH with IPv6 jumbogram is not supported\n")); m_freem(m); return EINVAL; } @@ -397,11 +406,12 @@ ah6_output(m, nexthdrp, md, isr) ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { - printf("ah6_output: internal error: " + ipseclog((LOG_DEBUG, "ah6_output: internal error: " "sav->replay is null: SPI=%u\n", - (u_int32_t)ntohl(sav->spi)); + (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]; @@ -431,6 +441,17 @@ ah6_output(m, nexthdrp, md, isr) ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is @@ -444,13 +465,15 @@ ah6_output(m, nexthdrp, md, isr) * calcurate the checksum, based on security association * and the algorithm specified. */ - error = ah6_calccksum(m, (caddr_t)ahsumpos, algo, sav); - if (error) + error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); + 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); } @@ -480,7 +503,8 @@ ah4_finaldst(m) hlen = (ip->ip_hl << 2); if (m->m_len < hlen) { - printf("ah4_finaldst: parameter mbuf wrong (not pulled up)\n"); + ipseclog((LOG_DEBUG, + "ah4_finaldst: parameter mbuf wrong (not pulled up)\n")); return NULL; } @@ -489,7 +513,8 @@ ah4_finaldst(m) optlen = hlen - sizeof(struct ip); if (optlen < 0) { - printf("ah4_finaldst: wrong optlen %d\n", optlen); + ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n", + optlen)); return NULL; } @@ -507,9 +532,10 @@ ah4_finaldst(m) case IPOPT_SSRR: if (q[i + IPOPT_OLEN] <= 0 || optlen - i < q[i + IPOPT_OLEN]) { - printf("ip_finaldst: invalid IP option " - "(code=%02x len=%02x)\n", - q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + ipseclog((LOG_ERR, + "ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); return NULL; } i += q[i + IPOPT_OLEN] - sizeof(struct in_addr); @@ -517,9 +543,10 @@ ah4_finaldst(m) default: if (q[i + IPOPT_OLEN] <= 0 || optlen - i < q[i + IPOPT_OLEN]) { - printf("ip_finaldst: invalid IP option " - "(code=%02x len=%02x)\n", - q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + ipseclog((LOG_ERR, + "ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); return NULL; } i += q[i + IPOPT_OLEN]; diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c index b79a0ad..268d8c9 100644 --- a/sys/netinet6/dest6.c +++ b/sys/netinet6/dest6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: dest6.c,v 1.12 2000/05/05 11:00:57 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -43,9 +47,9 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> /* * Destination options header processing. @@ -61,12 +65,24 @@ dest6_input(mp, offp, proto) u_int8_t *opt; /* validation of the length of the header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE); dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, sizeof(*dstopts)); + if (dstopts == NULL) + return IPPROTO_DONE; +#endif dstoptlen = (dstopts->ip6d_len + 1) << 3; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE); dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, dstoptlen); + if (dstopts == NULL) + return IPPROTO_DONE; +#endif off += dstoptlen; dstoptlen -= sizeof(struct ip6_dest); opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest); diff --git a/sys/netinet6/esp.h b/sys/netinet6/esp.h index b2f62cf..95deec3 100644 --- a/sys/netinet6/esp.h +++ b/sys/netinet6/esp.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,7 +35,7 @@ */ #ifndef _NETINET6_ESP_H_ -#define _NETINET6_ESP_H_ +#define _NETINET6_ESP_H_ struct secasvar; @@ -77,6 +78,7 @@ struct esp_algorithm { int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ + const char *name; int (*ivlen) __P((struct secasvar *)); int (*decrypt) __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); @@ -89,9 +91,9 @@ extern struct esp_algorithm esp_algorithms[]; /* crypt routines */ extern int esp4_output __P((struct mbuf *, struct ipsecrequest *)); -extern void esp4_input __P((struct mbuf *, int, int)); +extern void esp4_input __P((struct mbuf *, ...)); extern size_t esp_hdrsiz __P((struct ipsecrequest *)); -#endif +#endif /*_KERNEL*/ extern int esp_auth __P((struct mbuf *, size_t, size_t, struct secasvar *, u_char *)); diff --git a/sys/netinet6/esp6.h b/sys/netinet6/esp6.h index 0e3ba9b..5d2f984 100644 --- a/sys/netinet6/esp6.h +++ b/sys/netinet6/esp6.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,12 +35,12 @@ */ #ifndef _NETINET6_ESP6_H_ -#define _NETINET6_ESP6_H_ +#define _NETINET6_ESP6_H_ #ifdef _KERNEL extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); extern int esp6_input __P((struct mbuf **, int *, int)); -#endif +#endif /*_KERNEL*/ #endif /*_NETINET6_ESP6_H_*/ diff --git a/sys/netinet6/esp_core.c b/sys/netinet6/esp_core.c index 98b973d..e5f8ec5 100644 --- a/sys/netinet6/esp_core.c +++ b/sys/netinet6/esp_core.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: esp_core.c,v 1.15 2000/06/14 10:41:18 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,12 +28,10 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -40,6 +41,7 @@ #include <sys/socket.h> #include <sys/errno.h> #include <sys/time.h> +#include <sys/syslog.h> #include <net/if.h> #include <net/route.h> @@ -47,25 +49,24 @@ #include <netinet/in.h> #include <netinet/in_var.h> #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif -#ifdef IPSEC_ESP #include <netinet6/esp.h> #ifdef INET6 #include <netinet6/esp6.h> #endif -#endif #include <net/pfkeyv2.h> -#include <netkey/key_var.h> #include <netkey/keydb.h> #include <crypto/des/des.h> #include <crypto/blowfish/blowfish.h> @@ -113,19 +114,19 @@ static caddr_t mbuf_find_offset __P((struct mbuf *, size_t, size_t)); /* NOTE: The order depends on SADB_EALG_x in netkey/keyv2.h */ struct esp_algorithm esp_algorithms[] = { { 0, 0, 0, 0, 0, 0, 0, }, - { 8, esp_descbc_mature, 64, 64, + { 8, esp_descbc_mature, 64, 64, "des-cbc", esp_descbc_ivlen, esp_descbc_decrypt, esp_descbc_encrypt, }, - { 8, esp_cbc_mature, 192, 192, + { 8, esp_cbc_mature, 192, 192, "3des-cbc", esp_3descbc_ivlen, esp_3descbc_decrypt, esp_3descbc_encrypt, }, - { 1, esp_null_mature, 0, 2048, + { 1, esp_null_mature, 0, 2048, "null", esp_null_ivlen, esp_null_decrypt, esp_null_encrypt, }, - { 8, esp_cbc_mature, 40, 448, + { 8, esp_cbc_mature, 40, 448, "blowfish-cbc", esp_blowfish_cbc_ivlen, esp_blowfish_cbc_decrypt, esp_blowfish_cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 128, + { 8, esp_cbc_mature, 40, 128, "cast128-cbc", esp_cast128cbc_ivlen, esp_cast128cbc_decrypt, esp_cast128cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 2040, + { 8, esp_cbc_mature, 40, 2040, "rc5-cbc", esp_rc5cbc_ivlen, esp_rc5cbc_decrypt, esp_rc5cbc_encrypt, }, }; @@ -179,25 +180,28 @@ esp_descbc_mature(sav) struct esp_algorithm *algo; if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) { - printf("esp_cbc_mature: algorithm incompatible with 4 octets IV length\n"); + ipseclog((LOG_ERR, "esp_cbc_mature: " + "algorithm incompatible with 4 octets IV length\n")); return 1; } if (!sav->key_enc) { - printf("esp_descbc_mature: no key is given.\n"); + ipseclog((LOG_ERR, "esp_descbc_mature: no key is given.\n")); return 1; } algo = &esp_algorithms[sav->alg_enc]; if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_mature: invalid key length %d.\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, + "esp_descbc_mature: invalid key length %d.\n", + _KEYBITS(sav->key_enc))); return 1; } /* weak key check */ if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc))) { - printf("esp_descbc_mature: weak key was passed.\n"); + ipseclog((LOG_ERR, + "esp_descbc_mature: weak key was passed.\n")); return 1; } @@ -231,18 +235,19 @@ esp_descbc_decrypt(m, off, sav, algo, ivlen) size_t plen; u_int8_t tiv[8]; int derived; + int error; derived = 0; /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } @@ -282,7 +287,8 @@ esp_descbc_decrypt(m, off, sav, algo, ivlen) iv = &tiv[0]; m_copydata(m, ivoff, 8, &tiv[0]); } else { - printf("esp_descbc_decrypt: unsupported ivlen %d\n", ivlen); + ipseclog((LOG_ERR, "esp_descbc_decrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } @@ -293,8 +299,8 @@ esp_descbc_decrypt(m, off, sav, algo, ivlen) plen -= bodyoff; if (plen % 8) { - printf("esp_descbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_descbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } @@ -304,11 +310,13 @@ esp_descbc_decrypt(m, off, sav, algo, ivlen) deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); if (deserr != 0) { - printf("esp_descbc_decrypt: key error %d\n", deserr); + ipseclog((LOG_ERR, + "esp_descbc_decrypt: key error %d\n", deserr)); return EINVAL; } - des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); + error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, + DES_DECRYPT); /* for safety */ bzero(&ks, sizeof(des_key_schedule)); @@ -317,7 +325,7 @@ esp_descbc_decrypt(m, off, sav, algo, ivlen) /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int @@ -334,24 +342,25 @@ esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) u_int8_t *iv; u_int8_t tiv[8]; int derived; + int error; derived = 0; /* sanity check */ if (plen % 8) { - printf("esp_descbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_descbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } @@ -411,7 +420,8 @@ esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) } else if (ivlen == 8) bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); else { - printf("esp_descbc_encrypt: unsupported ivlen %d\n", ivlen); + ipseclog((LOG_ERR, + "esp_descbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -421,11 +431,13 @@ esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); if (deserr != 0) { - printf("esp_descbc_encrypt: key error %d\n", deserr); + ipseclog((LOG_ERR, + "esp_descbc_encrypt: key error %d\n", deserr)); return EINVAL; } - des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); + error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, + DES_ENCRYPT); /* for safety */ bzero(&ks, sizeof(des_key_schedule)); @@ -436,7 +448,7 @@ esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int @@ -447,23 +459,26 @@ esp_cbc_mature(sav) struct esp_algorithm *algo; if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cbc_mature: algorithm incompatible with esp-old\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: algorithm incompatible with esp-old\n")); return 1; } if (sav->flags & SADB_X_EXT_DERIV) { - printf("esp_cbc_mature: algorithm incompatible with derived\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: algorithm incompatible with derived\n")); return 1; } if (!sav->key_enc) { - printf("esp_cbc_mature: no key is given.\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: no key is given.\n")); return 1; } algo = &esp_algorithms[sav->alg_enc]; keylen = sav->key_enc->sadb_key_bits; if (keylen < algo->keymin || algo->keymax < keylen) { - printf("esp_cbc_mature: invalid key length %d.\n", - sav->key_enc->sadb_key_bits); + ipseclog((LOG_ERR, "esp_cbc_mature: invalid key length %d.\n", + sav->key_enc->sadb_key_bits)); return 1; } switch (sav->alg_enc) { @@ -472,7 +487,8 @@ esp_cbc_mature(sav) if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc)) || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 8)) || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 16))) { - printf("esp_cbc_mature: weak key was passed.\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: weak key was passed.\n")); return 1; } break; @@ -500,27 +516,31 @@ esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) size_t plen; static BF_KEY key; /* made static to avoid kernel stack overflow */ int s; + int error; /* sanity check */ if (sav->ivlen != ivlen) { - printf("esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_blowfish_cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_blowfish_cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_blowfish_cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -536,15 +556,19 @@ esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) plen -= bodyoff; if (plen % 8) { - printf("esp_blowfish_cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_blowfish_cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } +#ifdef __NetBSD__ + s = splsoftnet(); /* XXX correct? */ +#else s = splnet(); /* XXX correct? */ +#endif BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); + error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); /* for safety */ bzero(&key, sizeof(BF_KEY)); @@ -554,7 +578,7 @@ esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int @@ -571,32 +595,36 @@ esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) u_int8_t *iv; static BF_KEY key; /* made static to avoid kernel stack overflow */ int s; + int error; /* sanity check */ if (plen % 8) { - printf("esp_blowfish_cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_blowfish_cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_blowfish_cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_blowfish_cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_blowfish_cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -611,10 +639,14 @@ esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); +#ifdef __NetBSD__ + s = splsoftnet(); /* XXX correct? */ +#else s = splnet(); /* XXX correct? */ +#endif BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); + error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); /* for safety */ bzero(&key, sizeof(BF_KEY)); @@ -623,7 +655,7 @@ esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) esp_increment_iv(sav); - return 0; + return error; } static int @@ -652,27 +684,30 @@ esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) size_t bodyoff; u_int8_t iv[8]; size_t plen; + int error; /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_cast128cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_cast128cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cast128cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_cast128cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -690,8 +725,8 @@ esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) plen -= bodyoff; if (plen % 8) { - printf("esp_cast128cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } @@ -704,15 +739,15 @@ esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); set_cast128_subkey(subkey, key); - cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); + error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); /* for safety */ bzero(subkey, sizeof(subkey)); bzero(key, sizeof(key)); } - return 0; + return error; } static int @@ -727,32 +762,35 @@ esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) size_t ivoff; size_t bodyoff; u_int8_t *iv; + int error; /* sanity check */ if (plen % 8) { - printf("esp_cast128cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_cast128cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_cast128cbc_encrypt: unsupported key length %d: " - "needs %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported key length %d: " + "needs %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cast128cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_cast128cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -776,8 +814,8 @@ esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); set_cast128_subkey(subkey, key); - cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); + error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); /* for safety */ bzero(subkey, sizeof(subkey)); @@ -786,7 +824,7 @@ esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) esp_increment_iv(sav); - return 0; + return error; } static int @@ -812,23 +850,24 @@ esp_3descbc_decrypt(m, off, sav, algo, ivlen) /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_3descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_3descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_3descbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_3descbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_3descbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_3descbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -845,8 +884,8 @@ esp_3descbc_decrypt(m, off, sav, algo, ivlen) plen -= bodyoff; if (plen % 8) { - printf("esp_3descbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } @@ -859,8 +898,8 @@ esp_3descbc_decrypt(m, off, sav, algo, ivlen) deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - printf("esp_3descbc_decrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2]); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2])); return EINVAL; } @@ -891,28 +930,29 @@ esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) /* sanity check */ if (plen % 8) { - printf("esp_3descbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_3descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_3descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_3descbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_3descbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_3descbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_3descbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } @@ -936,8 +976,8 @@ esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - printf("esp_3descbc_encrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2]); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2])); return EINVAL; } @@ -971,25 +1011,28 @@ esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) size_t bodyoff; u_int8_t iv[8]; size_t plen; + int error; /* sanity check */ if (sav->ivlen != ivlen) { - printf("esp_rc5cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if ((_KEYBITS(sav->key_enc) < 40) || (_KEYBITS(sav->key_enc) > 2040)) { - printf("esp_rc5cbc_decrypt: unsupported key length %d: " - "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, + "esp_rc5cbc_decrypt: unsupported key length %d: " + "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_rc5cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_rc5cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_rc5cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } @@ -1007,8 +1050,8 @@ esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) plen -= bodyoff; if (plen % 8) { - printf("esp_rc5cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } @@ -1018,13 +1061,13 @@ esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), _KEYBITS(sav->key_enc) / 8, 16); - rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); + error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); /* for safety */ bzero(e_key, sizeof(e_key)); } - return 0; + return error; } static int @@ -1039,32 +1082,35 @@ esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) size_t ivoff; size_t bodyoff; u_int8_t *iv; + int error; /* sanity check */ if (plen % 8) { - printf("esp_rc5cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_rc5cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_rc5cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_rc5cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_rc5cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_rc5cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_rc5cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } @@ -1085,7 +1131,7 @@ esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), _KEYBITS(sav->key_enc) / 8, 16); - rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); + error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); /* for safety */ bzero(e_key, sizeof(e_key)); @@ -1093,7 +1139,7 @@ esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) esp_increment_iv(sav); - return 0; + return error; } /* @@ -1107,8 +1153,11 @@ esp_increment_iv(sav) u_int8_t y; int i; +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + y = time.tv_sec & 0xff; +#else y = time_second & 0xff; - +#endif if (!y) y++; x = (u_int8_t *)sav->iv; for (i = 0; i < sav->ivlen; i++) { @@ -1145,6 +1194,7 @@ mbuf_find_offset(m, off, len) /*------------------------------------------------------------*/ +/* does not free m0 on error */ int esp_auth(m0, skip, length, sav, sum) struct mbuf *m0; @@ -1159,14 +1209,16 @@ esp_auth(m0, skip, length, sav, sum) u_char sumbuf[AH_MAXSUMSIZE]; struct ah_algorithm *algo; size_t siz; + int error; /* sanity checks */ if (m0->m_pkthdr.len < skip) { - printf("esp_auth: mbuf length < skip\n"); + ipseclog((LOG_DEBUG, "esp_auth: mbuf length < skip\n")); return EINVAL; } if (m0->m_pkthdr.len < skip + length) { - printf("esp_auth: mbuf length < skip + length\n"); + ipseclog((LOG_DEBUG, + "esp_auth: mbuf length < skip + length\n")); return EINVAL; } /* @@ -1174,15 +1226,17 @@ esp_auth(m0, skip, length, sav, sum) * since nexthdr must be at offset 4n+3. */ if (length % 4) { - printf("esp_auth: length is not multiple of 4\n"); + ipseclog((LOG_ERR, "esp_auth: length is not multiple of 4\n")); return EINVAL; } if (!sav) { - printf("esp_auth: NULL SA passed\n"); + ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n")); return EINVAL; } if (!sav->alg_auth) { - printf("esp_auth: bad ESP auth algorithm passed: %d\n", sav->alg_auth); + ipseclog((LOG_ERR, + "esp_auth: bad ESP auth algorithm passed: %d\n", + sav->alg_auth)); return EINVAL; } @@ -1192,8 +1246,9 @@ esp_auth(m0, skip, length, sav, sum) algo = &ah_algorithms[sav->alg_auth]; siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1)); if (sizeof(sumbuf) < siz) { - printf("esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n", + (u_long)siz)); return EINVAL; } @@ -1211,7 +1266,10 @@ esp_auth(m0, skip, length, sav, sum) } } - (*algo->init)(&s, sav); + error = (*algo->init)(&s, sav); + if (error) + return error; + while (0 < length) { if (!m) panic("mbuf chain?"); @@ -1229,6 +1287,6 @@ esp_auth(m0, skip, length, sav, sum) } (*algo->result)(&s, sumbuf); bcopy(sumbuf, sum, siz); /*XXX*/ - + return 0; } diff --git a/sys/netinet6/esp_input.c b/sys/netinet6/esp_input.c index ef55069..48de49c 100644 --- a/sys/netinet6/esp_input.c +++ b/sys/netinet6/esp_input.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: esp_input.c,v 1.25 2000/05/08 08:04:30 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -35,7 +36,6 @@ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -63,49 +63,57 @@ #endif #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif -#ifdef IPSEC_ESP #include <netinet6/esp.h> #ifdef INET6 #include <netinet6/esp6.h> #endif -#endif #include <netkey/key.h> #include <netkey/keydb.h> #ifdef IPSEC_DEBUG #include <netkey/key_debug.h> #else -#define KEYDEBUG(lev,arg) +#define KEYDEBUG(lev,arg) #endif -#include <netinet/ipprotosw.h> - #include <machine/stdarg.h> #include <net/net_osdep.h> +#define IPLEN_FLIPPED + #ifdef INET +#include <netinet/ipprotosw.h> extern struct ipprotosw inetsw[]; +#define ESPMAXLEN \ + (sizeof(struct esp) < sizeof(struct newesp) \ + ? sizeof(struct newesp) : sizeof(struct esp)) + void -esp4_input(m, off, proto) +#if __STDC__ +esp4_input(struct mbuf *m, ...) +#else +esp4_input(m, va_alist) struct mbuf *m; - int off, proto; - + va_dcl +#endif { struct ip *ip; struct esp *esp; - struct esptail *esptail; + struct esptail esptail; u_int32_t spi; struct secasvar *sav = NULL; size_t taillen; @@ -115,19 +123,27 @@ esp4_input(m, off, proto) size_t hlen; size_t esplen; int s; + va_list ap; + int off, proto; + + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { - printf("IPv4 ESP input: packet alignment problem " - "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipseclog((LOG_ERR, "IPv4 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len)); ipsecstat.in_inval++; goto bad; } - if (m->m_len < off + sizeof(struct esp)) { - m = m_pullup(m, off + sizeof(struct esp)); + if (m->m_len < off + ESPMAXLEN) { + m = m_pullup(m, off + ESPMAXLEN); if (!m) { - printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: can't pullup in esp4_input\n")); ipsecstat.in_inval++; goto bad; } @@ -147,9 +163,9 @@ esp4_input(m, off, proto) if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, IPPROTO_ESP, spi)) == 0) { - printf("IPv4 ESP input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv4 ESP input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_nosa++; goto bad; } @@ -157,17 +173,16 @@ esp4_input(m, off, proto) printf("DP esp4_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv4 ESP input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } if (sav->alg_enc == SADB_EALG_NONE) { - printf("IPv4 ESP input: unspecified encryption algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv4 ESP input: " + "unspecified encryption algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } @@ -177,9 +192,8 @@ esp4_input(m, off, proto) /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { - log(LOG_NOTICE, "inproper ivlen in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "inproper ivlen in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } @@ -198,16 +212,14 @@ esp4_input(m, off, proto) ; /*okey*/ else { ipsecstat.in_espreplay++; - log(LOG_AUTH, "replay packet in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); goto bad; } /* check ICV */ { - struct mbuf *n; - int len; u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; struct ah_algorithm *sumalgo; @@ -216,49 +228,37 @@ esp4_input(m, off, proto) sumalgo = &ah_algorithms[sav->alg_auth]; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { - printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz)); ipsecstat.in_inval++; goto bad; } - n = m; - len = m->m_pkthdr.len; - len -= siz; - while (n && 0 < len) { - if (len < n->m_len) - break; - len -= n->m_len; - n = n->m_next; - } - if (!n) { - printf("mbuf chain problem?\n"); - ipsecstat.in_inval++; - goto bad; - } - m_copydata(n, len, siz, &sum0[0]); + m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]); if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { - log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_espauthfail++; goto bad; } if (bcmp(sum0, sum, siz) != 0) { - log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_espauthfail++; goto bad; } - /* strip off */ - m->m_pkthdr.len -= siz; - n->m_len -= siz; + /* strip off the authentication data */ + m_adj(m, -siz); ip = mtod(m, struct ip *); +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - siz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - siz); +#endif m->m_flags |= M_AUTHIPDGM; ipsecstat.in_espauthsucc++; } @@ -267,7 +267,10 @@ esp4_input(m, off, proto) * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) { + ipsecstat.in_espreplay++; + goto bad; + } } noreplaycheck: @@ -284,10 +287,18 @@ noreplaycheck: esplen = sizeof(struct newesp); } + if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) { + ipseclog((LOG_WARNING, + "IPv4 ESP input: packet too short\n")); + ipsecstat.in_inval++; + goto bad; + } + if (m->m_len < off + esplen + ivlen) { m = m_pullup(m, off + esplen + ivlen); if (!m) { - printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: can't pullup in esp4_input\n")); ipsecstat.in_inval++; goto bad; } @@ -300,9 +311,8 @@ noreplaycheck: if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - log(LOG_AUTH, "decrypt fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } @@ -311,145 +321,34 @@ noreplaycheck: m->m_flags |= M_DECRYPTED; } -#ifdef garbled_data_found_on_mbuf_after_packet - { - /* - * For simplicity, we'll trim the packet so that there's no extra - * part appended after IP packet. - * This is rare case for some odd drivers, so there should be no - * performance hit. - */ - - /* - * Note that, in ip_input, ip_len was already flipped and header - * length was subtracted from ip_len. - */ - if (m->m_pkthdr.len != hlen + ip->ip_len) - { - size_t siz; - struct mbuf *n; - - siz = hlen + ip->ip_len; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %d\n", n->m_len, siz); - ipsecstat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len = hlen + ip->ip_len; - } - } -#endif - - { /* * find the trailer of the ESP. */ - struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ - struct mbuf *o; /*the last mbuf on the mbuf chain */ - - o = m; - n = NULL; - while (o) { - if (0 < o->m_len) - n = o; - o = o->m_next; - } - if (!n || n->m_len < sizeof(struct esptail)) { - printf("IPv4 ESP input: assertion on pad part failed; " - "dropping the packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - esptail = (struct esptail *) - (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); - nxt = esptail->esp_nxt; - taillen = esptail->esp_padlen + 2; + m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail), + (caddr_t)&esptail); + nxt = esptail.esp_nxt; + taillen = esptail.esp_padlen + sizeof(esptail); if (m->m_pkthdr.len < taillen || m->m_pkthdr.len - taillen < hlen) { /*?*/ - log(LOG_NOTICE, "bad pad length in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "bad pad length in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } - /* - * strip off the trailing pad area. - */ - if (taillen < n->m_len) { - /* trailing pad data is included in the last mbuf item. */ - n->m_len -= taillen; - m->m_pkthdr.len -= taillen; - } else { - /* trailing pad data spans on multiple mbuf item. */ - size_t siz; - - siz = m->m_pkthdr.len; - if (siz < taillen) { - log(LOG_NOTICE, "bad packet length in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); - ipsecstat.in_inval++; - goto bad; - } - siz -= taillen; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); - ipsecstat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len -= taillen; - } + /* strip off the trailing pad area. */ + m_adj(m, -taillen); +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - taillen; - } +#else + ip->ip_len = htons(ntohs(ip->ip_len) - taillen); +#endif /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + if (ipsec4_tunnel_validate(ip, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP4 xx ESP IP4' payload -> IP4' payload @@ -457,11 +356,9 @@ noreplaycheck: * XXX more sanity checks * XXX relationship with gif? */ - struct ip oip; /* for debug */ u_int8_t tos; tos = ip->ip_tos; - bcopy(mtod(m, struct ip *), &oip, sizeof(oip)); /* for debug */ m_adj(m, off + esplen + ivlen); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); @@ -475,13 +372,21 @@ noreplaycheck: ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); if (!key_checktunnelsanity(sav, AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "ipsec tunnel address mismatch " + "in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } +#if 0 /* XXX should call ipfw rather than ipsec_in_reject, shouldn't it ? */ + /* drop it if it does not match the default policy */ + if (ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } +#endif + key_sa_recordxfer(sav, m); s = splimp(); @@ -497,8 +402,8 @@ noreplaycheck: } else { /* * strip off ESP header and IV. - * We do deep-copy since KAME requires packet to be placed - * in a single mbuf. + * even in m_pulldown case, we need to strip off ESP so that + * we can always compute checksum for AH correctly. */ size_t stripsiz; @@ -511,7 +416,11 @@ noreplaycheck: m->m_pkthdr.len -= stripsiz; ip = mtod(m, struct ip *); +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - stripsiz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz); +#endif ip->ip_p = nxt; key_sa_recordxfer(sav, m); @@ -553,7 +462,7 @@ esp6_input(mp, offp, proto) int off = *offp; struct ip6_hdr *ip6; struct esp *esp; - struct esptail *esptail; + struct esptail esptail; u_int32_t spi; struct secasvar *sav = NULL; size_t taillen; @@ -565,19 +474,27 @@ esp6_input(mp, offp, proto) /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { - printf("IPv6 ESP input: packet alignment problem " - "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipseclog((LOG_ERR, "IPv6 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len)); ipsec6stat.in_inval++; goto bad; } - IP6_EXTHDR_CHECK(m, off, sizeof(struct esp), IPPROTO_DONE); - +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, ESPMAXLEN, IPPROTO_DONE); + esp = (struct esp *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(esp, struct esp *, m, off, ESPMAXLEN); + if (esp == NULL) { + ipsec6stat.in_inval++; + return IPPROTO_DONE; + } +#endif ip6 = mtod(m, struct ip6_hdr *); - esp = (struct esp *)(((u_int8_t *)ip6) + off); if (ntohs(ip6->ip6_plen) == 0) { - printf("IPv6 ESP input: ESP with IPv6 jumbogram is not supported.\n"); + ipseclog((LOG_ERR, "IPv6 ESP input: " + "ESP with IPv6 jumbogram is not supported.\n")); ipsec6stat.in_inval++; goto bad; } @@ -588,9 +505,9 @@ esp6_input(mp, offp, proto) if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, IPPROTO_ESP, spi)) == 0) { - printf("IPv6 ESP input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv6 ESP input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_nosa++; goto bad; } @@ -598,17 +515,16 @@ esp6_input(mp, offp, proto) printf("DP esp6_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv6 ESP input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv6 ESP input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } if (sav->alg_enc == SADB_EALG_NONE) { - printf("IPv6 ESP input: unspecified encryption algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv6 ESP input: " + "unspecified encryption algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } @@ -618,9 +534,8 @@ esp6_input(mp, offp, proto) /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { - log(LOG_NOTICE, "inproper ivlen in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "inproper ivlen in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_badspi++; goto bad; } @@ -639,16 +554,14 @@ esp6_input(mp, offp, proto) ; /*okey*/ else { ipsec6stat.in_espreplay++; - log(LOG_AUTH, "replay packet in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); goto bad; } /* check ICV */ { - struct mbuf *n; - size_t len; u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; struct ah_algorithm *sumalgo; @@ -657,47 +570,31 @@ esp6_input(mp, offp, proto) sumalgo = &ah_algorithms[sav->alg_auth]; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { - printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz)); ipsec6stat.in_inval++; goto bad; } - n = m; - len = m->m_pkthdr.len; - len -= siz; /*XXX*/ - while (n && 0 < len) { - if (len < n->m_len) - break; - len -= n->m_len; - n = n->m_next; - } - if (!n) { - printf("mbuf chain problem?\n"); - ipsec6stat.in_inval++; - goto bad; - } - m_copydata(n, len, siz, &sum0[0]); + m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]); if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { - log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_espauthfail++; goto bad; } if (bcmp(sum0, sum, siz) != 0) { - log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_espauthfail++; goto bad; } - /* strip off */ - m->m_pkthdr.len -= siz; - n->m_len -= siz; + /* strip off the authentication data */ + m_adj(m, -siz); ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - siz); @@ -709,7 +606,10 @@ esp6_input(mp, offp, proto) * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) { + ipsec6stat.in_espreplay++; + goto bad; + } } noreplaycheck: @@ -726,7 +626,24 @@ noreplaycheck: esplen = sizeof(struct newesp); } + if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) { + ipseclog((LOG_WARNING, + "IPv6 ESP input: packet too short\n")); + ipsec6stat.in_inval++; + goto bad; + } + +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, esplen + ivlen, IPPROTO_DONE); /*XXX*/ +#else + IP6_EXTHDR_GET(esp, struct esp *, m, off, esplen + ivlen); + if (esp == NULL) { + ipsec6stat.in_inval++; + m = NULL; + goto bad; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); /*set it again just in case*/ /* * decrypt the packet. @@ -734,9 +651,8 @@ noreplaycheck: if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - log(LOG_AUTH, "decrypt fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } @@ -744,145 +660,30 @@ noreplaycheck: m->m_flags |= M_DECRYPTED; -#ifdef garbled_data_found_on_mbuf_after_packet - { - /* - * For simplicity, we'll trim the packet so that there's no extra - * part appended after IP packet. - * This is rare case for some odd drivers, so there should be no - * performance hit. - */ - - /* - * Note that, in ip_input, ip_len was already flipped and header - * length was subtracted from ip_len. - */ - if (m->m_pkthdr.len != hlen + ip->ip_len) - { - size_t siz; - struct mbuf *n; - - siz = hlen + ip->ip_len; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %d\n", n->m_len, siz); - ipsec6stat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len = hlen + ip->ip_len; - } - } -#endif - - { /* * find the trailer of the ESP. */ - struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ - struct mbuf *o; /*the last mbuf on the mbuf chain */ - - o = m; - n = NULL; - while (o) { - if (0 < o->m_len) - n = o; - o = o->m_next; - } - if (!n || n->m_len < sizeof(struct esptail)) { - printf("IPv6 ESP input: assertion on pad part failed; " - "dropping the packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - esptail = (struct esptail *) - (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); - nxt = esptail->esp_nxt; - taillen = esptail->esp_padlen + 2; + m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail), + (caddr_t)&esptail); + nxt = esptail.esp_nxt; + taillen = esptail.esp_padlen + sizeof(esptail); if (m->m_pkthdr.len < taillen || m->m_pkthdr.len - taillen < sizeof(struct ip6_hdr)) { /*?*/ - log(LOG_NOTICE, "bad pad length in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "bad pad length in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } - /* - * XXX strip off the padding. - */ - if (taillen < n->m_len) { - /* trailing pad data is included in the last mbuf item. */ - n->m_len -= taillen; - m->m_pkthdr.len -= taillen; - } else { - /* trailing pad data spans on multiple mbuf item. */ - size_t siz; - - siz = m->m_pkthdr.len; - if (siz < taillen) { - log(LOG_NOTICE, "bad packet length in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); - ipsec6stat.in_inval++; - goto bad; - } - siz -= taillen; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); - ipsec6stat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len -= taillen; - } + /* strip off the trailing pad area. */ + m_adj(m, -taillen); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen); - } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + if (ipsec6_tunnel_validate(ip6, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP6 xx ESP IP6' payload -> IP6' payload @@ -894,10 +695,14 @@ noreplaycheck: flowinfo = ip6->ip6_flow; m_adj(m, off + esplen + ivlen); if (m->m_len < sizeof(*ip6)) { +#ifndef PULLDOWN_TEST /* * m_pullup is prohibited in KAME IPv6 input processing * but there's no other way! */ +#else + /* okay to pullup in m_pulldown style */ +#endif m = m_pullup(m, sizeof(*ip6)); if (!m) { ipsec6stat.in_inval++; @@ -909,13 +714,22 @@ noreplaycheck: ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); if (!key_checktunnelsanity(sav, AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "ipsec tunnel address mismatch " + "in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } +#if 0 /* XXX should call ipfw rather than ipsec_in_reject, shouldn't it ? */ + /* drop it if it does not match the default policy */ + if (ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto bad; + } +#endif + key_sa_recordxfer(sav, m); s = splimp(); @@ -931,8 +745,8 @@ noreplaycheck: } else { /* * strip off ESP header and IV. - * We do deep-copy since KAME requires packet to be placed - * in a single mbuf. + * even in m_pulldown case, we need to strip off ESP so that + * we can always compute checksum for AH correctly. */ size_t stripsiz; char *prvnxtp; @@ -946,11 +760,28 @@ noreplaycheck: stripsiz = esplen + ivlen; ip6 = mtod(m, struct ip6_hdr *); - ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), - off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; + 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 bad; + } + m_adj(n, stripsiz); + m_cat(m, n); + /* m_cat does not update m_pkthdr.len */ + m->m_pkthdr.len += n->m_pkthdr.len; + } ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); diff --git a/sys/netinet6/esp_output.c b/sys/netinet6/esp_output.c index fbe0d25..8d505ae 100644 --- a/sys/netinet6/esp_output.c +++ b/sys/netinet6/esp_output.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: esp_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,13 +28,10 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" /* * RFC1827/2406 Encapsulated Security Payload. @@ -55,33 +55,27 @@ #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_var.h> -#include <netinet/in_pcb.h> #ifdef INET6 -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif -#ifdef IPSEC_ESP #include <netinet6/esp.h> #ifdef INET6 #include <netinet6/esp6.h> #endif -#endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <net/net_osdep.h> @@ -111,18 +105,18 @@ esp_hdrsiz(isr) panic("unsupported mode passed to esp_hdrsiz"); if (sav == NULL) - goto contrive; + goto estimate; if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) - goto contrive; + goto estimate; /* we need transport mode ESP. */ algo = &esp_algorithms[sav->alg_enc]; if (!algo) - goto contrive; + goto estimate; ivlen = sav->ivlen; if (ivlen < 0) - goto contrive; + goto estimate; /* * XXX @@ -145,14 +139,14 @@ esp_hdrsiz(isr) return hdrsiz; - contrive: + estimate: /* * ASSUMING: * sizeof(struct newesp) > sizeof(struct esp). * 8 = ivlen for CBC mode (RFC2451). - * 9 = (maximum padding length without random Padding length) + * 9 = (maximum padding length without random padding length) * + (Pad Length field) + (Next Header field). - * 16 = maximum ICV we supported. + * 16 = maximum ICV we support. */ return sizeof(struct newesp) + 8 + 9 + 16; } @@ -212,7 +206,7 @@ esp_output(m, nexthdrp, md, isr, af) break; #endif default: - printf("esp_output: unsupported af %d\n", af); + ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af)); return 0; /* no change at all */ } @@ -225,12 +219,11 @@ esp_output(m, nexthdrp, md, isr, af) struct ip *ip; ip = mtod(m, struct ip *); - printf("esp4_output: internal error: " - "sav->replay is null: " - "%x->%x, SPI=%u\n", + 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)); + (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; m_freem(m); return EINVAL; @@ -242,9 +235,9 @@ esp_output(m, nexthdrp, md, isr, af) struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); - printf("esp6_output: internal error: " + ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", - (u_int32_t)ntohl(sav->spi)); + (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; m_freem(m); return EINVAL; @@ -293,7 +286,8 @@ esp_output(m, nexthdrp, md, isr, af) for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (mprev == NULL || mprev->m_next != md) { - printf("esp%d_output: md is not in chain\n", afnumber); + ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n", + afnumber)); m_freem(m); return EINVAL; } @@ -355,7 +349,7 @@ esp_output(m, nexthdrp, md, isr, af) m->m_pkthdr.len += esphlen; esp = mtod(md, struct esp *); } - + nxt = *nexthdrp; *nexthdrp = IPPROTO_ESP; switch (af) { @@ -364,7 +358,8 @@ esp_output(m, nexthdrp, md, isr, af) if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + esphlen); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; @@ -385,6 +380,17 @@ esp_output(m, nexthdrp, md, isr, af) if ((sav->flags & SADB_X_EXT_OLD) == 0) { struct newesp *nesp; nesp = (struct newesp *)esp; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is @@ -402,6 +408,8 @@ esp_output(m, nexthdrp, md, isr, af) struct ip *ip = NULL; #endif size_t padbound; + u_char *extend; + int i; if (algo->padbound) padbound = algo->padbound; @@ -410,7 +418,7 @@ esp_output(m, nexthdrp, md, isr, af) /* ESP packet, including nxthdr field, must be length of 4n */ if (padbound < 4) padbound = 4; - + extendsiz = padbound - (plen % padbound); if (extendsiz == 1) extendsiz = padbound + 1; @@ -424,23 +432,7 @@ esp_output(m, nexthdrp, md, isr, af) * two consequtive TCP packets. */ if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { - switch (sav->flags & SADB_X_EXT_PMASK) { - case SADB_X_EXT_PRAND: - break; - case SADB_X_EXT_PZERO: - bzero((caddr_t)(mtod(n, u_int8_t *) + n->m_len), - extendsiz); - break; - case SADB_X_EXT_PSEQ: - { - int i; - u_char *p; - p = mtod(n, u_char *) + n->m_len; - for (i = 0; i < extendsiz; i++) - p[i] = i + 1; - break; - } - } + extend = mtod(n, u_char *) + n->m_len; n->m_len += extendsiz; m->m_pkthdr.len += extendsiz; } else { @@ -448,33 +440,32 @@ esp_output(m, nexthdrp, md, isr, af) MGET(nn, M_DONTWAIT, MT_DATA); if (!nn) { - printf("esp%d_output: can't alloc mbuf", afnumber); + ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf", + afnumber)); m_freem(m); error = ENOBUFS; goto fail; } + extend = mtod(nn, u_char *); nn->m_len = extendsiz; - switch (sav->flags & SADB_X_EXT_PMASK) { - case SADB_X_EXT_PRAND: - break; - case SADB_X_EXT_PZERO: - bzero(mtod(nn, caddr_t), extendsiz); - break; - case SADB_X_EXT_PSEQ: - { - int i; - u_char *p; - p = mtod(nn, u_char *); - for (i = 0; i < extendsiz; i++) - p[i] = i + 1; - break; - } - } nn->m_next = NULL; n->m_next = nn; n = nn; m->m_pkthdr.len += extendsiz; } + switch (sav->flags & SADB_X_EXT_PMASK) { + case SADB_X_EXT_PRAND: + for (i = 0; i < extendsiz; i++) + extend[i] = random() & 0xff; + break; + case SADB_X_EXT_PZERO: + bzero(extend, extendsiz); + break; + case SADB_X_EXT_PSEQ: + for (i = 0; i < extendsiz; i++) + extend[i] = (i + 1) & 0xff; + break; + } /* initialize esp trailer. */ esptail = (struct esptail *) @@ -490,7 +481,8 @@ esp_output(m, nexthdrp, md, isr, af) if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; @@ -513,7 +505,7 @@ esp_output(m, nexthdrp, md, isr, af) if (!algo->encrypt) panic("internal error: no encrypt function"); if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { - printf("packet encryption failure\n"); + ipseclog((LOG_ERR, "packet encryption failure\n")); m_freem(m); switch (af) { #ifdef INET @@ -551,8 +543,24 @@ esp_output(m, nexthdrp, md, isr, af) if (AH_MAXSUMSIZE < siz) panic("assertion failed for AH_MAXSUMSIZE"); - if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) - goto noantireplay; + if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) { + ipseclog((LOG_ERR, "ESP checksum generation failure\n")); + m_freem(m); + error = EINVAL; + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + goto fail; + } n = m; while (n->m_next) @@ -567,7 +575,8 @@ esp_output(m, nexthdrp, md, isr, af) MGET(nn, M_DONTWAIT, MT_DATA); if (!nn) { - printf("can't alloc mbuf in esp%d_output", afnumber); + ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output", + afnumber)); m_freem(m); error = ENOBUFS; goto fail; @@ -589,7 +598,8 @@ esp_output(m, nexthdrp, md, isr, af) if (siz < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + siz); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; @@ -606,9 +616,10 @@ esp_output(m, nexthdrp, md, isr, af) } noantireplay: - if (!m) - printf("NULL mbuf after encryption in esp%d_output", afnumber); - else { + if (!m) { + ipseclog((LOG_ERR, + "NULL mbuf after encryption in esp%d_output", afnumber)); + } else { switch (af) { #ifdef INET case AF_INET: @@ -653,9 +664,9 @@ esp4_output(m, isr) { struct ip *ip; if (m->m_len < sizeof(struct ip)) { - printf("esp4_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n")); m_freem(m); - return NULL; + return 0; } ip = mtod(m, struct ip *); /* XXX assumes that m->m_next points to payload */ @@ -672,9 +683,9 @@ esp6_output(m, nexthdrp, md, isr) struct ipsecrequest *isr; { if (m->m_len < sizeof(struct ip6_hdr)) { - printf("esp6_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n")); m_freem(m); - return NULL; + return 0; } return esp_output(m, nexthdrp, md, isr, AF_INET6); } diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c index f7cfbc0..1bf7e73 100644 --- a/sys/netinet6/frag6.c +++ b/sys/netinet6/frag6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: frag6.c,v 1.24 2000/03/25 07:23:41 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include <sys/param.h> @@ -46,9 +47,9 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <net/net_osdep.h> @@ -57,20 +58,23 @@ * You will need to perform an extra routing table lookup, per fragment, * to do it. This may, or may not be, a performance hit. */ -#define IN6_IFSTAT_STRICT +#define IN6_IFSTAT_STRICT -static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *)); -static void frag6_deq __P((struct ip6asfrag *)); -static void frag6_insque __P((struct ip6q *, struct ip6q *)); -static void frag6_remque __P((struct ip6q *)); -static void frag6_freef __P((struct ip6q *)); +static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *)); +static void frag6_deq __P((struct ip6asfrag *)); +static void frag6_insque __P((struct ip6q *, struct ip6q *)); +static void frag6_remque __P((struct ip6q *)); +static void frag6_freef __P((struct ip6q *)); -int frag6_doing_reass; -u_int frag6_nfragpackets; -struct ip6q ip6q; /* ip6 reassemble queue */ +int frag6_doing_reass; +u_int frag6_nfragpackets; +struct ip6q ip6q; /* ip6 reassemble queue */ -#if !defined(M_FTABLE) +/* FreeBSD tweak */ MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); + +#ifndef offsetof /* XXX */ +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif /* @@ -86,11 +90,40 @@ frag6_init() * as initialization during bootstrap time occur in fixed order. */ microtime(&tv); - ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q; ip6_id = random() ^ tv.tv_usec; + ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q; } /* + * In RFC2460, fragment and reassembly rule do not agree with each other, + * in terms of next header field handling in fragment header. + * While the sender will use the same value for all of the fragmented packets, + * receiver is suggested not to check the consistency. + * + * fragment rule (p20): + * (2) A Fragment header containing: + * The Next Header value that identifies the first header of + * the Fragmentable Part of the original packet. + * -> next header field is same for all fragments + * + * reassembly rule (p21): + * The Next Header field of the last header of the Unfragmentable + * Part is obtained from the Next Header field of the first + * fragment's Fragment header. + * -> should grab it from the first fragment only + * + * The following note also contradicts with fragment rule - noone is going to + * send different fragment with different next header field. + * + * additional note (p22): + * The Next Header values in the Fragment headers of different + * fragments of the same original packet may differ. Only the value + * from the Offset zero fragment packet is used for reassembly. + * -> should grab it from the first fragment only + * + * There is no explicit reason given in the RFC. Historical reason maybe? + */ +/* * Fragment input */ int @@ -102,20 +135,25 @@ frag6_input(mp, offp, proto) struct ip6_hdr *ip6; struct ip6_frag *ip6f; struct ip6q *q6; - struct ip6asfrag *af6, *ip6af; + struct ip6asfrag *af6, *ip6af, *af6dwn; int offset = *offp, nxt, i, next; int first_frag = 0; - u_short fragoff, frgpartlen; + int fragoff, frgpartlen; /* must be larger than u_int16_t */ struct ifnet *dstifp; #ifdef IN6_IFSTAT_STRICT static struct route_in6 ro; struct sockaddr_in6 *dst; #endif - IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); - ip6 = mtod(m, struct ip6_hdr *); +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); +#else + IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); + if (ip6f == NULL) + return IPPROTO_DONE; +#endif dstifp = NULL; #ifdef IN6_IFSTAT_STRICT @@ -159,7 +197,7 @@ frag6_input(mp, offp, proto) (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + offsetof(struct ip6_hdr, ip6_plen)); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } @@ -167,14 +205,8 @@ frag6_input(mp, offp, proto) ip6stat.ip6s_fragments++; in6_ifstat_inc(dstifp, ifs6_reass_reqd); - /* - * Presence of header sizes in mbufs - * would confuse code below. - */ - + /* offset now points to data portion */ offset += sizeof(struct ip6_frag); - m->m_data += offset; - m->m_len -= offset; for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && @@ -204,10 +236,15 @@ frag6_input(mp, offp, proto) M_DONTWAIT); if (q6 == NULL) goto dropfrag; + bzero(q6, sizeof(*q6)); frag6_insque(q6, &ip6q); + /* ip6q_nxt will be filled afterwards, from 1st fragment */ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; +#ifdef notyet + q6->ip6q_nxtp = (u_char *)nxtp; +#endif q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_arrive = 0; /* Is it used anywhere? */ q6->ip6q_ttl = IPV6_FRAGTTL; @@ -232,22 +269,20 @@ frag6_input(mp, offp, proto) * in size. * If it would exceed, discard the fragment and return an ICMP error. */ - frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; + frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; if (q6->ip6q_unfrglen >= 0) { /* The 1st fragment has already arrived. */ if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { - m->m_data -= offset; - m->m_len += offset; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - offset - sizeof(struct ip6_frag) + 2); + offset - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); return(IPPROTO_DONE); } } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { - m->m_data -= offset; - m->m_len += offset; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - offset - sizeof(struct ip6_frag) + 2); + offset - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); return(IPPROTO_DONE); } /* @@ -255,8 +290,6 @@ frag6_input(mp, offp, proto) * fragment already stored in the reassembly queue. */ if (fragoff == 0) { - struct ip6asfrag *af6dwn; - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6dwn) { af6dwn = af6->ip6af_down; @@ -269,10 +302,9 @@ frag6_input(mp, offp, proto) /* dequeue the fragment. */ frag6_deq(af6); + free(af6, M_FTABLE); /* adjust pointer. */ - merr->m_data -= af6->ip6af_offset; - merr->m_len += af6->ip6af_offset; ip6err = mtod(merr, struct ip6_hdr *); /* @@ -284,13 +316,21 @@ frag6_input(mp, offp, proto) icmp6_error(merr, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - erroff - sizeof(struct ip6_frag) + 2); + erroff - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); } } } - /* Override the IPv6 header */ - ip6af = (struct ip6asfrag *)ip6; + ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE, + M_DONTWAIT); + if (ip6af == NULL) + goto dropfrag; + bzero(ip6af, sizeof(*ip6af)); + ip6af->ip6af_head = ip6->ip6_flow; + ip6af->ip6af_len = ip6->ip6_plen; + ip6af->ip6af_nxt = ip6->ip6_nxt; + ip6af->ip6af_hlim = ip6->ip6_hlim; ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; ip6af->ip6af_off = fragoff; ip6af->ip6af_frglen = frgpartlen; @@ -310,6 +350,42 @@ frag6_input(mp, offp, proto) if (af6->ip6af_off > ip6af->ip6af_off) break; +#if 0 + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if (af6->ip6af_up != (struct ip6asfrag *)q6) { + i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen + - ip6af->ip6af_off; + if (i > 0) { + if (i >= ip6af->ip6af_frglen) + goto dropfrag; + m_adj(IP6_REASS_MBUF(ip6af), i); + ip6af->ip6af_off += i; + ip6af->ip6af_frglen -= i; + } + } + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (af6 != (struct ip6asfrag *)q6 && + ip6af->ip6af_off + ip6af->ip6af_frglen > af6->ip6af_off) { + i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; + if (i < af6->ip6af_frglen) { + af6->ip6af_frglen -= i; + af6->ip6af_off += i; + m_adj(IP6_REASS_MBUF(af6), i); + break; + } + af6 = af6->ip6af_down; + m_freem(IP6_REASS_MBUF(af6->ip6af_up)); + frag6_deq(af6->ip6af_up); + } +#else /* * If the incoming framgent overlaps some existing fragments in * the reassembly queue, drop it, since it is dangerous to override @@ -334,6 +410,7 @@ frag6_input(mp, offp, proto) goto dropfrag; } } +#endif insert: @@ -344,6 +421,12 @@ insert: * the most recently active fragmented packet. */ frag6_enq(ip6af, af6->ip6af_up); +#if 0 /* xxx */ + if (q6 != ip6q.ip6q_next) { + frag6_remque(q6); + frag6_insque(q6, &ip6q); + } +#endif next = 0; for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6->ip6af_down) { @@ -361,38 +444,52 @@ insert: /* * Reassembly is complete; concatenate fragments. */ - ip6af = q6->ip6q_down; t = m = IP6_REASS_MBUF(ip6af); af6 = ip6af->ip6af_down; + frag6_deq(ip6af); while (af6 != (struct ip6asfrag *)q6) { + af6dwn = af6->ip6af_down; + frag6_deq(af6); while (t->m_next) t = t->m_next; t->m_next = IP6_REASS_MBUF(af6); - af6 = af6->ip6af_down; + m_adj(t->m_next, af6->ip6af_offset); + free(af6, M_FTABLE); + af6 = af6dwn; } /* adjust offset to point where the original next header starts */ offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); - ip6 = (struct ip6_hdr *)ip6af; + free(ip6af, M_FTABLE); + ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr)); ip6->ip6_src = q6->ip6q_src; ip6->ip6_dst = q6->ip6q_dst; nxt = q6->ip6q_nxt; +#ifdef notyet + *q6->ip6q_nxtp = (u_char)(nxt & 0xff); +#endif /* * Delete frag6 header with as a few cost as possible. */ - - if (offset < m->m_len) + if (offset < m->m_len) { ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag), offset); - else { - ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len); - m->m_data -= sizeof(struct ip6_frag); + m->m_data += sizeof(struct ip6_frag); + m->m_len -= sizeof(struct ip6_frag); + } else { + /* this comes with no copy if the boundary is on cluster */ + if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) { + frag6_remque(q6); + free(q6, M_FTABLE); + frag6_nfragpackets--; + goto dropfrag; + } + m_adj(t, sizeof(struct ip6_frag)); + m_cat(m, t); } - m->m_data -= offset; - m->m_len += offset; /* * Store NXT to the original. @@ -458,8 +555,6 @@ frag6_freef(q6) struct ip6_hdr *ip6; /* adjust pointer */ - m->m_data -= af6->ip6af_offset; - m->m_len += af6->ip6af_offset; ip6 = mtod(m, struct ip6_hdr *); /* restoure source and destination addresses */ @@ -468,9 +563,9 @@ frag6_freef(q6) icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY, 0); - } - else + } else m_freem(m); + free(af6, M_FTABLE); } frag6_remque(q6); free(q6, M_FTABLE); @@ -530,6 +625,9 @@ frag6_slowtimo() { struct ip6q *q6; int s = splnet(); +#if 0 + extern struct route_in6 ip6_forward_rt; +#endif frag6_doing_reass = 1; q6 = ip6q.ip6q_next; @@ -554,6 +652,23 @@ frag6_slowtimo() frag6_freef(ip6q.ip6q_prev); } frag6_doing_reass = 0; + +#if 0 + /* + * Routing changes might produce a better route than we last used; + * make sure we notice eventually, even if forwarding only for one + * destination and the cache is never replaced. + */ + if (ip6_forward_rt.ro_rt) { + RTFREE(ip6_forward_rt.ro_rt); + ip6_forward_rt.ro_rt = 0; + } + if (ipsrcchk_rt.ro_rt) { + RTFREE(ipsrcchk_rt.ro_rt); + ipsrcchk_rt.ro_rt = 0; + } +#endif + splx(s); } diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 05edc91..b017bd8 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: icmp6.c,v 1.119 2000/07/03 14:16:46 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -64,6 +65,8 @@ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -84,57 +87,62 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet6/mld6_var.h> #include <netinet/in_pcb.h> +#include <netinet6/in6_pcb.h> #include <netinet6/nd6.h> #include <netinet6/in6_ifattach.h> #include <netinet6/ip6protosw.h> #ifdef IPSEC #include <netinet6/ipsec.h> -#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ipsec6.h> -#include <netinet6/ah6.h> +#endif #include <netkey/key.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) #endif -#endif /* IPSEC */ #include "faith.h" #include <net/net_osdep.h> -extern struct domain inet6domain; -extern struct ip6protosw inet6sw[]; -extern u_char ip6_protox[]; - -struct icmp6stat icmp6stat; - -extern struct inpcbhead ripcb; -extern u_int icmp6errratelim; - -static int icmp6_rip6_input __P((struct mbuf **, int)); -static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); -static const char *icmp6_redirect_diag __P((struct in6_addr *, - struct in6_addr *, - struct in6_addr *)); -static struct mbuf *ni6_input __P((struct mbuf *, int)); -static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, - struct ifnet **)); -static int ni6_store_addrs __P((struct icmp6_nodeinfo *, - struct icmp6_nodeinfo *, - struct ifnet *, int)); +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; +extern u_char ip6_protox[]; + +struct icmp6stat icmp6stat; + +extern struct inpcbhead ripcb; +extern struct timeval icmp6errratelim; +static struct timeval icmp6errratelim_last; +extern int icmp6errppslim; +static int icmp6errpps_count = 0; +extern int icmp6_nodeinfo; + +static void icmp6_errcount __P((struct icmp6errstat *, int, int)); +static int icmp6_rip6_input __P((struct mbuf **, int)); +static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *, + struct mbuf *)); +static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); +static const char *icmp6_redirect_diag __P((struct in6_addr *, + struct in6_addr *, struct in6_addr *)); +#ifndef HAVE_RATECHECK +static int ratecheck __P((struct timeval *, struct timeval *)); +#endif +static struct mbuf *ni6_input __P((struct mbuf *, int)); +static struct mbuf *ni6_nametodns __P((const char *, int, int)); +static int ni6_dnsmatch __P((const char *, int, const char *, int)); +static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, + struct ifnet **)); +static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, + struct ifnet *, int)); #ifdef COMPAT_RFC1885 -static struct route_in6 icmp6_reflect_rt; +static struct route_in6 icmp6_reflect_rt; #endif -static struct timeval icmp6_nextsend = {0, 0}; void icmp6_init() @@ -142,6 +150,64 @@ icmp6_init() mld6_init(); } +static void +icmp6_errcount(stat, type, code) + struct icmp6errstat *stat; + int type, code; +{ + switch(type) { + case ICMP6_DST_UNREACH: + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + stat->icp6errs_dst_unreach_noroute++; + return; + case ICMP6_DST_UNREACH_ADMIN: + stat->icp6errs_dst_unreach_admin++; + return; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + stat->icp6errs_dst_unreach_beyondscope++; + return; + case ICMP6_DST_UNREACH_ADDR: + stat->icp6errs_dst_unreach_addr++; + return; + case ICMP6_DST_UNREACH_NOPORT: + stat->icp6errs_dst_unreach_noport++; + return; + } + break; + case ICMP6_PACKET_TOO_BIG: + stat->icp6errs_packet_too_big++; + return; + case ICMP6_TIME_EXCEEDED: + switch(code) { + case ICMP6_TIME_EXCEED_TRANSIT: + stat->icp6errs_time_exceed_transit++; + return; + case ICMP6_TIME_EXCEED_REASSEMBLY: + stat->icp6errs_time_exceed_reassembly++; + return; + } + break; + case ICMP6_PARAM_PROB: + switch(code) { + case ICMP6_PARAMPROB_HEADER: + stat->icp6errs_paramprob_header++; + return; + case ICMP6_PARAMPROB_NEXTHEADER: + stat->icp6errs_paramprob_nextheader++; + return; + case ICMP6_PARAMPROB_OPTION: + stat->icp6errs_paramprob_option++; + return; + } + break; + case ND_REDIRECT: + stat->icp6errs_redirect++; + return; + } + stat->icp6errs_unknown++; +} + /* * Generate an error packet of type error in response to bad IP6 packet. */ @@ -152,15 +218,31 @@ icmp6_error(m, type, code, param) { struct ip6_hdr *oip6, *nip6; struct icmp6_hdr *icmp6; - u_int prep; + u_int preplen; int off; - u_char nxt; + int nxt; icmp6stat.icp6s_error++; - if (m->m_flags & M_DECRYPTED) + /* count per-type-code statistics */ + icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code); + +#ifdef M_DECRYPTED /*not openbsd*/ + if (m->m_flags & M_DECRYPTED) { + icmp6stat.icp6s_canterror++; goto freeit; + } +#endif +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); +#else + if (m->m_len < sizeof(struct ip6_hdr)) { + m = m_pullup(m, sizeof(struct ip6_hdr)); + if (m == NULL) + return; + } +#endif oip6 = mtod(m, struct ip6_hdr *); /* @@ -181,68 +263,41 @@ icmp6_error(m, type, code, param) goto freeit; /* - * If the erroneous packet is also an ICMP error, discard it. + * If we are about to send ICMPv6 against ICMPv6 error/redirect, + * don't do it. */ - IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); - off = sizeof(struct ip6_hdr); - nxt = oip6->ip6_nxt; - while(1) { /* XXX: should avoid inf. loop explicitly? */ - struct ip6_ext *ip6e; + nxt = -1; + off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + if (off >= 0 && nxt == IPPROTO_ICMPV6) { struct icmp6_hdr *icp; - switch(nxt) { - case IPPROTO_IPV6: - case IPPROTO_IPV4: - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ESP: - case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); + icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, + sizeof(*icp)); + if (icp == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + if (icp->icmp6_type < ICMP6_ECHO_REQUEST || + icp->icmp6_type == ND_REDIRECT) { /* - * ICMPv6 error must not be fragmented. - * XXX: but can we trust the sender? + * ICMPv6 error + * Special case: for redirect (which is + * informational) we must not send icmp6 error. */ - default: - /* What if unknown header followed by ICMP error? */ - goto generate; - case IPPROTO_ICMPV6: - IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); - icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); - if (icp->icmp6_type < ICMP6_ECHO_REQUEST - || icp->icmp6_type == ND_REDIRECT) { - /* - * ICMPv6 error - * Special case: for redirect (which is - * informational) we must not send icmp6 error. - */ - icmp6stat.icp6s_canterror++; - goto freeit; - } else { - /* ICMPv6 informational */ - goto generate; - } - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_AH: - IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), ); - ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); - if (nxt == IPPROTO_AH) - off += (ip6e->ip6e_len + 2) << 2; - else - off += (ip6e->ip6e_len + 1) << 3; - nxt = ip6e->ip6e_nxt; - break; + icmp6stat.icp6s_canterror++; + goto freeit; + } else { + /* ICMPv6 informational - send the error */ } + } else { + /* non-ICMPv6 - send the error */ } - freeit: - /* - * If we can't tell wheter or not we can generate ICMP6, free it. - */ - m_freem(m); - return; - - generate: oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ /* Finally, do rate limitation check. */ @@ -258,10 +313,10 @@ icmp6_error(m, type, code, param) if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); - prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); - M_PREPEND(m, prep, M_DONTWAIT); - if (m && m->m_len < prep) - m = m_pullup(m, prep); + preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + M_PREPEND(m, preplen, M_DONTWAIT); + if (m && m->m_len < preplen) + m = m_pullup(m, preplen); if (m == NULL) { printf("ENOBUFS in icmp6_error %d\n", __LINE__); return; @@ -283,6 +338,14 @@ icmp6_error(m, type, code, param) icmp6stat.icp6s_outhist[type]++; icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ + + return; + + freeit: + /* + * If we can't tell wheter or not we can generate ICMP6, free it. + */ + m_freem(m); } /* @@ -301,8 +364,10 @@ icmp6_input(mp, offp, proto) int code, sum, noff; struct sockaddr_in6 icmp6src; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); /* m might change if M_LOOP. So, call mtod after this */ +#endif /* * Locate icmp6 structure in mbuf, and check @@ -318,8 +383,15 @@ icmp6_input(mp, offp, proto) /* * calculate the checksum */ - +#ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif code = icmp6->icmp6_code; if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { @@ -373,12 +445,21 @@ icmp6_input(mp, offp, proto) break; case ICMP6_DST_UNREACH_ADMIN: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); + code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ + break; case ICMP6_DST_UNREACH_ADDR: - code = PRC_UNREACH_HOST; + code = PRC_HOSTDEAD; break; +#ifdef COMPAT_RFC1885 case ICMP6_DST_UNREACH_NOTNEIGHBOR: code = PRC_UNREACH_SRCFAIL; break; +#else + case ICMP6_DST_UNREACH_BEYONDSCOPE: + /* I mean "source address was incorrect." */ + code = PRC_PARAMPROB; + break; +#endif case ICMP6_DST_UNREACH_NOPORT: code = PRC_UNREACH_PORT; break; @@ -392,33 +473,14 @@ icmp6_input(mp, offp, proto) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); if (code != 0) goto badcode; - { - u_int mtu = ntohl(icmp6->icmp6_mtu); - struct rtentry *rt = NULL; - struct sockaddr_in6 sin6; code = PRC_MSGSIZE; - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = PF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; - rt = rtalloc1((struct sockaddr *)&sin6, 0, - RTF_CLONING | RTF_PRCLONING); - if (rt && (rt->rt_flags & RTF_HOST) - && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { - if (mtu < IPV6_MMTU) { - /* xxx */ - rt->rt_rmx.rmx_locks |= RTV_MTU; - } else if (mtu < rt->rt_ifp->if_mtu && - rt->rt_rmx.rmx_mtu > mtu) { - rt->rt_rmx.rmx_mtu = mtu; - } - } - if (rt) - RTFREE(rt); + /* + * Updating the path MTU will be done after examining + * intermediate extension headers. + */ goto deliver; - } break; case ICMP6_TIME_EXCEEDED: @@ -458,23 +520,37 @@ icmp6_input(mp, offp, proto) /* Give up remote */ break; } - if (n->m_flags & M_EXT) { - int gap, move; + if ((n->m_flags & M_EXT) != 0 + || n->m_len < off + sizeof(struct icmp6_hdr)) { struct mbuf *n0 = n; + const int maxlen = sizeof(*nip6) + sizeof(*nicmp6); /* * Prepare an internal mbuf. m_pullup() doesn't * always copy the length we specified. */ + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + m_freem(n0); + break; + } MGETHDR(n, M_DONTWAIT, n0->m_type); + if (n && maxlen >= MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ m_freem(n0); break; } M_COPY_PKTHDR(n, n0); - n0->m_flags &= ~M_PKTHDR; - n->m_next = n0; /* * Copy IPv6 and ICMPv6 only. */ @@ -482,16 +558,17 @@ icmp6_input(mp, offp, proto) bcopy(ip6, nip6, sizeof(struct ip6_hdr)); nicmp6 = (struct icmp6_hdr *)(nip6 + 1); bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); + noff = sizeof(struct ip6_hdr); + n->m_pkthdr.len = n->m_len = + noff + sizeof(struct icmp6_hdr); /* - * Adjust mbuf. ip6_plen will be adjusted. + * Adjust mbuf. ip6_plen will be adjusted in + * ip6_output(). */ - noff = sizeof(struct ip6_hdr); - n->m_len = noff + sizeof(struct icmp6_hdr); - move = off + sizeof(struct icmp6_hdr); - n0->m_len -= move; - n0->m_data += move; - gap = off - noff; - n->m_pkthdr.len -= gap; + m_adj(n0, off + sizeof(struct icmp6_hdr)); + n->m_pkthdr.len += n0->m_pkthdr.len; + n->m_next = n0; + n0->m_flags &= ~M_PKTHDR; } else { nip6 = mtod(n, struct ip6_hdr *); nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); @@ -520,8 +597,13 @@ icmp6_input(mp, offp, proto) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); else icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - mld6_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + mld6_input(m, off); + m = NULL; + goto freeit; + } + mld6_input(n, off); /* m stays. */ break; @@ -535,35 +617,63 @@ icmp6_input(mp, offp, proto) case MLD6_MTRACE: /* XXX: these two are experimental. not officially defind. */ /* XXX: per-interface statistics? */ - break; /* just pass it to the userland daemon */ + break; /* just pass it to applications */ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ { enum { WRU, FQDN } mode; - if (code != 0) - goto badcode; + if (!icmp6_nodeinfo) + break; + if (icmp6len == sizeof(struct icmp6_hdr) + 4) mode = WRU; - else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */ + else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) mode = FQDN; else goto badlen; #define hostnamelen strlen(hostname) if (mode == FQDN) { +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), IPPROTO_DONE); - n = ni6_input(m, off); +#endif + n = m_copy(m, 0, M_COPYALL); + if (n) + n = ni6_input(n, off); + /* XXX meaningless if n == NULL */ noff = sizeof(struct ip6_hdr); } else { u_char *p; - + int maxlen, maxhlen; + + if (code != 0) + goto badcode; + maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + break; + } MGETHDR(n, M_DONTWAIT, m->m_type); + if (n && maxlen > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ break; } + n->m_len = 0; + maxhlen = M_TRAILINGSPACE(n) - maxlen; + if (maxhlen > hostnamelen) + maxhlen = hostnamelen; /* * Copy IPv6 and ICMPv6 only. */ @@ -573,11 +683,11 @@ icmp6_input(mp, offp, proto) bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); p = (u_char *)(nicmp6 + 1); bzero(p, 4); - bcopy(hostname, p + 4, hostnamelen); + bcopy(hostname, p + 4, maxhlen); /*meaningless TTL*/ noff = sizeof(struct ip6_hdr); M_COPY_PKTHDR(n, m); /* just for recvif */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) + 4 + hostnamelen; + sizeof(struct icmp6_hdr) + 4 + maxhlen; nicmp6->icmp6_type = ICMP6_WRUREPLY; nicmp6->icmp6_code = 0; } @@ -601,8 +711,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_router_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_rs_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_rs_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_rs_input(n, off, icmp6len); /* m stays. */ break; @@ -612,8 +727,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_router_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ra_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ra_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ra_input(n, off, icmp6len); /* m stays. */ break; @@ -623,8 +743,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ns_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ns_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ns_input(n, off, icmp6len); /* m stays. */ break; @@ -634,8 +759,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_na_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_na_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_na_input(n, off, icmp6len); /* m stays. */ break; @@ -645,7 +775,13 @@ icmp6_input(mp, offp, proto) goto badcode; if (icmp6len < sizeof(struct nd_redirect)) goto badlen; - icmp6_redirect_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + icmp6_redirect_input(m, off); + m = NULL; + goto freeit; + } + icmp6_redirect_input(n, off); /* m stays. */ break; @@ -675,10 +811,19 @@ icmp6_input(mp, offp, proto) icmp6stat.icp6s_tooshort++; goto freeit; } +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), IPPROTO_DONE); icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif bzero(&icmp6src, sizeof(icmp6src)); icmp6src.sin6_len = sizeof(struct sockaddr_in6); icmp6src.sin6_family = AF_INET6; @@ -692,38 +837,153 @@ icmp6_input(mp, offp, proto) int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); struct ip6ctlparam ip6cp; + struct in6_addr *finaldst = NULL; + int icmp6type = icmp6->icmp6_type; + struct ip6_frag *fh; + struct ip6_rthdr *rth; + struct ip6_rthdr0 *rth0; + int rthlen; while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; switch(nxt) { - case IPPROTO_ESP: - case IPPROTO_NONE: - goto passit; case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: case IPPROTO_AH: - case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), IPPROTO_DONE); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); +#else + IP6_EXTHDR_GET(eh, struct ip6_ext *, m, + eoff, sizeof(*eh)); + if (eh == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (nxt == IPPROTO_AH) eoff += (eh->ip6e_len + 2) << 2; - else if (nxt == IPPROTO_FRAGMENT) - eoff += sizeof(struct ip6_frag); else eoff += (eh->ip6e_len + 1) << 3; nxt = eh->ip6e_nxt; break; + case IPPROTO_ROUTING: + /* + * When the erroneous packet contains a + * routing header, we should examine the + * header to determine the final destination. + * Otherwise, we can't properly update + * information that depends on the final + * destination (e.g. path MTU). + */ +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), + IPPROTO_DONE); + rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, + eoff, sizeof(*rth)); + if (rth == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + rthlen = (rth->ip6r_len + 1) << 3; + /* + * XXX: currently there is no + * officially defined type other + * than type-0. + * Note that if the segment left field + * is 0, all intermediate hops must + * have been passed. + */ + if (rth->ip6r_segleft && + rth->ip6r_type == IPV6_RTHDR_TYPE_0) { + int hops; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, + IPPROTO_DONE); + rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); +#else + IP6_EXTHDR_GET(rth0, + struct ip6_rthdr0 *, m, + eoff, rthlen); + if (rth0 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* just ignore a bogus header */ + if ((rth0->ip6r0_len % 2) == 0 && + (hops = rth0->ip6r0_len/2)) + finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); + } + eoff += rthlen; + nxt = rth->ip6r_nxt; + break; + case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + + sizeof(struct ip6_frag), + IPPROTO_DONE); + fh = (struct ip6_frag *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(fh, struct ip6_frag *, m, + eoff, sizeof(*fh)); + if (fh == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* + * Data after a fragment header is meaningless + * unless it is the first fragment, but + * we'll go to the notify label for path MTU + * discovery. + */ + if (fh->ip6f_offlg & IP6F_OFF_MASK) + goto notify; + + eoff += sizeof(struct ip6_frag); + nxt = fh->ip6f_nxt; + break; default: + /* + * This case includes ESP and the No Next + * Header. In such cases going to the notify + * label does not have any meaning + * (i.e. ctlfunc will be NULL), but we go + * anyway since we might have to update + * path MTU information. + */ goto notify; } } notify: +#ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (icmp6type == ICMP6_PACKET_TOO_BIG) { + if (finaldst == NULL) + finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + icmp6_mtudisc_update(finaldst, icmp6, m); + } + ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { @@ -744,8 +1004,11 @@ icmp6_input(mp, offp, proto) break; } - passit: +#ifdef HAVE_NRL_INPCB + rip6_input(&m, offp, IPPROTO_ICMPV6); +#else icmp6_rip6_input(&m, *offp); +#endif return IPPROTO_DONE; freeit: @@ -753,72 +1016,277 @@ icmp6_input(mp, offp, proto) return IPPROTO_DONE; } +static void +icmp6_mtudisc_update(dst, icmp6, m) + struct in6_addr *dst; + struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */ + struct mbuf *m; /* currently unused but added for scoped addrs */ +{ + u_int mtu = ntohl(icmp6->icmp6_mtu); + struct rtentry *rt = NULL; + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = PF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *dst; + /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ + rt = rtalloc1((struct sockaddr *)&sin6, 0, + RTF_CLONING | RTF_PRCLONING); + + if (rt && (rt->rt_flags & RTF_HOST) + && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { + if (mtu < IPV6_MMTU) { + /* xxx */ + rt->rt_rmx.rmx_locks |= RTV_MTU; + } else if (mtu < rt->rt_ifp->if_mtu && + rt->rt_rmx.rmx_mtu > mtu) { + rt->rt_rmx.rmx_mtu = mtu; + } + } + if (rt) + RTFREE(rt); +} + /* - * Process a Node Information Query + * Process a Node Information Query packet, (roughly) based on + * draft-ietf-ipngwg-icmp-name-lookups-05. + * + * Spec incompatibilities: + * - IPv6 Subject address handling + * - IPv4 Subject address handling support missing + * - Proxy reply (answer even if it's not for me) + * - "Supported Qtypes" support missing + * - joins NI group address at in6_ifattach() time only, does not cope + * with hostname changes by sethostname(3) */ -#define hostnamelen strlen(hostname) +#define hostnamelen strlen(hostname) #ifndef offsetof /* XXX */ #define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif - static struct mbuf * ni6_input(m, off) struct mbuf *m; int off; { - struct icmp6_nodeinfo *ni6 = - (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6; + struct icmp6_nodeinfo *ni6, *nni6; struct mbuf *n = NULL; - u_int16_t qtype = ntohs(ni6->ni_qtype); + u_int16_t qtype; + int subjlen; int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); struct ni_reply_fqdn *fqdn; int addrs; /* for NI_QTYPE_NODEADDR */ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ + struct sockaddr_in6 sin6; + struct ip6_hdr *ip6; + int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ + char *subj; + + ip6 = mtod(m, struct ip6_hdr *); +#ifndef PULLDOWN_TEST + ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); + if (ni6 == NULL) { + /* m is already reclaimed */ + return NULL; + } +#endif + + /* + * Validate IPv6 destination address. + * + * We accept packets with the following IPv6 destination address: + * - Responder's unicast/anycast address, + * - link-local multicast address + * This is a violation to last paragraph in icmp-name-lookups-05 + * page 4, which restricts IPv6 destination address of a query to: + * - Responder's unicast/anycast address, + * - NI group address for a name belongs to the Responder, or + * - NI group address for a name for which the Responder is providing + * proxy service. + * (note: NI group address is a link-local multicast address) + * + * We allow any link-local multicast address, since "ping6 -w ff02::1" + * has been really useful for us debugging our network. Also this is + * still questionable if the restriction in spec buy us security at all, + * since RFC2463 permits echo packet to multicast destination. + * Even if we forbid NI query to ff02::1, we can effectively get the + * same result as "ping6 -w ff02::1" by the following steps: + * - run "ping6 ff02::1", then + * - run "ping6 -w" for all addresses replied. + */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + /* XXX scopeid */ + if (ifa_ifwithaddr((struct sockaddr *)&sin6)) + ; /*unicast/anycast, fine*/ + else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) + ; /*violates spec slightly, see above*/ + else + goto bad; + + /* guess reply length */ + qtype = ntohs(ni6->ni_qtype); + switch (qtype) { + case NI_QTYPE_NOOP: + break; /* no reply data */ + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + /* XXX will append a mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + break; + case NI_QTYPE_NODEADDR: + addrs = ni6_addrs(ni6, m, &ifp); + if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) + replylen = MCLBYTES; /* XXX: we'll truncate later */ + break; + default: + /* + * XXX: We must return a reply with the ICMP6 code + * `unknown Qtype' in this case. However we regard the case + * as an FQDN query for backward compatibility. + * Older versions set a random value to this field, + * so it rarely varies in the defined qtypes. + * But the mechanism is not reliable... + * maybe we should obsolete older versions. + */ + qtype = NI_QTYPE_FQDN; + /* XXX will append a mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + oldfqdn++; + break; + } + + /* validate query Subject field. */ + subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); + switch (qtype) { + case NI_QTYPE_NOOP: + case NI_QTYPE_SUPTYPES: + if (subjlen != 0) + goto bad; + break; + + case NI_QTYPE_FQDN: + case NI_QTYPE_NODEADDR: + switch (ni6->ni_code) { + case ICMP6_NI_SUBJ_IPV6: +#if ICMP6_NI_SUBJ_IPV6 != 0 + case 0: +#endif + /* + * backward compatibility - try to accept 03 draft + * format, where no Subject is present. + */ + if (subjlen == 0) { + oldfqdn++; + break; + } + + if (subjlen != sizeof(sin6.sin6_addr)) + goto bad; - switch(qtype) { - case NI_QTYPE_NOOP: - break; /* no reply data */ - case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ - break; - case NI_QTYPE_FQDN: - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + - hostnamelen; - break; - case NI_QTYPE_NODEADDR: - addrs = ni6_addrs(ni6, m, &ifp); - if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) - replylen = MCLBYTES; /* XXX: we'll truncate later */ - - break; - default: - /* - * XXX: We must return a reply with the ICMP6 code - * `unknown Qtype' in this case. However we regard the case - * as an FQDN query for backward compatibility. - * Older versions set a random value to this field, - * so it rarely varies in the defined qtypes. - * But the mechanism is not reliable... - * maybe we should obsolete older versions. - */ - qtype = NI_QTYPE_FQDN; - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + - hostnamelen; - break; + /* + * Validate Subject address. + * + * Not sure what exactly does "address belongs to the + * node" mean in the spec, is it just unicast, or what? + * + * At this moment we consider Subject address as + * "belong to the node" if the Subject address equals + * to the IPv6 destination address; validation for + * IPv6 destination address should have done enough + * check for us. + * + * We do not do proxy at this moment. + */ + /* m_pulldown instead of copy? */ + m_copydata(m, off + sizeof(struct icmp6_nodeinfo), + subjlen, (caddr_t)&sin6.sin6_addr); + /* XXX kame scope hack */ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { +#ifdef FAKE_LOOPBACK_IF + if ((m->m_flags & M_PKTHDR) != 0 && + m->m_pkthdr.rcvif) { + sin6.sin6_addr.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + } +#else + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + sin6.sin6_addr.s6_addr16[1] = + ip6->ip6_dst.s6_addr16[1]; + } +#endif + } + if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr)) + break; + /* + * XXX if we are to allow other cases, we should really + * be careful about scope here. + * basically, we should disallow queries toward IPv6 + * destination X with subject Y, if scope(X) > scope(Y). + * if we allow scope(X) > scope(Y), it will result in + * information leakage across scope boundary. + */ + goto bad; + + case ICMP6_NI_SUBJ_FQDN: + /* + * Validate Subject name with gethostname(3). + * + * The behavior may need some debate, since: + * - we are not sure if the node has FQDN as + * hostname (returned by gethostname(3)). + * - the code does wildcard match for truncated names. + * however, we are not sure if we want to perform + * wildcard match, if gethostname(3) side has + * truncated hostname. + */ + n = ni6_nametodns(hostname, hostnamelen, 0); + if (!n || n->m_next || n->m_len == 0) + goto bad; + IP6_EXTHDR_GET(subj, char *, m, + off + sizeof(struct icmp6_nodeinfo), subjlen); + if (subj == NULL) + goto bad; + if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *), + n->m_len)) { + goto bad; + } + m_freem(n); + n = NULL; + break; + + case ICMP6_NI_SUBJ_IPV4: /* xxx: to be implemented? */ + default: + goto bad; + } + break; + + default: + /* should never be here due to "switch (qtype)" above */ + goto bad; } /* allocate a mbuf to reply. */ MGETHDR(n, M_DONTWAIT, m->m_type); - if (n == NULL) + if (n == NULL) { + m_freem(m); return(NULL); + } M_COPY_PKTHDR(n, m); /* just for recvif */ if (replylen > MHLEN) { - if (replylen > MCLBYTES) + if (replylen > MCLBYTES) { /* * XXX: should we try to allocate more? But MCLBYTES is * probably much larger than IPV6_MMTU... */ goto bad; + } MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { goto bad; @@ -829,56 +1297,60 @@ ni6_input(m, off) /* copy mbuf header and IPv6 + Node Information base headers */ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); - bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); + bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); /* qtype dependent procedure */ switch (qtype) { - case NI_QTYPE_NOOP: - nni6->ni_flags = 0; - break; - case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ - break; - case NI_QTYPE_FQDN: - if (hostnamelen > 255) { /* XXX: rare case, but may happen */ - printf("ni6_input: " - "hostname length(%d) is too large for reply\n", - hostnamelen); - goto bad; - } - fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + - sizeof(struct ip6_hdr) + - sizeof(struct icmp6_nodeinfo)); - nni6->ni_flags = 0; /* XXX: meaningless TTL */ - fqdn->ni_fqdn_ttl = 0; /* ditto. */ - fqdn->ni_fqdn_namelen = hostnamelen; - bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen); - break; - case NI_QTYPE_NODEADDR: - { - int lenlim, copied; - - if (n->m_flags & M_EXT) - lenlim = MCLBYTES - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); - else - lenlim = MHLEN - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); - copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); - /* XXX: reset mbuf length */ - n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_nodeinfo) + copied; - break; - } - default: - break; /* XXX impossible! */ + case NI_QTYPE_NOOP: + nni6->ni_flags = 0; + break; + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + + sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo)); + nni6->ni_flags = 0; /* XXX: meaningless TTL */ + fqdn->ni_fqdn_ttl = 0; /* ditto. */ + /* + * XXX do we really have FQDN in variable "hostname"? + */ + n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn); + if (n->m_next == NULL) + goto bad; + /* XXX we assume that n->m_next is not a chain */ + if (n->m_next->m_next != NULL) + goto bad; + n->m_pkthdr.len += n->m_next->m_len; + break; + case NI_QTYPE_NODEADDR: + { + int lenlim, copied; + + if (n->m_flags & M_EXT) + lenlim = MCLBYTES - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + else + lenlim = MHLEN - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); + /* XXX: reset mbuf length */ + n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo) + copied; + break; + } + default: + break; /* XXX impossible! */ } nni6->ni_type = ICMP6_NI_REPLY; nni6->ni_code = ICMP6_NI_SUCESS; + m_freem(m); return(n); bad: + m_freem(m); if (n) m_freem(n); return(NULL); @@ -886,6 +1358,168 @@ ni6_input(m, off) #undef hostnamelen /* + * make a mbuf with DNS-encoded string. no compression support. + * + * XXX names with less than 2 dots (like "foo" or "foo.section") will be + * treated as truncated name (two \0 at the end). this is a wild guess. + */ +static struct mbuf * +ni6_nametodns(name, namelen, old) + const char *name; + int namelen; + int old; /* return pascal string if non-zero */ +{ + struct mbuf *m; + char *cp, *ep; + const char *p, *q; + int i, len, nterm; + + if (old) + len = namelen + 1; + else + len = MCLBYTES; + + /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */ + MGET(m, M_DONTWAIT, MT_DATA); + if (m && len > MLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + goto fail; + } + if (!m) + goto fail; + m->m_next = NULL; + + if (old) { + m->m_len = len; + *mtod(m, char *) = namelen; + bcopy(name, mtod(m, char *) + 1, namelen); + return m; + } else { + m->m_len = 0; + cp = mtod(m, char *); + ep = mtod(m, char *) + M_TRAILINGSPACE(m); + + /* if not certain about my name, return empty buffer */ + if (namelen == 0) + return m; + + /* + * guess if it looks like shortened hostname, or FQDN. + * shortened hostname needs two trailing "\0". + */ + i = 0; + for (p = name; p < name + namelen; p++) { + if (*p && *p == '.') + i++; + } + if (i < 2) + nterm = 2; + else + nterm = 1; + + p = name; + while (cp < ep && p < name + namelen) { + i = 0; + for (q = p; q < name + namelen && *q && *q != '.'; q++) + i++; + /* result does not fit into mbuf */ + if (cp + i + 1 >= ep) + goto fail; + /* DNS label length restriction, RFC1035 page 8 */ + if (i >= 64) + goto fail; + *cp++ = i; + bcopy(p, cp, i); + cp += i; + p = q; + if (p < name + namelen && *p == '.') + p++; + } + /* termination */ + if (cp + nterm >= ep) + goto fail; + while (nterm-- > 0) + *cp++ = '\0'; + m->m_len = cp - mtod(m, char *); + return m; + } + + panic("should not reach here"); + /*NOTREACHED*/ + + fail: + if (m) + m_freem(m); + return NULL; +} + +/* + * check if two DNS-encoded string matches. takes care of truncated + * form (with \0\0 at the end). no compression support. + */ +static int +ni6_dnsmatch(a, alen, b, blen) + const char *a; + int alen; + const char *b; + int blen; +{ + const char *a0, *b0; + int l; + + /* simplest case - need validation? */ + if (alen == blen && bcmp(a, b, alen) == 0) + return 1; + + a0 = a; + b0 = b; + + /* termination is mandatory */ + if (alen < 2 || blen < 2) + return 0; + if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0') + return 0; + alen--; + blen--; + + while (a - a0 < alen && b - b0 < blen) { + if (a - a0 + 1 > alen || b - b0 + 1 > blen) + return 0; + + if ((signed char)a[0] < 0 || (signed char)b[0] < 0) + return 0; + /* we don't support compression yet */ + if (a[0] >= 64 || b[0] >= 64) + return 0; + + /* truncated case */ + if (a[0] == 0 && a - a0 == alen - 1) + return 1; + if (b[0] == 0 && b - b0 == blen - 1) + return 1; + if (a[0] == 0 || b[0] == 0) + return 0; + + if (a[0] != b[0]) + return 0; + l = a[0]; + if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen) + return 0; + if (bcmp(a + 1, b + 1, l) != 0) + return 0; + + a += 1 + l; + b += 1 + l; + } + + if (a - a0 == alen && b - b0 == blen) + return 1; + else + return 0; +} + +/* * calculate the number of addresses to be returned in the node info reply. */ static int @@ -903,7 +1537,8 @@ ni6_addrs(ni6, m, ifpp) for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { addrsofif = 0; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -914,12 +1549,18 @@ ni6_addrs(ni6, m, ifpp) &ifa6->ia_addr.sin6_addr)) iffound = 1; - if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { - if ((ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - != 0) - addrsofif++; + /* + * IPv4-mapped addresses can only be returned by a + * Node Information proxy, since they represent + * addresses of IPv4-only nodes, which perforce do + * not implement this protocol. + * [icmp-name-lookups-05] + * So we don't support NI_NODEADDR_FLAG_COMPAT in + * this function at this moment. + */ + + if (ifa6->ia6_flags & IN6_IFF_ANYCAST) continue; /* we need only unicast addresses */ - } if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | NI_NODEADDR_FLAG_SITELOCAL | @@ -972,7 +1613,8 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) { - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { docopy = 0; @@ -986,9 +1628,12 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) docopy = 1; else continue; - } else { /* unicast address */ + } + else { /* unicast address */ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) continue; + else + docopy = 1; } /* What do we have to do about ::1? */ @@ -1052,26 +1697,26 @@ icmp6_rip6_input(mp, off) struct icmp6_hdr *icmp6; struct mbuf *opts = NULL; +#ifndef PULLDOWN_TEST /* this is assumed to be safe. */ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + /* m is already reclaimed */ + return IPPROTO_DONE; + } +#endif bzero(&rip6src, sizeof(rip6src)); rip6src.sin6_len = sizeof(struct sockaddr_in6); rip6src.sin6_family = AF_INET6; - rip6src.sin6_addr = ip6->ip6_src; - if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) - rip6src.sin6_addr.s6_addr16[1] = 0; - if (m->m_pkthdr.rcvif) { - if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) - rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index; - else - rip6src.sin6_scope_id = 0; - } else - rip6src.sin6_scope_id = 0; + /* KAME hack: recover scopeid */ + (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif); LIST_FOREACH(in6p, &ripcb, inp_list) { - if ((in6p->inp_vflag & INP_IPV6) == 0) + if ((in6p->inp_vflag & INP_IPV6) == NULL) continue; if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) continue; @@ -1127,22 +1772,18 @@ icmp6_rip6_input(mp, off) /* * Reflect the ip6 packet back to the source. - * The caller MUST check if the destination is multicast or not. - * This function is usually called with a unicast destination which - * can be safely the source of the reply packet. But some exceptions - * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type). - * ``off'' points to the icmp6 header, counted from the top of the mbuf. + * OFF points to the icmp6 header, counted from the top of the mbuf. */ void icmp6_reflect(m, off) struct mbuf *m; size_t off; { - struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct ip6_hdr *ip6; struct icmp6_hdr *icmp6; struct in6_ifaddr *ia; struct in6_addr t, *src = 0; - int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); + int plen; int type, code; struct ifnet *outif = NULL; #ifdef COMPAT_RFC1885 @@ -1150,41 +1791,46 @@ icmp6_reflect(m, off) struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; #endif + /* too short to reflect */ + if (off < sizeof(struct ip6_hdr)) { + printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", + (u_long)off, (u_long)sizeof(struct ip6_hdr), + __FILE__, __LINE__); + goto bad; + } + /* * If there are extra headers between IPv6 and ICMPv6, strip * off that header first. */ - if (off != sizeof(struct ip6_hdr)) { - size_t siz; - - /* sanity checks */ - if (off < sizeof(struct ip6_hdr)) { - printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n", - (unsigned int)off, - (unsigned int)sizeof(struct ip6_hdr), - __FILE__, __LINE__); - goto bad; +#ifdef DIAGNOSTIC + if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) + panic("assumption failed in icmp6_reflect"); +#endif + if (off > sizeof(struct ip6_hdr)) { + size_t l; + struct ip6_hdr nip6; + + l = off - sizeof(struct ip6_hdr); + m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6); + m_adj(m, l); + l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + if (m->m_len < l) { + if ((m = m_pullup(m, l)) == NULL) + return; } - siz = off - sizeof(struct ip6_hdr); - if (plen < siz) { - printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n", - (unsigned int)siz, plen, __FILE__, __LINE__); - goto bad; + bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6)); + } else /* off == sizeof(struct ip6_hdr) */ { + size_t l; + l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + if (m->m_len < l) { + if ((m = m_pullup(m, l)) == NULL) + return; } - IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/); - IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/); - - ovbcopy((caddr_t)ip6, - (caddr_t)(mtod(m, u_char *) + siz), - sizeof(struct ip6_hdr)); - m->m_data += siz; - m->m_len -= siz; - m->m_pkthdr.len -= siz; - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_nxt = IPPROTO_ICMPV6; - plen -= siz; } - + plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_nxt = IPPROTO_ICMPV6; icmp6 = (struct icmp6_hdr *)(ip6 + 1); type = icmp6->icmp6_type; /* keep type for statistics */ code = icmp6->icmp6_code; /* ditto. */ @@ -1240,10 +1886,13 @@ icmp6_reflect(m, off) /* * If the incoming packet was addressed directly to us(i.e. unicast), * use dst as the src for the reply. + * The IN6_IFF_NOTREADY case would be VERY rare, but is possible + * (for example) when we encounter an error while forwarding procedure + * destined to a duplicated address of ours. */ for (ia = in6_ifaddr; ia; ia = ia->ia_next) if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && - (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) { + (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { src = &t; break; } @@ -1257,9 +1906,11 @@ icmp6_reflect(m, off) if (src == 0) /* - * We have not multicast routing yet. So this case matches - * to our multicast, our anycast or not to our unicast. - * Select a source address which has the same scope. + * This case matches to multicasts, our anycast, or unicasts + * that we do not own. Select a source address which has the + * same scope. + * XXX: for (non link-local) multicast addresses, this might + * not be a good choice. */ if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) src = &IA6_SIN6(ia)->sin6_addr; @@ -1270,7 +1921,8 @@ icmp6_reflect(m, off) ip6->ip6_src = *src; ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; if (m->m_pkthdr.rcvif) { /* XXX: This may not be the outgoing interface */ @@ -1286,6 +1938,10 @@ icmp6_reflect(m, off) */ m->m_flags &= ~(M_BCAST|M_MCAST); +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif /*IPSEC*/ #ifdef COMPAT_RFC1885 ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); @@ -1305,7 +1961,11 @@ icmp6_reflect(m, off) void icmp6_fasttimo() { + mld6_fasttimeo(); + + /* reset ICMPv6 pps limit */ + icmp6errpps_count = 0; } static const char * @@ -1327,7 +1987,7 @@ icmp6_redirect_input(m, off) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); + struct nd_redirect *nd_rd; int icmp6len = ntohs(ip6->ip6_plen); char *lladdr = NULL; int lladdrlen = 0; @@ -1337,8 +1997,8 @@ icmp6_redirect_input(m, off) int is_router; int is_onlink; struct in6_addr src6 = ip6->ip6_src; - struct in6_addr redtgt6 = nd_rd->nd_rd_target; - struct in6_addr reddst6 = nd_rd->nd_rd_dst; + struct in6_addr redtgt6; + struct in6_addr reddst6; union nd_opts ndopts; if (!m || !ifp) @@ -1346,9 +2006,22 @@ icmp6_redirect_input(m, off) /* XXX if we are router, we don't update route by icmp6 redirect */ if (ip6_forwarding) - return; + goto freeit; if (!icmp6_rediraccept) + goto freeit; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); + if (nd_rd == NULL) { + icmp6stat.icp6s_tooshort++; return; + } +#endif + redtgt6 = nd_rd->nd_rd_target; + reddst6 = nd_rd->nd_rd_dst; if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) redtgt6.s6_addr16[1] = htons(ifp->if_index); @@ -1360,14 +2033,14 @@ icmp6_redirect_input(m, off) log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "must be from linklocal\n", ip6_sprintf(&src6)); - return; + goto freeit; } if (ip6->ip6_hlim != 255) { log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", ip6_sprintf(&src6), ip6->ip6_hlim); - return; + goto freeit; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ @@ -1389,14 +2062,14 @@ icmp6_redirect_input(m, off) ip6_sprintf(gw6), icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); RTFREE(rt); - return; + goto freeit; } } else { log(LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } RTFREE(rt); rt = NULL; @@ -1406,7 +2079,7 @@ icmp6_redirect_input(m, off) "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } is_router = is_onlink = 0; @@ -1419,7 +2092,7 @@ icmp6_redirect_input(m, off) "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } /* validation passed */ @@ -1429,7 +2102,7 @@ icmp6_redirect_input(m, off) log(LOG_INFO, "icmp6_redirect_input: " "invalid ND option, rejected: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { @@ -1487,6 +2160,9 @@ icmp6_redirect_input(m, off) key_sa_routechange((struct sockaddr *)&sdst); #endif } + + freeit: + m_freem(m); } void @@ -1504,6 +2180,9 @@ icmp6_redirect_output(m0, rt) size_t maxlen; u_char *p; struct ifnet *outif = NULL; + struct sockaddr_in6 src_sa; + + icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); /* if we are not router, we don't send icmp6 redirect */ if (!ip6_forwarding || ip6_accept_rtadv) @@ -1520,7 +2199,13 @@ icmp6_redirect_output(m0, rt) * [RFC 2461, sec 8.2] */ sip6 = mtod(m0, struct ip6_hdr *); - if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0) + bzero(&src_sa, sizeof(src_sa)); + src_sa.sin6_family = AF_INET6; + src_sa.sin6_len = sizeof(src_sa); + src_sa.sin6_addr = sip6->ip6_src; + /* we don't currently use sin6_scope_id, but eventually use it */ + src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src); + if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) goto fail; if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) goto fail; /* what should we do here? */ @@ -1534,21 +2219,28 @@ icmp6_redirect_output(m0, rt) * we almost always ask for an mbuf cluster for simplicity. * (MHLEN < IPV6_MMTU is almost always true) */ +#if IPV6_MMTU >= MCLBYTES +# error assumption failed about IPV6_MMTU and MCLBYTES +#endif MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m && IPV6_MMTU >= MHLEN) + MCLGET(m, M_DONTWAIT); if (!m) goto fail; - if (MHLEN < IPV6_MMTU) - MCLGET(m, M_DONTWAIT); maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ - if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + + ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { goto fail; + } { /* get ip6 linklocal address for ifp(my outgoing interface). */ - struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp); - if (ia == NULL) + struct in6_ifaddr *ia; + if ((ia = in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY| + IN6_IFF_ANYCAST)) == NULL) goto fail; ifp_ll6 = &ia->ia_addr.sin6_addr; } @@ -1566,7 +2258,8 @@ icmp6_redirect_output(m0, rt) /* ip6 */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; @@ -1614,18 +2307,22 @@ icmp6_redirect_output(m0, rt) rt_router = nd6_lookup(router_ll6, 0, ifp); if (!rt_router) goto nolladdropt; - if (!(rt_router->rt_flags & RTF_GATEWAY) - && (rt_router->rt_flags & RTF_LLINFO) - && (rt_router->rt_gateway->sa_family == AF_LINK) - && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) { + len = sizeof(*nd_opt) + ifp->if_addrlen; + len = (len + 7) & ~7; /*round by 8*/ + /* safety check */ + if (len + (p - (u_char *)ip6) > maxlen) + goto nolladdropt; + if (!(rt_router->rt_flags & RTF_GATEWAY) && + (rt_router->rt_flags & RTF_LLINFO) && + (rt_router->rt_gateway->sa_family == AF_LINK) && + (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) && + sdl->sdl_alen) { nd_opt = (struct nd_opt_hdr *)p; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - len = 2 + ifp->if_addrlen; - len = (len + 7) & ~7; /*round by 8*/ nd_opt->nd_opt_len = len >> 3; - p += len; lladdr = (char *)(nd_opt + 1); bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); + p += len; } } nolladdropt:; @@ -1633,8 +2330,12 @@ nolladdropt:; m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* just to be safe */ +#ifdef M_DECRYPTED /*not openbsd*/ if (m0->m_flags & M_DECRYPTED) goto noredhdropt; +#endif + if (p - (u_char *)ip6 > maxlen) + goto noredhdropt; { /* redirected header option */ @@ -1711,6 +2412,12 @@ noredhdropt:; sip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) sip6->ip6_dst.s6_addr16[1] = 0; +#if 0 + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; +#endif if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) nd_rd->nd_rd_target.s6_addr16[1] = 0; if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) @@ -1723,6 +2430,10 @@ noredhdropt:; = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); /* send the packet to outside... */ +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif /*IPSEC*/ ip6_output(m, NULL, NULL, 0, NULL, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); @@ -1741,6 +2452,9 @@ fail: /* * ICMPv6 socket option processing. + * + * NOTE: for OSes that use NRL inpcb (bsdi4/openbsd), do not forget to modify + * sys/netinet6/raw_ipv6.c:rip6_ctloutput(). */ int icmp6_ctloutput(so, sopt) @@ -1812,12 +2526,58 @@ icmp6_ctloutput(so, sopt) return(error); } +#ifndef HAVE_RATECHECK +/* + * ratecheck() returns true if it is okay to send. We return + * true if it is not okay to send. + */ +static int +ratecheck(last, limit) + struct timeval *last; + struct timeval *limit; +{ + struct timeval tp; + struct timeval nextsend; + + microtime(&tp); + tp.tv_sec = time_second; + + /* rate limit */ + if (last->tv_sec != 0 || last->tv_usec != 0) { + nextsend.tv_sec = last->tv_sec + limit->tv_sec; + nextsend.tv_usec = last->tv_usec + limit->tv_usec; + nextsend.tv_sec += (nextsend.tv_usec / 1000000); + nextsend.tv_usec %= 1000000; + + if (nextsend.tv_sec == tp.tv_sec && nextsend.tv_usec <= tp.tv_usec) + ; + else if (nextsend.tv_sec <= tp.tv_sec) + ; + else { + /* The packet is subject to rate limit */ + return 0; + } + } + + *last = tp; + return 1; +} +#endif + /* * Perform rate limit check. * Returns 0 if it is okay to send the icmp6 packet. * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate * limitation. * + * There are two limitations defined: + * - pps limit: ICMPv6 error packet cannot exceed defined packet-per-second. + * we measure it every 0.2 second, since fasttimo works every 0.2 second. + * - rate limit: ICMPv6 error packet cannot appear more than once per + * defined interval. + * In any case, if we perform rate limitation, we'll see jitter in the ICMPv6 + * error packets. + * * XXX per-destination/type check necessary? */ static int @@ -1826,29 +2586,21 @@ icmp6_ratelimit(dst, type, code) const int type; /* not used at this moment */ const int code; /* not used at this moment */ { - struct timeval tp; - long sec_diff, usec_diff; + int ret; - /* If we are not doing rate limitation, it is always okay to send */ - if (!icmp6errratelim) - return 0; + ret = 0; /*okay to send*/ - microtime(&tp); - tp.tv_sec = time_second; - if (tp.tv_sec < icmp6_nextsend.tv_sec - || (tp.tv_sec == icmp6_nextsend.tv_sec - && tp.tv_usec < icmp6_nextsend.tv_usec)) { - /* The packet is subject to rate limit */ - return 1; + /* PPS limit */ + icmp6errpps_count++; + if (icmp6errppslim && icmp6errpps_count > icmp6errppslim / 5) { + /* The packet is subject to pps limit */ + ret++; } - sec_diff = icmp6errratelim / 1000000; - usec_diff = icmp6errratelim % 1000000; - icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff; - if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) { - icmp6_nextsend.tv_sec++; - icmp6_nextsend.tv_usec -= 1000000; + + if (!ratecheck(&icmp6errratelim_last, &icmp6errratelim)) { + /* The packet is subject to rate limit */ + ret++; } - /* it is okay to send this */ - return 0; + return ret; } diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h index 495297f..a6414ef 100644 --- a/sys/netinet6/icmp6.h +++ b/sys/netinet6/icmp6.h @@ -1,615 +1,4 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 - */ - -#ifndef _NETINET6_ICMPV6_H_ -#define _NETINET6_ICMPV6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_ICMP6_H_INCLUDED_) -#error "do not include netinet6/icmp6.h directly, include netinet/icmp6.h" -#endif - -#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_hdr) */ - -struct icmp6_hdr { - u_int8_t icmp6_type; /* type field */ - u_int8_t icmp6_code; /* code field */ - u_int16_t icmp6_cksum; /* checksum field */ - union { - u_int32_t icmp6_un_data32[1]; /* type-specific field */ - u_int16_t icmp6_un_data16[2]; /* type-specific field */ - u_int8_t icmp6_un_data8[4]; /* type-specific field */ - } icmp6_dataun; -}; - -#define icmp6_data32 icmp6_dataun.icmp6_un_data32 -#define icmp6_data16 icmp6_dataun.icmp6_un_data16 -#define icmp6_data8 icmp6_dataun.icmp6_un_data8 -#define icmp6_pptr icmp6_data32[0] /* parameter prob */ -#define icmp6_mtu icmp6_data32[0] /* packet too big */ -#define icmp6_id icmp6_data16[0] /* echo request/reply */ -#define icmp6_seq icmp6_data16[1] /* echo request/reply */ -#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ - -#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ -#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ -#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ -#define ICMP6_PARAM_PROB 4 /* ip6 header bad */ - -#define ICMP6_ECHO_REQUEST 128 /* echo service */ -#define ICMP6_ECHO_REPLY 129 /* echo reply */ -#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ -#define MLD6_LISTENER_QUERY 130 /* multicast listener query */ -#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ -#define MLD6_LISTENER_REPORT 131 /* multicast listener report */ -#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ -#define MLD6_LISTENER_DONE 132 /* multicast listener done */ - -#define ND_ROUTER_SOLICIT 133 /* router solicitation */ -#define ND_ROUTER_ADVERT 134 /* router advertisment */ -#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ -#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */ -#define ND_REDIRECT 137 /* redirect */ - -#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ - -#define ICMP6_WRUREQUEST 139 /* who are you request */ -#define ICMP6_WRUREPLY 140 /* who are you reply */ -#define ICMP6_FQDN_QUERY 139 /* FQDN query */ -#define ICMP6_FQDN_REPLY 140 /* FQDN reply */ -#define ICMP6_NI_QUERY 139 /* node information request */ -#define ICMP6_NI_REPLY 140 /* node information reply */ - -/* The definitions below are experimental. TBA */ -#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */ -#define MLD6_MTRACE 142 /* mtrace messages */ - -#define ICMP6_MAXTYPE 142 - -#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ -#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ -#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ -#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ -#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ -#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ - -#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ -#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ - -#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ -#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ -#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ - -#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ - -#define ICMP6_NI_SUCESS 0 /* node information successful reply */ -#define ICMP6_NI_REFUSED 1 /* node information request is refused */ -#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ - -#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ -#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ -#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ - -/* Used in kernel only */ -#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ -#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ - -/* - * Multicast Listener Discovery - */ -struct mld6_hdr { - struct icmp6_hdr mld6_hdr; - struct in6_addr mld6_addr; /* multicast address */ -}; - -#define mld6_type mld6_hdr.icmp6_type -#define mld6_code mld6_hdr.icmp6_code -#define mld6_cksum mld6_hdr.icmp6_cksum -#define mld6_maxdelay mld6_hdr.icmp6_data16[0] -#define mld6_reserved mld6_hdr.icmp6_data16[1] - -/* - * Neighbor Discovery - */ - -struct nd_router_solicit { /* router solicitation */ - struct icmp6_hdr nd_rs_hdr; - /* could be followed by options */ -}; - -#define nd_rs_type nd_rs_hdr.icmp6_type -#define nd_rs_code nd_rs_hdr.icmp6_code -#define nd_rs_cksum nd_rs_hdr.icmp6_cksum -#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] - -struct nd_router_advert { /* router advertisement */ - struct icmp6_hdr nd_ra_hdr; - u_int32_t nd_ra_reachable; /* reachable time */ - u_int32_t nd_ra_retransmit; /* retransmit timer */ - /* could be followed by options */ -}; - -#define nd_ra_type nd_ra_hdr.icmp6_type -#define nd_ra_code nd_ra_hdr.icmp6_code -#define nd_ra_cksum nd_ra_hdr.icmp6_cksum -#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] -#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] -#define ND_RA_FLAG_MANAGED 0x80 -#define ND_RA_FLAG_OTHER 0x40 -#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] - -struct nd_neighbor_solicit { /* neighbor solicitation */ - struct icmp6_hdr nd_ns_hdr; - struct in6_addr nd_ns_target; /*target address */ - /* could be followed by options */ -}; - -#define nd_ns_type nd_ns_hdr.icmp6_type -#define nd_ns_code nd_ns_hdr.icmp6_code -#define nd_ns_cksum nd_ns_hdr.icmp6_cksum -#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] - -struct nd_neighbor_advert { /* neighbor advertisement */ - struct icmp6_hdr nd_na_hdr; - struct in6_addr nd_na_target; /* target address */ - /* could be followed by options */ -}; - -#define nd_na_type nd_na_hdr.icmp6_type -#define nd_na_code nd_na_hdr.icmp6_code -#define nd_na_cksum nd_na_hdr.icmp6_cksum -#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] -#if BYTE_ORDER == BIG_ENDIAN -#define ND_NA_FLAG_ROUTER 0x80000000 -#define ND_NA_FLAG_SOLICITED 0x40000000 -#define ND_NA_FLAG_OVERRIDE 0x20000000 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ND_NA_FLAG_ROUTER 0x80 -#define ND_NA_FLAG_SOLICITED 0x40 -#define ND_NA_FLAG_OVERRIDE 0x20 -#endif - -struct nd_redirect { /* redirect */ - struct icmp6_hdr nd_rd_hdr; - struct in6_addr nd_rd_target; /* target address */ - struct in6_addr nd_rd_dst; /* destination address */ - /* could be followed by options */ -}; - -#define nd_rd_type nd_rd_hdr.icmp6_type -#define nd_rd_code nd_rd_hdr.icmp6_code -#define nd_rd_cksum nd_rd_hdr.icmp6_cksum -#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] - -struct nd_opt_hdr { /* Neighbor discovery option header */ - u_int8_t nd_opt_type; - u_int8_t nd_opt_len; - /* followed by option specific data*/ -}; - -#define ND_OPT_SOURCE_LINKADDR 1 -#define ND_OPT_TARGET_LINKADDR 2 -#define ND_OPT_PREFIX_INFORMATION 3 -#define ND_OPT_REDIRECTED_HEADER 4 -#define ND_OPT_MTU 5 - -struct nd_opt_prefix_info { /* prefix information */ - u_int8_t nd_opt_pi_type; - u_int8_t nd_opt_pi_len; - u_int8_t nd_opt_pi_prefix_len; - u_int8_t nd_opt_pi_flags_reserved; - u_int32_t nd_opt_pi_valid_time; - u_int32_t nd_opt_pi_preferred_time; - u_int32_t nd_opt_pi_reserved2; - struct in6_addr nd_opt_pi_prefix; -}; - -#define ND_OPT_PI_FLAG_ONLINK 0x80 -#define ND_OPT_PI_FLAG_AUTO 0x40 - -struct nd_opt_rd_hdr { /* redirected header */ - u_int8_t nd_opt_rh_type; - u_int8_t nd_opt_rh_len; - u_int16_t nd_opt_rh_reserved1; - u_int32_t nd_opt_rh_reserved2; - /* followed by IP header and data */ -}; - -struct nd_opt_mtu { /* MTU option */ - u_int8_t nd_opt_mtu_type; - u_int8_t nd_opt_mtu_len; - u_int16_t nd_opt_mtu_reserved; - u_int32_t nd_opt_mtu_mtu; -}; - -/* - * icmp6 namelookup - */ - -struct icmp6_namelookup { - struct icmp6_hdr icmp6_nl_hdr; - u_int64_t icmp6_nl_nonce; - u_int32_t icmp6_nl_ttl; - /* could be followed by options */ -}; - -/* - * icmp6 node information - */ -struct icmp6_nodeinfo { - struct icmp6_hdr icmp6_ni_hdr; - u_int64_t icmp6_ni_nonce; - /* could be followed by reply data */ -}; - -#define ni_type icmp6_ni_hdr.icmp6_type -#define ni_code icmp6_ni_hdr.icmp6_code -#define ni_cksum icmp6_ni_hdr.icmp6_cksum -#define ni_qtype icmp6_ni_hdr.icmp6_data16[0] -#define ni_flags icmp6_ni_hdr.icmp6_data16[1] - - -#define NI_QTYPE_NOOP 0 /* NOOP */ -#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ -#define NI_QTYPE_FQDN 2 /* FQDN */ -#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */ - -#if BYTE_ORDER == BIG_ENDIAN -#define NI_SUPTYPE_FLAG_COMPRESS 0x1 -#define NI_FQDN_FLAG_VALIDTTL 0x1 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define NI_SUPTYPE_FLAG_COMPRESS 0x0100 -#define NI_FQDN_FLAG_VALIDTTL 0x0100 -#endif - -#if BYTE_ORDER == BIG_ENDIAN -#define NI_NODEADDR_FLAG_TRUNCATE 0x1 -#define NI_NODEADDR_FLAG_ALL 0x2 -#define NI_NODEADDR_FLAG_COMPAT 0x4 -#define NI_NODEADDR_FLAG_LINKLOCAL 0x8 -#define NI_NODEADDR_FLAG_SITELOCAL 0x10 -#define NI_NODEADDR_FLAG_GLOBAL 0x20 -#define NI_NODEADDR_FLAG_ANYCAST 0x40 /* just experimental. not in spec */ -#elif BYTE_ORDER == LITTLE_ENDIAN -#define NI_NODEADDR_FLAG_TRUNCATE 0x0100 -#define NI_NODEADDR_FLAG_ALL 0x0200 -#define NI_NODEADDR_FLAG_COMPAT 0x0400 -#define NI_NODEADDR_FLAG_LINKLOCAL 0x0800 -#define NI_NODEADDR_FLAG_SITELOCAL 0x1000 -#define NI_NODEADDR_FLAG_GLOBAL 0x2000 -#define NI_NODEADDR_FLAG_ANYCAST 0x4000 /* just experimental. not in spec */ -#endif - -struct ni_reply_fqdn { - u_int32_t ni_fqdn_ttl; /* TTL */ - u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ - u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ -}; - -/* - * Router Renumbering. as router-renum-08.txt - */ -struct icmp6_router_renum { /* router renumbering header */ - struct icmp6_hdr rr_hdr; - u_int8_t rr_segnum; - u_int8_t rr_flags; - u_int16_t rr_maxdelay; - u_int32_t rr_reserved; -}; -#define ICMP6_RR_FLAGS_SEGNUM 0x80 -#define ICMP6_RR_FLAGS_TEST 0x40 -#define ICMP6_RR_FLAGS_REQRESULT 0x20 -#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10 -#define ICMP6_RR_FLAGS_SPECSITE 0x08 -#define ICMP6_RR_FLAGS_PREVDONE 0x04 - -#define rr_type rr_hdr.icmp6_type -#define rr_code rr_hdr.icmp6_code -#define rr_cksum rr_hdr.icmp6_cksum -#define rr_seqnum rr_hdr.icmp6_data32[0] - -struct rr_pco_match { /* match prefix part */ - u_int8_t rpm_code; - u_int8_t rpm_len; - u_int8_t rpm_ordinal; - u_int8_t rpm_matchlen; - u_int8_t rpm_minlen; - u_int8_t rpm_maxlen; - u_int16_t rpm_reserved; - struct in6_addr rpm_prefix; -}; - -#define RPM_PCO_ADD 1 -#define RPM_PCO_CHANGE 2 -#define RPM_PCO_SETGLOBAL 3 - -struct rr_pco_use { /* use prefix part */ - u_int8_t rpu_uselen; - u_int8_t rpu_keeplen; - u_int8_t rpu_ramask; - u_int8_t rpu_raflags; - u_int32_t rpu_vltime; - u_int32_t rpu_pltime; - u_int32_t rpu_flags; - struct in6_addr rpu_prefix; -}; -#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 -#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 - -#if BYTE_ORDER == BIG_ENDIAN -#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 -#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80 -#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40 -#endif - -struct rr_result { /* router renumbering result message */ - u_int16_t rrr_flags; - u_int8_t rrr_ordinal; - u_int8_t rrr_matchedlen; - u_int32_t rrr_ifid; - struct in6_addr rrr_prefix; -}; -#if BYTE_ORDER == BIG_ENDIAN -#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 -#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ICMP6_RR_RESULT_FLAGS_OOB 0x02 -#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01 -#endif - -/* - * icmp6 filter structures. - */ - -struct icmp6_filter { - u_int32_t icmp6_filter[8]; -}; - -#ifdef _KERNEL -#define ICMP6_FILTER_SETPASSALL(filterp) \ - { \ - int i; u_char *p; \ - p = (u_char *)filterp; \ - for (i = 0; i < sizeof(struct icmp6_filter); i++) \ - p[i] = 0xff; \ - } -#define ICMP6_FILTER_SETBLOCKALL(filterp) \ - bzero(filterp, sizeof(struct icmp6_filter)) -#else /* _KERNEL */ -#define ICMP6_FILTER_SETPASSALL(filterp) \ - memset(filterp, 0xff, sizeof(struct icmp6_filter)) -#define ICMP6_FILTER_SETBLOCKALL(filterp) \ - memset(filterp, 0x00, sizeof(struct icmp6_filter)) -#endif /* _KERNEL */ - -#define ICMP6_FILTER_SETPASS(type, filterp) \ - (((filterp)->icmp6_filter[(type) >> 5]) |= (1 << ((type) & 31))) -#define ICMP6_FILTER_SETBLOCK(type, filterp) \ - (((filterp)->icmp6_filter[(type) >> 5]) &= ~(1 << ((type) & 31))) -#define ICMP6_FILTER_WILLPASS(type, filterp) \ - ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) != 0) -#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ - ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) == 0) - -/* - * Variables related to this implementation - * of the internet control message protocol version 6. - */ -struct icmp6stat { -/* statistics related to icmp6 packets generated */ - u_long icp6s_error; /* # of calls to icmp6_error */ - u_long icp6s_canterror; /* no error 'cuz old was icmp */ - u_long icp6s_toofreq; /* no error 'cuz rate limitation */ - u_long icp6s_outhist[256]; -/* statistics related to input messages proccesed */ - u_long icp6s_badcode; /* icmp6_code out of range */ - u_long icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */ - u_long icp6s_checksum; /* bad checksum */ - u_long icp6s_badlen; /* calculated bound mismatch */ - u_long icp6s_reflect; /* number of responses */ - u_long icp6s_inhist[256]; -}; - -/* - * Names for ICMP sysctl objects - */ -#define ICMPV6CTL_STATS 1 -#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ -#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ -#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ -#define ICMPV6CTL_ND6_PRUNE 6 -#define ICMPV6CTL_ND6_DELAY 8 -#define ICMPV6CTL_ND6_UMAXTRIES 9 -#define ICMPV6CTL_ND6_MMAXTRIES 10 -#define ICMPV6CTL_ND6_USELOOPBACK 11 -#define ICMPV6CTL_ND6_PROXYALL 12 -#define ICMPV6CTL_MAXID 13 - -#define ICMPV6CTL_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ - { "rediraccept", CTLTYPE_INT }, \ - { "redirtimeout", CTLTYPE_INT }, \ - { 0, 0 }, \ - { "errratelimit", CTLTYPE_INT }, \ - { "nd6_prune", CTLTYPE_INT }, \ - { 0, 0 }, \ - { "nd6_delay", CTLTYPE_INT }, \ - { "nd6_umaxtries", CTLTYPE_INT }, \ - { "nd6_mmaxtries", CTLTYPE_INT }, \ - { "nd6_useloopback", CTLTYPE_INT }, \ - { "nd6_proxyall", CTLTYPE_INT }, \ -} - -#define ICMPV6CTL_VARS { \ - 0, \ - 0, \ - &icmp6_rediraccept, \ - &icmp6_redirtimeout, \ - 0, \ - 0, \ - &icmp6errratelim, \ - &nd6_prune, \ - 0, \ - &nd6_delay, \ - &nd6_umaxtries, \ - &nd6_mmaxtries, \ - &nd6_useloopback, \ - &nd6_proxyall, \ -} - -#define RTF_PROBEMTU RTF_PROTO1 - -#ifdef _KERNEL -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_icmp6); -#endif -# ifdef __STDC__ -struct rtentry; -struct rttimer; -struct in6_multi; -# endif -void icmp6_init __P((void)); -void icmp6_paramerror __P((struct mbuf *, int)); -void icmp6_error __P((struct mbuf *, int, int, int)); -int icmp6_input __P((struct mbuf **, int *, int)); -void icmp6_fasttimo __P((void)); -void icmp6_reflect __P((struct mbuf *, size_t)); -void icmp6_prepare __P((struct mbuf *)); -void icmp6_redirect_input __P((struct mbuf *, int)); -void icmp6_redirect_output __P((struct mbuf *, struct rtentry *)); - -/* XXX: is this the right place for these macros? */ -#define icmp6_ifstat_inc(ifp, tag) \ -do { \ - if ((ifp) && (ifp)->if_index <= if_index \ - && (ifp)->if_index < icmp6_ifstatmax \ - && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \ - icmp6_ifstat[(ifp)->if_index]->tag++; \ - } \ -} while (0) - -#define icmp6_ifoutstat_inc(ifp, type, code) \ -do { \ - icmp6_ifstat_inc(ifp, ifs6_out_msg); \ - if (type < ICMP6_INFOMSG_MASK) \ - icmp6_ifstat_inc(ifp, ifs6_out_error); \ - switch(type) { \ - case ICMP6_DST_UNREACH: \ - icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \ - if (code == ICMP6_DST_UNREACH_ADMIN) \ - icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \ - break; \ - case ICMP6_PACKET_TOO_BIG: \ - icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \ - break; \ - case ICMP6_TIME_EXCEEDED: \ - icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \ - break; \ - case ICMP6_PARAM_PROB: \ - icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \ - break; \ - case ICMP6_ECHO_REQUEST: \ - icmp6_ifstat_inc(ifp, ifs6_out_echo); \ - break; \ - case ICMP6_ECHO_REPLY: \ - icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \ - break; \ - case MLD6_LISTENER_QUERY: \ - icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \ - break; \ - case MLD6_LISTENER_REPORT: \ - icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \ - break; \ - case MLD6_LISTENER_DONE: \ - icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \ - break; \ - case ND_ROUTER_SOLICIT: \ - icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \ - break; \ - case ND_ROUTER_ADVERT: \ - icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \ - break; \ - case ND_NEIGHBOR_SOLICIT: \ - icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \ - break; \ - case ND_NEIGHBOR_ADVERT: \ - icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \ - break; \ - case ND_REDIRECT: \ - icmp6_ifstat_inc(ifp, ifs6_out_redirect); \ - break; \ - } \ -} while (0) - -extern int icmp6_rediraccept; /* accept/process redirects */ -extern int icmp6_redirtimeout; /* cache time for redirect routes */ -#endif /* _KERNEL */ - -#endif /* not _NETINET6_ICMPV6_H_ */ +/* $FreeBSD$ */ +/* $KAME: icmp6.h,v 1.17 2000/06/11 17:23:40 jinmei Exp $ */ +#error "netinet6/icmp6.h is obsolete. use netinet/icmp6.h" diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 234bd52..2d6ce68 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6.c,v 1.87 2000/07/03 15:44:21 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -64,6 +65,9 @@ * @(#)in.c 8.2 (Berkeley) 11/15/93 */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/errno.h> #include <sys/malloc.h> @@ -79,8 +83,6 @@ #include <net/if.h> #include <net/if_types.h> #include <net/route.h> -#include "gif.h" - #include <net/if_dl.h> #include <netinet/in.h> @@ -88,10 +90,14 @@ #include <netinet/if_ether.h> #include <netinet6/nd6.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/mld6_var.h> +#include <netinet6/ip6_mroute.h> #include <netinet6/in6_ifattach.h> +#include <netinet6/scope6_var.h> + +#include "gif.h" #if NGIF > 0 #include <net/if_gif.h> #endif @@ -103,40 +109,25 @@ MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); /* * Definitions of some costant IP6 addresses. */ -const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; -const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; -const struct in6_addr in6addr_nodelocal_allnodes = +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; -const struct in6_addr in6addr_linklocal_allnodes = +const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; -const struct in6_addr in6addr_linklocal_allrouters = +const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; -const struct in6_addr in6mask0 = IN6MASK0; -const struct in6_addr in6mask32 = IN6MASK32; -const struct in6_addr in6mask64 = IN6MASK64; -const struct in6_addr in6mask96 = IN6MASK96; -const struct in6_addr in6mask128 = IN6MASK128; +const struct in6_addr in6mask0 = IN6MASK0; +const struct in6_addr in6mask32 = IN6MASK32; +const struct in6_addr in6mask64 = IN6MASK64; +const struct in6_addr in6mask96 = IN6MASK96; +const struct in6_addr in6mask128 = IN6MASK128; -static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, +static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); -struct in6_multihead in6_multihead; /* XXX BSS initialization */ -/* - * Determine whether an IP6 address is in a reserved set of addresses - * that may not be forwarded, or whether datagrams to that destination - * may be forwarded. - */ -int -in6_canforward(src, dst) - struct in6_addr *src, *dst; -{ - if (IN6_IS_ADDR_LINKLOCAL(src) || - IN6_IS_ADDR_LINKLOCAL(dst) || - IN6_IS_ADDR_MULTICAST(dst)) - return(0); - return(1); -} +struct in6_multihead in6_multihead; /* XXX BSS initialization */ /* * Check if the loopback entry will be automatically generated. @@ -184,7 +175,11 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) lo_sa.sin6_addr = in6addr_loopback; all1_sa.sin6_addr = in6mask128; - /* So we add or remove static loopback entry, here. */ + /* + * So we add or remove static loopback entry, here. + * This request for deletion could fail, e.g. when we remove + * an address right after adding it. + */ rtrequest(cmd, ifa->ifa_addr, (struct sockaddr *)&lo_sa, (struct sockaddr *)&all1_sa, @@ -198,7 +193,7 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) * loopback address. */ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { - nrt->rt_ifa->ifa_refcnt--; + IFAFREE(nrt->rt_ifa); ifa->ifa_refcnt++; nrt->rt_ifa = ifa; } @@ -233,10 +228,18 @@ in6_ifaddloop(struct ifaddr *ifa) static void in6_ifremloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct in6_ifaddr *ia; - int ia_count = 0; + struct in6_ifaddr *ia; + int ia_count = 0; + /* + * All BSD variants except BSD/OS do not remove cloned routes + * from an interface direct route, when removing the direct route + * (see commens in net/net_osdep.h). + * So we should remove the route corresponding to the deleted address + * regardless of the result of in6_is_ifloop_auto(). + */ + if (1) + { /* If only one ifa for the loopback entry, delete it. */ for (ia = in6_ifaddr; ia; ia = ia->ia_next) { if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), @@ -251,90 +254,6 @@ in6_ifremloop(struct ifaddr *ifa) } } -/* - * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). - * This routine does actual work. - * call in6_addmulti() when cmd == 1. - * call in6_delmulti() when cmd == 2. - */ -static int -in6_ifproxy_request(int cmd, struct in6_ifaddr *ia) -{ - int error = 0; - - /* - * If we have an IPv6 dstaddr on adding p2p interface, - * join dstaddr's solicited multicast on necessary interface. - */ - if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && - ia->ia_dstaddr.sin6_family == AF_INET6 && - !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - struct in6_ifaddr *ia_lan; - - /* - * TODO: Join only on some specified interfaces by some - * configuration. - * Unsolicited Neighbor Advertisements will be also necessary. - * - * Now, join on interfaces which meets following. - * -IFF_BROADCAST and IFF_MULTICAST - * (NBMA is out of scope) - * -the prefix value is same as p2p dstaddr - */ - for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { - struct in6_addr llsol; - - if ((ia_lan->ia_ifp->if_flags & - (IFF_BROADCAST|IFF_MULTICAST)) != - (IFF_BROADCAST|IFF_MULTICAST)) - continue; - if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), - IA6_IN6(ia_lan), - IA6_MASKIN6(ia_lan))) - continue; - if (ia_lan->ia_ifp == ia->ia_ifp) - continue; - - /* init llsol */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_dstaddr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - if (cmd == 1) - (void)in6_addmulti(&llsol, - ia_lan->ia_ifp, - &error); - else if (cmd == 2) { - struct in6_multi *in6m; - - IN6_LOOKUP_MULTI(llsol, - ia_lan->ia_ifp, - in6m); - if (in6m) - in6_delmulti(in6m); - } - } - } - return error; -} - -static int -in6_ifaddproxy(struct in6_ifaddr *ia) -{ - return(in6_ifproxy_request(1, ia)); -} - -static void -in6_ifremproxy(struct in6_ifaddr *ia) -{ - in6_ifproxy_request(2, ia); -} - int in6_ifindex2scopeid(idx) int idx; @@ -393,10 +312,8 @@ in6_len2mask(mask, len) mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; } -int in6_interfaces; /* number of external internet interfaces */ - #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) -#define ia62ifa(ia6) ((struct ifaddr *)(ia6)) +#define ia62ifa(ia6) (&((ia6)->ia_ifa)) int in6_control(so, cmd, data, ifp, p) @@ -407,10 +324,14 @@ in6_control(so, cmd, data, ifp, p) struct proc *p; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia, *oia; + struct in6_ifaddr *ia = NULL, *oia; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; - struct sockaddr_in6 oldaddr, net; - int error = 0, hostIsNew, prefixIsNew; + struct sockaddr_in6 oldaddr; +#ifdef COMPAT_IN6IFIOCTL + struct sockaddr_in6 net; +#endif + int error = 0, hostIsNew, prefixIsNew; + int newifaddr; int privileged; privileged = 0; @@ -433,14 +354,21 @@ in6_control(so, cmd, data, ifp, p) } } #endif + switch (cmd) { + case SIOCGETSGCNT_IN6: + case SIOCGETMIFCNT_IN6: + return (mrt6_ioctl(cmd, data)); + } - if (ifp == 0) + if (ifp == NULL) return(EOPNOTSUPP); switch (cmd) { case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: + case SIOCSDEFIFACE_IN6: + case SIOCSIFINFO_FLAGS: if (!privileged) return(EPERM); /*fall through*/ @@ -448,6 +376,7 @@ in6_control(so, cmd, data, ifp, p) case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: case SIOCGNBRINFO_IN6: + case SIOCGDEFIFACE_IN6: return(nd6_ioctl(cmd, data, ifp)); } @@ -466,6 +395,20 @@ in6_control(so, cmd, data, ifp, p) return(in6_prefix_ioctl(so, cmd, data, ifp)); } + switch(cmd) { + case SIOCSSCOPE6: + if (!privileged) + return(EPERM); + return(scope6_set(ifp, ifr->ifr_ifru.ifru_scope_id)); + break; + case SIOCGSCOPE6: + return(scope6_get(ifp, ifr->ifr_ifru.ifru_scope_id)); + break; + case SIOCGSCOPE6DEF: + return(scope6_get_default(ifr->ifr_ifru.ifru_scope_id)); + break; + } + switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: @@ -479,8 +422,7 @@ in6_control(so, cmd, data, ifp, p) /* * Find address for this interface, if it exists. */ - { - + if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ifra->ifra_addr; @@ -489,10 +431,10 @@ in6_control(so, cmd, data, ifp, p) /* interface ID is not embedded by the user */ sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (sa6->sin6_addr.s6_addr16[1] != - htons(ifp->if_index)) - return(EINVAL); /* ifid is contradict */ + } else if (sa6->sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + return(EINVAL); /* ifid is contradict */ + } if (sa6->sin6_scope_id) { if (sa6->sin6_scope_id != (u_int32_t)ifp->if_index) @@ -500,30 +442,61 @@ in6_control(so, cmd, data, ifp, p) sa6->sin6_scope_id = 0; /* XXX: good way? */ } } + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); } - ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); switch (cmd) { case SIOCDIFADDR_IN6: - if (ia == 0) + /* + * for IPv4, we look for existing in6_ifaddr here to allow + * "ifconfig if0 delete" to remove first IPv4 address on the + * interface. For IPv6, as the spec allow multiple interface + * address from the day one, we consider "remove the first one" + * semantics to be not preferrable. + */ + if (ia == NULL) return(EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: case SIOCSIFADDR_IN6: - case SIOCSIFNETMASK_IN6: +#ifdef COMPAT_IN6IFIOCTL case SIOCSIFDSTADDR_IN6: + case SIOCSIFNETMASK_IN6: + /* + * Since IPv6 allows a node to assign multiple addresses + * on a single interface, SIOCSIFxxx ioctls are not suitable + * and should be unused. + */ +#endif + if (ifra->ifra_addr.sin6_family != AF_INET6) + return(EAFNOSUPPORT); if (!privileged) return(EPERM); - if (ia == 0) { + if (ia == NULL) { ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR, M_WAITOK); if (ia == NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof(*ia)); + /* Initialize the address and masks */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr - = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_len = sizeof(ia->ia_addr); +#if 1 + if (ifp->if_flags & IFF_POINTOPOINT) { + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_dstaddr.sin6_family = AF_INET6; + ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr); + } else { + ia->ia_ifa.ifa_dstaddr = NULL; + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + } +#else /* always initilize by NULL */ + ia->ia_ifa.ifa_dstaddr = NULL; + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); +#endif ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; @@ -534,11 +507,17 @@ in6_control(so, cmd, data, ifp, p) oia->ia_next = ia; } else in6_ifaddr = ia; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, - (struct ifaddr *)ia, ifa_list); - if ((ifp->if_flags & IFF_LOOPBACK) == 0) - in6_interfaces++; /*XXX*/ - } + /* gain a refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; + + TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, + ifa_list); + /* gain another refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; + + newifaddr = 1; + } else + newifaddr = 0; if (cmd == SIOCAIFADDR_IN6) { /* sanity for overflow - beware unsigned */ @@ -563,7 +542,7 @@ in6_control(so, cmd, data, ifp, p) case SIOCGIFDSTADDR_IN6: case SIOCGIFALIFETIME_IN6: /* must think again about its semantics */ - if (ia == 0) + if (ia == NULL) return(EADDRNOTAVAIL); break; case SIOCSIFALIFETIME_IN6: @@ -572,7 +551,7 @@ in6_control(so, cmd, data, ifp, p) if (!privileged) return(EPERM); - if (ia == 0) + if (ia == NULL) return(EADDRNOTAVAIL); /* sanity for overflow - beware unsigned */ lt = &ifr->ifr_ifru.ifru_lifetime; @@ -597,6 +576,10 @@ in6_control(so, cmd, data, ifp, p) case SIOCGIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return(EINVAL); + /* + * XXX: should we check if ifa_dstaddr is NULL and return + * an error? + */ ifr->ifr_dstaddr = ia->ia_dstaddr; break; @@ -633,6 +616,7 @@ in6_control(so, cmd, data, ifp, p) *icmp6_ifstat[ifp->if_index]; break; +#ifdef COMPAT_IN6IFIOCTL /* should be unused */ case SIOCSIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return(EINVAL); @@ -645,12 +629,11 @@ in6_control(so, cmd, data, ifp, p) /* interface ID is not embedded by the user */ ia->ia_dstaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } } if (ifp->if_ioctl && (error = (ifp->if_ioctl) @@ -658,6 +641,7 @@ in6_control(so, cmd, data, ifp, p) ia->ia_dstaddr = oldaddr; return(error); } + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; if (ia->ia_flags & IFA_ROUTE) { ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); @@ -667,6 +651,7 @@ in6_control(so, cmd, data, ifp, p) } break; +#endif case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; break; @@ -687,8 +672,41 @@ in6_control(so, cmd, data, ifp, p) break; case SIOCSIFADDR_IN6: - return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1)); + error = in6_ifinit(ifp, ia, &ifr->ifr_addr, 1); +#if 0 + /* + * the code chokes if we are to assign multiple addresses with + * the same address prefix (rtinit() will return EEXIST, which + * is not fatal actually). we will get memory leak if we + * don't do it. + * -> we may want to hide EEXIST from rtinit(). + */ + undo: + if (error && newifaddr) { + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); + /* release a refcnt for the link from if_addrlist */ + IFAFREE(&ia->ia_ifa); + + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else { + printf("Didn't unlink in6_ifaddr " + "from list\n"); + } + } + /* release another refcnt for the link from in6_ifaddr */ + IFAFREE(&oia->ia_ifa); + } +#endif + return error; +#ifdef COMPAT_IN6IFIOCTL /* XXX should be unused */ case SIOCSIFNETMASK_IN6: ia->ia_prefixmask = ifr->ifr_addr; bzero(&net, sizeof(net)); @@ -710,6 +728,7 @@ in6_control(so, cmd, data, ifp, p) ia->ia_prefixmask.sin6_addr.s6_addr32[3]; ia->ia_net = net; break; +#endif case SIOCAIFADDR_IN6: prefixIsNew = 0; @@ -722,6 +741,22 @@ in6_control(so, cmd, data, ifp, p) &ia->ia_addr.sin6_addr)) hostIsNew = 0; + /* Validate address families: */ + /* + * The destination address for a p2p link must have a family + * of AF_UNSPEC or AF_INET6. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && + ifra->ifra_dstaddr.sin6_family != AF_INET6 && + ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) + return(EAFNOSUPPORT); + /* + * The prefixmask must have a family of AF_UNSPEC or AF_INET6. + */ + if (ifra->ifra_prefixmask.sin6_family != AF_INET6 && + ifra->ifra_prefixmask.sin6_family != AF_UNSPEC) + return(EAFNOSUPPORT); + if (ifra->ifra_prefixmask.sin6_len) { in6_ifscrub(ifp, ia); ia->ia_prefixmask = ifra->ifra_prefixmask; @@ -730,6 +765,7 @@ in6_control(so, cmd, data, ifp, p) if ((ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { in6_ifscrub(ifp, ia); + oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = ifra->ifra_dstaddr; /* link-local index check: should be a separate function? */ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { @@ -740,20 +776,22 @@ in6_control(so, cmd, data, ifp, p) */ ia->ia_dstaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } } prefixIsNew = 1; /* We lie; but effect's the same */ } - if (ifra->ifra_addr.sin6_family == AF_INET6 && - (hostIsNew || prefixIsNew)) + if (hostIsNew || prefixIsNew) { error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); - if (ifra->ifra_addr.sin6_family == AF_INET6 - && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { +#if 0 + if (error) + goto undo; +#endif + } + if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { int error_local = 0; /* @@ -772,14 +810,6 @@ in6_control(so, cmd, data, ifp, p) if (error == 0) error = error_local; } - /* Join dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall && hostIsNew) { - int error_local; - - error_local = in6_ifaddproxy(ia); - if (error == 0) - error = error_local; - } ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ @@ -806,8 +836,15 @@ in6_control(so, cmd, data, ifp, p) case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: - ia->ia6_flags |= IN6_IFF_TENTATIVE; - nd6_dad_start((struct ifaddr *)ia, NULL); +#if 0 + case IFT_ATM: + case IFT_SLIP: + case IFT_PPP: +#endif + { + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); + } break; #ifdef IFT_DUMMY case IFT_DUMMY: @@ -833,64 +870,103 @@ in6_control(so, cmd, data, ifp, p) return(error); case SIOCDIFADDR_IN6: - in6_ifscrub(ifp, ia); - - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); - } - /* Leave dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall) - in6_ifremproxy(ia); - - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; - else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; - else - printf("Didn't unlink in6_ifaddr from list\n"); - } - { - int iilen; - - iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&oia->ia_prefixmask.sin6_addr); - in6_prefix_remove_ifid(iilen, oia); - } - IFAFREE((&oia->ia_ifa)); + in6_purgeaddr(&ia->ia_ifa, ifp); break; default: - if (ifp == 0 || ifp->if_ioctl == 0) + if (ifp == NULL || ifp->if_ioctl == 0) return(EOPNOTSUPP); return((*ifp->if_ioctl)(ifp, cmd, data)); } return(0); } +void +in6_purgeaddr(ifa, ifp) + struct ifaddr *ifa; + struct ifnet *ifp; +{ + struct in6_ifaddr *oia, *ia = (void *) ifa; + int plen; + + in6_ifscrub(ifp, ia); + + if (ifp->if_flags & IFF_MULTICAST) { + /* + * delete solicited multicast addr for deleting host id + */ + struct in6_multi *in6m; + struct in6_addr llsol; + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } + + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); + /* release a refcnt for the link from if_addrlist */ + IFAFREE(&ia->ia_ifa); + + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else + printf("Didn't unlink in6_ifaddr from list\n"); + } + { + int iilen; + + plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr); + iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - plen; + in6_prefix_remove_ifid(iilen, oia); + } + + /* + * Check if we have another address that has the same prefix of + * the purged address. If we have one, reinstall the corresponding + * interface route. + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + int e; + + if (in6_are_prefix_equal(&ia->ia_addr.sin6_addr, + &oia->ia_addr.sin6_addr, plen)) { + if ((e = rtinit(&(ia->ia_ifa), (int)RTM_ADD, + ia->ia_flags)) == 0) { + ia->ia_flags |= IFA_ROUTE; + break; + } + else { + log(LOG_NOTICE, + "in6_purgeaddr: failed to add an interface" + " route for %s/%d on %s, errno = %d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + plen, if_name(ia->ia_ifp), e); + /* still trying */ + } + } + } + + /* release another refcnt for the link from in6_ifaddr */ + IFAFREE(&oia->ia_ifa); +} + /* * SIOC[GAD]LIFADDR. - * SIOCGLIFADDR: get first address. (?!?) + * SIOCGLIFADDR: get first address. (???) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. @@ -921,6 +997,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; + struct sockaddr *sa; /* sanity checks */ if (!data || !ifp) { @@ -937,20 +1014,25 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) case SIOCALIFADDR: case SIOCDLIFADDR: /* address must be specified on ADD and DELETE */ - if (iflr->addr.ss_family != AF_INET6) + sa = (struct sockaddr *)&iflr->addr; + if (sa->sa_family != AF_INET6) return EINVAL; - if (iflr->addr.ss_len != sizeof(struct sockaddr_in6)) + if (sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; /* XXX need improvement */ - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_family != AF_INET6) + sa = (struct sockaddr *)&iflr->dstaddr; + if (sa->sa_family && sa->sa_family != AF_INET6) return EINVAL; - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in6)) + if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; break; default: /*shouldn't happen*/ +#if 0 + panic("invalid cmd to in6_lifaddr_ioctl"); + /*NOTREACHED*/ +#else return EOPNOTSUPP; +#endif } if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) return EINVAL; @@ -970,7 +1052,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) * address. hostid points to the first link-local * address attached to the interface. */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); if (!ifa) return EADDRNOTAVAIL; hostid = IFA_IN6(ifa); @@ -994,7 +1076,8 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); - bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); + bcopy(&iflr->addr, &ifra.ifra_addr, + ((struct sockaddr *)&iflr->addr)->sa_len); if (hostid) { /* fill in hostid part */ ifra.ifra_addr.sin6_addr.s6_addr32[2] = @@ -1003,9 +1086,9 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) hostid->s6_addr32[3]; } - if (iflr->dstaddr.ss_family) { /*XXX*/ + if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, - iflr->dstaddr.ss_len); + ((struct sockaddr *)&iflr->dstaddr)->sa_len); if (hostid) { ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; @@ -1107,6 +1190,9 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, ia->ia_dstaddr.sin6_len); + } else { + bzero(&ifra.ifra_dstaddr, + sizeof(ifra.ifra_dstaddr)); } bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, ia->ia_prefixmask.sin6_len); @@ -1206,6 +1292,9 @@ in6_ifinit(ifp, ia, sin6, scrub) } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; + /* XXX check if the subnet route points to the same interface */ + if (error == EEXIST) + error = 0; /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ in6_ifaddloop(&(ia->ia_ifa)); @@ -1305,8 +1394,9 @@ in6_delmulti(in6m) * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * -in6ifa_ifpforlinklocal(ifp) +in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; + int ignoreflags; { register struct ifaddr *ifa; @@ -1316,8 +1406,12 @@ in6ifa_ifpforlinklocal(ifp) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; - if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) + if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) { + if ((((struct in6_ifaddr *)ifa)->ia6_flags & + ignoreflags) != 0) + continue; break; + } } return((struct in6_ifaddr *)ifa); @@ -1419,69 +1513,9 @@ in6_localaddr(in6) } /* - * Get a scope of the address. Node-local, link-local, site-local or global. - */ -int -in6_addrscope (addr) -struct in6_addr *addr; -{ - int scope; - - if (addr->s6_addr8[0] == 0xfe) { - scope = addr->s6_addr8[1] & 0xc0; - - switch (scope) { - case 0x80: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case 0xc0: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ - break; - } - } - - - if (addr->s6_addr8[0] == 0xff) { - scope = addr->s6_addr8[1] & 0x0f; - - /* - * due to other scope such as reserved, - * return scope doesn't work. - */ - switch (scope) { - case IPV6_ADDR_SCOPE_NODELOCAL: - return IPV6_ADDR_SCOPE_NODELOCAL; - break; - case IPV6_ADDR_SCOPE_LINKLOCAL: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case IPV6_ADDR_SCOPE_SITELOCAL: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; - break; - } - } - - if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { - if (addr->s6_addr8[15] == 1) /* loopback */ - return IPV6_ADDR_SCOPE_NODELOCAL; - if (addr->s6_addr8[15] == 0) /* unspecified */ - return IPV6_ADDR_SCOPE_LINKLOCAL; - } - - return IPV6_ADDR_SCOPE_GLOBAL; -} - -/* * return length of part which dst and src are equal * hard coding... */ - int in6_matchlen(src, dst) struct in6_addr *src, *dst; @@ -1502,6 +1536,7 @@ struct in6_addr *src, *dst; return match; } +/* XXX: to be scope conscious */ int in6_are_prefix_equal(p1, p2, len) struct in6_addr *p1, *p2; @@ -1555,114 +1590,241 @@ in6_prefixlen2mask(maskp, len) /* * return the best address out of the same scope */ - struct in6_ifaddr * -in6_ifawithscope(ifp, dst) - register struct ifnet *ifp; +in6_ifawithscope(oifp, dst) + register struct ifnet *oifp; register struct in6_addr *dst; { - int dst_scope = in6_addrscope(dst), blen = -1, tlen; + int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; + int blen = -1; struct ifaddr *ifa; - struct in6_ifaddr *besta = NULL, *ia; - struct in6_ifaddr *dep[3]; /*last-resort: deprecated*/ - - dep[0] = dep[1] = dep[2] = NULL; + struct ifnet *ifp; + struct in6_ifaddr *ifa_best = NULL; + + if (oifp == NULL) { + printf("in6_ifawithscope: output interface is not specified\n"); + return(NULL); + } /* - * We first look for addresses in the same scope. - * If there is one, return it. - * If two or more, return one which matches the dst longest. - * If none, return one of global addresses assigned other ifs. + * We search for all addresses on all interfaces from the beginning. + * Comparing an interface with the outgoing interface will be done + * only at the final stage of tiebreaking. */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) - continue; /* XXX: is there any case to allow anycast? */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) - continue; /* don't use this interface */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) - continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { - if (ip6_use_deprecated) - dep[0] = (struct in6_ifaddr *)ifa; + /* + * We can never take an address that breaks the scope zone + * of the destination. + */ + if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst)) continue; - } - if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + int tlen = -1, dscopecmp, bscopecmp, matchcmp; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + src_scope = in6_addrscope(IFA_IN6(ifa)); + /* - * call in6_matchlen() as few as possible + * Don't use an address before completing DAD + * nor a duplicated address. */ - if (besta) { - if (blen == -1) - blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); - tlen = in6_matchlen(IFA_IN6(ifa), dst); - if (tlen > blen) { - blen = tlen; - besta = (struct in6_ifaddr *)ifa; - } - } else - besta = (struct in6_ifaddr *)ifa; - } - } - if (besta) - return besta; + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_NOTREADY) + continue; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IPV6_ADDR_SCOPE_GLOBAL != - in6_addrscope(&(ia->ia_addr.sin6_addr))) - continue; - /* XXX: is there any case to allow anycast? */ - if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - if (ip6_use_deprecated) - dep[1] = (struct in6_ifaddr *)ifa; - continue; - } - return ia; - } + /* XXX: is there any case to allow anycasts? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_ANYCAST) + continue; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IPV6_ADDR_SCOPE_SITELOCAL != - in6_addrscope(&(ia->ia_addr.sin6_addr))) - continue; - /* XXX: is there any case to allow anycast? */ - if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - if (ip6_use_deprecated) - dep[2] = (struct in6_ifaddr *)ifa; - continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DETACHED) + continue; + + /* + * If this is the first address we find, + * keep it anyway. + */ + if (ifa_best == NULL) + goto replace; + + /* + * ifa_best is never NULL beyond this line except + * within the block labeled "replace". + */ + + /* + * If ifa_best has a smaller scope than dst and + * the current address has a larger one than + * (or equal to) dst, always replace ifa_best. + * Also, if the current address has a smaller scope + * than dst, ignore it unless ifa_best also has a + * smaller scope. + */ + if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) + goto replace; + if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) + continue; + + /* + * A deprecated address SHOULD NOT be used in new + * communications if an alternate (non-deprecated) + * address is available and has sufficient scope. + * RFC 2462, Section 5.5.4. + */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) { + /* + * Ignore any deprecated addresses if + * specified by configuration. + */ + if (!ip6_use_deprecated) + continue; + + /* + * If we have already found a non-deprecated + * candidate, just ignore deprecated addresses. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) + == 0) + continue; + } + + /* + * A non-deprecated address is always preferred + * to a deprecated one regardless of scopes and + * address matching. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && + (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) == 0) + goto replace; + + /* + * At this point, we have two cases: + * 1. we are looking at a non-deprecated address, + * and ifa_best is also non-deprecated. + * 2. we are looking at a deprecated address, + * and ifa_best is also deprecated. + * Also, we do not have to consider a case where + * the scope of if_best is larger(smaller) than dst and + * the scope of the current address is smaller(larger) + * than dst. Such a case has already been covered. + * Tiebreaking is done according to the following + * items: + * - the scope comparison between the address and + * dst (dscopecmp) + * - the scope comparison between the address and + * ifa_best (bscopecmp) + * - if the address match dst longer than ifa_best + * (matchcmp) + * - if the address is on the outgoing I/F (outI/F) + * + * Roughly speaking, the selection policy is + * - the most important item is scope. The same scope + * is best. Then search for a larger scope. + * Smaller scopes are the last resort. + * - A deprecated address is chosen only when we have + * no address that has an enough scope, but is + * prefered to any addresses of smaller scopes. + * - Longest address match against dst is considered + * only for addresses that has the same scope of dst. + * - If there is no other reasons to choose one, + * addresses on the outgoing I/F are preferred. + * + * The precise decision table is as follows: + * dscopecmp bscopecmp matchcmp outI/F | replace? + * !equal equal N/A Yes | Yes (1) + * !equal equal N/A No | No (2) + * larger larger N/A N/A | No (3) + * larger smaller N/A N/A | Yes (4) + * smaller larger N/A N/A | Yes (5) + * smaller smaller N/A N/A | No (6) + * equal smaller N/A N/A | Yes (7) + * equal larger (already done) + * equal equal larger N/A | Yes (8) + * equal equal smaller N/A | No (9) + * equal equal equal Yes | Yes (a) + * eaual eqaul equal No | No (b) + */ + dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope); + bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope); + + if (dscopecmp && bscopecmp == 0) { + if (oifp == ifp) /* (1) */ + goto replace; + continue; /* (2) */ + } + if (dscopecmp > 0) { + if (bscopecmp > 0) /* (3) */ + continue; + goto replace; /* (4) */ + } + if (dscopecmp < 0) { + if (bscopecmp > 0) /* (5) */ + goto replace; + continue; /* (6) */ + } + + /* now dscopecmp must be 0 */ + if (bscopecmp < 0) + goto replace; /* (7) */ + + /* + * At last both dscopecmp and bscopecmp must be 0. + * We need address matching against dst for + * tiebreaking. + */ + tlen = in6_matchlen(IFA_IN6(ifa), dst); + matchcmp = tlen - blen; + if (matchcmp > 0) /* (8) */ + goto replace; + if (matchcmp < 0) /* (9) */ + continue; + if (oifp == ifp) /* (a) */ + goto replace; + continue; /* (b) */ + + replace: + ifa_best = (struct in6_ifaddr *)ifa; + blen = tlen >= 0 ? tlen : + in6_matchlen(IFA_IN6(ifa), dst); + best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr); } - return ia; } - /* use the last-resort values, that are, deprecated addresses */ - if (dep[0]) - return dep[0]; - if (dep[1]) - return dep[1]; - if (dep[2]) - return dep[2]; + /* count statistics for future improvements */ + if (ifa_best == NULL) + ip6stat.ip6s_sources_none++; + else { + if (oifp == ifa_best->ia_ifp) + ip6stat.ip6s_sources_sameif[best_scope]++; + else + ip6stat.ip6s_sources_otherif[best_scope]++; + + if (best_scope == dst_scope) + ip6stat.ip6s_sources_samescope[best_scope]++; + else + ip6stat.ip6s_sources_otherscope[best_scope]++; + + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0) + ip6stat.ip6s_sources_deprecated[best_scope]++; + } - return NULL; + return(ifa_best); } /* * return the best address out of the same scope. if no address was * found, return the first valid address from designated IF. */ - struct in6_ifaddr * in6_ifawithifp(ifp, dst) register struct ifnet *ifp; @@ -1753,61 +1915,13 @@ in6_if_up(ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; - struct sockaddr_dl *sdl; - int type; - struct ether_addr ea; - int off; int dad_delay; /* delay ticks before DAD output */ - bzero(&ea, sizeof(ea)); - sdl = NULL; - - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) - { - if (ifa->ifa_addr->sa_family == AF_INET6 - && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { - goto dad; - } - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - break; - } - - switch (ifp->if_type) { - case IFT_SLIP: - case IFT_PPP: -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_GIF: - case IFT_FAITH: - type = IN6_IFT_P2P; - in6_ifattach(ifp, type, 0, 1); - break; - case IFT_ETHER: - case IFT_FDDI: - case IFT_ATM: - type = IN6_IFT_802; - if (sdl == NULL) - break; - off = sdl->sdl_nlen; - if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0) - in6_ifattach(ifp, type, LLADDR(sdl), 0); - break; - case IFT_ARCNET: - type = IN6_IFT_ARCNET; - if (sdl == NULL) - break; - off = sdl->sdl_nlen; - if (sdl->sdl_data[off] != 0) /* XXX ?: */ - in6_ifattach(ifp, type, LLADDR(sdl), 0); - break; - default: - break; - } + /* + * special cases, like 6to4, are handled in in6_ifattach + */ + in6_ifattach(ifp, NULL); -dad: dad_delay = 0; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 424c319..edcbe62 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6.h,v 1.48 2000/06/26 15:55:32 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -60,25 +63,24 @@ * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 - * $FreeBSD$ */ -#ifndef _NETINET6_IN6_H_ -#define _NETINET6_IN6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_IN_H_INCLUDED_) +#ifndef __KAME_NETINET_IN_H_INCLUDED_ #error "do not include netinet6/in6.h directly, include netinet/in.h" #endif -#if !defined(_XOPEN_SOURCE) +#ifndef _NETINET6_IN6_H_ +#define _NETINET6_IN6_H_ + +#ifndef _XOPEN_SOURCE #include <sys/queue.h> #endif /* * Identification of the network protocol stack */ -#define __KAME__ -#define __KAME_VERSION "SNAP 19991101" +#define __KAME__ +#define __KAME_VERSION "20000701/FreeBSD-current" /* * Local port number conventions: @@ -89,7 +91,7 @@ * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * - * The default range is IPPORT_ANONMIX to IPPORT_ANONMAX, although + * The default range is IPPORT_ANONMIN to IPPORT_ANONMAX, although * that is settable by sysctl(3); net.inet.ip.anonportmin and * net.inet.ip.anonportmax respectively. * @@ -119,33 +121,33 @@ */ struct in6_addr { union { - u_int8_t __u6_addr8[16]; - u_int16_t __u6_addr16[8]; - u_int32_t __u6_addr32[4]; + u_int8_t __u6_addr8[16]; + u_int16_t __u6_addr16[8]; + u_int32_t __u6_addr32[4]; } __u6_addr; /* 128-bit IP6 address */ }; -#define s6_addr __u6_addr.__u6_addr8 +#define s6_addr __u6_addr.__u6_addr8 #ifdef _KERNEL /*XXX nonstandard*/ -#define s6_addr8 __u6_addr.__u6_addr8 -#define s6_addr16 __u6_addr.__u6_addr16 -#define s6_addr32 __u6_addr.__u6_addr32 +#define s6_addr8 __u6_addr.__u6_addr8 +#define s6_addr16 __u6_addr.__u6_addr16 +#define s6_addr32 __u6_addr.__u6_addr32 #endif -#define INET6_ADDRSTRLEN 46 +#define INET6_ADDRSTRLEN 46 /* * Socket address for IPv6 */ -#if !defined(_XOPEN_SOURCE) -#define SIN6_LEN +#ifndef _XOPEN_SOURCE +#define SIN6_LEN #endif struct sockaddr_in6 { - u_char sin6_len; /* length of this struct(sa_family_t)*/ - u_char sin6_family; /* AF_INET6 (sa_family_t) */ + u_int8_t sin6_len; /* length of this struct(sa_family_t)*/ + u_int8_t sin6_family; /* AF_INET6 (sa_family_t) */ u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/ u_int32_t sin6_flowinfo; /* IP6 flow information */ - struct in6_addr sin6_addr; /* IP6 address */ + struct in6_addr sin6_addr; /* IP6 address */ u_int32_t sin6_scope_id; /* intface scope id */ }; @@ -153,14 +155,14 @@ struct sockaddr_in6 { * Local definition for masks */ #ifdef _KERNEL /*XXX nonstandard*/ -#define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} -#define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ +#define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} +#define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}} #endif @@ -177,42 +179,42 @@ extern const struct in6_addr in6mask128; */ #ifdef _KERNEL /*XXX nonstandard*/ #if BYTE_ORDER == BIG_ENDIAN -#define IPV6_ADDR_INT32_ONE 1 -#define IPV6_ADDR_INT32_TWO 2 -#define IPV6_ADDR_INT32_MNL 0xff010000 -#define IPV6_ADDR_INT32_MLL 0xff020000 -#define IPV6_ADDR_INT32_SMP 0x0000ffff -#define IPV6_ADDR_INT16_ULL 0xfe80 -#define IPV6_ADDR_INT16_USL 0xfec0 -#define IPV6_ADDR_INT16_MLL 0xff02 +#define IPV6_ADDR_INT32_ONE 1 +#define IPV6_ADDR_INT32_TWO 2 +#define IPV6_ADDR_INT32_MNL 0xff010000 +#define IPV6_ADDR_INT32_MLL 0xff020000 +#define IPV6_ADDR_INT32_SMP 0x0000ffff +#define IPV6_ADDR_INT16_ULL 0xfe80 +#define IPV6_ADDR_INT16_USL 0xfec0 +#define IPV6_ADDR_INT16_MLL 0xff02 #elif BYTE_ORDER == LITTLE_ENDIAN -#define IPV6_ADDR_INT32_ONE 0x01000000 -#define IPV6_ADDR_INT32_TWO 0x02000000 -#define IPV6_ADDR_INT32_MNL 0x000001ff -#define IPV6_ADDR_INT32_MLL 0x000002ff -#define IPV6_ADDR_INT32_SMP 0xffff0000 -#define IPV6_ADDR_INT16_ULL 0x80fe -#define IPV6_ADDR_INT16_USL 0xc0fe -#define IPV6_ADDR_INT16_MLL 0x02ff +#define IPV6_ADDR_INT32_ONE 0x01000000 +#define IPV6_ADDR_INT32_TWO 0x02000000 +#define IPV6_ADDR_INT32_MNL 0x000001ff +#define IPV6_ADDR_INT32_MLL 0x000002ff +#define IPV6_ADDR_INT32_SMP 0xffff0000 +#define IPV6_ADDR_INT16_ULL 0x80fe +#define IPV6_ADDR_INT16_USL 0xc0fe +#define IPV6_ADDR_INT16_MLL 0x02ff #endif #endif /* * Definition of some useful macros to handle IP6 addresses */ -#define IN6ADDR_ANY_INIT \ +#define IN6ADDR_ANY_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6ADDR_LOOPBACK_INIT \ +#define IN6ADDR_LOOPBACK_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_NODELOCAL_ALLNODES_INIT \ +#define IN6ADDR_NODELOCAL_ALLNODES_INIT \ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} @@ -229,17 +231,17 @@ extern const struct in6_addr in6addr_linklocal_allrouters; * in ANSI standard. */ #ifdef _KERNEL -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (bcmp((a), (b), sizeof(struct in6_addr)) == 0) +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (bcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #else -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (memcmp((a), (b), sizeof(struct in6_addr)) == 0) +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #endif /* * Unspecified */ -#define IN6_IS_ADDR_UNSPECIFIED(a) \ +#define IN6_IS_ADDR_UNSPECIFIED(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ @@ -248,7 +250,7 @@ extern const struct in6_addr in6addr_linklocal_allrouters; /* * Loopback */ -#define IN6_IS_ADDR_LOOPBACK(a) \ +#define IN6_IS_ADDR_LOOPBACK(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ @@ -257,7 +259,7 @@ extern const struct in6_addr in6addr_linklocal_allrouters; /* * IPv4 compatible */ -#define IN6_IS_ADDR_V4COMPAT(a) \ +#define IN6_IS_ADDR_V4COMPAT(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ @@ -267,7 +269,7 @@ extern const struct in6_addr in6addr_linklocal_allrouters; /* * Mapped */ -#define IN6_IS_ADDR_V4MAPPED(a) \ +#define IN6_IS_ADDR_V4MAPPED(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) @@ -277,26 +279,26 @@ extern const struct in6_addr in6addr_linklocal_allrouters; */ #ifdef _KERNEL /*XXX nonstandard*/ -#define IPV6_ADDR_SCOPE_NODELOCAL 0x01 -#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 -#define IPV6_ADDR_SCOPE_SITELOCAL 0x05 -#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ -#define IPV6_ADDR_SCOPE_GLOBAL 0x0e +#define IPV6_ADDR_SCOPE_NODELOCAL 0x01 +#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 +#define IPV6_ADDR_SCOPE_SITELOCAL 0x05 +#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ +#define IPV6_ADDR_SCOPE_GLOBAL 0x0e #else -#define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 -#define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02 -#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 -#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ -#define __IPV6_ADDR_SCOPE_GLOBAL 0x0e +#define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 +#define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02 +#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 +#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ +#define __IPV6_ADDR_SCOPE_GLOBAL 0x0e #endif /* * Unicast Scope * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373). */ -#define IN6_IS_ADDR_LINKLOCAL(a) \ +#define IN6_IS_ADDR_LINKLOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) -#define IN6_IS_ADDR_SITELOCAL(a) \ +#define IN6_IS_ADDR_SITELOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) /* @@ -305,44 +307,44 @@ extern const struct in6_addr in6addr_linklocal_allrouters; #define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) #ifdef _KERNEL /*XXX nonstandard*/ -#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #else -#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #endif /* * Multicast Scope */ #ifdef _KERNEL /*refers nonstandard items */ -#define IN6_IS_ADDR_MC_NODELOCAL(a) \ +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL)) -#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL)) -#define IN6_IS_ADDR_MC_SITELOCAL(a) \ +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL)) -#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL)) -#define IN6_IS_ADDR_MC_GLOBAL(a) \ +#define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL)) #else -#define IN6_IS_ADDR_MC_NODELOCAL(a) \ +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL)) -#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL)) -#define IN6_IS_ADDR_MC_SITELOCAL(a) \ +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL)) -#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL)) -#define IN6_IS_ADDR_MC_GLOBAL(a) \ +#define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL)) #endif @@ -351,7 +353,7 @@ extern const struct in6_addr in6addr_linklocal_allrouters; * KAME Scope */ #ifdef _KERNEL /*nonstandard*/ -#define IN6_IS_SCOPE_LINKLOCAL(a) \ +#define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) #endif @@ -359,7 +361,7 @@ extern const struct in6_addr in6addr_linklocal_allrouters; /* * IP6 route structure */ -#if !defined(_XOPEN_SOURCE) +#ifndef _XOPEN_SOURCE struct route_in6 { struct rtentry *ro_rt; struct sockaddr_in6 ro_dst; @@ -370,66 +372,68 @@ struct route_in6 { * Options for use with [gs]etsockopt at the IPV6 level. * First word of comment is data type; bool is stored in int. */ -#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */ /* no hdrincl */ -#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */ -#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */ -#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */ -#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */ -#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */ -#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */ -#define IPV6_MULTICAST_IF 9 /* u_char; set/get IP6 multicast i/f */ -#define IPV6_MULTICAST_HOPS 10 /* u_char; set/get IP6 multicast hops */ -#define IPV6_MULTICAST_LOOP 11 /* u_char; set/get IP6 multicast loopback */ -#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */ -#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */ -#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ -#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ -#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */ -#define IPV6_HOPLIMIT 20 /* bool; hop limit */ -#define IPV6_NEXTHOP 21 /* bool; next hop addr */ -#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */ -#define IPV6_DSTOPTS 23 /* bool; destination option */ -#define IPV6_RTHDR 24 /* bool; routing header */ -#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ -#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ -#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */ +#if 0 /* the followings are relic in IPv4 and hence are disabled */ +#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */ +#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */ +#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */ +#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */ +#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */ +#endif +#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */ +#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */ +#define IPV6_MULTICAST_IF 9 /* u_char; set/get IP6 multicast i/f */ +#define IPV6_MULTICAST_HOPS 10 /* u_char; set/get IP6 multicast hops */ +#define IPV6_MULTICAST_LOOP 11 /* u_char; set/get IP6 multicast loopback */ +#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */ +#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */ +#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ +#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ +#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */ +#define IPV6_HOPLIMIT 20 /* bool; hop limit */ +#define IPV6_NEXTHOP 21 /* bool; next hop addr */ +#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */ +#define IPV6_DSTOPTS 23 /* bool; destination option */ +#define IPV6_RTHDR 24 /* bool; routing header */ +#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ +#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ +#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */ /* for IPsec */ -#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ -#define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ +#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ +#define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ /* for IPV6FIREWALL */ -#define IPV6_FW_ADD 30 /* add a firewall rule to chain */ -#define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ -#define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ -#define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ -#define IPV6_FW_GET 34 /* get entire firewall rule chain */ +#define IPV6_FW_ADD 30 /* add a firewall rule to chain */ +#define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ +#define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ +#define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ +#define IPV6_FW_GET 34 /* get entire firewall rule chain */ -#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ -#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ -#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ +#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ +#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ +#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ /* * Defaults and limits for options */ -#define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */ -#define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ +#define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */ +#define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ /* * Argument structure for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP. */ struct ipv6_mreq { - struct in6_addr ipv6mr_multiaddr; - u_int ipv6mr_interface; + struct in6_addr ipv6mr_multiaddr; + unsigned int ipv6mr_interface; }; /* * IPV6_PKTINFO: Packet information(RFC2292 sec 5) */ struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - u_int ipi6_ifindex; /* send/recv interface index */ + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ }; /* @@ -440,16 +444,16 @@ struct in6_pktinfo { #define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ -#if !defined(_XOPEN_SOURCE) +#ifndef _XOPEN_SOURCE /* * Definitions for inet6 sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ -#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */ +#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */ -#define CTL_IPV6PROTO_NAMES { \ +#define CTL_IPV6PROTO_NAMES { \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { "tcp6", CTLTYPE_NODE }, \ @@ -499,51 +503,50 @@ struct in6_pktinfo { /* * Names for IP sysctl objects */ -#define IPV6CTL_FORWARDING 1 /* act as router */ -#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/ -#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */ +#define IPV6CTL_FORWARDING 1 /* act as router */ +#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/ +#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */ #ifdef notyet -#define IPV6CTL_DEFMTU 4 /* default MTU */ +#define IPV6CTL_DEFMTU 4 /* default MTU */ #endif -#define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */ -#define IPV6CTL_STATS 6 /* stats */ -#define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */ -#define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */ -#define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */ -#define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */ -#define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */ -#define IPV6CTL_ACCEPT_RTADV 12 -#define IPV6CTL_KEEPFAITH 13 -#define IPV6CTL_LOG_INTERVAL 14 -#define IPV6CTL_HDRNESTLIMIT 15 -#define IPV6CTL_DAD_COUNT 16 -#define IPV6CTL_AUTO_FLOWLABEL 17 -#define IPV6CTL_DEFMCASTHLIM 18 -#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ -#define IPV6CTL_KAME_VERSION 20 -#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ -#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ -#define IPV6CTL_MAPPED_ADDR 23 +#define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */ +#define IPV6CTL_STATS 6 /* stats */ +#define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */ +#define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */ +#define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */ +#define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */ +#define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */ +#define IPV6CTL_ACCEPT_RTADV 12 +#define IPV6CTL_KEEPFAITH 13 +#define IPV6CTL_LOG_INTERVAL 14 +#define IPV6CTL_HDRNESTLIMIT 15 +#define IPV6CTL_DAD_COUNT 16 +#define IPV6CTL_AUTO_FLOWLABEL 17 +#define IPV6CTL_DEFMCASTHLIM 18 +#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ +#define IPV6CTL_KAME_VERSION 20 +#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ +#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ +#define IPV6CTL_MAPPED_ADDR 23 /* New entries should be added here from current IPV6CTL_MAXID value. */ -#define IPV6CTL_MAXID 24 +#define IPV6CTL_MAXID 24 #endif /* !_XOPEN_SOURCE */ /* * Redefinition of mbuf flags */ -#define M_ANYCAST6 M_PROTO1 -#define M_AUTHIPHDR M_PROTO2 -#define M_DECRYPTED M_PROTO3 -#define M_LOOP M_PROTO4 -#define M_AUTHIPDGM M_PROTO5 +#define M_ANYCAST6 M_PROTO1 +#define M_AUTHIPHDR M_PROTO2 +#define M_DECRYPTED M_PROTO3 +#define M_LOOP M_PROTO4 +#define M_AUTHIPDGM M_PROTO5 #ifdef _KERNEL struct cmsghdr; struct mbuf; struct ifnet; -int in6_canforward __P((struct in6_addr *, struct in6_addr *)); -int in6_cksum __P((struct mbuf *, u_int8_t, int, int)); +int in6_cksum __P((struct mbuf *, u_int8_t, u_int32_t, u_int32_t)); int in6_localaddr __P((struct in6_addr *)); int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithscope __P((struct ifnet *, struct in6_addr *)); @@ -558,9 +561,9 @@ void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin, void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam)); void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam)); -#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) -#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) #endif /* _KERNEL */ __BEGIN_DECLS diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c index 28af01f..1f26502 100644 --- a/sys/netinet6/in6_cksum.c +++ b/sys/netinet6/in6_cksum.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_cksum.c,v 1.6 2000/03/25 07:23:43 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -68,7 +69,7 @@ #include <sys/mbuf.h> #include <sys/systm.h> #include <netinet/in.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <net/net_osdep.h> @@ -79,11 +80,11 @@ * code and should be modified for each CPU to be as fast as possible. */ -#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) -#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} static union { - u_int16_t phs[4]; + u_int16_t phs[4]; struct { u_int32_t ph_len; u_int8_t ph_zero[3]; @@ -102,12 +103,15 @@ int in6_cksum(m, nxt, off, len) register struct mbuf *m; u_int8_t nxt; - register int off, len; + u_int32_t off, len; { register u_int16_t *w; register int sum = 0; register int mlen = 0; int byte_swapped = 0; +#if 0 + int srcifid = 0, dstifid = 0; +#endif struct ip6_hdr *ip6; union { @@ -129,6 +133,16 @@ in6_cksum(m, nxt, off, len) * First create IP6 pseudo header and calculate a summary. */ ip6 = mtod(m, struct ip6_hdr *); +#if 0 + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + srcifid = ip6->ip6_src.s6_addr16[1]; + ip6->ip6_src.s6_addr16[1] = 0; + } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + dstifid = ip6->ip6_dst.s6_addr16[1]; + ip6->ip6_dst.s6_addr16[1] = 0; + } +#endif w = (u_int16_t *)&ip6->ip6_src; uph.ph.ph_len = htonl(len); uph.ph.ph_nxt = nxt; @@ -149,6 +163,12 @@ in6_cksum(m, nxt, off, len) sum += uph.phs[0]; sum += uph.phs[1]; sum += uph.phs[2]; sum += uph.phs[3]; +#if 0 + if (srcifid) + ip6->ip6_src.s6_addr16[1] = srcifid; + if (dstifid) + ip6->ip6_dst.s6_addr16[1] = dstifid; +#endif /* * Secondly calculate a summary of the first mbuf excluding offset. */ diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index 35c4088..e554f5a 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,6 +35,7 @@ */ #include "opt_inet.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/systm.h> @@ -41,7 +43,6 @@ #include <sys/sockio.h> #include <sys/mbuf.h> #include <sys/errno.h> -#include <sys/protosw.h> #include <net/if.h> #include <net/route.h> @@ -51,17 +52,26 @@ #ifdef INET #include <netinet/ip.h> #endif -#include <netinet6/ip6.h> +#include <netinet/ip_encap.h> +#ifdef INET6 +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/in6_gif.h> -#include <netinet6/ip6.h> +#include <netinet6/in6_var.h> +#endif #include <netinet/ip_ecn.h> +#ifdef INET6 #include <netinet6/ip6_ecn.h> +#endif #include <net/if_gif.h> #include <net/net_osdep.h> +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + int in6_gif_output(ifp, family, m, rt) struct ifnet *ifp; @@ -101,6 +111,7 @@ in6_gif_output(ifp, family, m, rt) break; } #endif +#ifdef INET6 case AF_INET6: { struct ip6_hdr *ip6; @@ -114,15 +125,16 @@ in6_gif_output(ifp, family, m, rt) itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } +#endif default: -#ifdef DIAGNOSTIC +#ifdef DEBUG printf("in6_gif_output: warning: unknown family %d passed\n", family); #endif m_freem(m); return EAFNOSUPPORT; } - + /* prepend new IP header */ M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip6_hdr)) @@ -134,7 +146,8 @@ in6_gif_output(ifp, family, m, rt) ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); ip6->ip6_nxt = proto; ip6->ip6_hlim = ip6_gif_hlim; @@ -144,6 +157,10 @@ in6_gif_output(ifp, family, m, rt) if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) ip6->ip6_dst = sin6_dst->sin6_addr; else if (rt) { + if (family != AF_INET6) { + m_freem(m); + return EINVAL; /*XXX*/ + } ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; } else { m_freem(m); @@ -175,6 +192,9 @@ in6_gif_output(ifp, family, m, rt) RTFREE(sc->gif_ro6.ro_rt); sc->gif_ro6.ro_rt = NULL; } +#if 0 + sc->gif_if.if_mtu = GIF_MTU; +#endif } if (sc->gif_ro6.ro_rt == NULL) { @@ -183,9 +203,28 @@ in6_gif_output(ifp, family, m, rt) m_freem(m); return ENETUNREACH; } - } + /* if it constitutes infinite encapsulation, punt. */ + if (sc->gif_ro.ro_rt->rt_ifp == ifp) { + m_freem(m); + return ENETUNREACH; /*XXX*/ + } +#if 0 + ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu + - sizeof(struct ip6_hdr); +#endif + } + +#ifdef IPV6_MINMTU + /* + * force fragmentation to minimum MTU, to avoid path MTU discovery. + * it is too painful to ask for resend of inner packet, to achieve + * path MTU discovery for encapsulated packets. + */ + return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); +#else return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); +#endif } int in6_gif_input(mp, offp, proto) @@ -193,44 +232,21 @@ int in6_gif_input(mp, offp, proto) int *offp, proto; { struct mbuf *m = *mp; - struct gif_softc *sc; struct ifnet *gifp = NULL; struct ip6_hdr *ip6; - int i; int af = 0; u_int32_t otos; ip6 = mtod(m, struct ip6_hdr *); -#define satoin6(sa) (((struct sockaddr_in6 *)(sa))->sin6_addr) - for (i = 0, sc = gif; i < ngif; i++, sc++) { - if (sc->gif_psrc == NULL || - sc->gif_pdst == NULL || - sc->gif_psrc->sa_family != AF_INET6 || - sc->gif_pdst->sa_family != AF_INET6) { - continue; - } - if ((sc->gif_if.if_flags & IFF_UP) == 0) - continue; - if ((sc->gif_if.if_flags & IFF_LINK0) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) { - gifp = &sc->gif_if; - continue; - } - if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) { - gifp = &sc->gif_if; - break; - } - } + gifp = (struct ifnet *)encap_getarg(m); - if (gifp == NULL) { + if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { m_freem(m); ip6stat.ip6s_nogif++; return IPPROTO_DONE; } - + otos = ip6->ip6_flow; m_adj(m, *offp); @@ -253,6 +269,7 @@ int in6_gif_input(mp, offp, proto) break; } #endif /* INET */ +#ifdef INET6 case IPPROTO_IPV6: { struct ip6_hdr *ip6; @@ -267,12 +284,76 @@ int in6_gif_input(mp, offp, proto) ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); break; } +#endif default: ip6stat.ip6s_nogif++; m_freem(m); return IPPROTO_DONE; } - + gif_input(m, af, gifp); return IPPROTO_DONE; } + +/* + * we know that we are in IFF_UP, outer address available, and outer family + * matched the physical addr family. see gif_encapcheck(). + */ +int +gif_encapcheck6(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip6_hdr ip6; + struct gif_softc *sc; + struct sockaddr_in6 *src, *dst; + int addrmatch; + + /* sanity check done in caller */ + sc = (struct gif_softc *)arg; + src = (struct sockaddr_in6 *)sc->gif_psrc; + dst = (struct sockaddr_in6 *)sc->gif_pdst; + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); + + /* check for address match */ + addrmatch = 0; + if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) + addrmatch |= 1; + if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) + addrmatch |= 2; + else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && + IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { + addrmatch |= 2; /* we accept any source */ + } + if (addrmatch != 3) + return 0; + + /* martian filters on outer source - done in ip6_input */ + + /* ingress filters on outer source */ + if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + struct sockaddr_in6 sin6; + struct rtentry *rt; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = ip6.ip6_src; + /* XXX scopeid */ + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + if (!rt) + return 0; + if (rt->rt_ifp != m->m_pkthdr.rcvif) { + rtfree(rt); + return 0; + } + rtfree(rt); + } + + /* prioritize: IFF_LINK0 mode is less preferred */ + return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; +} diff --git a/sys/netinet6/in6_gif.h b/sys/netinet6/in6_gif.h index 65d7635..b1fe104 100644 --- a/sys/netinet6/in6_gif.h +++ b/sys/netinet6/in6_gif.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_gif.h,v 1.5 2000/04/14 08:36:03 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,21 +28,15 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_IN6_GIF_H_ -#define _NETINET6_IN6_GIF_H_ - -#define GIF_HLIM 30 +#define _NETINET6_IN6_GIF_H_ -struct mbuf; -struct ifnet; -struct rtentry; +#define GIF_HLIM 30 -int in6_gif_input __P((struct mbuf **, int *, int)); -int in6_gif_output __P((struct ifnet *, int, struct mbuf *, - struct rtentry *)); +int in6_gif_input __P((struct mbuf **, int *, int)); +int in6_gif_output __P((struct ifnet *, int, struct mbuf *, struct rtentry *)); +int gif_encapcheck6 __P((const struct mbuf *, int, int, void *)); #endif /*_NETINET6_IN6_GIF_H_*/ diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 7d332ed..39d26f7 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_ifattach.c,v 1.61 2000/06/13 08:15:27 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include <sys/param.h> @@ -46,267 +47,657 @@ #include <netinet/in_var.h> #include <netinet/if_ether.h> -#include <netinet6/in6.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/in6_ifattach.h> -#include <netinet6/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> +#include <netinet6/scope6_var.h> #include <net/net_osdep.h> -static struct in6_addr llsol; - -struct in6_ifstat **in6_ifstat = NULL; -struct icmp6_ifstat **icmp6_ifstat = NULL; -size_t in6_ifstatmax = 0; -size_t icmp6_ifstatmax = 0; -unsigned long in6_maxmtu = 0; - -int found_first_ifid = 0; -#define IFID_LEN 8 -static char first_ifid[IFID_LEN]; - -static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); -static int gen_rand_eui64 __P((u_int8_t *)); - -static int -laddr_to_eui64(dst, src, len) - u_int8_t *dst; - u_int8_t *src; - size_t len; -{ - static u_int8_t zero[8]; - - bzero(zero, sizeof(zero)); - - switch (len) { - case 6: - if (bcmp(zero, src, 6) == 0) - return EINVAL; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = 0xff; - dst[4] = 0xfe; - dst[5] = src[3]; - dst[6] = src[4]; - dst[7] = src[5]; - break; - case 8: - if (bcmp(zero, src, 8) == 0) - return EINVAL; - bcopy(src, dst, len); - break; - default: - return EINVAL; - } - - return 0; -} +struct in6_ifstat **in6_ifstat = NULL; +struct icmp6_ifstat **icmp6_ifstat = NULL; +size_t in6_ifstatmax = 0; +size_t icmp6_ifstatmax = 0; +unsigned long in6_maxmtu = 0; + +static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); +static int get_hw_ifid __P((struct ifnet *, struct in6_addr *)); +static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); +static int in6_ifattach_addaddr __P((struct ifnet *, struct in6_ifaddr *)); +static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); +static int in6_ifattach_loopback __P((struct ifnet *)); +static int nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); + +#define EUI64_GBIT 0x01 +#define EUI64_UBIT 0x02 +#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) +#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) +#define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6)) +#define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT) +#define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6)) + +#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6)) +#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6)) /* * Generate a last-resort interface identifier, when the machine has no * IEEE802/EUI64 address sources. - * The address should be random, and should not change across reboot. + * The goal here is to get an interface identifier that is + * (1) random enough and (2) does not change across reboot. + * We currently use MD5(hostname) for it. */ static int -gen_rand_eui64(dst) - u_int8_t *dst; +get_rand_ifid(ifp, in6) + struct ifnet *ifp; + struct in6_addr *in6; /*upper 64bits are preserved */ { MD5_CTX ctxt; u_int8_t digest[16]; int hostnamelen = strlen(hostname); - /* generate 8bytes of pseudo-random value. */ +#if 0 + /* we need at least several letters as seed for ifid */ + if (hostnamelen < 3) + return -1; +#endif + + /* generate 8 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); MD5Update(&ctxt, hostname, hostnamelen); MD5Final(digest, &ctxt); - /* assumes sizeof(digest) > sizeof(first_ifid) */ - bcopy(digest, dst, 8); + /* assumes sizeof(digest) > sizeof(ifid) */ + bcopy(digest, &in6->s6_addr[8], 8); /* make sure to set "u" bit to local, and "g" bit to individual. */ - dst[0] &= 0xfe; - dst[0] |= 0x02; /* EUI64 "local" */ + in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ + in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ + + /* convert EUI64 into IPv6 interface identifier */ + EUI64_TO_IFID(in6); return 0; } /* - * Find first ifid on list of interfaces. - * This is assumed that ifp0's interface token (for example, IEEE802 MAC) - * is globally unique. We may need to have a flag parameter in the future. + * Get interface identifier for the specified interface. + * XXX assumes single sockaddr_dl (AF_LINK address) per an interface */ -int -in6_ifattach_getifid(ifp0) - struct ifnet *ifp0; -{ +static int +get_hw_ifid(ifp, in6) struct ifnet *ifp; + struct in6_addr *in6; /*upper 64bits are preserved */ +{ struct ifaddr *ifa; - u_int8_t *addr = NULL; - int addrlen = 0; struct sockaddr_dl *sdl; - - if (found_first_ifid) - return 0; - - TAILQ_FOREACH(ifp, &ifnet, if_list) + u_int8_t *addr; + size_t addrlen; + static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static u_int8_t allone[8] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) { - if (ifp0 != NULL && ifp0 != ifp) + if (ifa->ifa_addr->sa_family != AF_LINK) continue; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) - { - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - if (sdl == NULL) - continue; - if (sdl->sdl_alen == 0) - continue; - switch (ifp->if_type) { - case IFT_ETHER: - case IFT_FDDI: - case IFT_ATM: - /* IEEE802/EUI64 cases - what others? */ - addr = LLADDR(sdl); - addrlen = sdl->sdl_alen; - /* - * to copy ifid from IEEE802/EUI64 interface, - * u bit of the source needs to be 0. - */ - if ((addr[0] & 0x02) != 0) - break; - goto found; - case IFT_ARCNET: - /* - * ARCnet interface token cannot be used as - * globally unique identifier due to its - * small bitwidth. - */ - break; - default: - break; - } - } + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl == NULL) + continue; + if (sdl->sdl_alen == 0) + continue; + + goto found; } -#ifdef DEBUG - printf("in6_ifattach_getifid: failed to get EUI64"); -#endif - return EADDRNOTAVAIL; + + return -1; found: - if (laddr_to_eui64(first_ifid, addr, addrlen) == 0) - found_first_ifid = 1; - - if (found_first_ifid) { - printf("%s: supplying EUI64: " - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - if_name(ifp), - first_ifid[0] & 0xff, first_ifid[1] & 0xff, - first_ifid[2] & 0xff, first_ifid[3] & 0xff, - first_ifid[4] & 0xff, first_ifid[5] & 0xff, - first_ifid[6] & 0xff, first_ifid[7] & 0xff); - - /* invert u bit to convert EUI64 to RFC2373 interface ID. */ - first_ifid[0] ^= 0x02; - - return 0; - } else { -#ifdef DEBUG - printf("in6_ifattach_getifid: failed to get EUI64"); + addr = LLADDR(sdl); + addrlen = sdl->sdl_alen; + + /* get EUI64 */ + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ATM: + /* IEEE802/EUI64 cases - what others? */ + + /* look at IEEE802/EUI64 only */ + if (addrlen != 8 && addrlen != 6) + return -1; + + /* + * check for invalid MAC address - on bsdi, we see it a lot + * since wildboar configures all-zero MAC on pccard before + * card insertion. + */ + if (bcmp(addr, allzero, addrlen) == 0) + return -1; + if (bcmp(addr, allone, addrlen) == 0) + return -1; + + /* make EUI64 address */ + if (addrlen == 8) + bcopy(addr, &in6->s6_addr[8], 8); + else if (addrlen == 6) { + in6->s6_addr[8] = addr[0]; + in6->s6_addr[9] = addr[1]; + in6->s6_addr[10] = addr[2]; + in6->s6_addr[11] = 0xff; + in6->s6_addr[12] = 0xfe; + in6->s6_addr[13] = addr[3]; + in6->s6_addr[14] = addr[4]; + in6->s6_addr[15] = addr[5]; + } + break; + + case IFT_ARCNET: + if (addrlen != 1) + return -1; + if (!addr[0]) + return -1; + + bzero(&in6->s6_addr[8], 8); + in6->s6_addr[15] = addr[0]; + + /* + * due to insufficient bitwidth, we mark it local. + */ + in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ + in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ + break; + + case IFT_GIF: +#ifdef IFT_STF + case IFT_STF: #endif - return EADDRNOTAVAIL; + /* + * mech-06 says: "SHOULD use IPv4 address as ifid source". + * however, IPv4 address is not very suitable as unique + * identifier source (can be renumbered). + * we don't do this. + */ + return -1; + + default: + return -1; } + + /* sanity check: g bit must not indicate "group" */ + if (EUI64_GROUP(in6)) + return -1; + + /* convert EUI64 into IPv6 interface identifier */ + EUI64_TO_IFID(in6); + + /* + * sanity check: ifid must not be all zero, avoid conflict with + * subnet router anycast + */ + if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && + bcmp(&in6->s6_addr[9], allzero, 7) == 0) { + return -1; + } + + return 0; } /* - * add link-local address to *pseudo* p2p interfaces. - * get called when the first MAC address is made available in in6_ifattach(). - * - * XXX I start considering this loop as a bad idea. (itojun) + * Get interface identifier for the specified interface. If it is not + * available on ifp0, borrow interface identifier from other information + * sources. */ -void -in6_ifattach_p2p() +static int +get_ifid(ifp0, altifp, in6) + struct ifnet *ifp0; + struct ifnet *altifp; /*secondary EUI64 source*/ + struct in6_addr *in6; { struct ifnet *ifp; - /* prevent infinite loop. just in case. */ - if (found_first_ifid == 0) - return; + /* first, try to get it from the interface itself */ + if (get_hw_ifid(ifp0, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: got interface identifier from itself\n", + if_name(ifp0)); +#endif + goto success; + } - TAILQ_FOREACH(ifp, &ifnet, if_list) + /* try secondary EUI64 source. this basically is for ATM PVC */ + if (altifp && get_hw_ifid(altifp, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: got interface identifier from %s\n", + if_name(ifp0), if_name(altifp)); +#endif + goto success; + } + + /* next, try to get it from some other hardware interface */ + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) { - switch (ifp->if_type) { - case IFT_GIF: - /* pseudo interfaces - safe to initialize here */ - in6_ifattach(ifp, IN6_IFT_P2P, 0, 0); - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: + if (ifp == ifp0) + continue; + if (get_hw_ifid(ifp, in6) != 0) + continue; + + /* + * to borrow ifid from other interface, ifid needs to be + * globally unique + */ + if (IFID_UNIVERSAL(in6)) { + +#ifdef ND6_DEBUG + printf("%s: borrow interface identifier from %s\n", + if_name(ifp0), if_name(ifp)); #endif - case IFT_FAITH: - /* this mistakingly becomes IFF_UP */ + goto success; + } + } + + /* last resort: get from random number source */ + if (get_rand_ifid(ifp, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: interface identifier generated by random number\n", + if_name(ifp0)); +#endif + goto success; + } + + printf("%s: failed to get interface identifier", if_name(ifp0)); + return -1; + +success: +#ifdef ND6_DEBUG + printf("%s: ifid: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + if_name(ifp0), + in6->s6_addr[8], in6->s6_addr[9], + in6->s6_addr[10], in6->s6_addr[11], + in6->s6_addr[12], in6->s6_addr[13], + in6->s6_addr[14], in6->s6_addr[15]); +#endif + return 0; +} + +/* + * configure IPv6 interface address. XXX code duplicated with in.c + */ +static int +in6_ifattach_addaddr(ifp, ia) + struct ifnet *ifp; + struct in6_ifaddr *ia; +{ + struct in6_ifaddr *oia; + struct ifaddr *ifa; + int error; + int rtflag; + struct in6_addr llsol; + + /* + * initialize if_addrlist, if we are the very first one + */ + ifa = TAILQ_FIRST(&ifp->if_addrlist); + if (ifa == NULL) { + TAILQ_INIT(&ifp->if_addrlist); + } + + /* + * link the interface address to global list + */ + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + /* gain a refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; + + /* + * Also link into the IPv6 address chain beginning with in6_ifaddr. + * kazu opposed it, but itojun & jinmei wanted. + */ + if ((oia = in6_ifaddr) != NULL) { + for (; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + /* gain another refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; + + /* + * give the interface a chance to initialize, in case this + * is the first address to be added. + */ + if (ifp->if_ioctl != NULL) { + int s; + s = splimp(); + error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); + splx(s); + } else + error = 0; + if (error) { + switch (error) { + case EAFNOSUPPORT: + printf("%s: IPv6 not supported\n", if_name(ifp)); break; - case IFT_SLIP: - /* IPv6 is not supported */ + default: + printf("%s: SIOCSIFADDR error %d\n", if_name(ifp), + error); break; - case IFT_PPP: - /* this is not a pseudo interface, skip it */ + } + + /* undo changes */ + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); + if (oia) + oia->ia_next = ia->ia_next; + else + in6_ifaddr = ia->ia_next; + IFAFREE(&ia->ia_ifa); + return -1; + } + + /* configure link-layer address resolution */ + rtflag = 0; + if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128)) + rtflag = RTF_HOST; + else { + switch (ifp->if_type) { + case IFT_LOOP: +#ifdef IFT_STF + case IFT_STF: +#endif + rtflag = 0; break; default: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + rtflag = RTF_CLONING; break; } } + + /* add route to the interface. */ + { + int e; + + e = rtrequest(RTM_ADD, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_prefixmask, + RTF_UP | rtflag, + (struct rtentry **)0); + if (e) { + printf("in6_ifattach_addaddr: rtrequest failed. errno = %d\n", e); + } + } + ia->ia_flags |= IFA_ROUTE; + + if ((rtflag & RTF_CLONING) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + /* + * join solicited multicast address + */ + bzero(&llsol, sizeof(llsol)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + (void)in6_addmulti(&llsol, ifp, &error); + + /* XXX should we run DAD on other interface types? */ + switch (ifp->if_type) { +#if 1 + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: +#else + default: +#endif + /* mark the address TENTATIVE, if needed. */ + ia->ia6_flags |= IN6_IFF_TENTATIVE; + /* nd6_dad_start() will be called in in6_if_up */ + } + } + + return 0; } -void -in6_ifattach(ifp, type, laddr, noloop) +static int +in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; - u_int type; - caddr_t laddr; - /* size_t laddrlen; */ - int noloop; + struct ifnet *altifp; /*secondary EUI64 source*/ { - static size_t if_indexlim = 8; - struct sockaddr_in6 mltaddr; - struct sockaddr_in6 mltmask; - struct sockaddr_in6 gate; - struct sockaddr_in6 mask; + struct in6_ifaddr *ia; - struct in6_ifaddr *ia, *ib, *oia; - struct ifaddr *ifa; - int rtflag = 0; - - if (type == IN6_IFT_P2P && found_first_ifid == 0) { - printf("%s: no ifid available for IPv6 link-local address\n", - if_name(ifp)); - /* last resort */ - if (gen_rand_eui64(first_ifid) == 0) { - printf("%s: using random value as EUI64: " - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - if_name(ifp), - first_ifid[0] & 0xff, first_ifid[1] & 0xff, - first_ifid[2] & 0xff, first_ifid[3] & 0xff, - first_ifid[4] & 0xff, first_ifid[5] & 0xff, - first_ifid[6] & 0xff, first_ifid[7] & 0xff); - /* - * invert u bit to convert EUI64 to RFC2373 interface - * ID. - */ - first_ifid[0] ^= 0x02; + /* + * configure link-local address + */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + if (ifp->if_flags & IFF_POINTOPOINT) + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + else + ia->ia_ifa.ifa_dstaddr = NULL; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; + + bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; +#ifdef SCOPEDROUTING + /* take into accound the sin6_scope_id field for routing */ + ia->ia_prefixmask.sin6_scope_id = 0xffffffff; +#endif + ia->ia_prefixmask.sin6_addr = in6mask64; - found_first_ifid = 1; + /* just in case */ + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_dstaddr.sin6_family = AF_INET6; + + bzero(&ia->ia_addr, sizeof(ia->ia_addr)); + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); + ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + ia->ia_addr.sin6_addr.s6_addr32[1] = 0; + if (ifp->if_flags & IFF_LOOPBACK) { + ia->ia_addr.sin6_addr.s6_addr32[2] = 0; + ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); + } else { + if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) { +#ifdef ND6_DEBUG + printf("%s: no ifid available\n", if_name(ifp)); +#endif + free(ia, M_IFADDR); + return -1; } } +#ifdef SCOPEDROUTING + ia->ia_addr.sin6_scope_id = in6_addr2scopeid(ifp, + &ia->ia_addr.sin6_addr); +#endif - if ((ifp->if_flags & IFF_MULTICAST) == 0) { - printf("%s: not multicast capable, IPv6 not enabled\n", - if_name(ifp)); + ia->ia_ifa.ifa_metric = ifp->if_metric; + + if (in6_ifattach_addaddr(ifp, ia) != 0) { + /* ia will be freed on failure */ + return -1; + } + + return 0; +} + +static int +in6_ifattach_loopback(ifp) + struct ifnet *ifp; /* must be IFT_LOOP */ +{ + struct in6_ifaddr *ia; + + /* + * configure link-local address + */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; + + bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; + ia->ia_prefixmask.sin6_addr = in6mask128; + + /* + * Always initialize ia_dstaddr (= broadcast address) to loopback + * address, to make getifaddr happier. + * + * For BSDI, it is mandatory. The BSDI version of + * ifa_ifwithroute() rejects to add a route to the loopback + * interface. Even for other systems, loopback looks somewhat + * special. + */ + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_dstaddr.sin6_family = AF_INET6; + ia->ia_dstaddr.sin6_addr = in6addr_loopback; + + bzero(&ia->ia_addr, sizeof(ia->ia_addr)); + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_addr = in6addr_loopback; + + ia->ia_ifa.ifa_metric = ifp->if_metric; + + if (in6_ifattach_addaddr(ifp, ia) != 0) { + /* ia will be freed on failure */ + return -1; + } + + return 0; +} + +/* + * compute NI group address, based on the current hostname setting. + * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later). + * + * when ifp == NULL, the caller is responsible for filling scopeid. + */ +static int +nigroup(ifp, name, namelen, in6) + struct ifnet *ifp; + const char *name; + int namelen; + struct in6_addr *in6; +{ + const char *p; + MD5_CTX ctxt; + u_int8_t digest[16]; + char l; + + if (!namelen || !name) + return -1; + + p = name; + while (p && *p && *p != '.' && p - name < namelen) + p++; + if (p - name > 63) + return -1; /*label too long*/ + l = p - name; + + /* generate 8 bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, &l, sizeof(l)); + /* LINTED const cast */ + MD5Update(&ctxt, (void *)name, p - name); + MD5Final(digest, &ctxt); + + bzero(in6, sizeof(*in6)); + in6->s6_addr16[0] = htons(0xff02); + if (ifp) + in6->s6_addr16[1] = htons(ifp->if_index); + in6->s6_addr8[11] = 2; + bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3])); + + return 0; +} + +void +in6_nigroup_attach(name, namelen) + const char *name; + int namelen; +{ + struct ifnet *ifp; + struct sockaddr_in6 mltaddr; + struct in6_multi *in6m; + int error; + + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) + return; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (!in6m) + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + } +} + +void +in6_nigroup_detach(name, namelen) + const char *name; + int namelen; +{ + struct ifnet *ifp; + struct sockaddr_in6 mltaddr; + struct in6_multi *in6m; + + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) return; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m) + in6_delmulti(in6m); } +} + +/* + * XXX multiple loopback interface needs more care. for instance, + * nodelocal address needs to be configured onto only one of them. + * XXX multiple link-local address case + */ +void +in6_ifattach(ifp, altifp) + struct ifnet *ifp; + struct ifnet *altifp; /* secondary EUI64 source */ +{ + static size_t if_indexlim = 8; + struct sockaddr_in6 mltaddr; + struct sockaddr_in6 mltmask; + struct sockaddr_in6 gate; + struct sockaddr_in6 mask; + struct in6_ifaddr *ia; + struct in6_addr in6; + int hostnamelen = strlen(hostname); /* * We have some arrays that should be indexed by if_index. @@ -314,8 +705,8 @@ in6_ifattach(ifp, type, laddr, noloop) * struct in6_ifstat **in6_ifstat * struct icmp6_ifstat **icmp6_ifstat */ - if (in6_ifstat == NULL || icmp6_ifstat == NULL - || if_index >= if_indexlim) { + if (in6_ifstat == NULL || icmp6_ifstat == NULL || + if_index >= if_indexlim) { size_t n; caddr_t q; size_t olim; @@ -349,144 +740,53 @@ in6_ifattach(ifp, type, laddr, noloop) icmp6_ifstatmax = if_indexlim; } + /* initialize scope identifiers */ + scope6_ifattach(ifp); + /* - * To prevent to assign link-local address to PnP network - * cards multiple times. - * This is lengthy for P2P and LOOP but works. + * quirks based on interface type */ - ifa = TAILQ_FIRST(&ifp->if_addrlist); - if (ifa != NULL) { - for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr)) - return; - } - } else { - TAILQ_INIT(&ifp->if_addrlist); + switch (ifp->if_type) { +#ifdef IFT_STF + case IFT_STF: + /* + * 6to4 interface is a very speical kind of beast. + * no multicast, no linklocal (based on 03 draft). + */ + goto statinit; +#endif + default: + break; } /* - * link-local address + * usually, we require multicast capability to the interface */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - /* - * Also link into the IPv6 address chain beginning with in6_ifaddr. - * kazu opposed it, but itojun & jinmei wanted. - */ - if ((oia = in6_ifaddr) != NULL) { - for (; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_addr = in6mask64; - - bzero(&ia->ia_addr, sizeof(struct sockaddr_in6)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); - ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - ia->ia_addr.sin6_addr.s6_addr32[1] = 0; - - switch (type) { - case IN6_IFT_LOOP: - ia->ia_addr.sin6_addr.s6_addr32[2] = 0; - ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); - break; - case IN6_IFT_802: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - /* fall through */ - case IN6_IFT_P2P802: - if (laddr == NULL) - break; - /* XXX use laddrlen */ - if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8], - laddr, 6) != 0) { - break; - } - /* invert u bit to convert EUI64 to RFC2373 interface ID. */ - ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02; - if (found_first_ifid == 0) { - if (in6_ifattach_getifid(ifp) == 0) - in6_ifattach_p2p(); - } - break; - case IN6_IFT_P2P: - bcopy((caddr_t)first_ifid, - (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8], - IFID_LEN); - break; - case IN6_IFT_ARCNET: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - if (laddr == NULL) - break; - - /* make non-global IF id out of link-level address */ - bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7); - ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr; + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + printf("%s: not multicast capable, IPv6 not enabled\n", + if_name(ifp)); + return; } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - if (ifp->if_ioctl != NULL) { - int s; - int error; - - /* - * give the interface a chance to initialize, in case this - * is the first address to be added. - */ - s = splimp(); - error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); - splx(s); + /* + * assign link-local address, if there's none + */ + ia = in6ifa_ifpforlinklocal(ifp, 0); + if (ia == NULL) { + if (in6_ifattach_linklocal(ifp, altifp) != 0) + return; + ia = in6ifa_ifpforlinklocal(ifp, 0); - if (error) { - switch (error) { - case EAFNOSUPPORT: - printf("%s: IPv6 not supported\n", - if_name(ifp)); - break; - default: - printf("%s: SIOCSIFADDR error %d\n", - if_name(ifp), error); - break; - } + if (ia == NULL) { + printf("%s: failed to add link-local address", + if_name(ifp)); - /* undo changes */ - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - if (oia) - oia->ia_next = ia->ia_next; - else - in6_ifaddr = ia->ia_next; - free(ia, M_IFADDR); - return; + /* we can't initialize multicasts without link-local */ + goto statinit; } } - /* add route to the interface. */ - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP|rtflag, - (struct rtentry **)0); - ia->ia_flags |= IFA_ROUTE; - - if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) { + if (ifp->if_flags & IFF_POINTOPOINT) { /* * route local address to loopback */ @@ -507,45 +807,30 @@ in6_ifattach(ifp, type, laddr, noloop) } /* - * loopback address + * assign loopback address for loopback interface + * XXX multiple loopback interface case */ - ib = (struct in6_ifaddr *)NULL; - if (type == IN6_IFT_LOOP) { - ib = (struct in6_ifaddr *) - malloc(sizeof(*ib), M_IFADDR, M_WAITOK); - bzero((caddr_t)ib, sizeof(*ib)); - ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr; - ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr; - ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask; - ib->ia_ifp = ifp; - - ia->ia_next = ib; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib, - ifa_list); - - ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ib->ia_prefixmask.sin6_family = AF_INET6; - ib->ia_prefixmask.sin6_addr = in6mask128; - ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ib->ia_addr.sin6_family = AF_INET6; - ib->ia_addr.sin6_addr = in6addr_loopback; - ib->ia_ifa.ifa_metric = ifp->if_metric; - - rtrequest(RTM_ADD, - (struct sockaddr *)&ib->ia_addr, - (struct sockaddr *)&ib->ia_addr, - (struct sockaddr *)&ib->ia_prefixmask, - RTF_UP|RTF_HOST, - (struct rtentry **)0); + in6 = in6addr_loopback; + if (ifp->if_flags & IFF_LOOPBACK) { + if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { + if (in6_ifattach_loopback(ifp) != 0) + return; + } + } - ib->ia_flags |= IFA_ROUTE; +#ifdef DIAGNOSTIC + if (!ia) { + panic("ia == NULL in in6_ifattach"); + /*NOTREACHED*/ } +#endif /* * join multicast */ if (ifp->if_flags & IFF_MULTICAST) { int error; /* not used */ + struct in6_multi *in6m; bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); @@ -560,41 +845,53 @@ in6_ifattach(ifp, type, laddr, noloop) mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP|RTF_CLONING, /* xxx */ - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - if (type == IN6_IFT_LOOP) { - /* - * join node-local all-nodes address - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL) { rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ib->ia_addr, + (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, - RTF_UP, + RTF_UP|RTF_CLONING, /* xxx */ (struct rtentry **)0); (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - } else { + } + + /* + * join node information group address + */ + if (nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) + == 0) { + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + (void)in6_addmulti(&mltaddr.sin6_addr, + ifp, &error); + } + } + + if (ifp->if_flags & IFF_LOOPBACK) { + in6 = in6addr_loopback; + ia = in6ifa_ifpwithaddr(ifp, &in6); /* - * join solicited multicast address + * join node-local all-nodes address, on loopback */ - bzero(&llsol, sizeof(llsol)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error); + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP, + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + } } } +statinit:; + /* update dynamically. */ if (in6_maxmtu < ifp->if_mtu) in6_maxmtu = ifp->if_mtu; @@ -612,39 +909,44 @@ in6_ifattach(ifp, type, laddr, noloop) /* initialize NDP variables */ nd6_ifattach(ifp); - - /* mark the address TENTATIVE, if needed. */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - ia->ia6_flags |= IN6_IFF_TENTATIVE; - /* nd6_dad_start() will be called in in6_if_up */ - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_GIF: /*XXX*/ - case IFT_LOOP: - case IFT_FAITH: - default: - break; - } - - return; } +/* + * NOTE: in6_ifdetach() does not support loopback if at this moment. + */ void in6_ifdetach(ifp) struct ifnet *ifp; { struct in6_ifaddr *ia, *oia; - struct ifaddr *ifa; + struct ifaddr *ifa, *next; struct rtentry *rt; short rtflags; + struct sockaddr_in6 sin6; + struct in6_multi *in6m; + struct in6_multi *in6m_next; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + /* nuke prefix list. this may try to remove some of ifaddrs as well */ + in6_purgeprefix(ifp); + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* nuke any of IPv6 addresses we have */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { + next = ifa->ifa_list.tqe_next; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + in6_purgeaddr(ifa, ifp); + } + + /* undo everything done by in6_ifattach(), just in case */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) + { + next = ifa->ifa_list.tqe_next; + + if (ifa->ifa_addr->sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { continue; @@ -666,6 +968,7 @@ in6_ifdetach(ifp) /* remove from the linked list */ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); /* also remove from the IPv6 address chain(itojun&jinmei) */ oia = ia; @@ -676,13 +979,38 @@ in6_ifdetach(ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; -#ifdef DEBUG +#ifdef ND6_DEBUG else printf("%s: didn't unlink in6ifaddr from " "list\n", if_name(ifp)); #endif } - free(ia, M_IFADDR); + IFAFREE(&oia->ia_ifa); + } + + /* leave from all multicast groups joined */ + for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) { + in6m_next = LIST_NEXT(in6m, in6m_entry); + if (in6m->in6m_ifp != ifp) + continue; + in6_delmulti(in6m); + in6m = NULL; + } + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* remove route to link-local allnodes multicast (ff02::1) */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_linklocal_allnodes; + sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + if ((rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL)) != NULL) + { + rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); + rtfree(rt); } } diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index 03dbac7..4f82e41 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_ifattach.h,v 1.10 2000/05/27 02:57:05 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,26 +28,16 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_IN6_IFATTACH_H_ -#define _NETINET6_IN6_IFATTACH_H_ +#define _NETINET6_IN6_IFATTACH_H_ #ifdef _KERNEL -extern int found_first_ifid; - -int in6_ifattach_getifid __P((struct ifnet *)); -void in6_ifattach_p2p __P((void)); -void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int)); -void in6_ifdetach __P((struct ifnet *)); +void in6_nigroup_attach __P((const char *, int)); +void in6_nigroup_detach __P((const char *, int)); +void in6_ifattach __P((struct ifnet *, struct ifnet *)); +void in6_ifdetach __P((struct ifnet *)); #endif /* _KERNEL */ -#define IN6_IFT_LOOP 1 -#define IN6_IFT_P2P 2 -#define IN6_IFT_802 3 -#define IN6_IFT_P2P802 4 -#define IN6_IFT_ARCNET 5 - #endif /* _NETINET6_IN6_IFATTACH_H_ */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 73c169b..bc7b0cd 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_pcb.c,v 1.8 2000/06/09 00:37:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -26,7 +29,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* @@ -62,7 +64,6 @@ * SUCH DAMAGE. * * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 - * $FreeBSD$ */ #include "opt_ipsec.h" @@ -90,7 +91,7 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/in_systm.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet/ip_var.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> @@ -105,11 +106,6 @@ #include <netinet6/ipsec6.h> #include <netinet6/ah6.h> #include <netkey/key.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif /* IPSEC_DEBUG */ #endif /* IPSEC */ struct in6_addr zeroin6_addr; @@ -121,12 +117,10 @@ in6_pcbbind(inp, nam, p) struct proc *p; { struct socket *so = inp->inp_socket; - unsigned short *lastport; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - int error; if (!in6_ifaddr) /* XXX broken! */ return (EADDRNOTAVAIL); @@ -144,36 +138,11 @@ in6_pcbbind(inp, nam, p) if (nam->sa_family != AF_INET6) return(EAFNOSUPPORT); - /* - * If the scope of the destination is link-local, embed the - * interface index in the address. - */ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { - /* XXX boundary check is assumed to be already done. */ - /* XXX sin6_scope_id is weaker than advanced-api. */ - struct in6_pktinfo *pi; - if (inp->in6p_outputopts && - (pi = inp->in6p_outputopts->ip6po_pktinfo) && - pi->ipi6_ifindex) { - sin6->sin6_addr.s6_addr16[1] - = htons(pi->ipi6_ifindex); - } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) - && inp->in6p_moptions - && inp->in6p_moptions->im6o_multicast_ifp) { - sin6->sin6_addr.s6_addr16[1] = - htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); - } else if (sin6->sin6_scope_id) { - /* boundary check */ - if (sin6->sin6_scope_id < 0 - || if_index < sin6->sin6_scope_id) { - return ENXIO; /* XXX EINVAL? */ - } - sin6->sin6_addr.s6_addr16[1] - = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ - /* this must be cleared for ifa_ifwithaddr() */ - sin6->sin6_scope_id = 0; - } - } + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) + return EINVAL; + /* this must be cleared for ifa_ifwithaddr() */ + sin6->sin6_scope_id = 0; lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { @@ -224,7 +193,7 @@ in6_pcbbind(inp, nam, p) (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0 && + if (ip6_mapped_addr_on != 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -246,7 +215,7 @@ in6_pcbbind(inp, nam, p) lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return(EADDRINUSE); - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0 && + if (ip6_mapped_addr_on != 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -266,82 +235,17 @@ in6_pcbbind(inp, nam, p) inp->in6p_laddr = sin6->sin6_addr; } if (lport == 0) { - ushort first, last; - int count; - - inp->inp_flags |= INP_ANONPORT; - - if (inp->inp_flags & INP_HIGHPORT) { - first = ipport_hifirstauto; /* sysctl */ - last = ipport_hilastauto; - lastport = &pcbinfo->lasthi; - } else if (inp->inp_flags & INP_LOWPORT) { - if (p && (error = suser_xxx(0, p, PRISON_ROOT))) - return error; - first = ipport_lowfirstauto; /* 1023 */ - last = ipport_lowlastauto; /* 600 */ - lastport = &pcbinfo->lastlow; - } else { - first = ipport_firstauto; /* sysctl */ - last = ipport_lastauto; - lastport = &pcbinfo->lastport; - } - /* - * Simple check to ensure all ports are not used up causing - * a deadlock here. - * - * We split the two cases (up and down) so that the direction - * is not being tested on each round of the loop. - */ - if (first > last) { - /* - * counting down - */ - count = first - last; - - do { - if (count-- < 0) { /* completely used? */ - /* - * Undo any address bind that may have - * occurred above. - */ - inp->in6p_laddr = in6addr_any; - return (EAGAIN); - } - --*lastport; - if (*lastport > first || *lastport < last) - *lastport = first; - lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, - &inp->in6p_laddr, lport, wild)); - } else { - /* - * counting up - */ - count = last - first; - - do { - if (count-- < 0) { /* completely used? */ - /* - * Undo any address bind that may have - * occurred above. - */ - inp->in6p_laddr = in6addr_any; - return (EAGAIN); - } - ++*lastport; - if (*lastport < first || *lastport > last) - *lastport = first; - lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, - &inp->in6p_laddr, lport, wild)); - } + int e; + if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, p)) != 0) + return(e); } - inp->inp_lport = lport; - if (in_pcbinshash(inp) != 0) { - inp->in6p_laddr = in6addr_any; - inp->inp_lport = 0; - return (EAGAIN); + else { + inp->inp_lport = lport; + if (in_pcbinshash(inp) != 0) { + inp->in6p_laddr = in6addr_any; + inp->inp_lport = 0; + return (EAGAIN); + } } inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/ return(0); @@ -377,35 +281,9 @@ in6_pcbladdr(inp, nam, plocal_addr6) if (sin6->sin6_port == 0) return (EADDRNOTAVAIL); - /* - * If the scope of the destination is link-local, embed the interface - * index in the address. - */ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { - /* XXX boundary check is assumed to be already done. */ - /* XXX sin6_scope_id is weaker than advanced-api. */ - if (inp->in6p_outputopts && - (pi = inp->in6p_outputopts->ip6po_pktinfo) && - pi->ipi6_ifindex) { - sin6->sin6_addr.s6_addr16[1] = htons(pi->ipi6_ifindex); - ifp = ifindex2ifnet[pi->ipi6_ifindex]; - } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && - inp->in6p_moptions && - inp->in6p_moptions->im6o_multicast_ifp) { - sin6->sin6_addr.s6_addr16[1] = - htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); - ifp = ifindex2ifnet[inp->in6p_moptions->im6o_multicast_ifp->if_index]; - } else if (sin6->sin6_scope_id) { - /* boundary check */ - if (sin6->sin6_scope_id < 0 - || if_index < sin6->sin6_scope_id) { - return ENXIO; /* XXX EINVAL? */ - } - sin6->sin6_addr.s6_addr16[1] - = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ - ifp = ifindex2ifnet[sin6->sin6_scope_id]; - } - } + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0) + return EINVAL; if (in6_ifaddr) { /* @@ -440,8 +318,6 @@ in6_pcbladdr(inp, nam, plocal_addr6) if (inp->in6p_route.ro_rt) ifp = inp->in6p_route.ro_rt->rt_ifp; - inp->in6p_ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp); - return(0); } @@ -499,6 +375,7 @@ in6_pcbconnect(inp, nam, p) return (0); } +#if 0 /* * Return an IPv6 address, which is the most appropriate for given * destination and user specified options. @@ -699,6 +576,7 @@ in6_selecthlim(in6p, ifp) else return(ip6_defhlim); } +#endif void in6_pcbdisconnect(inp) @@ -891,10 +769,11 @@ in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) int cmd; void (*notify) __P((struct inpcb *, int)); { - struct inpcb *inp, *oinp; + struct inpcb *inp, *ninp; struct in6_addr faddr6; u_short fport = fport_arg, lport = lport_arg; int errno, s; + int do_rtchange = (notify == in6_rtchange); if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return; @@ -904,8 +783,9 @@ in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) /* * Redirects go to all references to the destination, - * and use in_rtchange to invalidate the route cache. - * Dead host indications: notify all references to the destination. + * and use in6_rtchange to invalidate the route cache. + * Dead host indications: also use in6_rtchange to invalidate + * the cache, and deliver the error to all the sockets. * Otherwise, if we have knowledge of the local port and address, * deliver only to that socket. */ @@ -913,29 +793,43 @@ in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) fport = 0; lport = 0; bzero((caddr_t)laddr6, sizeof(*laddr6)); - if (cmd != PRC_HOSTDEAD) - notify = in6_rtchange; + + do_rtchange = 1; } errno = inet6ctlerrmap[cmd]; s = splnet(); - for (inp = LIST_FIRST(head); inp != NULL;) { - if ((inp->inp_vflag & INP_IPV6) == 0) { - inp = LIST_NEXT(inp, inp_list); + for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) { + ninp = LIST_NEXT(inp, inp_list); + + if ((inp->inp_vflag & INP_IPV6) == NULL) continue; - } + + if (do_rtchange) { + /* + * Since a non-connected PCB might have a cached route, + * we always call in6_rtchange without matching + * the PCB to the src/dst pair. + * + * XXX: we assume in6_rtchange does not free the PCB. + */ + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_route.ro_dst.sin6_addr, + &faddr6)) + in6_rtchange(inp, errno); + + if (notify == in6_rtchange) + continue; /* there's nothing to do any more */ + } + if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &faddr6) || inp->inp_socket == 0 || (lport && inp->inp_lport != lport) || (!IN6_IS_ADDR_UNSPECIFIED(laddr6) && !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr6)) || - (fport && inp->inp_fport != fport)) { - inp = LIST_NEXT(inp, inp_list); + (fport && inp->inp_fport != fport)) continue; - } - oinp = inp; - inp = LIST_NEXT(inp, inp_list); + if (notify) - (*notify)(oinp, errno); + (*notify)(inp, errno); } splx(s); } diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index 6c59574..2ea3107 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_pcb.h,v 1.5 2000/07/03 06:19:53 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -26,7 +29,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* @@ -100,9 +102,14 @@ struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, struct ip6_moptions *, struct route_in6 *, struct in6_addr *, int *)); -int in6_selecthlim __P((struct inpcb *, struct ifnet *)); - +int in6_selecthlim __P((struct in6pcb *, struct ifnet *)); +int in6_pcbsetport __P((struct in6_addr *, struct inpcb *, struct proc *)); void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m)); + +int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, + struct inpcb *, struct ifnet **)); +int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, + struct ifnet *)); #endif /* _KERNEL */ #endif /* !_NETINET6_IN6_PCB_H_ */ diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c index a323991..b75a72f 100644 --- a/sys/netinet6/in6_prefix.c +++ b/sys/netinet6/in6_prefix.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_prefix.c,v 1.30 2000/06/12 14:53:17 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -78,7 +79,7 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/in6_prefix.h> #include <netinet6/ip6_var.h> @@ -91,11 +92,10 @@ struct rr_prhead rr_prefix; static void add_each_addr __P((struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)); -static int create_ra_entry __P((struct rp_addr **rapp)); -static int add_each_prefix __P((struct socket *so, - struct rr_prefix *rpp)); -static void free_rp_entries __P((struct rr_prefix *rpp)); -static int link_stray_ia6s __P((struct rr_prefix *rpp)); +static int create_ra_entry __P((struct rp_addr **rapp)); +static int add_each_prefix __P((struct socket *so, struct rr_prefix *rpp)); +static void free_rp_entries __P((struct rr_prefix *rpp)); +static int link_stray_ia6s __P((struct rr_prefix *rpp)); static void rp_remove __P((struct rr_prefix *rpp)); /* @@ -155,7 +155,8 @@ in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst) /* search matched prefix */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -203,7 +204,8 @@ search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr) return rpp; for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -213,7 +215,7 @@ search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr) } if (ifpr != NULL) log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s" - "has no pointer to prefix %s", ip6_sprintf(IFA_IN6(ifa)), + "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)), ip6_sprintf(IFPR_IN6(ifpr))); return ifpr2rp(ifpr); } @@ -232,7 +234,8 @@ mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr) /* search matched prefixes */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -272,7 +275,7 @@ mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr) } else log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:" "no back pointer to ifprefix for %s. " - "ND autoconfigured addr?", + "ND autoconfigured addr?\n", ip6_sprintf(IFA_IN6(ifa))); } return matched; @@ -288,7 +291,8 @@ delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr) /* search matched prefixes */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -307,7 +311,8 @@ unmark_prefixes(struct ifnet *ifp) /* unmark all prefix */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -320,6 +325,7 @@ unmark_prefixes(struct ifnet *ifp) static void init_prefix_ltimes(struct rr_prefix *rpp) { + if (rpp->rp_pltime == RR_INFINITE_LIFETIME || rpp->rp_rrf_decrprefd == 0) rpp->rp_preferred = 0; @@ -368,15 +374,17 @@ search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid) struct rp_addr *rap; LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) + { if (rr_are_ifid_equal(ifid, &rap->ra_ifid, (sizeof(struct in6_addr) << 3) - rpp->rp_plen)) break; + } return rap; } static int -assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) +assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) { int error = 0; struct rp_addr *rap; @@ -391,10 +399,10 @@ assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen); /* link to ia, and put into list */ rap->ra_addr = ia; - /* - * Can't point rp2ifpr(rpp) from ia->ia6_ifpr now, - * because rpp may be on th stack. should fix it? - */ + rap->ra_addr->ia_ifa.ifa_refcnt++; +#if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */ + ia->ia6_ifpr = rp2ifpr(rpp); +#endif s = splnet(); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); splx(s); @@ -402,6 +410,10 @@ assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) return 0; } +/* + * add a link-local address to an interface. we will add new interface address + * (prefix database + new interface id). + */ static int in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia) { @@ -419,7 +431,14 @@ in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia) /* XXX: init dummy so */ bzero(&so, sizeof(so)); /* insert into list */ - LIST_FOREACH(rpp, &rr_prefix, rp_entry) { + LIST_FOREACH(rpp, &rr_prefix, rp_entry) + { + /* + * do not attempt to add an address, if ifp does not match + */ + if (rpp->rp_ifp != ia->ia_ifp) + continue; + s = splnet(); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); splx(s); @@ -428,7 +447,10 @@ in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia) return 0; } - +/* + * add an address to an interface. if the interface id portion is new, + * we will add new interface address (prefix database + new interface id). + */ int in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) { @@ -490,12 +512,13 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) } rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia)); if (rap != NULL) { - if (rap->ra_addr == NULL) + if (rap->ra_addr == NULL) { rap->ra_addr = ia; - else if (rap->ra_addr != ia) { + rap->ra_addr->ia_ifa.ifa_refcnt++; + } else if (rap->ra_addr != ia) { /* There may be some inconsistencies between addrs. */ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix" - "has already another ia %p(%s) on its ifid list", + "has already another ia %p(%s) on its ifid list\n", ip6_sprintf(IA6_IN6(ia)), plen, rap->ra_addr, ip6_sprintf(IA6_IN6(rap->ra_addr))); @@ -504,7 +527,7 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) ia->ia6_ifpr = ifpr; return 0; } - error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia); + error = assign_ra_entry(ifpr2rp(ifpr), iilen, ia); if (error == 0) ia->ia6_ifpr = ifpr; return (error); @@ -522,12 +545,33 @@ in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia) int s = splnet(); LIST_REMOVE(rap, ra_entry); splx(s); + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } + if (LIST_EMPTY(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead)) rp_remove(ifpr2rp(ia->ia6_ifpr)); } +void +in6_purgeprefix(ifp) + struct ifnet *ifp; +{ + struct ifprefix *ifpr, *nextifpr; + + /* delete prefixes before ifnet goes away */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = nextifpr) + { + nextifpr = TAILQ_NEXT(ifpr, ifpr_list); + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + (void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL); + } +} + static void add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) { @@ -559,12 +603,16 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (ia6 != NULL) { if (ia6->ia6_ifpr == NULL) { /* link this addr and the prefix each other */ + IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; + rap->ra_addr->ia_ifa.ifa_refcnt++; ia6->ia6_ifpr = rp2ifpr(rpp); return; } if (ia6->ia6_ifpr == rp2ifpr(rpp)) { + IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; + rap->ra_addr->ia_ifa.ifa_refcnt++; return; } /* @@ -578,7 +626,7 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) * log it and return. */ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because there is already another addr %s/%d", + "%s/%d failed because there is already another addr %s/%d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, ip6_sprintf(IA6_IN6(ia6)), in6_mask2len(&ia6->ia_prefixmask.sin6_addr)); @@ -588,10 +636,10 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (rap->ra_flags.anycast != 0) ifra.ifra_flags |= IN6_IFF_ANYCAST; error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp, - curproc); + curproc); if (error != 0) log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because in6_control failed for error %d", + "%s/%d failed because in6_control failed for error %d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, error); return; @@ -612,7 +660,8 @@ rrpr_update(struct socket *so, struct rr_prefix *new) /* search existing prefix */ for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; @@ -653,6 +702,8 @@ rrpr_update(struct socket *so, struct rr_prefix *new) LIST_REMOVE(rap, ra_entry); if (search_ifidwithprefix(rpp, &rap->ra_ifid) != NULL) { + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); continue; } @@ -669,7 +720,7 @@ rrpr_update(struct socket *so, struct rr_prefix *new) M_NOWAIT); if (rpp == NULL) { log(LOG_ERR, "in6_prefix.c: rrpr_update:%d" - ": ENOBUFS for rr_prefix", __LINE__); + ": ENOBUFS for rr_prefix\n", __LINE__); return(ENOBUFS); } /* initilization */ @@ -685,7 +736,7 @@ rrpr_update(struct socket *so, struct rr_prefix *new) /* let rp_ifpr.ifpr_prefix point rr_prefix. */ rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix; - /* link rr_prefix entry to if_prefixhead */ + /* link rr_prefix entry to if_prefixlist */ { struct ifnet *ifp = rpp->rp_ifp; struct ifprefix *ifpr; @@ -715,7 +766,8 @@ rrpr_update(struct socket *so, struct rr_prefix *new) * If it existed but not pointing to the prefix yet, * init the prefix pointer. */ - LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) { + LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) + { if (rap->ra_addr != NULL) { if (rap->ra_addr->ia6_ifpr == NULL) rap->ra_addr->ia6_ifpr = rp2ifpr(rpp); @@ -739,7 +791,7 @@ rp_remove(struct rr_prefix *rpp) int s; s = splnet(); - /* unlink rp_entry from if_prefixhead */ + /* unlink rp_entry from if_prefixlist */ { struct ifnet *ifp = rpp->rp_ifp; struct ifprefix *ifpr; @@ -754,8 +806,8 @@ rp_remove(struct rr_prefix *rpp) if (TAILQ_NEXT(ifpr, ifpr_list)) TAILQ_NEXT(ifpr, ifpr_list) = TAILQ_NEXT(rp2ifpr(rpp), ifpr_list); - else - printf("Couldn't unlink rr_prefix from ifp\n"); + else + printf("Couldn't unlink rr_prefix from ifp\n"); } } /* unlink rp_entry from rr_prefix list */ @@ -771,7 +823,7 @@ create_ra_entry(struct rp_addr **rapp) M_NOWAIT); if (*rapp == NULL) { log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS" - "for rp_addr", __LINE__); + "for rp_addr\n", __LINE__); return ENOBUFS; } bzero(*rapp, sizeof(*(*rapp))); @@ -803,7 +855,8 @@ init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr, irr->irr_u_uselen, min(ifpr->ifpr_plen - irr->irr_u_uselen, irr->irr_u_keeplen)); - LIST_FOREACH(orap, &(ifpr2rp(ifpr)->rp_addrhead), ra_entry) { + LIST_FOREACH(orap, &(ifpr2rp(ifpr)->rp_addrhead), ra_entry) + { struct rp_addr *rap; int error = 0; @@ -841,6 +894,8 @@ free_rp_entries(struct rr_prefix *rpp) rap = LIST_FIRST(&rpp->rp_addrhead); LIST_REMOVE(rap, ra_entry); + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } } @@ -854,7 +909,8 @@ add_useprefixes(struct socket *so, struct ifnet *ifp, int error = 0; /* add prefixes to each of marked prefix */ - for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) + { nextifpr = TAILQ_NEXT(ifpr, ifpr_list); if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) @@ -876,7 +932,8 @@ unprefer_prefix(struct rr_prefix *rpp) { struct rp_addr *rap; - LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) { + for (rap = rpp->rp_addrhead.lh_first; rap != NULL; + rap = rap->ra_entry.le_next) { if (rap->ra_addr == NULL) continue; rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second; @@ -885,15 +942,14 @@ unprefer_prefix(struct rr_prefix *rpp) } int -delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin) +delete_each_prefix(struct rr_prefix *rpp, u_char origin) { - struct in6_aliasreq ifra; int error = 0; if (rpp->rp_origin > origin) return(EPERM); - while (!LIST_EMPTY(&rpp->rp_addrhead)) { + while (rpp->rp_addrhead.lh_first != NULL) { struct rp_addr *rap; int s; @@ -909,22 +965,8 @@ delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin) } rap->ra_addr->ia6_ifpr = NULL; - bzero(&ifra, sizeof(ifra)); - strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), - sizeof(ifra.ifra_name)); - ifra.ifra_addr = rap->ra_addr->ia_addr; - ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr; - ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask; - - error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, - rpp->rp_ifp, curproc); - if (error != 0) - log(LOG_ERR, "in6_prefix.c: delete_each_prefix:" - "deletion of an addr %s/%d failed because" - "in6_control failed for error %d", - ip6_sprintf(&ifra.ifra_addr.sin6_addr), - rpp->rp_plen, error); - + in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp); + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } rp_remove(rpp); @@ -933,18 +975,19 @@ delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin) } static void -delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin) +delete_prefixes(struct ifnet *ifp, u_char origin) { struct ifprefix *ifpr, *nextifpr; /* delete prefixes marked as tobe deleted */ - for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) + { nextifpr = TAILQ_NEXT(ifpr, ifpr_list); if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (ifpr2rp(ifpr)->rp_statef_delmark) - (void)delete_each_prefix(so, ifpr2rp(ifpr), origin); + (void)delete_each_prefix(ifpr2rp(ifpr), origin); } } @@ -953,7 +996,8 @@ link_stray_ia6s(struct rr_prefix *rpp) { struct ifaddr *ifa; - TAILQ_FOREACH(ifa, &rpp->rp_ifp->if_addrlist, ifa_list) + for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { struct rp_addr *rap; struct rr_prefix *orpp; @@ -970,13 +1014,13 @@ link_stray_ia6s(struct rr_prefix *rpp) rpp->rp_plen)) log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:" "addr %s/%d already linked to a prefix" - "and it matches also %s/%d", + "and it matches also %s/%d\n", ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen, ip6_sprintf(RP_IN6(rpp)), rpp->rp_plen); continue; } - if ((error = assigne_ra_entry(rpp, + if ((error = assign_ra_entry(rpp, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen, (struct in6_ifaddr *)ifa)) != 0) @@ -985,6 +1029,7 @@ link_stray_ia6s(struct rr_prefix *rpp) return 0; } +/* XXX assumes that permission is already checked by the caller */ int in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp) @@ -1013,7 +1058,7 @@ in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, if (irr->irr_pltime > irr->irr_vltime) { log(LOG_NOTICE, "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)", + "(%ld) is greater than valid lifetime(%ld)\n", (u_long)irr->irr_pltime, (u_long)irr->irr_vltime); error = EINVAL; break; @@ -1024,7 +1069,7 @@ in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, != 0) goto failed; if (cmd != SIOCAIFPREFIX_IN6) - delete_prefixes(so, ifp, irr->irr_origin); + delete_prefixes(ifp, irr->irr_origin); } else return (EADDRNOTAVAIL); failed: @@ -1048,7 +1093,7 @@ in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, if (ipr->ipr_pltime > ipr->ipr_vltime) { log(LOG_NOTICE, "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)", + "(%ld) is greater than valid lifetime(%ld)\n", (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime); error = EINVAL; break; @@ -1069,7 +1114,10 @@ in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, free_rp_entries(&rp_tmp); break; } - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { if (ifa->ifa_addr == NULL) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) @@ -1103,7 +1151,7 @@ in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, if (rpp == NULL || ifp != rpp->rp_ifp) return (EADDRNOTAVAIL); - error = delete_each_prefix(so, rpp, ipr->ipr_origin); + error = delete_each_prefix(rpp, ipr->ipr_origin); break; } bad: @@ -1124,13 +1172,9 @@ in6_rr_timer(void *ignored_arg) while (rpp) { if (rpp->rp_expire && rpp->rp_expire < time_second) { struct rr_prefix *next_rpp; - struct socket so; - - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); next_rpp = LIST_NEXT(rpp, rp_entry); - delete_each_prefix(&so, rpp, PR_ORIG_KERNEL); + delete_each_prefix(rpp, PR_ORIG_KERNEL); rpp = next_rpp; continue; } diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h index fa5a0e3..2ce18db 100644 --- a/sys/netinet6/in6_prefix.h +++ b/sys/netinet6/in6_prefix.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_prefix.h,v 1.6 2000/03/25 07:23:45 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. * All rights reserved. @@ -25,64 +28,61 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ struct rr_prefix { - struct ifprefix rp_ifpr; + struct ifprefix rp_ifpr; LIST_ENTRY(rr_prefix) rp_entry; LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead; - struct sockaddr_in6 rp_prefix; /* prefix */ - u_int32_t rp_vltime; /* advertised valid lifetime */ - u_int32_t rp_pltime; /* advertised preferred lifetime */ - time_t rp_expire; /* expiration time of the prefix */ - time_t rp_preferred; /* preferred time of the prefix */ - struct in6_prflags rp_flags; + struct sockaddr_in6 rp_prefix; /* prefix */ + u_int32_t rp_vltime; /* advertised valid lifetime */ + u_int32_t rp_pltime; /* advertised preferred lifetime */ + time_t rp_expire; /* expiration time of the prefix */ + time_t rp_preferred; /* preferred time of the prefix */ + struct in6_prflags rp_flags; u_char rp_origin; /* from where this prefix info is obtained */ struct rp_stateflags { /* if some prefix should be added to this prefix */ - u_char addmark : 1; - u_char delmark : 1; /* if this prefix will be deleted */ + u_char addmark : 1; + u_char delmark : 1; /* if this prefix will be deleted */ } rp_stateflags; }; -#define rp_type rp_ifpr.ifpr_type -#define rp_ifp rp_ifpr.ifpr_ifp -#define rp_plen rp_ifpr.ifpr_plen +#define rp_type rp_ifpr.ifpr_type +#define rp_ifp rp_ifpr.ifpr_ifp +#define rp_plen rp_ifpr.ifpr_plen -#define rp_raf rp_flags.prf_ra -#define rp_raf_onlink rp_flags.prf_ra.onlink -#define rp_raf_auto rp_flags.prf_ra.autonomous +#define rp_raf rp_flags.prf_ra +#define rp_raf_onlink rp_flags.prf_ra.onlink +#define rp_raf_auto rp_flags.prf_ra.autonomous -#define rp_statef_addmark rp_stateflags.addmark -#define rp_statef_delmark rp_stateflags.delmark +#define rp_statef_addmark rp_stateflags.addmark +#define rp_statef_delmark rp_stateflags.delmark -#define rp_rrf rp_flags.prf_rr -#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid -#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd +#define rp_rrf rp_flags.prf_rr +#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid +#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd struct rp_addr { - LIST_ENTRY(rp_addr) ra_entry; - struct in6_addr ra_ifid; - struct in6_ifaddr *ra_addr; - struct ra_flags { - u_char anycast : 1; + LIST_ENTRY(rp_addr) ra_entry; + struct in6_addr ra_ifid; + struct in6_ifaddr *ra_addr; + struct ra_flags { + u_char anycast : 1; } ra_flags; }; -#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) -#define rp2ifpr(rp) ((struct ifprefix *)(rp)) +#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) +#define rp2ifpr(rp) ((struct ifprefix *)(rp)) -#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) +#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) -#define RR_INFINITE_LIFETIME 0xffffffff +#define RR_INFINITE_LIFETIME 0xffffffff LIST_HEAD(rr_prhead, rr_prefix); -extern struct rr_prhead rr_prefix; +extern struct rr_prhead rr_prefix; -void in6_rr_timer __P((void *)); -int delete_each_prefix __P((struct socket *so, struct rr_prefix *rpp, - u_char origin)); +void in6_rr_timer __P((void *)); +int delete_each_prefix __P((struct rr_prefix *rpp, u_char origin)); diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c index 318dcab..8af642f 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_proto.c,v 1.64 2000/06/20 16:20:27 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -65,6 +66,7 @@ */ #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -84,11 +86,12 @@ #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> +#include <netinet/ip_encap.h> #include <netinet/ip.h> #include <netinet/ip_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet/tcp.h> #include <netinet/tcp_timer.h> @@ -96,6 +99,7 @@ #include <netinet/udp.h> #include <netinet/udp_var.h> #include <netinet6/tcp6_var.h> + #include <netinet6/udp6_var.h> #include <netinet6/pim6_var.h> @@ -105,13 +109,15 @@ #ifdef IPSEC #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #include <netinet6/ipsec6.h> +#include <netinet6/ah.h> #include <netinet6/ah6.h> #ifdef IPSEC_ESP #include <netinet6/esp.h> #include <netinet6/esp6.h> #endif +#include <netinet6/ipcomp.h> +#include <netinet6/ipcomp6.h> #endif /*IPSEC*/ #include <netinet6/ip6protosw.h> @@ -129,8 +135,8 @@ * TCP/IP protocol family: IP6, ICMP6, UDP, TCP. */ -extern struct domain inet6domain; -static struct pr_usrreqs nousrreqs; +extern struct domain inet6domain; +static struct pr_usrreqs nousrreqs; struct ip6protosw inet6sw[] = { { 0, &inet6domain, IPPROTO_IPV6, 0, @@ -151,12 +157,12 @@ struct ip6protosw inet6sw[] = { #ifdef INET /* don't call timeout routines twice */ tcp_init, 0, 0, tcp_drain, #else - tcp_init, 0, tcp_slowtimo, tcp_drain, + tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain, #endif &tcp6_usrreqs, }, { SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR, - rip6_input, rip6_output, 0, rip6_ctloutput, + rip6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs @@ -169,68 +175,88 @@ struct ip6protosw inet6sw[] = { }, { SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR, dest6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR, route6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR, frag6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, #ifdef IPSEC { SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, ah6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs, }, #ifdef IPSEC_ESP { SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, esp6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs, }, #endif +{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR, + ipcomp6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs, +}, #endif /* IPSEC */ -#if NGIF > 0 +#ifdef INET { SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, - in6_gif_input,0, 0, 0, - 0, + encap6_input, rip6_output, 0, rip6_ctloutput, + 0, 0, 0, 0, 0, - &nousrreqs + &rip6_usrreqs }, +#endif /*INET*/ { SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - in6_gif_input,0, 0, 0, + encap6_input, rip6_output, 0, rip6_ctloutput, 0, +#ifndef INET + encap_init, 0, 0, 0, +#else 0, 0, 0, 0, - &nousrreqs +#endif + &rip6_usrreqs }, -#endif /* GIF */ { SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR, - pim6_input, rip6_output, 0, rip6_ctloutput, + pim6_input, rip6_output, 0, rip6_ctloutput, 0, - 0, 0, 0, 0, + 0, 0, 0, 0, &rip6_usrreqs }, /* raw wildcard */ { SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR, rip6_input, rip6_output, 0, rip6_ctloutput, - 0, - 0, 0, 0, 0, + 0, 0, + 0, 0, 0, &rip6_usrreqs }, }; -extern int in6_inithead __P((void **, int)); +#if NGIF > 0 +struct ip6protosw in6_gif_protosw = +{ SOCK_RAW, &inet6domain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, + in6_gif_input, rip6_output, 0, rip6_ctloutput, + 0, + 0, 0, 0, 0, + &rip6_usrreqs +}; +#endif /*NGIF*/ + +extern int in6_inithead __P((void **, int)); struct domain inet6domain = { AF_INET6, "internet6", 0, 0, 0, @@ -266,7 +292,7 @@ int ip6_maxfragpackets = 200; int ip6_log_interval = 5; int ip6_hdrnestlimit = 50; /* appropriate? */ int ip6_dad_count = 1; /* DupAddrDetectionTransmits */ -u_int32_t ip6_flow_seq; +u_int32_t ip6_flow_seq; int ip6_auto_flowlabel = 1; #if NGIF > 0 int ip6_gif_hlim = GIF_HLIM; @@ -288,8 +314,8 @@ time_t ip6_log_time = (time_t)0L; * XXX: what if we don't define INET? Should we define pmtu6_expire * or so? (jinmei@kame.net 19990310) */ -int pmtu_expire = 60*10; -int pmtu_probe = 60*2; +int pmtu_expire = 60*10; +int pmtu_probe = 60*2; /* raw IP6 parameters */ /* @@ -304,7 +330,84 @@ u_long rip6_recvspace = RIPV6RCVQ; /* ICMPV6 parameters */ int icmp6_rediraccept = 1; /* accept and process redirects */ int icmp6_redirtimeout = 10 * 60; /* 10 minutes */ -u_int icmp6errratelim = 1000; /* 1000usec = 1msec */ +struct timeval icmp6errratelim = { 0, 0 }; /* no ratelimit */ +int icmp6errppslim = 100; /* 100pps */ +int icmp6_nodeinfo = 1; /* enable/disable NI response */ + +#ifdef TCP6 +/* TCP on IP6 parameters */ +int tcp6_sendspace = 1024 * 8; +int tcp6_recvspace = 1024 * 8; +int tcp6_mssdflt = TCP6_MSS; +int tcp6_rttdflt = TCP6TV_SRTTDFLT / PR_SLOWHZ; +int tcp6_do_rfc1323 = 1; +int tcp6_conntimeo = TCP6TV_KEEP_INIT; /* initial connection timeout */ +int tcp6_43maxseg = 0; +int tcp6_pmtu = 0; + +/* + * Parameters for keepalive option. + * Connections for which SO_KEEPALIVE is set will be probed + * after being idle for a time of tcp6_keepidle (in units of PR_SLOWHZ). + * Starting at that time, the connection is probed at intervals + * of tcp6_keepintvl (same units) until a response is received + * or until tcp6_keepcnt probes have been made, at which time + * the connection is dropped. Note that a tcp6_keepidle value + * under 2 hours is nonconformant with RFC-1122, Internet Host Requirements. + */ +int tcp6_keepidle = TCP6TV_KEEP_IDLE; /* time before probing idle */ +int tcp6_keepintvl = TCP6TV_KEEPINTVL; /* interval betwn idle probes */ +int tcp6_keepcnt = TCP6TV_KEEPCNT; /* max idle probes */ +int tcp6_maxpersistidle = TCP6TV_KEEP_IDLE; /* max idle time in persist */ + +#ifndef INET_SERVER +#define TCP6_LISTEN_HASH_SIZE 17 +#define TCP6_CONN_HASH_SIZE 97 +#define TCP6_SYN_HASH_SIZE 293 +#define TCP6_SYN_BUCKET_SIZE 35 +#else +#define TCP6_LISTEN_HASH_SIZE 97 +#define TCP6_CONN_HASH_SIZE 9973 +#define TCP6_SYN_HASH_SIZE 997 +#define TCP6_SYN_BUCKET_SIZE 35 +#endif +int tcp6_listen_hash_size = TCP6_LISTEN_HASH_SIZE; +int tcp6_conn_hash_size = TCP6_CONN_HASH_SIZE; +struct tcp6_hash_list tcp6_listen_hash[TCP6_LISTEN_HASH_SIZE], + tcp6_conn_hash[TCP6_CONN_HASH_SIZE]; + +int tcp6_syn_cache_size = TCP6_SYN_HASH_SIZE; +int tcp6_syn_cache_limit = TCP6_SYN_HASH_SIZE*TCP6_SYN_BUCKET_SIZE; +int tcp6_syn_bucket_limit = 3*TCP6_SYN_BUCKET_SIZE; +struct syn_cache_head6 tcp6_syn_cache[TCP6_SYN_HASH_SIZE]; +struct syn_cache_head6 *tcp6_syn_cache_first; +int tcp6_syn_cache_interval = 8; /* runs timer every 4 seconds */ +int tcp6_syn_cache_timeo = TCP6TV_KEEP_INIT; + +/* + * Parameters for computing a desirable data segment size + * given an upper bound (either interface MTU, or peer's MSS option)_. + * As applications tend to use a buffer size that is a multiple + * of kilobytes, try for something that divides evenly. However, + * do not round down too much. + * + * Round segment size down to a multiple of TCP6_ROUNDSIZE if this + * does not result in lowering by more than (size/TCP6_ROUNDFRAC). + * For example, round 536 to 512. Older versions of the system + * effectively used MCLBYTES (1K or 2K) as TCP6_ROUNDSIZE, with + * a value of 1 for TCP6_ROUNDFRAC (eliminating its effect). + * We round to a multiple of 256 for SLIP. + */ +#ifndef TCP6_ROUNDSIZE +#define TCP6_ROUNDSIZE 256 /* round to multiple of 256 */ +#endif +#ifndef TCP6_ROUNDFRAC +#define TCP6_ROUNDFRAC 10 /* round down at most N/10, or 10% */ +#endif + +int tcp6_roundsize = TCP6_ROUNDSIZE; +int tcp6_roundfrac = TCP6_ROUNDFRAC; +#endif /*TCP6*/ /* UDP on IP6 parameters */ int udp6_sendspace = 9216; /* really max datagram size */ @@ -344,30 +447,57 @@ sysctl_ip6_forwarding(SYSCTL_HANDLER_ARGS) changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0); if (changed == 0) return (error); + /* + * XXX while host->router removes prefix got from RA, + * router->host case nukes all the prefixes managed by in6_prefix.c + * (both RR and static). therefore, switching from host->router->host + * will remove statically configured addresses/prefixes. + * not sure if it is intended behavior or not. + */ if (ip6_forwarding != 0) { /* host becomes router */ int s = splnet(); struct nd_prefix *pr, *next; - for (pr = LIST_FIRST(&nd_prefix); pr; pr = next) { - next = LIST_NEXT(pr, ndpr_entry); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); prelist_remove(pr); } splx(s); } else { /* router becomes host */ - struct socket so; - - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); while(!LIST_EMPTY(&rr_prefix)) - delete_each_prefix(&so, LIST_FIRST(&rr_prefix), + delete_each_prefix(LIST_FIRST(&rr_prefix), PR_ORIG_KERNEL); } return (error); } +static int +sysctl_icmp6_ratelimit (SYSCTL_HANDLER_ARGS) +{ + int rate_usec, error, s; + + /* + * The sysctl specifies the rate in usec-between-icmp, + * so we must convert from/to a timeval. + */ + rate_usec = (icmp6errratelim.tv_sec * 1000000) + + icmp6errratelim.tv_usec; + error = sysctl_handle_int(oidp, &rate_usec, 0, req); + if (error) + return (error); + if (rate_usec < 0) + return (EINVAL); + s = splnet(); + icmp6errratelim.tv_sec = rate_usec / 1000000; + icmp6errratelim.tv_usec = rate_usec % 1000000; + splx(s); + + return (0); +} + SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding, CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding, "I", ""); @@ -409,8 +539,9 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT, redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, ""); SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD, &icmp6stat, icmp6stat, ""); -SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, - errratelimit, CTLFLAG_RW, &icmp6errratelim, 0, ""); +SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, + errratelimit, CTLTYPE_INT|CTLFLAG_RW, + 0, sizeof(int), sysctl_icmp6_ratelimit, "I", ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE, nd6_prune, CTLFLAG_RW, &nd6_prune, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY, @@ -421,5 +552,9 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MMAXTRIES, nd6_mmaxtries, CTLFLAG_RW, &nd6_mmaxtries, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_USELOOPBACK, nd6_useloopback, CTLFLAG_RW, &nd6_useloopback, 0, ""); -SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PROXYALL, - nd6_proxyall, CTLFLAG_RW, &nd6_proxyall, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_NODEINFO, + nodeinfo, CTLFLAG_RW, &icmp6_nodeinfo, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, + errppslimit, CTLFLAG_RW, &icmp6errppslim, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXNUDHINT, + nd6_maxnudhint, CTLFLAG_RW, &nd6_maxnudhint, 0, ""); diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c index 56e9d94..cb74a49 100644 --- a/sys/netinet6/in6_rmx.c +++ b/sys/netinet6/in6_rmx.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_rmx.c,v 1.7 2000/04/06 08:30:43 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -57,7 +58,6 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: in6_rmx.c,v 1.3 1999/08/16 13:42:53 itojun Exp $ */ /* @@ -89,10 +89,10 @@ #include <netinet/ip_var.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet/tcp.h> #include <netinet/tcp_seq.h> @@ -101,14 +101,14 @@ extern int in6_inithead __P((void **head, int off)); -#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ +#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ /* * Do what we need to do when inserting a route. */ static struct radix_node * in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, - struct radix_node *treenodes) + struct radix_node *treenodes) { struct rtentry *rt = (struct rtentry *)treenodes; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt); @@ -453,6 +453,24 @@ in6_mtutimo(void *rock) timeout(in6_mtutimo, rock, tvtohz(&atv)); } +#if 0 +void +in6_rtqdrain() +{ + struct radix_node_head *rnh = rt_tables[AF_INET6]; + struct rtqk_arg arg; + int s; + arg.found = arg.killed = 0; + arg.rnh = rnh; + arg.nextstop = 0; + arg.draining = 1; + arg.updating = 0; + s = splnet(); + rnh->rnh_walktree(rnh, in6_rtqkill, &arg); + splx(s); +} +#endif + /* * Initialize our routing tree. */ diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c new file mode 100644 index 0000000..40b0df4 --- /dev/null +++ b/sys/netinet6/in6_src.c @@ -0,0 +1,550 @@ +/* $FreeBSD$ */ +/* $KAME: in6_src.c,v 1.27 2000/06/21 08:07:13 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/proc.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> +#include <netinet6/in6_var.h> +#include <netinet/ip6.h> +#include <netinet6/in6_pcb.h> +#include <netinet6/ip6_var.h> +#include <netinet6/nd6.h> +#ifdef ENABLE_DEFAULT_SCOPE +#include <netinet6/scope6_var.h> +#endif + +#include <net/net_osdep.h> + +#include "loop.h" + +/* + * Return an IPv6 address, which is the most appropriate for given + * destination and user specified options. + * If necessary, this function lookups the routing table and return + * an entry to the caller for later use. + */ +struct in6_addr * +in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) + struct sockaddr_in6 *dstsock; + struct ip6_pktopts *opts; + struct ip6_moptions *mopts; + struct route_in6 *ro; + struct in6_addr *laddr; + int *errorp; +{ + struct in6_addr *dst; + struct in6_ifaddr *ia6 = 0; + struct in6_pktinfo *pi = NULL; + + dst = &dstsock->sin6_addr; + *errorp = 0; + + /* + * If the source address is explicitly specified by the caller, + * use it. + */ + if (opts && (pi = opts->ip6po_pktinfo) && + !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) + return(&pi->ipi6_addr); + + /* + * If the source address is not specified but the socket(if any) + * is already bound, use the bound address. + */ + if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) + return(laddr); + + /* + * If the caller doesn't specify the source address but + * the outgoing interface, use an address associated with + * the interface. + */ + if (pi && pi->ipi6_ifindex) { + /* XXX boundary check is assumed to be already done. */ + ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a link-local unicast address or + * a multicast address, and if the outgoing interface is specified + * by the sin6_scope_id filed, use an address associated with the + * interface. + * XXX: We're now trying to define more specific semantics of + * sin6_scope_id field, so this part will be rewritten in + * the near future. + */ + if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) && + dstsock->sin6_scope_id) { + /* + * I'm not sure if boundary check for scope_id is done + * somewhere... + */ + if (dstsock->sin6_scope_id < 0 || + if_index < dstsock->sin6_scope_id) { + *errorp = ENXIO; /* XXX: better error? */ + return(0); + } + ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a multicast address and + * the outgoing interface for the address is specified + * by the caller, use an address associated with the interface. + * There is a sanity check here; if the destination has node-local + * scope, the outgoing interfacde should be a loopback address. + * Even if the outgoing interface is not specified, we also + * choose a loopback interface as the outgoing interface. + */ + if (IN6_IS_ADDR_MULTICAST(dst)) { + struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL; + + if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) { + ifp = &loif[0]; + } + + if (ifp) { + ia6 = in6_ifawithscope(ifp, dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + } + + /* + * If the next hop address for the packet is specified + * by caller, use an address associated with the route + * to the next hop. + */ + { + struct sockaddr_in6 *sin6_next; + struct rtentry *rt; + + if (opts && opts->ip6po_nexthop) { + sin6_next = satosin6(opts->ip6po_nexthop); + rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL); + if (rt) { + ia6 = in6_ifawithscope(rt->rt_ifp, dst); + if (ia6 == 0) + ia6 = ifatoia6(rt->rt_ifa); + } + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + } + + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + if (ro) { + if (ro->ro_rt && + !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if (ro->ro_rt == (struct rtentry *)0 || + ro->ro_rt->rt_ifp == (struct ifnet *)0) { + /* No route yet, so try to acquire one */ + bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); + ro->ro_dst.sin6_family = AF_INET6; + ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); + ro->ro_dst.sin6_addr = *dst; + ro->ro_dst.sin6_scope_id = dstsock->sin6_scope_id; + if (IN6_IS_ADDR_MULTICAST(dst)) { + ro->ro_rt = rtalloc1(&((struct route *)ro) + ->ro_dst, 0, 0UL); + } else { + rtalloc((struct route *)ro); + } + } + + /* + * in_pcbconnect() checks out IFF_LOOPBACK to skip using + * the address. But we don't know why it does so. + * It is necessary to ensure the scope even for lo0 + * so doesn't check out IFF_LOOPBACK. + */ + + if (ro->ro_rt) { + ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst); + if (ia6 == 0) /* xxx scope error ?*/ + ia6 = ifatoia6(ro->ro_rt->rt_ifa); + } +#if 0 + /* + * xxx The followings are necessary? (kazu) + * I don't think so. + * It's for SO_DONTROUTE option in IPv4.(jinmei) + */ + if (ia6 == 0) { + struct sockaddr_in6 sin6 = {sizeof(sin6), AF_INET6, 0}; + + sin6->sin6_addr = *dst; + + ia6 = ifatoia6(ifa_ifwithdstaddr(sin6tosa(&sin6))); + if (ia6 == 0) + ia6 = ifatoia6(ifa_ifwithnet(sin6tosa(&sin6))); + if (ia6 == 0) + return(0); + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } +#endif /* 0 */ + if (ia6 == 0) { + *errorp = EHOSTUNREACH; /* no route */ + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + *errorp = EADDRNOTAVAIL; + return(0); +} + +/* + * Default hop limit selection. The precedence is as follows: + * 1. Hoplimit value specified via ioctl. + * 2. (If the outgoing interface is detected) the current + * hop limit of the interface specified by router advertisement. + * 3. The system default hoplimit. +*/ +int +in6_selecthlim(in6p, ifp) + struct in6pcb *in6p; + struct ifnet *ifp; +{ + if (in6p && in6p->in6p_hops >= 0) + return(in6p->in6p_hops); + else if (ifp) + return(nd_ifinfo[ifp->if_index].chlim); + else + return(ip6_defhlim); +} + +/* + * XXX: this is borrowed from in6_pcbbind(). If possible, we should + * share this function by all *bsd*... + */ +int +in6_pcbsetport(laddr, inp, p) + struct in6_addr *laddr; + struct inpcb *inp; + struct proc *p; +{ + struct socket *so = inp->inp_socket; + u_int16_t lport = 0, first, last, *lastport; + int count, error = 0, wild = 0; + struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + + /* XXX: this is redundant when called from in6_pcbbind */ + if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) + wild = INPLOOKUP_WILDCARD; + + inp->inp_flags |= INP_ANONPORT; + + if (inp->inp_flags & INP_HIGHPORT) { + first = ipport_hifirstauto; /* sysctl */ + last = ipport_hilastauto; + lastport = &pcbinfo->lasthi; + } else if (inp->inp_flags & INP_LOWPORT) { + if (p && (error = suser(p))) + return error; + first = ipport_lowfirstauto; /* 1023 */ + last = ipport_lowlastauto; /* 600 */ + lastport = &pcbinfo->lastlow; + } else { + first = ipport_firstauto; /* sysctl */ + last = ipport_lastauto; + lastport = &pcbinfo->lastport; + } + /* + * Simple check to ensure all ports are not used up causing + * a deadlock here. + * + * We split the two cases (up and down) so that the direction + * is not being tested on each round of the loop. + */ + if (first > last) { + /* + * counting down + */ + count = first - last; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + --*lastport; + if (*lastport > first || *lastport < last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } else { + /* + * counting up + */ + count = last - first; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + ++*lastport; + if (*lastport < first || *lastport > last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } + + inp->inp_lport = lport; + if (in_pcbinshash(inp) != 0) { + inp->in6p_laddr = in6addr_any; + inp->inp_lport = 0; + return (EAGAIN); + } + + return(0); +} + +/* + * generate kernel-internal form (scopeid embedded into s6_addr16[1]). + * If the address scope of is link-local, embed the interface index in the + * address. The routine determines our precedence + * between advanced API scope/interface specification and basic API + * specification. + * + * this function should be nuked in the future, when we get rid of + * embedded scopeid thing. + * + * XXX actually, it is over-specification to return ifp against sin6_scope_id. + * there can be multiple interfaces that belong to a particular scope zone + * (in specification, we have 1:N mapping between a scope zone and interfaces). + * we may want to change the function to return something other than ifp. + */ +int +in6_embedscope(in6, sin6, in6p, ifpp) + struct in6_addr *in6; + const struct sockaddr_in6 *sin6; +#ifdef HAVE_NRL_INPCB + struct inpcb *in6p; +#define in6p_outputopts inp_outputopts6 +#define in6p_moptions inp_moptions6 +#else + struct in6pcb *in6p; +#endif + struct ifnet **ifpp; +{ + struct ifnet *ifp = NULL; + u_int32_t scopeid; + + *in6 = sin6->sin6_addr; + scopeid = sin6->sin6_scope_id; + if (ifpp) + *ifpp = NULL; + + /* + * don't try to read sin6->sin6_addr beyond here, since the caller may + * ask us to overwrite existing sockaddr_in6 + */ + +#ifdef ENABLE_DEFAULT_SCOPE + if (scopeid == 0) + scopeid = scope6_addr2default(in6); +#endif + + if (IN6_IS_SCOPE_LINKLOCAL(in6)) { + struct in6_pktinfo *pi; + + /* + * KAME assumption: link id == interface id + */ + + if (in6p && in6p->in6p_outputopts && + (pi = in6p->in6p_outputopts->ip6po_pktinfo) && + pi->ipi6_ifindex) { + ifp = ifindex2ifnet[pi->ipi6_ifindex]; + in6->s6_addr16[1] = htons(pi->ipi6_ifindex); + } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) && + in6p->in6p_moptions && + in6p->in6p_moptions->im6o_multicast_ifp) { + ifp = in6p->in6p_moptions->im6o_multicast_ifp; + in6->s6_addr16[1] = htons(ifp->if_index); + } else if (scopeid) { + /* boundary check */ + if (scopeid < 0 || if_index < scopeid) + return ENXIO; /* XXX EINVAL? */ + ifp = ifindex2ifnet[scopeid]; + /*XXX assignment to 16bit from 32bit variable */ + in6->s6_addr16[1] = htons(scopeid & 0xffff); + } + + if (ifpp) + *ifpp = ifp; + } + + return 0; +} +#ifdef HAVE_NRL_INPCB +#undef in6p_outputopts +#undef in6p_moptions +#endif + +/* + * generate standard sockaddr_in6 from embedded form. + * touches sin6_addr and sin6_scope_id only. + * + * this function should be nuked in the future, when we get rid of + * embedded scopeid thing. + */ +int +in6_recoverscope(sin6, in6, ifp) + struct sockaddr_in6 *sin6; + const struct in6_addr *in6; + struct ifnet *ifp; +{ + u_int32_t scopeid; + + sin6->sin6_addr = *in6; + + /* + * don't try to read *in6 beyond here, since the caller may + * ask us to overwrite existing sockaddr_in6 + */ + + sin6->sin6_scope_id = 0; + if (IN6_IS_SCOPE_LINKLOCAL(in6)) { + /* + * KAME assumption: link id == interface id + */ + scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]); + if (scopeid) { + /* sanity check */ + if (scopeid < 0 || if_index < scopeid) + return ENXIO; +#ifndef FAKE_LOOPBACK_IF + if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0 && + ifp->if_index != scopeid) { + return ENXIO; + } +#else + if (ifp && ifp->if_index != scopeid) + return ENXIO; +#endif + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = scopeid; + } + } + + return 0; +} diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 163c167..511c1b2 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_var.h,v 1.33 2000/05/17 05:07:26 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -60,11 +63,10 @@ * SUCH DAMAGE. * * @(#)in_var.h 8.1 (Berkeley) 6/10/93 - * $FreeBSD$ */ -#ifndef _NETINET6_IN6_VAR_H_ -#define _NETINET6_IN6_VAR_H_ +#ifndef _NETINET6_IN6_VAR_H_ +#define _NETINET6_IN6_VAR_H_ /* * Interface address, Internet version. One of these structures @@ -82,63 +84,63 @@ * in kernel: modify preferred/expire only */ struct in6_addrlifetime { - time_t ia6t_expire; /* valid lifetime expiration time */ - time_t ia6t_preferred; /* preferred lifetime expiration time */ - u_int32_t ia6t_vltime; /* valid lifetime */ - u_int32_t ia6t_pltime; /* prefix lifetime */ + time_t ia6t_expire; /* valid lifetime expiration time */ + time_t ia6t_preferred; /* preferred lifetime expiration time */ + u_int32_t ia6t_vltime; /* valid lifetime */ + u_int32_t ia6t_pltime; /* prefix lifetime */ }; struct in6_ifaddr { struct ifaddr ia_ifa; /* protocol-independent info */ #define ia_ifp ia_ifa.ifa_ifp -#define ia_flags ia_ifa.ifa_flags +#define ia_flags ia_ifa.ifa_flags struct sockaddr_in6 ia_addr; /* interface address */ struct sockaddr_in6 ia_net; /* network number of interface */ struct sockaddr_in6 ia_dstaddr; /* space for destination addr */ struct sockaddr_in6 ia_prefixmask; /* prefix mask */ - u_int32_t ia_plen; /* prefix length */ + u_int32_t ia_plen; /* prefix length */ struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ int ia6_flags; - struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ - struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ + struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ + struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ }; /* * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12). */ struct in6_ifstat { - u_int64_t ifs6_in_receive; /* # of total input datagram */ - u_int64_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ - u_int64_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ - u_int64_t ifs6_in_noroute; /* # of datagrams with no route */ - u_int64_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ - u_int64_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ + u_quad_t ifs6_in_receive; /* # of total input datagram */ + u_quad_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ + u_quad_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ + u_quad_t ifs6_in_noroute; /* # of datagrams with no route */ + u_quad_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ + u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_in_truncated; /* # of truncated datagrams */ - u_int64_t ifs6_in_discard; /* # of discarded datagrams */ + u_quad_t ifs6_in_truncated; /* # of truncated datagrams */ + u_quad_t ifs6_in_discard; /* # of discarded datagrams */ /* NOTE: fragment timeout is not here */ - u_int64_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ + u_quad_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_out_forward; /* # of datagrams forwarded */ + u_quad_t ifs6_out_forward; /* # of datagrams forwarded */ /* NOTE: increment on outgoing if */ - u_int64_t ifs6_out_request; /* # of outgoing datagrams from ULP */ + u_quad_t ifs6_out_request; /* # of outgoing datagrams from ULP */ /* NOTE: does not include forwrads */ - u_int64_t ifs6_out_discard; /* # of discarded datagrams */ - u_int64_t ifs6_out_fragok; /* # of datagrams fragmented */ - u_int64_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ - u_int64_t ifs6_out_fragcreat; /* # of fragment datagrams */ + u_quad_t ifs6_out_discard; /* # of discarded datagrams */ + u_quad_t ifs6_out_fragok; /* # of datagrams fragmented */ + u_quad_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ + u_quad_t ifs6_out_fragcreat; /* # of fragment datagrams */ /* NOTE: this is # after fragment */ - u_int64_t ifs6_reass_reqd; /* # of incoming fragmented packets */ + u_quad_t ifs6_reass_reqd; /* # of incoming fragmented packets */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_reass_ok; /* # of reassembled packets */ + u_quad_t ifs6_reass_ok; /* # of reassembled packets */ /* NOTE: this is # after reass */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_reass_fail; /* # of reass failures */ + u_quad_t ifs6_reass_fail; /* # of reass failures */ /* NOTE: may not be packet count */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_in_mcast; /* # of inbound multicast datagrams */ - u_int64_t ifs6_out_mcast; /* # of outbound multicast datagrams */ + u_quad_t ifs6_in_mcast; /* # of inbound multicast datagrams */ + u_quad_t ifs6_out_mcast; /* # of outbound multicast datagrams */ }; /* @@ -150,77 +152,77 @@ struct icmp6_ifstat { * Input statistics */ /* ipv6IfIcmpInMsgs, total # of input messages */ - u_int64_t ifs6_in_msg; + u_quad_t ifs6_in_msg; /* ipv6IfIcmpInErrors, # of input error messages */ - u_int64_t ifs6_in_error; + u_quad_t ifs6_in_error; /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */ - u_int64_t ifs6_in_dstunreach; + u_quad_t ifs6_in_dstunreach; /* ipv6IfIcmpInAdminProhibs, # of input administratively prohibited errs */ - u_int64_t ifs6_in_adminprohib; + u_quad_t ifs6_in_adminprohib; /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */ - u_int64_t ifs6_in_timeexceed; + u_quad_t ifs6_in_timeexceed; /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */ - u_int64_t ifs6_in_paramprob; + u_quad_t ifs6_in_paramprob; /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */ - u_int64_t ifs6_in_pkttoobig; + u_quad_t ifs6_in_pkttoobig; /* ipv6IfIcmpInEchos, # of input echo requests */ - u_int64_t ifs6_in_echo; + u_quad_t ifs6_in_echo; /* ipv6IfIcmpInEchoReplies, # of input echo replies */ - u_int64_t ifs6_in_echoreply; + u_quad_t ifs6_in_echoreply; /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */ - u_int64_t ifs6_in_routersolicit; + u_quad_t ifs6_in_routersolicit; /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */ - u_int64_t ifs6_in_routeradvert; + u_quad_t ifs6_in_routeradvert; /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */ - u_int64_t ifs6_in_neighborsolicit; + u_quad_t ifs6_in_neighborsolicit; /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advertisements */ - u_int64_t ifs6_in_neighboradvert; + u_quad_t ifs6_in_neighboradvert; /* ipv6IfIcmpInRedirects, # of input redirects */ - u_int64_t ifs6_in_redirect; + u_quad_t ifs6_in_redirect; /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */ - u_int64_t ifs6_in_mldquery; + u_quad_t ifs6_in_mldquery; /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */ - u_int64_t ifs6_in_mldreport; + u_quad_t ifs6_in_mldreport; /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */ - u_int64_t ifs6_in_mlddone; + u_quad_t ifs6_in_mlddone; /* * Output statistics. We should solve unresolved routing problem... */ /* ipv6IfIcmpOutMsgs, total # of output messages */ - u_int64_t ifs6_out_msg; + u_quad_t ifs6_out_msg; /* ipv6IfIcmpOutErrors, # of output error messages */ - u_int64_t ifs6_out_error; + u_quad_t ifs6_out_error; /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */ - u_int64_t ifs6_out_dstunreach; + u_quad_t ifs6_out_dstunreach; /* ipv6IfIcmpOutAdminProhibs, # of output administratively prohibited errs */ - u_int64_t ifs6_out_adminprohib; + u_quad_t ifs6_out_adminprohib; /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */ - u_int64_t ifs6_out_timeexceed; + u_quad_t ifs6_out_timeexceed; /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */ - u_int64_t ifs6_out_paramprob; + u_quad_t ifs6_out_paramprob; /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */ - u_int64_t ifs6_out_pkttoobig; + u_quad_t ifs6_out_pkttoobig; /* ipv6IfIcmpOutEchos, # of output echo requests */ - u_int64_t ifs6_out_echo; + u_quad_t ifs6_out_echo; /* ipv6IfIcmpOutEchoReplies, # of output echo replies */ - u_int64_t ifs6_out_echoreply; + u_quad_t ifs6_out_echoreply; /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */ - u_int64_t ifs6_out_routersolicit; + u_quad_t ifs6_out_routersolicit; /* ipv6IfIcmpOutRouterAdvertisements, # of output router advertisements */ - u_int64_t ifs6_out_routeradvert; + u_quad_t ifs6_out_routeradvert; /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */ - u_int64_t ifs6_out_neighborsolicit; + u_quad_t ifs6_out_neighborsolicit; /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advertisements */ - u_int64_t ifs6_out_neighboradvert; + u_quad_t ifs6_out_neighboradvert; /* ipv6IfIcmpOutRedirects, # of output redirects */ - u_int64_t ifs6_out_redirect; + u_quad_t ifs6_out_redirect; /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */ - u_int64_t ifs6_out_mldquery; + u_quad_t ifs6_out_mldquery; /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */ - u_int64_t ifs6_out_mldreport; + u_quad_t ifs6_out_mldreport; /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */ - u_int64_t ifs6_out_mlddone; + u_quad_t ifs6_out_mlddone; }; struct in6_ifreq { @@ -232,9 +234,10 @@ struct in6_ifreq { int ifru_flags6; int ifru_metric; caddr_t ifru_data; - struct in6_addrlifetime ifru_lifetime; - struct in6_ifstat ifru_stat; - struct icmp6_ifstat ifru_icmp6stat; + struct in6_addrlifetime ifru_lifetime; + struct in6_ifstat ifru_stat; + struct icmp6_ifstat ifru_icmp6stat; + u_int32_t ifru_scope_id[16]; } ifr_ifru; }; @@ -244,12 +247,12 @@ struct in6_aliasreq { struct sockaddr_in6 ifra_dstaddr; struct sockaddr_in6 ifra_prefixmask; int ifra_flags; - struct in6_addrlifetime ifra_lifetime; + struct in6_addrlifetime ifra_lifetime; }; /* prefix type macro */ -#define IN6_PREFIX_ND 1 -#define IN6_PREFIX_RR 2 +#define IN6_PREFIX_ND 1 +#define IN6_PREFIX_RR 2 /* * prefix related flags passed between kernel(NDP related part) and @@ -257,44 +260,44 @@ struct in6_aliasreq { */ struct in6_prflags { struct prf_ra { - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; + u_char onlink : 1; + u_char autonomous : 1; + u_char reserved : 6; } prf_ra; - u_char prf_reserved1; - u_short prf_reserved2; + u_char prf_reserved1; + u_short prf_reserved2; /* want to put this on 4byte offset */ struct prf_rr { - u_char decrvalid : 1; - u_char decrprefd : 1; - u_char reserved : 6; + u_char decrvalid : 1; + u_char decrprefd : 1; + u_char reserved : 6; } prf_rr; - u_char prf_reserved3; - u_short prf_reserved4; + u_char prf_reserved3; + u_short prf_reserved4; }; struct in6_prefixreq { char ipr_name[IFNAMSIZ]; u_char ipr_origin; u_char ipr_plen; - u_int32_t ipr_vltime; - u_int32_t ipr_pltime; - struct in6_prflags ipr_flags; + u_int32_t ipr_vltime; + u_int32_t ipr_pltime; + struct in6_prflags ipr_flags; struct sockaddr_in6 ipr_prefix; }; -#define PR_ORIG_RA 0 -#define PR_ORIG_RR 1 -#define PR_ORIG_STATIC 2 -#define PR_ORIG_KERNEL 3 +#define PR_ORIG_RA 0 +#define PR_ORIG_RR 1 +#define PR_ORIG_STATIC 2 +#define PR_ORIG_KERNEL 3 -#define ipr_raf_onlink ipr_flags.prf_ra.onlink -#define ipr_raf_auto ipr_flags.prf_ra.autonomous +#define ipr_raf_onlink ipr_flags.prf_ra.onlink +#define ipr_raf_auto ipr_flags.prf_ra.autonomous -#define ipr_statef_onlink ipr_flags.prf_state.onlink +#define ipr_statef_onlink ipr_flags.prf_state.onlink -#define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid -#define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd +#define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid +#define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd struct in6_rrenumreq { char irr_name[IFNAMSIZ]; @@ -304,114 +307,136 @@ struct in6_rrenumreq { u_char irr_m_maxlen; /* maxlen for matching prefix */ u_char irr_u_uselen; /* uselen for adding prefix */ u_char irr_u_keeplen; /* keeplen from matching prefix */ - struct irr_raflagmask { - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; + struct irr_raflagmask { + u_char onlink : 1; + u_char autonomous : 1; + u_char reserved : 6; } irr_raflagmask; - u_int32_t irr_vltime; - u_int32_t irr_pltime; - struct in6_prflags irr_flags; + u_int32_t irr_vltime; + u_int32_t irr_pltime; + struct in6_prflags irr_flags; struct sockaddr_in6 irr_matchprefix; struct sockaddr_in6 irr_useprefix; }; -#define irr_raf_mask_onlink irr_raflagmask.onlink -#define irr_raf_mask_auto irr_raflagmask.autonomous -#define irr_raf_mask_reserved irr_raflagmask.reserved +#define irr_raf_mask_onlink irr_raflagmask.onlink +#define irr_raf_mask_auto irr_raflagmask.autonomous +#define irr_raf_mask_reserved irr_raflagmask.reserved -#define irr_raf_onlink irr_flags.prf_ra.onlink -#define irr_raf_auto irr_flags.prf_ra.autonomous +#define irr_raf_onlink irr_flags.prf_ra.onlink +#define irr_raf_auto irr_flags.prf_ra.autonomous -#define irr_statef_onlink irr_flags.prf_state.onlink +#define irr_statef_onlink irr_flags.prf_state.onlink -#define irr_rrf irr_flags.prf_rr -#define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid -#define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd +#define irr_rrf irr_flags.prf_rr +#define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid +#define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd /* * Given a pointer to an in6_ifaddr (ifaddr), * return a pointer to the addr as a sockaddr_in6 */ -#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr)) -#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr)) -#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr)) -#define IA6_SIN6(ia) (&((ia)->ia_addr)) -#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) -#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) -#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) +#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr)) +#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr)) +#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr)) +#define IA6_SIN6(ia) (&((ia)->ia_addr)) +#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) +#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) +#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) -#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) +#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) #ifdef _KERNEL -#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ +#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif -#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq) -#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq) -#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq) -#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq) -#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq) -#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq) +#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq) +#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq) + +#ifdef _KERNEL +/* + * SIOCSxxx ioctls should be unused (see comments in in6.c), but + * we do not shift numbers for binary compatibility. + */ +#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq) +#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq) +#endif -#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq) -#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) +#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq) +#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq) -#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq) +#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq) +#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) + +#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq) #define SIOCGIFPSRCADDR_IN6 _IOWR('i', 71, struct in6_ifreq) #define SIOCGIFPDSTADDR_IN6 _IOWR('i', 72, struct in6_ifreq) -#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) - -#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) -#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist) -#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq) -#define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) -#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) -#define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) -#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq) - -#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) -#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq) -#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq) -#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq) - -#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */ -#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */ -#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */ -#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */ -#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \ +#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) + +#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) +#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist) +#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq) +#define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) +#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) +#define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) +#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq) + +#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) +#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq) +#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq) +#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq) + +#define SIOCSDEFIFACE_IN6 _IOWR('i', 85, struct in6_ndifreq) +#define SIOCGDEFIFACE_IN6 _IOWR('i', 86, struct in6_ndifreq) + +#define SIOCSIFINFO_FLAGS _IOWR('i', 87, struct in6_ndireq) /* XXX */ + +#define SIOCSSCOPE6 _IOW('i', 88, struct in6_ifreq) +#define SIOCGSCOPE6 _IOWR('i', 89, struct in6_ifreq) +#define SIOCGSCOPE6DEF _IOWR('i', 90, struct in6_ifreq) + +#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */ +#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */ +#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */ +#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */ +#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \ struct in6_rrenumreq) /* change */ -#define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \ +#define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \ struct in6_rrenumreq) /* set global */ -#define SIOCGETSGCNT_IN6 _IOWR('u', 106, \ +#define SIOCGETSGCNT_IN6 _IOWR('u', 106, \ struct sioc_sg_req6) /* get s,g pkt cnt */ -#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ +#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ struct sioc_mif_req6) /* get pkt cnt per if */ -#define IN6_IFF_ANYCAST 0x01 /* anycast address */ -#define IN6_IFF_TENTATIVE 0x02 /* tentative address */ -#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ -#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ -#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ +#define IN6_IFF_ANYCAST 0x01 /* anycast address */ +#define IN6_IFF_TENTATIVE 0x02 /* tentative address */ +#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ +#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ +#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ /* do not input/output */ -#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) +#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) + +#ifdef _KERNEL +#define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b)) +#define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b)) +#endif #ifdef _KERNEL -extern struct in6_ifaddr *in6_ifaddr; - -extern struct in6_ifstat **in6_ifstat; -extern size_t in6_ifstatmax; -extern struct icmp6stat icmp6stat; -extern struct icmp6_ifstat **icmp6_ifstat; -extern size_t icmp6_ifstatmax; -#define in6_ifstat_inc(ifp, tag) \ +extern struct in6_ifaddr *in6_ifaddr; + +extern struct in6_ifstat **in6_ifstat; +extern size_t in6_ifstatmax; +extern struct icmp6stat icmp6stat; +extern struct icmp6_ifstat **icmp6_ifstat; +extern size_t icmp6_ifstatmax; +#define in6_ifstat_inc(ifp, tag) \ do { \ if ((ifp) && (ifp)->if_index <= if_index \ && (ifp)->if_index < in6_ifstatmax \ @@ -420,24 +445,25 @@ do { \ } \ } while (0) -extern struct ifqueue ip6intrq; /* IP6 packet input queue */ -extern struct in6_addr zeroin6_addr; -extern u_char inet6ctlerrmap[]; -extern u_long in6_maxmtu; +extern struct ifqueue ip6intrq; /* IP6 packet input queue */ +extern struct in6_addr zeroin6_addr; +extern u_char inet6ctlerrmap[]; +extern unsigned long in6_maxmtu; #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPMADDR); -#endif /* MALLOC_DECLARE */ +#endif /* * Macro for finding the internet address structure (in6_ifaddr) corresponding * to a given interface (ifnet structure). */ -#define IFP_TO_IA6(ifp, ia) \ + +#define IFP_TO_IA6(ifp, ia) \ /* struct ifnet *ifp; */ \ /* struct in6_ifaddr *ia; */ \ do { \ struct ifaddr *ifa; \ - TAILQ_FOREACH(ifa, &(ifp)->if_addrlist, ifa_list) { \ + for (ifa = (ifp)->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { \ if (!ifa->ifa_addr) \ continue; \ if (ifa->ifa_addr->sa_family == AF_INET6) \ @@ -445,6 +471,7 @@ do { \ } \ (ia) = (struct in6_ifaddr *)ifa; \ } while (0) + #endif /* _KERNEL */ /* @@ -453,11 +480,11 @@ do { \ */ struct in6_multi_mship { struct in6_multi *i6mm_maddr; /* Multicast address pointer */ - LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ + LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ }; struct in6_multi { - LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ + LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ struct in6_addr in6m_addr; /* IP6 multicast address */ struct ifnet *in6m_ifp; /* back pointer to ifnet */ struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */ @@ -467,11 +494,6 @@ struct in6_multi { }; #ifdef _KERNEL - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_ip6); -#endif - extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead; /* @@ -489,7 +511,7 @@ struct in6_multistep { * returns NLL. */ -#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ +#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ /* struct in6_addr addr; */ \ /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ @@ -512,48 +534,52 @@ do { \ * and get the first record. Both macros return a NULL "in6m" when there * are no remaining records. */ -#define IN6_NEXT_MULTI(step, in6m) \ +#define IN6_NEXT_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m; */ \ do { \ if (((in6m) = (step).i_in6m) != NULL) \ - (step).i_in6m = LIST_NEXT((step).i_in6m, in6m_entry); \ + (step).i_in6m = (step).i_in6m->in6m_entry.le_next; \ } while(0) -#define IN6_FIRST_MULTI(step, in6m) \ +#define IN6_FIRST_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m */ \ do { \ - (step).i_in6m = LIST_FIRST(&in6_multihead); \ + (step).i_in6m = in6_multihead.lh_first; \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) -int in6_ifinit __P((struct ifnet *, +int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, int)); -struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, +struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, int *)); -void in6_delmulti __P((struct in6_multi *)); -void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); -extern int in6_ifindex2scopeid __P((int)); -extern int in6_mask2len __P((struct in6_addr *)); -extern void in6_len2mask __P((struct in6_addr *, int)); -int in6_control __P((struct socket *, - u_long, caddr_t, struct ifnet *, struct proc *)); -void in6_savemkludge __P((struct in6_ifaddr *)); -void in6_setmaxmtu __P((void)); -void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); -struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *)); -struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, +void in6_delmulti __P((struct in6_multi *)); +void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); +extern int in6_ifindex2scopeid __P((int)); +extern int in6_mask2len __P((struct in6_addr *)); +extern void in6_len2mask __P((struct in6_addr *, int)); +int in6_control __P((struct socket *, + u_long, caddr_t, struct ifnet *, struct proc *)); +void in6_purgeaddr __P((struct ifaddr *, struct ifnet *)); +void in6_savemkludge __P((struct in6_ifaddr *)); +void in6_setmaxmtu __P((void)); +void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); +void in6_purgemkludge __P((struct ifnet *)); +struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); +struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *)); char *ip6_sprintf __P((struct in6_addr *)); -int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); -int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, +int in6_addr2scopeid __P((struct ifnet *, struct in6_addr *)); +int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); +int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, int len)); -void in6_prefixlen2mask __P((struct in6_addr *maskp, int len)); -int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, +void in6_prefixlen2mask __P((struct in6_addr *maskp, int len)); +int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)); -int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia)); -void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia)); +int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia)); +void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia)); +void in6_purgeprefix __P((struct ifnet *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ diff --git a/sys/netinet6/ip6.h b/sys/netinet6/ip6.h index 14ab71d..9eec13f 100644 --- a/sys/netinet6/ip6.h +++ b/sys/netinet6/ip6.h @@ -1,253 +1,4 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ +/* $FreeBSD$ */ +/* $KAME: ip6.h,v 1.7 2000/03/25 07:23:36 sumikawa Exp $ */ -/* - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ip.h 8.1 (Berkeley) 6/10/93 - */ - -#ifndef _NETINET6_IPV6_H_ -#define _NETINET6_IPV6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_IP6_H_INCLUDED_) -#error "do not include netinet6/ip6.h directly, include netinet/ip6.h" -#endif - -/* - * Definition for internet protocol version 6. - * RFC 2460 - */ - -struct ip6_hdr { - union { - struct ip6_hdrctl { - u_int32_t ip6_un1_flow; /* 4 bits version, - * 8 bits traffic - * class, - * 20 bits flow-ID */ - u_int16_t ip6_un1_plen; /* payload length */ - u_int8_t ip6_un1_nxt; /* next header */ - u_int8_t ip6_un1_hlim; /* hop limit */ - } ip6_un1; - u_int8_t ip6_un2_vfc; /* 4 bits version, - * top 4 bits trafic class */ - } ip6_ctlun; - struct in6_addr ip6_src; /* source address */ - struct in6_addr ip6_dst; /* destination address */ -}; - -#define ip6_vfc ip6_ctlun.ip6_un2_vfc -#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow -#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen -#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt -#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim -#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim - -#define IPV6_VERSION 0x60 -#define IPV6_VERSION_MASK 0xf0 - -#if BYTE_ORDER == BIG_ENDIAN -#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ -#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ -#endif /* BIG_ENDIAN */ -#if BYTE_ORDER == LITTLE_ENDIAN -#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ -#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ -#endif /* LITTLE_ENDIAN */ -/* ECN bits proposed by Sally Floyd */ -#define IP6TOS_CE 0x01 /* congestion experienced */ -#define IP6TOS_ECT 0x02 /* ECN-capable transport */ - -/* - * Extension Headers - */ - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -/* Hop-by-Hop options header */ -/* XXX should we pad it to force alignment on an 8-byte boundary? */ -struct ip6_hbh { - u_int8_t ip6h_nxt; /* next header */ - u_int8_t ip6h_len; /* length in units of 8 octets */ - /* followed by options */ -}; - -/* Destination options header */ -/* XXX should we pad it to force alignment on an 8-byte boundary? */ -struct ip6_dest { - u_int8_t ip6d_nxt; /* next header */ - u_int8_t ip6d_len; /* length in units of 8 octets */ - /* followed by options */ -}; - -/* Option types and related macros */ -#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ -#define IP6OPT_PADN 0x01 /* 00 0 00001 */ -#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ -#define IP6OPT_JUMBO_LEN 6 -#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */ -#define IP6OPT_RTALERT_LEN 4 -#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ -#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ -#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ -#define IP6OPT_MINLEN 2 - -#define IP6OPT_TYPE(o) ((o) & 0xC0) -#define IP6OPT_TYPE_SKIP 0x00 -#define IP6OPT_TYPE_DISCARD 0x40 -#define IP6OPT_TYPE_FORCEICMP 0x80 -#define IP6OPT_TYPE_ICMP 0xC0 - -#define IP6OPT_MUTABLE 0x20 - -/* Routing header */ -struct ip6_rthdr { - u_int8_t ip6r_nxt; /* next header */ - u_int8_t ip6r_len; /* length in units of 8 octets */ - u_int8_t ip6r_type; /* routing type */ - u_int8_t ip6r_segleft; /* segments left */ - /* followed by routing type specific data */ -}; - -/* Type 0 Routing header */ -struct ip6_rthdr0 { - u_int8_t ip6r0_nxt; /* next header */ - u_int8_t ip6r0_len; /* length in units of 8 octets */ - u_int8_t ip6r0_type; /* always zero */ - u_int8_t ip6r0_segleft; /* segments left */ - u_int8_t ip6r0_reserved; /* reserved field */ - u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ - struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ -}; - -/* Fragment header */ -struct ip6_frag { - u_int8_t ip6f_nxt; /* next header */ - u_int8_t ip6f_reserved; /* reserved field */ - u_int16_t ip6f_offlg; /* offset, reserved, and flag */ - u_int32_t ip6f_ident; /* identification */ -}; - -#if BYTE_ORDER == BIG_ENDIAN -#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ -#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ -#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ -#else /* BYTE_ORDER == LITTLE_ENDIAN */ -#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ -#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ -#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ -#endif /* BYTE_ORDER == LITTLE_ENDIAN */ - -/* - * Internet implementation parameters. - */ -#define IPV6_MAXHLIM 255 /* maximun hoplimit */ -#define IPV6_DEFHLIM 64 /* default hlim */ -#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */ -#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */ - -#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ -#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/ - -/* - * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the - * target header (including IPv6 itself, extension headers and - * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers - * to store incoming data into one internal mbuf or one or more external - * mbufs(never into two or more internal mbufs). Thus, the third case is - * supposed to never be matched but is prepared just in case. - */ - -#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \ -do { \ - if ((m)->m_next != NULL) { \ - if (((m)->m_flags & M_LOOP) && \ - ((m)->m_len < (off) + (hlen)) && \ - (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - return ret; \ - } else if ((m)->m_flags & M_EXT) { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - m_freem(m); \ - return ret; \ - } \ - } else { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - m_freem(m); \ - return ret; \ - } \ - } \ - } else { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_tooshort++; \ - in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \ - m_freem(m); \ - return ret; \ - } \ - } \ -} while (0) - -#endif /* not _NETINET_IPV6_H_ */ +#error "netinet6/ip6.h is obsolete. use netinet/ip6.h" diff --git a/sys/netinet6/ip6_ecn.h b/sys/netinet6/ip6_ecn.h index ede21bc..e8dd11f 100644 --- a/sys/netinet6/ip6_ecn.h +++ b/sys/netinet6/ip6_ecn.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $ */ + /* * Copyright (C) 1999 WIDE Project. * All rights reserved. @@ -26,8 +29,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_ecn.h,v 1.2 1999/08/19 12:57:44 itojun Exp $ - * $FreeBSD$ */ /* * ECN consideration on tunnel ingress/egress operation. @@ -38,6 +39,3 @@ extern void ip6_ecn_ingress __P((int, u_int32_t *, u_int32_t *)); extern void ip6_ecn_egress __P((int, u_int32_t *, u_int32_t *)); #endif - - - diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 249d38c..ba9bdad 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_forward.c,v 1.39 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -46,20 +50,15 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip_var.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet6/nd6.h> #ifdef IPSEC_IPV6FWD #include <netinet6/ipsec.h> -#include <netinet6/ipsec6.h> #include <netkey/key.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #endif /* IPSEC_IPV6FWD */ #ifdef IPV6FIREWALL @@ -92,7 +91,8 @@ ip6_forward(m, srcrt) register struct sockaddr_in6 *dst; register struct rtentry *rt; int error, type = 0, code = 0; - struct mbuf *mcopy; + struct mbuf *mcopy = NULL; + struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC_IPV6FWD struct secpolicy *sp = NULL; #endif @@ -112,19 +112,17 @@ ip6_forward(m, srcrt) } #endif /*IPSEC_IPV6FWD*/ - if (m->m_flags & (M_BCAST|M_MCAST) || - in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) { + if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { ip6stat.ip6s_cantforward++; - ip6stat.ip6s_badscope++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { - char addr[INET6_ADDRSTRLEN]; ip6_log_time = time_second; - strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr)); log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", - addr, ip6_sprintf(&ip6->ip6_dst), + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } @@ -140,13 +138,30 @@ ip6_forward(m, srcrt) } ip6->ip6_hlim -= IPV6_HLIMDEC; + /* + * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - + * size of IPv6 + ICMPv6 headers) bytes of the packet in case + * we need to generate an ICMP6 message to the src. + * Thanks to M_EXT, in most cases copy will not occur. + * + * It is important to save it before IPsec processing as IPsec + * processing may modify the mbuf. + */ + mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); + #ifdef IPSEC_IPV6FWD /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -162,7 +177,13 @@ ip6_forward(m, srcrt) ipsec6stat.out_polvio++; ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; @@ -171,14 +192,20 @@ ip6_forward(m, srcrt) /* no need to do IPsec. */ key_freesp(sp); goto skip_ipsec; - + case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -212,7 +239,10 @@ ip6_forward(m, srcrt) error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; - /* XXX allocate a route (ro, dst) again later */ +#if 0 /* XXX allocate a route (ro, dst) again later */ + ro = (struct route_in6 *)state.ro; + dst = (struct sockaddr_in6 *)state.dst; +#endif key_freesp(sp); if (error) { @@ -232,7 +262,13 @@ ip6_forward(m, srcrt) break; } ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -255,12 +291,15 @@ ip6_forward(m, srcrt) rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } - + if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } else if ((rt = ip6_forward_rt.ro_rt) == 0 || @@ -278,26 +317,89 @@ ip6_forward(m, srcrt) if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; - if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){ + + /* + * Scope check: if a packet can't be delivered to its destination + * for the reason that the destination is beyond the scope of the + * source address, discard the packet and return an icmp6 destination + * unreachable error with Code 2 (beyond scope of source address). + * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] + */ + if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != + in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); + + if (ip6_log_time + ip6_log_interval < time_second) { + ip6_log_time = time_second; + log(LOG_DEBUG, + "cannot forward " + "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, + if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); + } + if (mcopy) + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_BEYONDSCOPE, 0); + m_freem(m); + return; + } + + if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); - icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu); + if (mcopy) { + u_long mtu; +#ifdef IPSEC_IPV6FWD + struct secpolicy *sp; + int ipsecerror; + size_t ipsechdrsiz; +#endif + + mtu = rt->rt_ifp->if_mtu; +#ifdef IPSEC_IPV6FWD + /* + * When we do IPsec tunnel ingress, we need to play + * with if_mtu value (decrement IPsec header size + * from mtu value). The code is much simpler than v4 + * case, as we have the outgoing interface for + * encapsulated packet as "rt->rt_ifp". + */ + sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, + IP_FORWARDING, &ipsecerror); + if (sp) { + ipsechdrsiz = ipsec6_hdrsiz(mcopy, + IPSEC_DIR_OUTBOUND, NULL); + if (ipsechdrsiz < mtu) + mtu -= ipsechdrsiz; + } + + /* + * if mtu becomes less than minimum MTU, + * tell minimum MTU (and I'll need to fragment it). + */ + if (mtu < IPV6_MMTU) + mtu = IPV6_MMTU; +#endif + icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); + } + m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; - /* - * Save at most 528 bytes of the packet in case - * we need to generate an ICMP6 message to the src. - * Thanks to M_EXT, in most cases copy will not occur. - */ - mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); /* * If we are to forward the packet using the same interface @@ -328,7 +430,66 @@ ip6_forward(m, srcrt) } #endif - error = nd6_output(rt->rt_ifp, m, dst, rt); + /* + * Fake scoped addresses. Note that even link-local source or + * destinaion can appear, if the originating node just sends the + * packet to us (without address resolution for the destination). + * Since both icmp6_error and icmp6_redirect_output fill the embedded + * link identifiers, we can do this stuff after make a copy for + * returning error. + */ + if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + /* + * See corresponding comments in ip6_output. + * XXX: but is it possible that ip6_forward() sends a packet + * to a loopback interface? I don't think so, and thus + * I bark here. (jinmei@kame.net) + * XXX: it is common to route invalid packets to loopback. + * also, the codepath will be visited on use of ::1 in + * rthdr. (itojun) + */ +#if 1 + if (0) +#else + if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) +#endif + { + printf("ip6_forward: outgoing interface is loopback. " + "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), + if_name(rt->rt_ifp)); + } + + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + else + origifp = rt->rt_ifp; + } + else + origifp = rt->rt_ifp; +#ifndef FAKE_LOOPBACK_IF + if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + +#ifdef OLDIP6OUTPUT + error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, + (struct sockaddr *)dst, + ip6_forward_rt.ro_rt); +#else + error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); +#endif if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; @@ -347,10 +508,12 @@ ip6_forward(m, srcrt) switch (error) { case 0: +#if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } +#endif goto freecopy; case EMSGSIZE: diff --git a/sys/netinet6/ip6_fw.c b/sys/netinet6/ip6_fw.c index d6b4ca2..1ec33a1 100644 --- a/sys/netinet6/ip6_fw.c +++ b/sys/netinet6/ip6_fw.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_fw.c,v 1.15 2000/07/02 14:17:37 itojun Exp $ */ + /* * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich @@ -11,9 +14,6 @@ * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. - * - * $Id: ip6_fw.c,v 1.7 1999/08/31 12:25:57 shin Exp $ - * $FreeBSD$ */ /* @@ -21,6 +21,15 @@ */ #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" + +#ifdef IP6DIVERT +#error "NOT SUPPORTED IPV6 DIVERT" +#endif +#ifdef IP6FW_DIVERT_RESTART +#error "NOT SUPPORTED IPV6 DIVERT" +#endif #include <sys/param.h> #include <sys/systm.h> @@ -36,10 +45,14 @@ #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> -#include <netinet/in_pcb.h> + +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> + +#include <netinet/in_pcb.h> + #include <netinet6/ip6_fw.h> #include <netinet/ip_var.h> #include <netinet/tcp.h> @@ -76,11 +89,11 @@ SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw6_verbose, 0, "" SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, ""); #endif -#define dprintf(a) if (!fw6_debug); else printf a +#define dprintf(a) if (!fw6_debug); else printf a -#define print_ip6(a) printf("[%s]", ip6_sprintf(a)) +#define print_ip6(a) printf("[%s]", ip6_sprintf(a)) -#define dprint_ip6(a) if (!fw6_debug); else print_ip6(a) +#define dprint_ip6(a) if (!fw6_debug); else print_ip6(a) static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl)); static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number)); @@ -131,7 +144,7 @@ static int tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f) { u_char flg_set, flg_clr; - + if ((f->fw_tcpf & IPV6_FW_TCPF_ESTAB) && (tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK))) return 1; @@ -344,7 +357,7 @@ ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6, case IPV6_FW_F_SKIPTO: printf("SkipTo %d", f->fw_skipto_rule); break; - default: + default: printf("UNKNOWN"); break; } @@ -468,7 +481,7 @@ ip6_fw_chk(struct ip6_hdr **pip6, continue; } -#define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\ +#define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\ (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \ (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \ (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \ @@ -522,7 +535,7 @@ ip6_fw_chk(struct ip6_hdr **pip6, if (nxt != f->fw_prot) continue; -#define PULLUP_TO(len) do { \ +#define PULLUP_TO(len) do { \ if ((*m)->m_len < (len) \ && (*m = m_pullup(*m, (len))) == 0) { \ goto dropit; \ @@ -780,7 +793,7 @@ add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl) ftmp->fw_pcnt = 0L; ftmp->fw_bcnt = 0L; fwc->rule = ftmp; - + s = splnet(); if (!chainptr->lh_first) { diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h index a356ac3..6b4c922 100644 --- a/sys/netinet6/ip6_fw.h +++ b/sys/netinet6/ip6_fw.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_fw.h,v 1.3 2000/04/06 08:30:44 sumikawa Exp $ */ + /* * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich @@ -11,8 +14,6 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip6_fw.h,v 1.1 1999/08/06 14:10:09 itojun Exp $ - * $FreeBSD$ */ #ifndef _IP6_FW_H @@ -34,11 +35,11 @@ */ union ip6_fw_if { - struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ - struct { /* Specified by interface name */ + struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ + struct { /* Specified by interface name */ #define FW_IFNLEN IFNAMSIZ - char name[FW_IFNLEN]; - short unit; /* -1 means match any unit */ + char name[FW_IFNLEN]; + short unit; /* -1 means match any unit */ } fu_via_if; }; @@ -52,95 +53,89 @@ union ip6_fw_if { */ struct ip6_fw { - u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ - struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ - /* Mask for src and dest IPv6 addr */ - struct in6_addr fw_smsk, fw_dmsk; - u_short fw_number; /* Rule number */ - u_short fw_flg; /* Flags word */ -#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ - /* Array of port numbers to match */ - u_short fw_pts[IPV6_FW_MAX_PORTS]; - u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ - u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ -#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) - /* ICMP types bitmap */ - unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; - long timestamp; /* timestamp (tv_sec) of last match */ - /* Incoming and outgoing interfaces */ - union ip6_fw_if fw_in_if, fw_out_if; - union { - u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ - u_short fu_skipto_rule; /* SKIPTO command rule number */ - u_short fu_reject_code; /* REJECT response code */ - } fw_un; - u_char fw_prot; /* IPv6 protocol */ - u_char fw_nports; /* N'of src ports and # of dst ports */ - /* in ports array (dst ports follow */ - /* src ports; max of 10 ports in all; */ - /* count of 0 means match all ports) */ + u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ + struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ + struct in6_addr fw_smsk, fw_dmsk; /* Mask for src and dest IPv6 addr */ + u_short fw_number; /* Rule number */ + u_short fw_flg; /* Flags word */ +#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ + u_short fw_pts[IPV6_FW_MAX_PORTS]; /* Array of port numbers to match */ + u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ + u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ +#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) + unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */ + long timestamp; /* timestamp (tv_sec) of last match */ + union ip6_fw_if fw_in_if, fw_out_if;/* Incoming and outgoing interfaces */ + union { + u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ + u_short fu_skipto_rule; /* SKIPTO command rule number */ + u_short fu_reject_code; /* REJECT response code */ + } fw_un; + u_char fw_prot; /* IPv6 protocol */ + u_char fw_nports; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ }; -#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) -#define IPV6_FW_SETNSRCP(rule, n) do { \ +#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) +#define IPV6_FW_SETNSRCP(rule, n) do { \ (rule)->fw_nports &= ~0x0f; \ (rule)->fw_nports |= (n); \ } while (0) -#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) -#define IPV6_FW_SETNDSTP(rule, n) do { \ +#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) +#define IPV6_FW_SETNDSTP(rule, n) do { \ (rule)->fw_nports &= ~0xf0; \ (rule)->fw_nports |= (n) << 4;\ } while (0) -#define fw_divert_port fw_un.fu_divert_port -#define fw_skipto_rule fw_un.fu_skipto_rule -#define fw_reject_code fw_un.fu_reject_code +#define fw_divert_port fw_un.fu_divert_port +#define fw_skipto_rule fw_un.fu_skipto_rule +#define fw_reject_code fw_un.fu_reject_code -struct ip6_fw_chain { - LIST_ENTRY(ip6_fw_chain) chain; - struct ip6_fw *rule; +struct ip6_fw_chain { + LIST_ENTRY(ip6_fw_chain) chain; + struct ip6_fw *rule; }; /* * Values for "flags" field . */ -#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ -#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ -#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ -#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ - -#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ -#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ -#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ -#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ -#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ -#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ -#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ -#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ - -#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ - -#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * - * and max range (stored in host byte * - * order). */ - -#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * +#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ +#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ +#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ +#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ + +#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ +#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ +#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ +#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ +#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ +#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ +#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ + +#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ + +#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * + * and max range (stored in host byte * + * order). */ + +#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * * and max range (stored in host byte * * order). */ -/* In interface by name/unit (not IP) */ -#define IPV6_FW_F_IIFNAME 0x0400 -/* Out interface by name/unit (not IP) */ -#define IPV6_FW_F_OIFNAME 0x0800 +#define IPV6_FW_F_IIFNAME 0x0400 /* In interface by name/unit (not IP) */ +#define IPV6_FW_F_OIFNAME 0x0800 /* Out interface by name/unit (not IP) */ -#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ -#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ +#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ +#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ -#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ +#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ -#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ +#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ -#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ +#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ /* * For backwards compatibility with rules specifying "via iface" but @@ -148,36 +143,35 @@ struct ip6_fw_chain { * of bits to represent this configuration. */ -#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|\ - IPV6_FW_F_OIFACE) +#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|IPV6_FW_F_OIFACE) /* * Definitions for REJECT response codes. * Values less than 256 correspond to ICMP unreachable codes. */ -#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ +#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ /* * Definitions for IPv6 option names. */ -#define IPV6_FW_IP6OPT_HOPOPT 0x01 -#define IPV6_FW_IP6OPT_ROUTE 0x02 -#define IPV6_FW_IP6OPT_FRAG 0x04 -#define IPV6_FW_IP6OPT_ESP 0x08 -#define IPV6_FW_IP6OPT_AH 0x10 -#define IPV6_FW_IP6OPT_NONXT 0x20 -#define IPV6_FW_IP6OPT_OPTS 0x40 +#define IPV6_FW_IP6OPT_HOPOPT 0x01 +#define IPV6_FW_IP6OPT_ROUTE 0x02 +#define IPV6_FW_IP6OPT_FRAG 0x04 +#define IPV6_FW_IP6OPT_ESP 0x08 +#define IPV6_FW_IP6OPT_AH 0x10 +#define IPV6_FW_IP6OPT_NONXT 0x20 +#define IPV6_FW_IP6OPT_OPTS 0x40 /* * Definitions for TCP flags. */ -#define IPV6_FW_TCPF_FIN TH_FIN -#define IPV6_FW_TCPF_SYN TH_SYN -#define IPV6_FW_TCPF_RST TH_RST -#define IPV6_FW_TCPF_PSH TH_PUSH -#define IPV6_FW_TCPF_ACK TH_ACK -#define IPV6_FW_TCPF_URG TH_URG -#define IPV6_FW_TCPF_ESTAB 0x40 +#define IPV6_FW_TCPF_FIN TH_FIN +#define IPV6_FW_TCPF_SYN TH_SYN +#define IPV6_FW_TCPF_RST TH_RST +#define IPV6_FW_TCPF_PSH TH_PUSH +#define IPV6_FW_TCPF_ACK TH_ACK +#define IPV6_FW_TCPF_URG TH_URG +#define IPV6_FW_TCPF_ESTAB 0x40 /* * Main firewall chains definitions and global var's definitions. @@ -187,13 +181,13 @@ struct ip6_fw_chain { /* * Function definitions. */ -void ip6_fw_init(void); +void ip6_fw_init(void); /* Firewall hooks */ -struct ip6_hdr; -typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, +struct ip6_hdr; +typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, u_short *, struct mbuf**)); -typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); +typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); extern ip6_fw_chk_t *ip6_fw_chk_ptr; extern ip6_fw_ctl_t *ip6_fw_ctl_ptr; diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 449a4f3..870bdd9 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_input.c,v 1.95 2000/07/02 07:49:37 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -64,8 +65,10 @@ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ -#include "opt_ipsec.h" #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/systm.h> @@ -89,13 +92,15 @@ #include <netinet/in.h> #include <netinet/in_systm.h> +#ifdef INET #include <netinet/ip.h> #include <netinet/ip_icmp.h> -#include <netinet/in_pcb.h> +#endif /*INET*/ +#include <netinet/ip6.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/in_pcb.h> +#include <netinet/icmp6.h> #include <netinet6/in6_ifattach.h> #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> @@ -104,50 +109,40 @@ #include <netinet6/ip6_fw.h> #endif -#ifdef ALTQ -#include <netinet/altq_cdnr.h> -#endif - #include <netinet6/ip6protosw.h> /* we need it for NLOOP. */ #include "loop.h" - #include "faith.h" #include "gif.h" #include <net/net_osdep.h> -extern struct domain inet6domain; -extern struct ip6protosw inet6sw[]; +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; -u_char ip6_protox[IPPROTO_MAX]; -static int ip6qmaxlen = IFQ_MAXLEN; -struct in6_ifaddr *in6_ifaddr; +u_char ip6_protox[IPPROTO_MAX]; +static int ip6qmaxlen = IFQ_MAXLEN; +struct in6_ifaddr *in6_ifaddr; -int ip6_forward_srcrt; /* XXX */ -int ip6_sourcecheck; /* XXX */ -int ip6_sourcecheck_interval; /* XXX */ - -const int int6intrq_present = 1; +int ip6_forward_srcrt; /* XXX */ +int ip6_sourcecheck; /* XXX */ +int ip6_sourcecheck_interval; /* XXX */ +const int int6intrq_present = 1; #ifdef IPV6FIREWALL /* firewall hooks */ -ip6_fw_chk_t *ip6_fw_chk_ptr; -ip6_fw_ctl_t *ip6_fw_ctl_ptr; +ip6_fw_chk_t *ip6_fw_chk_ptr; +ip6_fw_ctl_t *ip6_fw_ctl_ptr; #endif -struct ip6stat ip6stat; - -static void ip6_init2 __P((void *)); +struct ip6stat ip6stat; -static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); +static void ip6_init2 __P((void *)); -#if defined(PTR) -extern int ip6_protocol_tr; - -int ptr_in6 __P((struct mbuf *, struct mbuf **)); -extern void ip_forward __P((struct mbuf *, int)); +static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); +#ifdef PULLDOWN_TEST +static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); #endif /* @@ -190,22 +185,12 @@ static void ip6_init2(dummy) void *dummy; { - int i; - int ret; - - /* get EUI64 from somewhere */ - ret = in6_ifattach_getifid(NULL); /* * to route local address of p2p link to loopback, * assign loopback address first. */ - for (i = 0; i < NLOOP; i++) - in6_ifattach(&loif[i], IN6_IFT_LOOP, NULL, 0); - - /* attach pseudo interfaces */ - if (ret == 0) - in6_ifattach_p2p(); + in6_ifattach(&loif[0], NULL); /* nd6_timer_init */ timeout(nd6_timer, (caddr_t)0, hz); @@ -283,7 +268,10 @@ ip6_input(m) in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); ip6stat.ip6s_total++; +#ifndef PULLDOWN_TEST + /* XXX is the line really necessary? */ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/); +#endif if (m->m_len < sizeof(struct ip6_hdr)) { struct ifnet *inifp; @@ -322,13 +310,6 @@ ip6_input(m) } #endif -#ifdef ALTQ - if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { - /* packet is dropped by traffic conditioner */ - return; - } -#endif - /* * Scope check */ @@ -338,22 +319,46 @@ ip6_input(m) in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } + + /* + * Don't check IPv4 mapped address here. SIIT assumes that + * routers would forward IPv6 native packets with IPv4 mapped + * address normally. + */ +#if 0 + /* + * Reject packets with IPv4 compatible addresses (auto tunnel). + * + * The code forbids auto tunnel relay case in RFC1933 (the check is + * stronger than RFC1933). We may want to re-enable it if mech-xx + * is revised to forbid relaying case. + */ + if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) || + IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } +#endif if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { - if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } else { ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } } - if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { - ours = 1; - deliverifp = m->m_pkthdr.rcvif; - goto hbhcheck; - } - } else { +#ifndef FAKE_LOOPBACK_IF + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); @@ -362,32 +367,20 @@ ip6_input(m) = htons(m->m_pkthdr.rcvif->if_index); } -#if defined(PTR) /* - * + * XXX we need this since we do not have "goto ours" hack route + * for some of our ifaddrs on loopback interface. + * we should correct it by changing in6_ifattach to install + * "goto ours" hack route. */ - if (ip6_protocol_tr) - { - struct mbuf *m1 = NULL; - - switch (ptr_in6(m, &m1)) - { - case IPPROTO_IP: goto mcastcheck; - case IPPROTO_IPV4: ip_forward(m1, 0); break; - case IPPROTO_IPV6: ip6_forward(m1, 0); break; - case IPPROTO_MAX: /* discard this packet */ - default: - } - - if (m != m1) - m_freem(m); - - return; + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } } - mcastcheck: -#endif - /* * Multicast check */ @@ -415,17 +408,27 @@ ip6_input(m) /* * Unicast check */ - if (ip6_forward_rt.ro_rt == 0 || - !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &ip6_forward_rt.ro_dst.sin6_addr)) { + if (ip6_forward_rt.ro_rt != NULL && + (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &ip6_forward_rt.ro_dst.sin6_addr)) + ip6stat.ip6s_forward_cachehit++; + else { if (ip6_forward_rt.ro_rt) { + /* route is down or destination is different */ + ip6stat.ip6s_forward_cachemiss++; RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } + bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ip6_forward_rt.ro_dst.sin6_family = AF_INET6; ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst; +#ifdef SCOPEDROUTING + ip6_forward_rt.ro_dst.sin6_scope_id = + in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_dst); +#endif rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } @@ -444,24 +447,37 @@ ip6_input(m) if (ip6_forward_rt.ro_rt && (ip6_forward_rt.ro_rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && +#if 0 /* - * The comparison of the destination and the key of the rtentry - * has already done through looking up the routing table, - * so no need to do such a comparison here again. + * The check below is redundant since the comparison of + * the destination and the key of the rtentry has + * already done through looking up the routing table. */ + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && +#endif ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; - /* packet to tentative address must not be received */ if (ia6->ia6_flags & IN6_IFF_ANYCAST) m->m_flags |= M_ANYCAST6; + /* + * packets to a tentative, duplicated, or somehow invalid + * address must not be accepted. + */ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { - /* this interface is ready */ + /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ goto hbhcheck; } else { - /* this interface is not ready, fall through */ + /* address is not ready, so discard the packet. */ + log(LOG_INFO, + "ip6_input: packet to an unready address %s->%s", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst)); + + goto bad; } } @@ -498,13 +514,49 @@ ip6_input(m) */ plen = (u_int32_t)ntohs(ip6->ip6_plen); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { + struct ip6_hbh *hbh; + if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) { +#if 0 /*touches NULL pointer*/ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); +#endif return; /* m have already been freed */ } + /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); - nxt = ((struct ip6_hbh *)(ip6 + 1))->ip6h_nxt; + + /* + * if the payload length field is 0 and the next header field + * indicates Hop-by-Hop Options header, then a Jumbo Payload + * option MUST be included. + */ + if (ip6->ip6_plen == 0 && plen == 0) { + /* + * Note that if a valid jumbo payload option is + * contained, ip6_hoptops_input() must set a valid + * (non-zero) payload length to the variable plen. + */ + ip6stat.ip6s_badoptions++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + return; + } +#ifndef PULLDOWN_TEST + /* ip6_hopopts_input() ensures that mbuf is contiguous */ + hbh = (struct ip6_hbh *)(ip6 + 1); +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), + sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif + nxt = hbh->ip6h_nxt; /* * accept the packet if a router alert option is included @@ -560,18 +612,27 @@ ip6_input(m) return; } + ip6 = mtod(m, struct ip6_hdr *); + /* - * Tell launch routine the next header + * Malicious party may be able to use IPv4 mapped addr to confuse + * tcp/udp stack and bypass security checks (act as if it was from + * 127.0.0.1 by using IPv6 src ::ffff:127.0.0.1). Be cautious. + * + * For SIIT end node behavior, you may want to disable the check. + * However, you will become vulnerable to attacks using IPv4 mapped + * source. */ -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS && deliverifp != NULL) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); - if (ia6) - ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len; + if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || + IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; } -#endif + + /* + * Tell launch routine the next header + */ ip6stat.ip6s_delivered++; in6_ifstat_inc(deliverifp, ifs6_in_deliver); nest = 0; @@ -615,12 +676,28 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) u_int8_t *opt; /* validation of the length of the header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); hbhlen = (hbh->ip6h_len + 1) << 3; IP6_EXTHDR_CHECK(m, off, hbhlen, -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return -1; + } + hbhlen = (hbh->ip6h_len + 1) << 3; + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), + hbhlen); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return -1; + } +#endif off += hbhlen; hbhlen -= sizeof(struct ip6_hbh); opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); @@ -652,6 +729,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) int optlen = 0; u_int8_t *opt = opthead; u_int16_t rtalert_val; + u_int32_t jumboplen; for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { switch(*opt) { @@ -680,49 +758,75 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) *rtalertp = ntohs(rtalert_val); break; case IP6OPT_JUMBO: - /* XXX may need check for alignment */ - if (hbhlen < IP6OPT_JUMBO_LEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of jumbopayload opt " - "is inconsistent(%d)", - *(opt + 1)); - optlen = IP6OPT_JUMBO_LEN; + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_JUMBO_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) + /* XXX: should we discard the packet? */ + log(LOG_ERR, "length of jumbopayload opt " + "is inconsistent(%d)", + *(opt + 1)); + optlen = IP6OPT_JUMBO_LEN; - bcopy(opt + 2, plenp, sizeof(*plenp)); - *plenp = htonl(*plenp); - if (*plenp <= IPV6_MAXPACKET) { - /* - * jumbo payload length must be larger - * than 65535 - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); - return(-1); - } + /* + * IPv6 packets that have non 0 payload length + * must not contain a jumbo paylod option. + */ + ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_plen) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt - opthead); + return(-1); + } - ip6 = mtod(m, struct ip6_hdr *); - if (ip6->ip6_plen) { - /* - * IPv6 packets that have non 0 payload length - * must not contain a jumbo paylod option. - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead); - return(-1); - } - break; + /* + * We may see jumbolen in unaligned location, so + * we'd need to perform bcopy(). + */ + bcopy(opt + 2, &jumboplen, sizeof(jumboplen)); + jumboplen = (u_int32_t)htonl(jumboplen); + +#if 1 + /* + * if there are multiple jumbo payload options, + * *plenp will be non-zero and the packet will be + * rejected. + * the behavior may need some debate in ipngwg - + * multiple options does not make sense, however, + * there's no explicit mention in specification. + */ + if (*plenp != 0) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } +#endif + + /* + * jumbo payload length must be larger than 65535. + */ + if (jumboplen <= IPV6_MAXPACKET) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } + *plenp = jumboplen; + + break; default: /* unknown option */ if (hbhlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; @@ -786,11 +890,19 @@ ip6_unknown_opt(optp, m, off) } /* - * Create the "control" list for this pcb + * Create the "control" list for this pcb. + * + * The routine will be called from upper layer handlers like tcp6_input(). + * Thus the routine assumes that the caller (tcp6_input) have already + * called IP6_EXTHDR_CHECK() and all the extension headers are located in the + * very first mbuf on the mbuf chain. + * We may want to add some infinite loop prevention or sanity checks for safety. + * (This applies only when you are using KAME mbuf chain restriction, i.e. + * you are using IP6_EXTHDR_CHECK() not m_pulldown()) */ void ip6_savecontrol(in6p, mp, ip6, m) - register struct inpcb *in6p; + register struct in6pcb *in6p; register struct mbuf **mp; register struct ip6_hdr *ip6; register struct mbuf *m; @@ -811,13 +923,15 @@ ip6_savecontrol(in6p, mp, ip6, m) if (*mp) mp = &(*mp)->m_next; } - if (in6p->in6p_flags & IN6P_RECVDSTADDR) { - *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, - sizeof(struct in6_addr), IPV6_RECVDSTADDR, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - } + +#ifdef noyet + /* options were tossed above */ + if (in6p->in6p_flags & IN6P_RECVOPTS) + /* broken */ + /* ip6_srcroute doesn't do what we want here, need to fix */ + if (in6p->in6p_flags & IPV6P_RECVRETOPTS) + /* broken */ +#endif /* RFC 2292 sec. 5 */ if (in6p->in6p_flags & IN6P_PKTINFO) { @@ -859,7 +973,27 @@ ip6_savecontrol(in6p, mp, ip6, m) */ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { - struct ip6_hbh *hbh = (struct ip6_hbh *)(ip6 + 1); + struct ip6_hbh *hbh; + int hbhlen; + +#ifndef PULLDOWN_TEST + hbh = (struct ip6_hbh *)(ip6 + 1); + hbhlen = (hbh->ip6h_len + 1) << 3; +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } + hbhlen = (hbh->ip6h_len + 1) << 3; + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), hbhlen); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif /* * XXX: We copy whole the header even if a jumbo @@ -867,8 +1001,7 @@ ip6_savecontrol(in6p, mp, ip6, m) * be removed before returning in the RFC 2292. * But it's too painful operation... */ - *mp = sbcreatecontrol((caddr_t)hbh, - (hbh->ip6h_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, IPV6_HOPOPTS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; @@ -888,8 +1021,32 @@ ip6_savecontrol(in6p, mp, ip6, m) * the chain of ancillary data. */ while(1) { /* is explicit loop prevention necessary? */ - struct ip6_ext *ip6e = - (struct ip6_ext *)(mtod(m, caddr_t) + off); + struct ip6_ext *ip6e; + int elen; + +#ifndef PULLDOWN_TEST + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + if (nxt == IPPROTO_AH) + elen = (ip6e->ip6e_len + 2) << 2; + else + elen = (ip6e->ip6e_len + 1) << 3; +#else + IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, + sizeof(struct ip6_ext)); + if (ip6e == NULL) { + ip6stat.ip6s_tooshort++; + return; + } + if (nxt == IPPROTO_AH) + elen = (ip6e->ip6e_len + 2) << 2; + else + elen = (ip6e->ip6e_len + 1) << 3; + IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen); + if (ip6e == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif switch(nxt) { case IPPROTO_DSTOPTS: @@ -904,8 +1061,7 @@ ip6_savecontrol(in6p, mp, ip6, m) if (!privileged) break; - *mp = sbcreatecontrol((caddr_t)ip6e, - (ip6e->ip6e_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)ip6e, elen, IPV6_DSTOPTS, IPPROTO_IPV6); if (*mp) @@ -916,8 +1072,7 @@ ip6_savecontrol(in6p, mp, ip6, m) if (!in6p->in6p_flags & IN6P_RTHDR) break; - *mp = sbcreatecontrol((caddr_t)ip6e, - (ip6e->ip6e_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)ip6e, elen, IPV6_RTHDR, IPPROTO_IPV6); if (*mp) @@ -940,10 +1095,7 @@ ip6_savecontrol(in6p, mp, ip6, m) } /* proceed with the next header. */ - if (nxt == IPPROTO_AH) - off += (ip6e->ip6e_len + 2) << 2; - else - off += (ip6e->ip6e_len + 1) << 3; + off += elen; nxt = ip6e->ip6e_nxt; } loopend: @@ -955,6 +1107,7 @@ ip6_savecontrol(in6p, mp, ip6, m) /* to be done */ } /* IN6P_RTHDR - to be done */ + } /* @@ -1008,6 +1161,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_mroute.c b/sys/netinet6/ip6_mroute.c index 5d5c065..6bc7e6b 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_mroute.c,v 1.24 2000/05/19 07:37:05 jinmei Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* BSDI ip_mroute.c,v 2.10 1996/11/14 00:29:52 jch Exp */ @@ -45,6 +46,7 @@ */ #include "opt_inet.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/systm.h> @@ -57,7 +59,6 @@ #include <sys/errno.h> #include <sys/time.h> #include <sys/kernel.h> -#include <sys/sockio.h> #include <sys/syslog.h> #include <net/if.h> @@ -67,7 +68,7 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/ip6_mroute.h> #include <netinet6/pim6.h> @@ -75,45 +76,45 @@ static MALLOC_DEFINE(M_MRTABLE, "mf6c", "multicast forwarding cache entry"); -#define M_HASCL(m) ((m)->m_flags & M_EXT) +#define M_HASCL(m) ((m)->m_flags & M_EXT) -static int ip6_mdq __P((struct mbuf *, struct ifnet *, struct mf6c *)); -static void phyint_send __P((struct ip6_hdr *, struct mif6 *, - struct mbuf *)); +static int ip6_mdq __P((struct mbuf *, struct ifnet *, struct mf6c *)); +static void phyint_send __P((struct ip6_hdr *, struct mif6 *, struct mbuf *)); -static int set_pim6 __P((int *)); -static int socket_send __P((struct socket *, struct mbuf *, - struct sockaddr_in6 *)); -static int register_send __P((struct ip6_hdr *, struct mif6 *, - struct mbuf *)); +static int set_pim6 __P((int *)); +static int socket_send __P((struct socket *, struct mbuf *, + struct sockaddr_in6 *)); +static int register_send __P((struct ip6_hdr *, struct mif6 *, + struct mbuf *)); /* * Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static, * except for netstat or debugging purposes. */ -struct socket *ip6_mrouter = NULL; -int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */ -struct mrt6stat mrt6stat; +struct socket *ip6_mrouter = NULL; +int ip6_mrouter_ver = 0; +int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */ +struct mrt6stat mrt6stat; -#define NO_RTE_FOUND 0x1 -#define RTE_FOUND 0x2 +#define NO_RTE_FOUND 0x1 +#define RTE_FOUND 0x2 -struct mf6c *mf6ctable[MF6CTBLSIZ]; -u_char nexpire[MF6CTBLSIZ]; -static struct mif6 mif6table[MAXMIFS]; +struct mf6c *mf6ctable[MF6CTBLSIZ]; +u_char nexpire[MF6CTBLSIZ]; +static struct mif6 mif6table[MAXMIFS]; #ifdef MRT6DEBUG u_int mrt6debug = 0; /* debug level */ -#define DEBUG_MFC 0x02 -#define DEBUG_FORWARD 0x04 -#define DEBUG_EXPIRE 0x08 -#define DEBUG_XMIT 0x10 -#define DEBUG_REG 0x20 -#define DEBUG_PIM 0x40 +#define DEBUG_MFC 0x02 +#define DEBUG_FORWARD 0x04 +#define DEBUG_EXPIRE 0x08 +#define DEBUG_XMIT 0x10 +#define DEBUG_REG 0x20 +#define DEBUG_PIM 0x40 #endif static void expire_upcalls __P((void *)); -#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */ -#define UPCALL_EXPIRE 6 /* number of timeouts */ +#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */ +#define UPCALL_EXPIRE 6 /* number of timeouts */ #ifdef INET #ifdef MROUTING @@ -131,7 +132,7 @@ extern struct socket *ip_mrouter; */ struct ifnet multicast_register_if; -#define ENCAP_HOPS 64 +#define ENCAP_HOPS 64 /* * Private variables. @@ -151,7 +152,7 @@ static int pim6; /* * Hash function for a source, group entry */ -#define MF6CHASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \ +#define MF6CHASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \ (a).s6_addr32[2] ^ (a).s6_addr32[3] ^ \ (g).s6_addr32[0] ^ (g).s6_addr32[1] ^ \ (g).s6_addr32[2] ^ (g).s6_addr32[3]) @@ -161,7 +162,7 @@ static int pim6; * Quality of service parameter to be added in the future!!! */ -#define MF6CFIND(o, g, rt) { \ +#define MF6CFIND(o, g, rt) do { \ register struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ rt = NULL; \ mrt6stat.mrt6s_mfc_lookups++; \ @@ -177,13 +178,13 @@ static int pim6; if (rt == NULL) { \ mrt6stat.mrt6s_mfc_misses++; \ } \ -} +} while (0) /* * Macros to compute elapsed time efficiently * Borrowed from Van Jacobson's scheduling code */ -#define TV_DELTA(a, b, delta) { \ +#define TV_DELTA(a, b, delta) do { \ register int xxs; \ \ delta = (a).tv_usec - (b).tv_usec; \ @@ -199,20 +200,20 @@ static int pim6; delta += (1000000 * xxs); \ } \ } \ -} +} while (0) -#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \ +#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \ (a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec) #ifdef UPCALL_TIMING -#define UPCALL_MAX 50 +#define UPCALL_MAX 50 u_long upcall_data[UPCALL_MAX + 1]; static void collate(); #endif /* UPCALL_TIMING */ static int get_sg_cnt __P((struct sioc_sg_req6 *)); static int get_mif6_cnt __P((struct sioc_mif_req6 *)); -static int ip6_mrouter_init __P((struct socket *, struct mbuf *)); +static int ip6_mrouter_init __P((struct socket *, struct mbuf *, int)); static int add_m6if __P((struct mif6ctl *)); static int del_m6if __P((mifi_t *)); static int add_m6fc __P((struct mf6cctl *)); @@ -239,7 +240,10 @@ ip6_mrouter_set(so, sopt) switch (sopt->sopt_name) { case MRT6_INIT: - error = ip6_mrouter_init(so, m); +#ifdef MRT6_OINIT + case MRT6_OINIT: +#endif + error = ip6_mrouter_init(so, m, sopt->sopt_name); break; case MRT6_DONE: error = ip6_mrouter_done(); @@ -331,6 +335,9 @@ get_sg_cnt(req) req->wrong_if = rt->mf6c_wrong_if; } else return(ESRCH); +#if 0 + req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff; +#endif return 0; } @@ -371,9 +378,10 @@ set_pim6(i) * Enable multicast routing */ static int -ip6_mrouter_init(so, m) +ip6_mrouter_init(so, m, cmd) struct socket *so; struct mbuf *m; + int cmd; { int *v; @@ -398,6 +406,7 @@ ip6_mrouter_init(so, m) if (ip6_mrouter != NULL) return EADDRINUSE; ip6_mrouter = so; + ip6_mrouter_ver = cmd; bzero((caddr_t)mf6ctable, sizeof(mf6ctable)); bzero((caddr_t)nexpire, sizeof(nexpire)); @@ -458,6 +467,10 @@ ip6_mrouter_done() } } } +#ifdef notyet + bzero((caddr_t)qtable, sizeof(qtable)); + bzero((caddr_t)tbftable, sizeof(tbftable)); +#endif bzero((caddr_t)mif6table, sizeof(mif6table)); nummifs = 0; @@ -494,6 +507,7 @@ ip6_mrouter_done() reg_mif_num = -1; ip6_mrouter = NULL; + ip6_mrouter_ver = 0; splx(s); @@ -517,6 +531,9 @@ add_m6if(mifcp) register struct mif6 *mifp; struct ifnet *ifp; int error, s; +#ifdef notyet + struct tbf *m_tbf = tbftable + mifcp->mif6c_mifi; +#endif if (mifcp->mif6c_mifi >= MAXMIFS) return EINVAL; @@ -553,6 +570,10 @@ add_m6if(mifcp) s = splnet(); mifp->m6_flags = mifcp->mif6c_flags; mifp->m6_ifp = ifp; +#ifdef notyet + /* scaling up here allows division by 1024 in critical code */ + mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000; +#endif /* initialize per mif pkt counters */ mifp->m6_pkt_in = 0; mifp->m6_pkt_out = 0; @@ -604,6 +625,10 @@ del_m6if(mifip) if_allmulti(ifp, 0); } +#ifdef notyet + bzero((caddr_t)qtable[*mifip], sizeof(qtable[*mifip])); + bzero((caddr_t)mifp->m6_tbf, sizeof(*(mifp->m6_tbf))); +#endif bzero((caddr_t)mifp, sizeof (*mifp)); /* Adjust nummifs down */ @@ -726,7 +751,7 @@ add_m6fc(mfccp) #endif for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) { - + if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr, &mfccp->mf6cc_origin.sin6_addr)&& IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr, @@ -753,7 +778,7 @@ add_m6fc(mfccp) splx(s); return ENOBUFS; } - + /* insert new entry at head of hash chain */ rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; @@ -764,7 +789,7 @@ add_m6fc(mfccp) rt->mf6c_wrong_if = 0; rt->mf6c_expire = 0; rt->mf6c_stall = NULL; - + /* link into table */ rt->mf6c_next = mf6ctable[hash]; mf6ctable[hash] = rt; @@ -791,11 +816,11 @@ collate(t) if (TV_LT(*t, tp)) { TV_DELTA(tp, *t, delta); - + d = delta >> 10; if (d > UPCALL_MAX) d = UPCALL_MAX; - + ++upcall_data[d]; } } @@ -967,7 +992,7 @@ ip6_mforward(ip6, ifp, m) splx(s); return ENOBUFS; } - + /* is there an upcall waiting for this packet? */ hash = MF6CHASH(ip6->ip6_src, ip6->ip6_dst); for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) { @@ -981,6 +1006,9 @@ ip6_mforward(ip6, ifp, m) if (rt == NULL) { struct mrt6msg *im; +#ifdef MRT6_OINIT + struct omrt6msg *oim; +#endif /* no upcall, so make a new entry */ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE, @@ -1009,10 +1037,31 @@ ip6_mforward(ip6, ifp, m) * Send message to routing daemon */ sin6.sin6_addr = ip6->ip6_src; - - im = mtod(mm, struct mrt6msg *); - im->im6_msgtype = MRT6MSG_NOCACHE; - im->im6_mbz = 0; + + im = NULL; +#ifdef MRT6_OINIT + oim = NULL; +#endif + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim = mtod(mm, struct omrt6msg *); + oim->im6_msgtype = MRT6MSG_NOCACHE; + oim->im6_mbz = 0; + break; +#endif + case MRT6_INIT: + im = mtod(mm, struct mrt6msg *); + im->im6_msgtype = MRT6MSG_NOCACHE; + im->im6_mbz = 0; + break; + default: + free(rte, M_MRTABLE); + m_freem(mb0); + free(rt, M_MRTABLE); + splx(s); + return EINVAL; + } #ifdef MRT6DEBUG if (mrt6debug & DEBUG_FORWARD) @@ -1025,7 +1074,16 @@ ip6_mforward(ip6, ifp, m) mifp++, mifi++) ; - im->im6_mif = mifi; + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim->im6_mif = mifi; + break; +#endif + case MRT6_INIT: + im->im6_mif = mifi; + break; + } if (socket_send(ip6_mrouter, mm, &sin6) < 0) { log(LOG_WARNING, "ip6_mforward: ip6_mrouter " @@ -1144,7 +1202,7 @@ expire_upcalls(unused) } splx(s); expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); } /* @@ -1167,12 +1225,12 @@ ip6_mdq(m, ifp, rt) * seperate. */ -#define MC6_SEND(ip6,mifp,m) { \ - if ((mifp)->m6_flags & MIFF_REGISTER) \ - register_send((ip6), (mifp), (m)); \ - else \ - phyint_send((ip6), (mifp), (m)); \ -} +#define MC6_SEND(ip6, mifp, m) do { \ + if ((mifp)->m6_flags & MIFF_REGISTER) \ + register_send((ip6), (mifp), (m)); \ + else \ + phyint_send((ip6), (mifp), (m)); \ +} while (0) /* * Don't forward if it didn't arrive from the parent mif @@ -1195,55 +1253,82 @@ ip6_mdq(m, ifp, rt) * packets on this interface, send a message to the * routing daemon. */ - if(mifi < nummifs) /* have to make sure this is a valid mif */ - if(mif6table[mifi].m6_ifp) - - if (pim6 && (m->m_flags & M_LOOP) == 0) { - /* - * Check the M_LOOP flag to avoid an - * unnecessary PIM assert. - * XXX: M_LOOP is an ad-hoc hack... - */ - static struct sockaddr_in6 sin6 = - { sizeof(sin6), AF_INET6 }; - - register struct mbuf *mm; - struct mrt6msg *im; - - mm = m_copy(m, 0, - sizeof(struct ip6_hdr)); - if (mm && - (M_HASCL(mm) || - mm->m_len < sizeof(struct ip6_hdr))) - mm = m_pullup(mm, sizeof(struct ip6_hdr)); - if (mm == NULL) - return ENOBUFS; - - im = mtod(mm, struct mrt6msg *); - im->im6_msgtype = MRT6MSG_WRONGMIF; - im->im6_mbz = 0; + /* have to make sure this is a valid mif */ + if (mifi < nummifs && mif6table[mifi].m6_ifp) + if (pim6 && (m->m_flags & M_LOOP) == 0) { + /* + * Check the M_LOOP flag to avoid an + * unnecessary PIM assert. + * XXX: M_LOOP is an ad-hoc hack... + */ + static struct sockaddr_in6 sin6 = + { sizeof(sin6), AF_INET6 }; - for (mifp = mif6table, iif = 0; - iif < nummifs && mifp && - mifp->m6_ifp != ifp; - mifp++, iif++); + register struct mbuf *mm; + struct mrt6msg *im; +#ifdef MRT6_OINIT + struct omrt6msg *oim; +#endif - im->im6_mif = iif; + mm = m_copy(m, 0, sizeof(struct ip6_hdr)); + if (mm && + (M_HASCL(mm) || + mm->m_len < sizeof(struct ip6_hdr))) + mm = m_pullup(mm, sizeof(struct ip6_hdr)); + if (mm == NULL) + return ENOBUFS; + +#ifdef MRT6_OINIT + oim = NULL; +#endif + im = NULL; + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim = mtod(mm, struct omrt6msg *); + oim->im6_msgtype = MRT6MSG_WRONGMIF; + oim->im6_mbz = 0; + break; +#endif + case MRT6_INIT: + im = mtod(mm, struct mrt6msg *); + im->im6_msgtype = MRT6MSG_WRONGMIF; + break; + default: + m_freem(mm); + return EINVAL; + } + for (mifp = mif6table, iif = 0; + iif < nummifs && mifp && + mifp->m6_ifp != ifp; + mifp++, iif++) + ; + + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim->im6_mif = iif; + sin6.sin6_addr = oim->im6_src; + break; +#endif + case MRT6_INIT: + im->im6_mif = iif; sin6.sin6_addr = im->im6_src; + break; + } - mrt6stat.mrt6s_upcalls++; + mrt6stat.mrt6s_upcalls++; - if (socket_send(ip6_mrouter, mm, - &sin6) < 0) { + if (socket_send(ip6_mrouter, mm, &sin6) < 0) { #ifdef MRT6DEBUG - if (mrt6debug) - log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n"); + if (mrt6debug) + log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n"); #endif - ++mrt6stat.mrt6s_upq_sockfull; - return ENOBUFS; - } /* if socket Q full */ - } /* if PIM */ + ++mrt6stat.mrt6s_upq_sockfull; + return ENOBUFS; + } /* if socket Q full */ + } /* if PIM */ return 0; } /* if wrong iif */ @@ -1265,6 +1350,25 @@ ip6_mdq(m, ifp, rt) */ for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++) if (IF_ISSET(mifi, &rt->mf6c_ifset)) { + /* + * check if the outgoing packet is going to break + * a scope boundary. + * XXX For packets through PIM register tunnel + * interface, we believe a routing daemon. + */ + if ((mif6table[rt->mf6c_parent].m6_flags & + MIFF_REGISTER) == 0 && + (mif6table[mifi].m6_flags & MIFF_REGISTER) == 0 && + (in6_addr2scopeid(ifp, &ip6->ip6_dst) != + in6_addr2scopeid(mif6table[mifi].m6_ifp, + &ip6->ip6_dst) || + in6_addr2scopeid(ifp, &ip6->ip6_src) != + in6_addr2scopeid(mif6table[mifi].m6_ifp, + &ip6->ip6_src))) { + ip6stat.ip6s_badscope++; + continue; + } + mifp->m6_pkt_out++; mifp->m6_bytes_out += plen; MC6_SEND(ip6, mifp, m); @@ -1495,14 +1599,22 @@ pim6_input(mp, offp, proto) * Make sure that the IP6 and PIM headers in contiguous memory, and * possibly the PIM REGISTER header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, minlen, IPPROTO_DONE); /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); /* adjust mbuf to point to the PIM header */ pim = (struct pim *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(pim, struct pim *, m, off, minlen); + if (pim == NULL) { + pim6stat.pim6s_rcv_tooshort++; + return IPPROTO_DONE; + } +#endif -#define PIM6_CHECKSUM +#define PIM6_CHECKSUM #ifdef PIM6_CHECKSUM { int cksumlen; @@ -1553,7 +1665,7 @@ pim6_input(mp, offp, proto) struct ip6_hdr *eip6; u_int32_t *reghdr; int rc; - + ++pim6stat.pim6s_rcv_registers; if ((reg_mif_num >= nummifs) || (reg_mif_num == (mifi_t) -1)) { @@ -1566,9 +1678,9 @@ pim6_input(mp, offp, proto) m_freem(m); return(IPPROTO_DONE); } - + reghdr = (u_int32_t *)(pim + 1); - + if ((ntohl(*reghdr) & PIM_NULL_REGISTER)) goto pim6_input_to_daemon; @@ -1587,9 +1699,9 @@ pim6_input(mp, offp, proto) m_freem(m); return(IPPROTO_DONE); } - + eip6 = (struct ip6_hdr *) (reghdr + 1); -#ifdef MRT6DEBUG +#ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG, "pim6_input[register], eip6: %s -> %s, " @@ -1598,7 +1710,7 @@ pim6_input(mp, offp, proto) ip6_sprintf(&eip6->ip6_dst), ntohs(eip6->ip6_plen)); #endif - + /* verify the inner packet is destined to a mcast group */ if (!IN6_IS_ADDR_MULTICAST(&eip6->ip6_dst)) { ++pim6stat.pim6s_rcv_badregisters; @@ -1612,7 +1724,7 @@ pim6_input(mp, offp, proto) m_freem(m); return(IPPROTO_DONE); } - + /* * make a copy of the whole header to pass to the daemon later. */ @@ -1626,7 +1738,7 @@ pim6_input(mp, offp, proto) m_freem(m); return(IPPROTO_DONE); } - + /* * forward the inner ip6 packet; point m_data at the inner ip6. */ @@ -1643,8 +1755,8 @@ pim6_input(mp, offp, proto) #endif rc = if_simloop(mif6table[reg_mif_num].m6_ifp, m, - dst.sin6_family, NULL); - + dst.sin6_family, NULL); + /* prepare the register head to send to the mrouting daemon */ m = mcp; } diff --git a/sys/netinet6/ip6_mroute.h b/sys/netinet6/ip6_mroute.h index 112ffbe..34ec538 100644 --- a/sys/netinet6/ip6_mroute.h +++ b/sys/netinet6/ip6_mroute.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_mroute.h,v 1.10 2000/05/19 02:38:53 itojun Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,238 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp */ - -/* - * Definitions for IP multicast forwarding. - * - * Written by David Waitzman, BBN Labs, August 1988. - * Modified by Steve Deering, Stanford, February 1989. - * Modified by Ajit Thyagarajan, PARC, August 1993. - * Modified by Ajit Thyagarajan, PARC, August 1994. - * Modified by Ahmed Helmy, USC, September 1996. - * - * MROUTING Revision: 1.2 - */ - -#ifndef _NETINET6_IP6_MROUTE_H_ -#define _NETINET6_IP6_MROUTE_H_ - -/* - * Multicast Routing set/getsockopt commands. - */ -#define MRT6_INIT 100 /* initialize forwarder */ -#define MRT6_DONE 101 /* shut down forwarder */ -#define MRT6_ADD_MIF 102 /* add multicast interface */ -#define MRT6_DEL_MIF 103 /* delete multicast interface */ -#define MRT6_ADD_MFC 104 /* insert forwarding cache entry */ -#define MRT6_DEL_MFC 105 /* delete forwarding cache entry */ -#define MRT6_PIM 107 /* enable pim code */ - -#define GET_TIME(t) microtime(&t) - -/* - * Types and macros for handling bitmaps with one bit per multicast interface. - */ -typedef u_short mifi_t; /* type of a mif index */ -#define MAXMIFS 64 - -#ifndef IF_SETSIZE -#define IF_SETSIZE 256 -#endif - -typedef long if_mask; -#define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */ - -#ifndef howmany -#define howmany(x, y) (((x) + ((y) - 1)) / (y)) -#endif - -typedef struct if_set { - fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; -} if_set; - -#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) -#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS))) -#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS))) -#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f))) -#define IF_ZERO(p) bzero(p, sizeof(*(p))) - -/* - * Argument structure for MRT6_ADD_IF. - */ -struct mif6ctl { - mifi_t mif6c_mifi; /* the index of the mif to be added */ - u_char mif6c_flags; /* MIFF_ flags defined below */ - u_short mif6c_pifi; /* the index of the physical IF */ -}; - -#define MIFF_REGISTER 0x1 /* mif represents a register end-point */ - -/* - * Argument structure for MRT6_ADD_MFC and MRT6_DEL_MFC - */ -struct mf6cctl { - struct sockaddr_in6 mf6cc_origin; /* IPv6 origin of mcasts */ - struct sockaddr_in6 mf6cc_mcastgrp; /* multicast group associated */ - mifi_t mf6cc_parent; /* incoming ifindex */ - struct if_set mf6cc_ifset; /* set of forwarding ifs */ -}; - -/* - * The kernel's multicast routing statistics. - */ -struct mrt6stat { - u_quad_t mrt6s_mfc_lookups; /* # forw. cache hash table hits */ - u_quad_t mrt6s_mfc_misses; /* # forw. cache hash table misses */ - u_quad_t mrt6s_upcalls; /* # calls to mrouted */ - u_quad_t mrt6s_no_route; /* no route for packet's origin */ - u_quad_t mrt6s_bad_tunnel; /* malformed tunnel options */ - u_quad_t mrt6s_cant_tunnel; /* no room for tunnel options */ - u_quad_t mrt6s_wrong_if; /* arrived on wrong interface */ - u_quad_t mrt6s_upq_ovflw; /* upcall Q overflow */ - u_quad_t mrt6s_cache_cleanups; /* # entries with no upcalls */ - u_quad_t mrt6s_drop_sel; /* pkts dropped selectively */ - u_quad_t mrt6s_q_overflow; /* pkts dropped - Q overflow */ - u_quad_t mrt6s_pkt2large; /* pkts dropped - size > BKT SIZE */ - u_quad_t mrt6s_upq_sockfull; /* upcalls dropped - socket full */ -}; - -/* - * Struct used to communicate from kernel to multicast router - * note the convenient similarity to an IPv6 header. - */ -struct mrt6msg { - u_long unused1; - u_char im6_msgtype; /* what type of message */ -#define MRT6MSG_NOCACHE 1 -#define MRT6MSG_WRONGMIF 2 -#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ - u_char im6_mbz; /* must be zero */ - u_char im6_mif; /* mif rec'd on */ - u_char unused2; - struct in6_addr im6_src, im6_dst; -}; - -/* - * Argument structure used by multicast routing daemon to get src-grp - * packet counts - */ -struct sioc_sg_req6 { - struct sockaddr_in6 src; - struct sockaddr_in6 grp; - u_quad_t pktcnt; - u_quad_t bytecnt; - u_quad_t wrong_if; -}; - -/* - * Argument structure used by mrouted to get mif pkt counts - */ -struct sioc_mif_req6 { - mifi_t mifi; /* mif number */ - u_quad_t icount; /* Input packet count on mif */ - u_quad_t ocount; /* Output packet count on mif */ - u_quad_t ibytes; /* Input byte count on mif */ - u_quad_t obytes; /* Output byte count on mif */ -}; - -#if defined(_KERNEL) || defined(KERNEL) -/* - * The kernel's multicast-interface structure. - */ -struct mif6 { - u_char m6_flags; /* MIFF_ flags defined above */ - u_int m6_rate_limit; /* max rate */ - struct in6_addr m6_lcl_addr; /* local interface address */ - struct ifnet *m6_ifp; /* pointer to interface */ - u_quad_t m6_pkt_in; /* # pkts in on interface */ - u_quad_t m6_pkt_out; /* # pkts out on interface */ - u_quad_t m6_bytes_in; /* # bytes in on interface */ - u_quad_t m6_bytes_out; /* # bytes out on interface */ - struct route_in6 m6_route;/* cached route if this is a tunnel */ -}; - -/* - * The kernel's multicast forwarding cache entry structure - */ -struct mf6c { - struct sockaddr_in6 mf6c_origin; /* IPv6 origin of mcasts */ - struct sockaddr_in6 mf6c_mcastgrp; /* multicast group associated*/ - mifi_t mf6c_parent; /* incoming IF */ - struct if_set mf6c_ifset; /* set of outgoing IFs */ - u_quad_t mf6c_pkt_cnt; /* pkt count for src-grp */ - u_quad_t mf6c_byte_cnt; /* byte count for src-grp */ - u_quad_t mf6c_wrong_if; /* wrong if for src-grp */ - int mf6c_expire; /* time to clean entry up */ - struct timeval mf6c_last_assert; /* last time I sent an assert*/ - struct rtdetq *mf6c_stall; /* pkts waiting for route */ - struct mf6c *mf6c_next; /* hash table linkage */ -}; - -#define MF6C_INCOMPLETE_PARENT ((mifi_t)-1) - -/* - * Argument structure used for pkt info. while upcall is made - */ -#ifndef _NETINET_IP_MROUTE_H_ -struct rtdetq { /* XXX: rtdetq is also defined in ip_mroute.h */ - struct mbuf *m; /* A copy of the packet */ - struct ifnet *ifp; /* Interface pkt came in on */ -#ifdef UPCALL_TIMING - struct timeval t; /* Timestamp */ -#endif /* UPCALL_TIMING */ - struct rtdetq *next; -}; -#endif /* _NETINET_IP_MROUTE_H_ */ - -#define MF6CTBLSIZ 256 -#if (MF6CTBLSIZ & (MF6CTBLSIZ - 1)) == 0 /* from sys:route.h */ -#define MF6CHASHMOD(h) ((h) & (MF6CTBLSIZ - 1)) -#else -#define MF6CHASHMOD(h) ((h) % MF6CTBLSIZ) -#endif - -#define MAX_UPQ6 4 /* max. no of pkts in upcall Q */ - -int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt)); -int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt)); -int ip6_mrouter_done __P((void)); -int mrt6_ioctl __P((int, caddr_t)); -#endif /* _KERNEL */ - -#endif /* !_NETINET6_IP6_MROUTE_H_ */ -/* - * Copyright (C) 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ /* BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp */ @@ -279,15 +50,18 @@ int mrt6_ioctl __P((int, caddr_t)); /* * Multicast Routing set/getsockopt commands. */ -#define MRT6_INIT 100 /* initialize forwarder */ +#ifdef _KERNEL +#define MRT6_OINIT 100 /* initialize forwarder (omrt6msg) */ +#endif #define MRT6_DONE 101 /* shut down forwarder */ #define MRT6_ADD_MIF 102 /* add multicast interface */ #define MRT6_DEL_MIF 103 /* delete multicast interface */ #define MRT6_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT6_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT6_PIM 107 /* enable pim code */ +#define MRT6_INIT 108 /* initialize forwarder (mrt6msg) */ -#if BSD >= 199103 +#if BSD >= 199103 #define GET_TIME(t) microtime(&t) #elif defined(sun) #define GET_TIME(t) uniqtime(&t) @@ -365,21 +139,45 @@ struct mrt6stat { u_quad_t mrt6s_upq_sockfull; /* upcalls dropped - socket full */ }; +#ifdef MRT6_OINIT /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IPv6 header. + * XXX old version, superseded by mrt6msg. */ -struct mrt6msg { +struct omrt6msg { u_long unused1; u_char im6_msgtype; /* what type of message */ -#define MRT6MSG_NOCACHE 1 +#if 0 +#define MRT6MSG_NOCACHE 1 #define MRT6MSG_WRONGMIF 2 #define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ +#endif u_char im6_mbz; /* must be zero */ u_char im6_mif; /* mif rec'd on */ u_char unused2; struct in6_addr im6_src, im6_dst; }; +#endif + +/* + * Structure used to communicate from kernel to multicast router. + * We'll overlay the structure onto an MLD header (not an IPv6 header + * like igmpmsg{} used for IPv4 implementation). This is because this + * structure will be passed via an IPv6 raw socket, on which an application + * will only receive the payload i.e. the data after the IPv6 header and all + * the extension headers. (see Section 3 of draft-ietf-ipngwg-2292bis-01) + */ +struct mrt6msg { +#define MRT6MSG_NOCACHE 1 +#define MRT6MSG_WRONGMIF 2 +#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ + u_char im6_mbz; /* must be zero */ + u_char im6_msgtype; /* what type of message */ + u_int16_t im6_mif; /* mif rec'd on */ + u_int32_t im6_pad; /* padding for 64bit arch */ + struct in6_addr im6_src, im6_dst; +}; /* * Argument structure used by multicast routing daemon to get src-grp @@ -413,7 +211,7 @@ struct mif6 { u_int m6_rate_limit; /* max rate */ #ifdef notyet struct tbf *m6_tbf; /* token bucket structure at intf. */ -#endif +#endif struct in6_addr m6_lcl_addr; /* local interface address */ struct ifnet *m6_ifp; /* pointer to interface */ u_quad_t m6_pkt_in; /* # pkts in on interface */ @@ -424,11 +222,11 @@ struct mif6 { #ifdef notyet u_int m6_rsvp_on; /* RSVP listening on this vif */ struct socket *m6_rsvpd; /* RSVP daemon socket */ -#endif +#endif }; /* - * The kernel's multicast forwarding cache entry structure + * The kernel's multicast forwarding cache entry structure */ struct mf6c { struct sockaddr_in6 mf6c_origin; /* IPv6 origin of mcasts */ @@ -470,13 +268,8 @@ struct rtdetq { /* XXX: rtdetq is also defined in ip_mroute.h */ #define MAX_UPQ6 4 /* max. no of pkts in upcall Q */ -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt)); int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt)); -#else -int ip6_mrouter_set __P((int, struct socket *, struct mbuf *)); -int ip6_mrouter_get __P((int, struct socket *, struct mbuf **)); -#endif int ip6_mrouter_done __P((void)); int mrt6_ioctl __P((int, caddr_t)); #endif /* _KERNEL */ diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index eb0474f..e23ba00 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_output.c,v 1.115 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -64,8 +65,10 @@ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ -#include "opt_ipsec.h" #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" #include <sys/param.h> #include <sys/malloc.h> @@ -83,25 +86,20 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> -#include <netinet6/icmp6.h> -#include <netinet/in_pcb.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> #include <netinet6/ip6_var.h> +#include <netinet/in_pcb.h> #include <netinet6/nd6.h> #ifdef IPSEC #include <netinet6/ipsec.h> +#ifdef INET6 #include <netinet6/ipsec6.h> -#include <netkey/key.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) #endif +#include <netkey/key.h> #endif /* IPSEC */ -#include "loop.h" - #include <net/net_osdep.h> #ifdef IPV6FIREWALL @@ -111,22 +109,22 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); struct ip6_exthdrs { - struct mbuf *ip6e_ip6; - struct mbuf *ip6e_hbh; - struct mbuf *ip6e_dest1; - struct mbuf *ip6e_rthdr; - struct mbuf *ip6e_dest2; + struct mbuf *ip6e_ip6; + struct mbuf *ip6e_hbh; + struct mbuf *ip6e_dest1; + struct mbuf *ip6e_rthdr; + struct mbuf *ip6e_dest2; }; -static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, +static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, struct socket *, struct sockopt *sopt)); -static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); -static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); -static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); -static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, +static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); +static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); +static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); +static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, struct ip6_frag **)); -static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); -static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); +static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); +static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 @@ -145,7 +143,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) struct ifnet **ifpp; /* XXX: just for statistics */ { struct ip6_hdr *ip6, *mhip6; - struct ifnet *ifp; + struct ifnet *ifp, *origifp; struct mbuf *m = m0; int hlen, tlen, len, off; struct route_in6 ip6route; @@ -165,16 +163,13 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) struct secpolicy *sp = NULL; /* for AH processing. stupid to have "socket" variable in IP layer... */ - if ((flags & IPV6_SOCKINMRCVIF) != 0) { - so = (struct socket *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; - } else - so = NULL; + so = ipsec_getsocket(m); + ipsec_setsocket(m, NULL); ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ -#define MAKE_EXTHDR(hp,mp) \ - { \ +#define MAKE_EXTHDR(hp, mp) \ + do { \ if (hp) { \ struct ip6_ext *eh = (struct ip6_ext *)(hp); \ error = ip6_copyexthdr((mp), (caddr_t)(hp), \ @@ -182,9 +177,10 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) if (error) \ goto freehdrs; \ } \ - } - + } while (0) + bzero(&exthdrs, sizeof(exthdrs)); + if (opt) { /* Hop-by-Hop options header */ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); @@ -205,7 +201,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) if (sp == NULL) { ipsec6stat.out_inval++; - goto bad; + goto freehdrs; } error = 0; @@ -217,7 +213,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) * This packet is just discarded. */ ipsec6stat.out_polvio++; - goto bad; + goto freehdrs; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: @@ -227,10 +223,9 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { - /* XXX should be panic ? */ - printf("ip6_output: No IPsec request specified.\n"); - error = EINVAL; - goto bad; + /* acquire a policy */ + error = key_spdacquire(sp); + goto freehdrs; } needipsec = 1; break; @@ -321,8 +316,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) ip6->ip6_nxt = IPPROTO_DSTOPTS; } -#define MAKE_CHAIN(m,mp,p,i)\ - {\ +#define MAKE_CHAIN(m, mp, p, i)\ + do {\ if (m) {\ if (!hdrsplit) \ panic("assumption failed: hdr not split"); \ @@ -333,7 +328,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) (mp)->m_next = (m);\ (mp) = (m);\ }\ - } + } while (0) /* * result: IPv6 hbh dest1 rthdr dest2 payload * m will point to IPv6 header. mprev will point to the @@ -470,6 +465,11 @@ skip_ipsec2:; dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_addr = ip6->ip6_dst; +#ifdef SCOPEDROUTING + /* XXX: sin6_scope_id should already be fixed at this point */ + if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) + dst->sin6_scope_id = ntohs(dst->sin6_addr.s6_addr16[1]); +#endif } #ifdef IPSEC if (needipsec && needipsectun) { @@ -520,7 +520,7 @@ skip_ipsec2:; exthdrs.ip6e_ip6 = m; } -#endif /*IPESC*/ +#endif /*IPSEC*/ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* Unicast */ @@ -532,8 +532,13 @@ skip_ipsec2:; * if an interface is specified from an upper layer, * ifp must point it. */ - if (ro->ro_rt == 0) + if (ro->ro_rt == 0) { + /* + * non-bsdi always clone routes, if parent is + * PRF_CLONING. + */ rtalloc((struct route *)ro); + } if (ro->ro_rt == 0) { ip6stat.ip6s_noroute++; error = EHOSTUNREACH; @@ -550,11 +555,11 @@ skip_ipsec2:; in6_ifstat_inc(ifp, ifs6_out_request); /* - * Check if there is the outgoing interface conflicts with - * the interface specified by ifi6_ifindex(if specified). + * Check if the outgoing interface conflicts with + * the interface specified by ifi6_ifindex (if specified). * Note that loopback interface is always okay. - * (this happens when we are sending packet toward my - * interface) + * (this may happen when we are sending a packet to one of + * our own addresses.) */ if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex) { @@ -676,7 +681,7 @@ skip_ipsec2:; * if necessary. */ if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) { - if (ip6_mforward(ip6, ifp, m) != NULL) { + if (ip6_mforward(ip6, ifp, m) != 0) { m_freem(m); goto done; } @@ -746,10 +751,38 @@ skip_ipsec2:; mtu = nd_ifinfo[ifp->if_index].linkmtu; } - /* - * Fake link-local scope-class addresses - */ - if ((ifp->if_flags & IFF_LOOPBACK) == 0) { + /* Fake scoped addresses */ + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + /* + * If source or destination address is a scoped address, and + * the packet is going to be sent to a loopback interface, + * we should keep the original interface. + */ + + /* + * XXX: this is a very experimental and temporary solution. + * We eventually have sockaddr_in6 and use the sin6_scope_id + * field of the structure here. + * We rely on the consistency between two scope zone ids + * of source add destination, which should already be assured + * Larger scopes than link will be supported in the near + * future. + */ + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + else + origifp = ifp; + } + else + origifp = ifp; +#ifndef FAKE_LOOPBACK_IF + if ((ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) @@ -762,6 +795,7 @@ skip_ipsec2:; */ if (ip6_fw_chk_ptr) { u_short port = 0; + m->m_pkthdr.rcvif = NULL; /*XXX*/ /* If ipfw says divert, we have to just drop packet */ if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) { m_freem(m); @@ -827,18 +861,7 @@ skip_ipsec2:; #endif ) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } -#endif - error = nd6_output(ifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; } else if (mtu < IPV6_MMTU) { /* @@ -954,18 +977,7 @@ sendorfree: m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } -#endif - error = nd6_output(ifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); } else m_freem(m); } @@ -1124,7 +1136,7 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp) ; if ((mlast->m_flags & M_EXT) == 0 && - M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) { + M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) { /* use the trailing space of the last mbuf for the fragment hdr */ *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); @@ -1198,9 +1210,6 @@ ip6_ctloutput(so, sopt) } /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: case IPV6_PKTINFO: case IPV6_HOPLIMIT: case IPV6_RTHDR: @@ -1233,18 +1242,6 @@ ip6_ctloutput(so, sopt) else \ in6p->in6p_flags &= ~bit; - case IPV6_RECVOPTS: - OPTSET(IN6P_RECVOPTS); - break; - - case IPV6_RECVRETOPTS: - OPTSET(IN6P_RECVRETOPTS); - break; - - case IPV6_RECVDSTADDR: - OPTSET(IN6P_RECVDSTADDR); - break; - case IPV6_PKTINFO: OPTSET(IN6P_PKTINFO); break; @@ -1308,50 +1305,51 @@ ip6_ctloutput(so, sopt) } break; - case IPV6_PORTRANGE: - error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) - break; + case IPV6_PORTRANGE: + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; - switch (optval) { - case IPV6_PORTRANGE_DEFAULT: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - break; + switch (optval) { + case IPV6_PORTRANGE_DEFAULT: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + break; - case IPV6_PORTRANGE_HIGH: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags |= IN6P_HIGHPORT; - break; + case IPV6_PORTRANGE_HIGH: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags |= IN6P_HIGHPORT; + break; - case IPV6_PORTRANGE_LOW: - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - in6p->in6p_flags |= IN6P_LOWPORT; - break; + case IPV6_PORTRANGE_LOW: + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + in6p->in6p_flags |= IN6P_LOWPORT; + break; - default: - error = EINVAL; + default: + error = EINVAL; + break; + } break; - } - break; #ifdef IPSEC case IPV6_IPSEC_POLICY: { caddr_t req = NULL; + size_t len = 0; struct mbuf *m; - if ((error = soopt_getm(sopt, &m)) - != 0) /* XXX */ + if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; - if ((error = soopt_mcopyin(sopt, m)) - != 0) /* XXX */ + if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; - if (m != 0) + if (m) { req = mtod(m, caddr_t); + len = m->m_len; + } error = ipsec6_set_policy(in6p, optname, req, - privileged); + len, privileged); m_freem(m); } break; @@ -1389,11 +1387,6 @@ ip6_ctloutput(so, sopt) case SOPT_GET: switch (optname) { - case IPV6_OPTIONS: - case IPV6_RETOPTS: - error = ENOPROTOOPT; - break; - case IPV6_PKTOPTIONS: if (in6p->in6p_options) { error = soopt_mcopyout(sopt, @@ -1410,9 +1403,6 @@ ip6_ctloutput(so, sopt) } /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: case IPV6_PKTINFO: case IPV6_HOPLIMIT: case IPV6_RTHDR: @@ -1428,18 +1418,6 @@ ip6_ctloutput(so, sopt) #define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) - case IPV6_RECVOPTS: - optval = OPTBIT(IN6P_RECVOPTS); - break; - - case IPV6_RECVRETOPTS: - optval = OPTBIT(IN6P_RECVRETOPTS); - break; - - case IPV6_RECVDSTADDR: - optval = OPTBIT(IN6P_RECVDSTADDR); - break; - case IPV6_PKTINFO: optval = OPTBIT(IN6P_PKTINFO); break; @@ -1509,17 +1487,25 @@ ip6_ctloutput(so, sopt) #ifdef IPSEC case IPV6_IPSEC_POLICY: { - - struct mbuf *m = NULL; caddr_t req = NULL; + size_t len = 0; + struct mbuf *m = NULL; + struct mbuf **mp = &m; - if (m != 0) + error = soopt_getm(sopt, &m); /* XXX */ + if (error != NULL) + break; + error = soopt_mcopyin(sopt, m); /* XXX */ + if (error != NULL) + break; + if (m) { req = mtod(m, caddr_t); - error = ipsec6_get_policy(in6p, req, &m); + len = m->m_len; + } + error = ipsec6_get_policy(in6p, req, len, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /*XXX*/ - if (error == 0) - m_freem(m); + m_freem(m); break; } #endif /* IPSEC */ @@ -1710,7 +1696,8 @@ ip6_setmoptions(optname, im6op, m) * all multicast addresses. Only super user is allowed * to do this. */ - if (suser(p)) { + if (suser(p)) + { error = EACCES; break; } @@ -1778,7 +1765,8 @@ ip6_setmoptions(optname, im6op, m) /* * See if the membership already exists. */ - LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) if (imm->i6mm_maddr->in6m_ifp == ifp && IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, &mreq->ipv6mr_multiaddr)) @@ -1844,7 +1832,8 @@ ip6_setmoptions(optname, im6op, m) /* * Find the membership in the membership list. */ - LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) { + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) { if ((ifp == NULL || imm->i6mm_maddr->in6m_ifp == ifp) && IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, @@ -1876,7 +1865,7 @@ ip6_setmoptions(optname, im6op, m) if (im6o->im6o_multicast_ifp == NULL && im6o->im6o_multicast_hlim == ip6_defmcasthlim && im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && - LIST_EMPTY(&im6o->im6o_memberships)) { + im6o->im6o_memberships.lh_first == NULL) { free(*im6op, M_IPMOPTS); *im6op = NULL; } @@ -1943,7 +1932,7 @@ ip6_freemoptions(im6o) if (im6o == NULL) return; - while ((imm = LIST_FIRST(&im6o->im6o_memberships)) != NULL) { + while ((imm = im6o->im6o_memberships.lh_first) != NULL) { LIST_REMOVE(imm, i6mm_chain); if (imm->i6mm_maddr) in6_delmulti(imm->i6mm_maddr); @@ -1976,8 +1965,6 @@ ip6_setpktoptions(control, opt, priv) if (control->m_next) return(EINVAL); - opt->ip6po_m = control; - for (; control->m_len; control->m_data += ALIGN(cm->cmsg_len), control->m_len -= ALIGN(cm->cmsg_len)) { cm = mtod(control, struct cmsghdr *); @@ -2120,12 +2107,46 @@ ip6_mloopback(ifp, m, dst) register struct mbuf *m; register struct sockaddr_in6 *dst; { - struct mbuf *copym; + struct mbuf *copym; + struct ip6_hdr *ip6; copym = m_copy(m, 0, M_COPYALL); - if (copym != NULL) { - (void)if_simloop(ifp, copym, dst->sin6_family, 0); + if (copym == NULL) + return; + + /* + * Make sure to deep-copy IPv6 header portion in case the data + * is in an mbuf cluster, so that we can safely override the IPv6 + * header portion later. + */ + if ((copym->m_flags & M_EXT) != 0 || + copym->m_len < sizeof(struct ip6_hdr)) { + copym = m_pullup(copym, sizeof(struct ip6_hdr)); + if (copym == NULL) + return; } + +#ifdef DIAGNOSTIC + if (copym->m_len < sizeof(*ip6)) { + m_freem(copym); + return; + } +#endif + +#ifndef FAKE_LOOPBACK_IF + if ((ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + ip6 = mtod(copym, struct ip6_hdr *); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + + (void)if_simloop(ifp, copym, dst->sin6_family, NULL); } /* diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 2e70bcb..f6f3824 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -72,35 +73,37 @@ * being reassembled is attached to one of these structures. */ struct ip6q { - u_long ip6q_head; - u_short ip6q_len; - u_char ip6q_nxt; - u_char ip6q_hlim; - struct ip6asfrag *ip6q_down; - struct ip6asfrag *ip6q_up; - u_long ip6q_ident; - u_char ip6q_arrive; - u_char ip6q_ttl; - struct in6_addr ip6q_src, ip6q_dst; - struct ip6q *ip6q_next; - struct ip6q *ip6q_prev; - int ip6q_unfrglen; + u_int32_t ip6q_head; + u_int16_t ip6q_len; + u_int8_t ip6q_nxt; /* ip6f_nxt in first fragment */ + u_int8_t ip6q_hlim; + struct ip6asfrag *ip6q_down; + struct ip6asfrag *ip6q_up; + u_int32_t ip6q_ident; + u_int8_t ip6q_arrive; + u_int8_t ip6q_ttl; + struct in6_addr ip6q_src, ip6q_dst; + struct ip6q *ip6q_next; + struct ip6q *ip6q_prev; + int ip6q_unfrglen; /* len of unfragmentable part */ +#ifdef notyet + u_char *ip6q_nxtp; +#endif }; struct ip6asfrag { - u_long ip6af_head; - u_short ip6af_len; - u_char ip6af_nxt; - u_char ip6af_hlim; + u_int32_t ip6af_head; + u_int16_t ip6af_len; + u_int8_t ip6af_nxt; + u_int8_t ip6af_hlim; /* must not override the above members during reassembling */ - struct ip6asfrag *ip6af_down; - struct ip6asfrag *ip6af_up; - u_short ip6af_mff; - u_short ip6af_off; - struct mbuf *ip6af_m; - u_long ip6af_offset; /* offset where next header starts */ - u_short ip6af_frglen; /* fragmentable part length */ - u_char ip6af_x1[10]; + struct ip6asfrag *ip6af_down; + struct ip6asfrag *ip6af_up; + struct mbuf *ip6af_m; + int ip6af_offset; /* offset in ip6af_m to next header */ + int ip6af_frglen; /* fragmentable part length */ + int ip6af_off; /* fragment offset */ + u_int16_t ip6af_mff; /* more fragment bit in frag off */ }; #define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m)) @@ -121,8 +124,8 @@ struct ip6po_rhinfo { struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */ struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */ }; -#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr -#define ip6po_route ip6po_rhinfo.ip6po_rhi_route +#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr +#define ip6po_route ip6po_rhinfo.ip6po_rhi_route struct ip6_pktopts { struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */ @@ -136,49 +139,88 @@ struct ip6_pktopts { }; struct ip6stat { - u_long ip6s_total; /* total packets received */ - u_long ip6s_tooshort; /* packet too short */ - u_long ip6s_toosmall; /* not enough data */ - u_long ip6s_fragments; /* fragments received */ - u_long ip6s_fragdropped; /* frags dropped(dups, out of space) */ - u_long ip6s_fragtimeout; /* fragments timed out */ - u_long ip6s_fragoverflow; /* fragments that exceeded limit */ - u_long ip6s_forward; /* packets forwarded */ - u_long ip6s_cantforward; /* packets rcvd for unreachable dest */ - u_long ip6s_redirectsent; /* packets forwarded on same net */ - u_long ip6s_delivered; /* datagrams delivered to upper level*/ - u_long ip6s_localout; /* total ip packets generated here */ - u_long ip6s_odropped; /* lost packets due to nobufs, etc. */ - u_long ip6s_reassembled; /* total packets reassembled ok */ - u_long ip6s_fragmented; /* datagrams sucessfully fragmented */ - u_long ip6s_ofragments; /* output fragments created */ - u_long ip6s_cantfrag; /* don't fragment flag was set, etc. */ - u_long ip6s_badoptions; /* error in option processing */ - u_long ip6s_noroute; /* packets discarded due to no route */ - u_long ip6s_badvers; /* ip6 version != 6 */ - u_long ip6s_rawout; /* total raw ip packets generated */ - u_long ip6s_badscope; /* scope error */ - u_long ip6s_notmember; /* don't join this multicast group */ - u_long ip6s_nxthist[256]; /* next header history */ - u_long ip6s_m1; /* one mbuf */ - u_long ip6s_m2m[32]; /* two or more mbuf */ - u_long ip6s_mext1; /* one ext mbuf */ - u_long ip6s_mext2m; /* two or more ext mbuf */ - u_long ip6s_exthdrtoolong; /* ext hdr are not continuous */ - u_long ip6s_nogif; /* no match gif found */ - u_long ip6s_toomanyhdr; /* discarded due to too many headers */ + u_quad_t ip6s_total; /* total packets received */ + u_quad_t ip6s_tooshort; /* packet too short */ + u_quad_t ip6s_toosmall; /* not enough data */ + u_quad_t ip6s_fragments; /* fragments received */ + u_quad_t ip6s_fragdropped; /* frags dropped(dups, out of space) */ + u_quad_t ip6s_fragtimeout; /* fragments timed out */ + u_quad_t ip6s_fragoverflow; /* fragments that exceeded limit */ + u_quad_t ip6s_forward; /* packets forwarded */ + u_quad_t ip6s_cantforward; /* packets rcvd for unreachable dest */ + u_quad_t ip6s_redirectsent; /* packets forwarded on same net */ + u_quad_t ip6s_delivered; /* datagrams delivered to upper level*/ + u_quad_t ip6s_localout; /* total ip packets generated here */ + u_quad_t ip6s_odropped; /* lost packets due to nobufs, etc. */ + u_quad_t ip6s_reassembled; /* total packets reassembled ok */ + u_quad_t ip6s_fragmented; /* datagrams sucessfully fragmented */ + u_quad_t ip6s_ofragments; /* output fragments created */ + u_quad_t ip6s_cantfrag; /* don't fragment flag was set, etc. */ + u_quad_t ip6s_badoptions; /* error in option processing */ + u_quad_t ip6s_noroute; /* packets discarded due to no route */ + u_quad_t ip6s_badvers; /* ip6 version != 6 */ + u_quad_t ip6s_rawout; /* total raw ip packets generated */ + u_quad_t ip6s_badscope; /* scope error */ + u_quad_t ip6s_notmember; /* don't join this multicast group */ + u_quad_t ip6s_nxthist[256]; /* next header history */ + u_quad_t ip6s_m1; /* one mbuf */ + u_quad_t ip6s_m2m[32]; /* two or more mbuf */ + u_quad_t ip6s_mext1; /* one ext mbuf */ + u_quad_t ip6s_mext2m; /* two or more ext mbuf */ + u_quad_t ip6s_exthdrtoolong; /* ext hdr are not continuous */ + u_quad_t ip6s_nogif; /* no match gif found */ + u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */ + /* XXX the following two items are not really AF_INET6 thing */ + u_quad_t ip6s_exthdrget; /* # of calls to IP6_EXTHDR_GET */ + u_quad_t ip6s_exthdrget0; /* # of calls to IP6_EXTHDR_GET0 */ + u_quad_t ip6s_pulldown; /* # of calls to m_pulldown */ + u_quad_t ip6s_pulldown_copy; /* # of mbuf copies in m_pulldown */ + u_quad_t ip6s_pulldown_alloc; /* # of mbuf allocs in m_pulldown */ + u_quad_t ip6s_pullup; /* # of calls to m_pullup */ + u_quad_t ip6s_pullup_copy; /* # of possible m_pullup copies */ + u_quad_t ip6s_pullup_alloc; /* # of possible m_pullup mallocs */ + u_quad_t ip6s_pullup_fail; /* # of possible m_pullup failures */ + u_quad_t ip6s_pullup2; /* # of calls to m_pullup2 */ + u_quad_t ip6s_pullup2_copy; /* # of possible m_pullup2 copies */ + u_quad_t ip6s_pullup2_alloc; /* # of possible m_pullup2 mallocs */ + u_quad_t ip6s_pullup2_fail; /* # of possible m_pullup2 failures */ + + /* + * statistics for improvement of the source address selection + * algorithm: + * XXX: hardcoded 16 = # of ip6 multicast scope types + 1 + */ + /* number of times that address selection fails */ + u_quad_t ip6s_sources_none; + /* number of times that an address on the outgoing I/F is chosen */ + u_quad_t ip6s_sources_sameif[16]; + /* number of times that an address on a non-outgoing I/F is chosen */ + u_quad_t ip6s_sources_otherif[16]; + /* + * number of times that an address that has the same scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_samescope[16]; + /* + * number of times that an address that has a different scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_otherscope[16]; + /* number of times that an deprecated address is chosen */ + u_quad_t ip6s_sources_deprecated[16]; + + u_quad_t ip6s_forward_cachehit; + u_quad_t ip6s_forward_cachemiss; }; #ifdef _KERNEL /* flags passed to ip6_output as last parameter */ #define IPV6_DADOUTPUT 0x01 /* DAD */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ -#define IPV6_SOCKINMRCVIF 0x100 /* IPSEC hack; - * socket pointer in sending - * packet's m_pkthdr.rcvif */ +#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */ extern struct ip6stat ip6stat; /* statistics */ -extern u_int32_t ip6_id; /* fragment identifier */ +extern u_int32_t ip6_id; /* fragment identifier */ extern int ip6_defhlim; /* default hop limit */ extern int ip6_defmcasthlim; /* default multicast hop limit */ extern int ip6_forwarding; /* act as router? */ @@ -189,7 +231,7 @@ extern int ip6_rr_prune; /* router renumbering prefix * walk list every 5 sec. */ extern int ip6_mapped_addr_on; -extern struct socket *ip6_mrouter; /* multicast routing daemon */ +extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */ extern int ip6_sourcecheck; /* Verify source interface */ @@ -201,12 +243,13 @@ extern time_t ip6_log_time; extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */ extern int ip6_dad_count; /* DupAddrDetectionTransmits */ -extern u_int32_t ip6_flow_seq; -extern int ip6_auto_flowlabel; +extern u_int32_t ip6_flow_seq; +extern int ip6_auto_flowlabel; extern struct pr_usrreqs rip6_usrreqs; -struct sockopt; -struct inpcb; +struct sockopt; + +struct inpcb; int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt)); @@ -216,6 +259,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 *)); @@ -231,6 +276,8 @@ int ip6_output __P((struct mbuf *, struct ip6_pktopts *, struct ip6_moptions *, struct ifnet **)); int ip6_ctloutput __P((struct socket *, struct sockopt *sopt)); int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int)); +void ip6_clearpktopts __P((struct ip6_pktopts *, int, int)); +struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int)); int ip6_optlen __P((struct inpcb *)); int route6_input __P((struct mbuf **, int *, int)); @@ -242,6 +289,7 @@ void frag6_drain __P((void)); void rip6_init __P((void)); int rip6_input __P((struct mbuf **mp, int *offp, int proto)); +void rip6_ctlinput __P((int, struct sockaddr *, void *)); int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt)); int rip6_output __P((struct mbuf *, ...)); int rip6_usrreq __P((struct socket *, diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h index 1e1c3c2..c361219 100644 --- a/sys/netinet6/ip6protosw.h +++ b/sys/netinet6/ip6protosw.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6protosw.h,v 1.10 2000/03/29 22:51:25 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -26,7 +29,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* BSDI protosw.h,v 2.3 1996/10/11 16:02:40 pjd Exp */ @@ -74,23 +76,22 @@ * All other definitions should refer to sys/protosw.h */ -struct mbuf; -struct sockaddr; -struct socket; -struct sockopt; -struct domain; -struct proc; -struct ip6_hdr; -struct pr_usrreqs; +struct mbuf; +struct sockaddr; +struct socket; +struct domain; +struct proc; +struct ip6_hdr; +struct pr_usrreqs; /* * argument type for the last arg of pr_ctlinput(). * should be consulted only with AF_INET6 family. */ struct ip6ctlparam { - struct mbuf *ip6c_m; /* start of mbuf chain */ - struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ - int ip6c_off; /* offset of the target proto header */ + struct mbuf *ip6c_m; /* start of mbuf chain */ + struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ + int ip6c_off; /* offset of the target proto header */ }; struct ip6protosw { diff --git a/sys/netinet6/ipcomp.h b/sys/netinet6/ipcomp.h new file mode 100644 index 0000000..dea5ea8 --- /dev/null +++ b/sys/netinet6/ipcomp.h @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#ifndef _NETINET6_IPCOMP_H_ +#define _NETINET6_IPCOMP_H_ + +struct ipcomp { + u_int8_t comp_nxt; /* Next Header */ + u_int8_t comp_flags; /* reserved, must be zero */ + u_int16_t comp_cpi; /* Compression parameter index */ +}; + +/* well-known algorithm number (in CPI), from RFC2409 */ +#define IPCOMP_OUI 1 /* vendor specific */ +#define IPCOMP_DEFLATE 2 /* RFC2394 */ +#define IPCOMP_LZS 3 /* RFC2395 */ +#define IPCOMP_MAX 4 + +#define IPCOMP_CPI_NEGOTIATE_MIN 256 + +#ifdef _KERNEL +struct ipcomp_algorithm { + int (*compress) __P((struct mbuf *, struct mbuf *, size_t *)); + int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *)); + size_t minplen; /* minimum required length for compression */ +}; + +struct ipsecrequest; +extern struct ipcomp_algorithm ipcomp_algorithms[]; +extern void ipcomp4_input __P((struct mbuf *, ...)); +extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_IPCOMP_H_*/ diff --git a/sys/netinet6/ipcomp6.h b/sys/netinet6/ipcomp6.h new file mode 100644 index 0000000..553c8df --- /dev/null +++ b/sys/netinet6/ipcomp6.h @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#ifndef _NETINET6_IPCOMP6_H_ +#define _NETINET6_IPCOMP6_H_ + +#ifdef _KERNEL +extern int ipcomp6_input __P((struct mbuf **, int *, int)); +extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_IPCOMP6_H_*/ diff --git a/sys/netinet6/ipcomp_core.c b/sys/netinet6/ipcomp_core.c new file mode 100644 index 0000000..1eee253 --- /dev/null +++ b/sys/netinet6/ipcomp_core.c @@ -0,0 +1,314 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_core.c,v 1.12 2000/05/05 11:01:01 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/syslog.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <net/zlib.h> +#include <machine/cpu.h> + +#include <netinet6/ipcomp.h> +#ifdef INET6 +#include <netinet6/ipcomp6.h> +#endif +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif + +#include <machine/stdarg.h> + +#include <net/net_osdep.h> + +static void *deflate_alloc __P((void *, u_int, u_int)); +static void deflate_free __P((void *, void *)); +static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int)); +static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *)); +static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *)); + +/* + * We need to use default window size (2^15 = 32Kbytes as of writing) for + * inbound case. Otherwise we get interop problem. + * Use negative value to avoid Adler32 checksum. This is an undocumented + * feature in zlib (see ipsec wg mailing list archive in January 2000). + */ +static int deflate_policy = Z_DEFAULT_COMPRESSION; +static int deflate_window_out = -12; +static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */ +static int deflate_memlevel = MAX_MEM_LEVEL; + +struct ipcomp_algorithm ipcomp_algorithms[] = { + { NULL, NULL, -1 }, + { NULL, NULL, -1 }, + { deflate_compress, deflate_decompress, 90 }, + { NULL, NULL, 90 }, +}; + +static void * +deflate_alloc(aux, items, siz) + void *aux; + u_int items; + u_int siz; +{ + void *ptr; + MALLOC(ptr, void *, items * siz, M_TEMP, M_NOWAIT); + return ptr; +} + +static void +deflate_free(aux, ptr) + void *aux; + void *ptr; +{ + FREE(ptr, M_TEMP); +} + +static int +deflate_common(m, md, lenp, mode) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; + int mode; /* 0: compress 1: decompress */ +{ + struct mbuf *mprev; + struct mbuf *p; + struct mbuf *n, *n0 = NULL, **np; + z_stream zs; + int error = 0; + int zerror; + size_t offset; + int firsttime, final, flush; + + for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) + ; + if (!mprev) + panic("md is not in m in deflate_common"); + + bzero(&zs, sizeof(zs)); + zs.zalloc = deflate_alloc; + zs.zfree = deflate_free; + + zerror = mode ? inflateInit2(&zs, deflate_window_in) + : deflateInit2(&zs, deflate_policy, Z_DEFLATED, + deflate_window_out, deflate_memlevel, + Z_DEFAULT_STRATEGY); + if (zerror != Z_OK) { + error = ENOBUFS; + goto fail; + } + + n0 = n = NULL; + np = &n0; + offset = 0; + firsttime = 1; + final = 0; + flush = Z_NO_FLUSH; + zerror = 0; + p = md; + while (1) { + /* + * first time, we need to setup the buffer before calling + * compression function. + */ + if (firsttime) + firsttime = 0; + else { + zerror = mode ? inflate(&zs, flush) + : deflate(&zs, flush); + } + + /* get input buffer */ + if (p && zs.avail_in == 0) { + zs.next_in = mtod(p, u_int8_t *); + zs.avail_in = p->m_len; + p = p->m_next; + if (!p) { + final = 1; + flush = Z_PARTIAL_FLUSH; + } + } + + /* get output buffer */ + if (zs.next_out == NULL || zs.avail_out == 0) { + /* keep the reply buffer into our chain */ + if (n) { + n->m_len = zs.total_out - offset; + offset = zs.total_out; + *np = n; + np = &n->m_next; + } + + /* get a fresh reply buffer */ + MGET(n, M_DONTWAIT, MT_DATA); + if (n) { + MCLGET(n, M_DONTWAIT); + } + if (!n) { + error = ENOBUFS; + goto fail; + } + n->m_len = 0; + n->m_len = M_TRAILINGSPACE(n); + n->m_next = NULL; + /* + * if this is the first reply buffer, reserve + * region for ipcomp header. + */ + if (*np == NULL) { + n->m_len -= sizeof(struct ipcomp); + n->m_data += sizeof(struct ipcomp); + } + + zs.next_out = mtod(n, u_int8_t *); + zs.avail_out = n->m_len; + } + + if (zerror == Z_OK) { + /* + * to terminate deflate/inflate process, we need to + * call {in,de}flate() with different flushing methods. + * + * deflate() needs at least one Z_PARTIAL_FLUSH, + * then use Z_FINISH until we get to the end. + * (if we use Z_FLUSH without Z_PARTIAL_FLUSH, deflate() + * will assume contiguous single output buffer, and that + * is not what we want) + * inflate() does not care about flushing method, but + * needs output buffer until it gets to the end. + * + * the most outer loop will be terminated with + * Z_STREAM_END. + */ + if (final == 1) { + /* reached end of mbuf chain */ + if (mode == 0) + final = 2; + else + final = 3; + } else if (final == 2) { + /* terminate deflate case */ + flush = Z_FINISH; + } else if (final == 3) { + /* terminate inflate case */ + ; + } + } else if (zerror == Z_STREAM_END) + break; + else { + ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg ? zs.msg : "unknown error")); + error = EINVAL; + goto fail; + } + } + zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs); + if (zerror != Z_OK) { + ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg ? zs.msg : "unknown error")); + error = EINVAL; + goto fail; + } + /* keep the final reply buffer into our chain */ + if (n) { + n->m_len = zs.total_out - offset; + offset = zs.total_out; + *np = n; + np = &n->m_next; + } + + /* switch the mbuf to the new one */ + mprev->m_next = n0; + m_freem(md); + *lenp = zs.total_out; + + return 0; + +fail: + if (m) + m_freem(m); + if (n0) + m_freem(n0); + return error; +} + +static int +deflate_compress(m, md, lenp) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; +{ + if (!m) + panic("m == NULL in deflate_compress"); + if (!md) + panic("md == NULL in deflate_compress"); + if (!lenp) + panic("lenp == NULL in deflate_compress"); + + return deflate_common(m, md, lenp, 0); +} + +static int +deflate_decompress(m, md, lenp) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; +{ + if (!m) + panic("m == NULL in deflate_decompress"); + if (!md) + panic("md == NULL in deflate_decompress"); + if (!lenp) + panic("lenp == NULL in deflate_decompress"); + + return deflate_common(m, md, lenp, 1); +} diff --git a/sys/netinet6/ipcomp_input.c b/sys/netinet6/ipcomp_input.c new file mode 100644 index 0000000..da0184c --- /dev/null +++ b/sys/netinet6/ipcomp_input.c @@ -0,0 +1,407 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_input.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <net/zlib.h> +#include <machine/cpu.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/ip_ecn.h> + +#ifdef INET6 +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#endif +#include <netinet6/ipcomp.h> +#ifdef INET6 +#include <netinet6/ipcomp6.h> +#endif + +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#include <netkey/key.h> +#include <netkey/keydb.h> + +#include <machine/stdarg.h> + +#include <net/net_osdep.h> + +#define IPLEN_FLIPPED + +#ifdef INET +#include <netinet/ipprotosw.h> +extern struct ipprotosw inetsw[]; + +void +#if __STDC__ +ipcomp4_input(struct mbuf *m, ...) +#else +ipcomp4_input(m, va_alist) + struct mbuf *m; + va_dcl +#endif +{ + struct ip *ip; + struct ipcomp *ipcomp; + struct ipcomp_algorithm *algo; + u_int16_t cpi; /* host order */ + u_int16_t nxt; + size_t hlen; + int error; + size_t newlen, olen; + struct secasvar *sav = NULL; + int off, proto; + va_list ap; + + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + + if (off + sizeof(struct ipcomp) > MHLEN) { + /*XXX the restriction should be relaxed*/ + ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " + "(header too long)\n")); + ipsecstat.in_inval++; + goto fail; + } + if (m->m_len < off + sizeof(struct ipcomp)) { + m = m_pullup(m, off + sizeof(struct ipcomp)); + if (!m) { + ipseclog((LOG_DEBUG, "IPv4 IPComp input: can't pullup;" + "dropping the packet for simplicity\n")); + ipsecstat.in_nomem++; + goto fail; + } + } else if (m->m_len > off + sizeof(struct ipcomp)) { + /* chop header part from the packet header chain */ + struct mbuf *n; + MGETHDR(n, M_DONTWAIT, MT_HEADER); + if (!n) { + ipsecstat.in_nomem++; + goto fail; + } + M_COPY_PKTHDR(n, m); + MH_ALIGN(n, off + sizeof(struct ipcomp)); + n->m_len = off + sizeof(struct ipcomp); + bcopy(mtod(m, caddr_t), mtod(n, caddr_t), + off + sizeof(struct ipcomp)); + m_adj(m, off + sizeof(struct ipcomp)); + m->m_flags &= ~M_PKTHDR; + n->m_next = m; + m = n; + } + + ip = mtod(m, struct ip *); + ipcomp = (struct ipcomp *)(((caddr_t)ip) + off); + nxt = ipcomp->comp_nxt; +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + cpi = ntohs(ipcomp->comp_cpi); + + if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { + sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, + (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi)); + if (sav != NULL + && (sav->state == SADB_SASTATE_MATURE + || sav->state == SADB_SASTATE_DYING)) { + cpi = sav->alg_enc; /*XXX*/ + /* other parameters to look at? */ + } + } + if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) + algo = &ipcomp_algorithms[cpi]; + else + algo = NULL; + if (!algo) { + ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n", + cpi)); + ipsecstat.in_nosa++; + goto fail; + } + + /* chop ipcomp header */ + ipcomp = NULL; + m->m_len -= sizeof(struct ipcomp); + m->m_pkthdr.len -= sizeof(struct ipcomp); +#ifdef IPLEN_FLIPPED + ip->ip_len -= sizeof(struct ipcomp); +#else + ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp)); +#endif + + olen = m->m_pkthdr.len; + newlen = m->m_pkthdr.len - off; + error = (*algo->decompress)(m, m->m_next, &newlen); + if (error != 0) { + if (error == EINVAL) + ipsecstat.in_inval++; + else if (error == ENOBUFS) + ipsecstat.in_nomem++; + m = NULL; + goto fail; + } + ipsecstat.in_comphist[cpi]++; + + /* + * returning decompressed packet onto icmp is meaningless. + * mark it decrypted to prevent icmp from attaching original packet. + */ + m->m_flags |= M_DECRYPTED; + + m->m_pkthdr.len = off + newlen; + ip = mtod(m, struct ip *); + { + size_t len; +#ifdef IPLEN_FLIPPED + len = ip->ip_len; +#else + len = ntohs(ip->ip_len); +#endif + /* + * be careful about underflow. also, do not assign exact value + * as ip_len is manipulated differently on *BSDs. + */ + len += m->m_pkthdr.len; + len -= olen; + if (len & ~0xffff) { + /* packet too big after decompress */ + ipsecstat.in_inval++; + goto fail; + } +#ifdef IPLEN_FLIPPED + ip->ip_len = len & 0xffff; +#else + ip->ip_len = htons(len & 0xffff); +#endif + ip->ip_p = nxt; + } + + if (sav) { + key_sa_recordxfer(sav, m); + key_freesav(sav); + sav = NULL; + } + + if (nxt != IPPROTO_DONE) + (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); + else + m_freem(m); + m = NULL; + + ipsecstat.in_success++; + return; + +fail: + if (sav) + key_freesav(sav); + if (m) + m_freem(m); + return; +} +#endif /* INET */ + +#ifdef INET6 +int +ipcomp6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m, *md; + int off; + struct ip6_hdr *ip6; + struct mbuf *ipcompm; + struct ipcomp *ipcomp; + struct ipcomp_algorithm *algo; + u_int16_t cpi; /* host order */ + u_int16_t nxt; + int error; + size_t newlen; + struct secasvar *sav = NULL; + + m = *mp; + off = *offp; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ipcomp), IPPROTO_DONE); + + { + int skip; + struct mbuf *n; + struct mbuf *p, *q; + size_t l; + + skip = off; + for (n = m; n && skip > 0; n = n->m_next) { + if (n->m_len <= skip) { + skip -= n->m_len; + continue; + } + break; + } + if (!n) { + ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); + ipsecstat.in_inval++; + goto fail; + } + if (n->m_len < skip + sizeof(struct ipcomp)) { + ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); + ipsecstat.in_inval++; + goto fail; + } + ip6 = mtod(m, struct ip6_hdr *); + ipcompm = n; + ipcomp = (struct ipcomp *)(mtod(n, caddr_t) + skip); + if (n->m_len > skip + sizeof(struct ipcomp)) { + /* split mbuf to ease the following steps*/ + l = n->m_len - (skip + sizeof(struct ipcomp)); + p = m_copym(n, skip + sizeof(struct ipcomp), l , M_DONTWAIT); + if (!p) { + ipsecstat.in_nomem++; + goto fail; + } + for (q = p; q && q->m_next; q = q->m_next) + ; + q->m_next = n->m_next; + n->m_next = p; + n->m_len -= l; + md = p; + } else + md = n->m_next; + } + + nxt = ipcomp->comp_nxt; + cpi = ntohs(ipcomp->comp_cpi); + + if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { + sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, + (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi)); + if (sav != NULL + && (sav->state == SADB_SASTATE_MATURE + || sav->state == SADB_SASTATE_DYING)) { + cpi = sav->alg_enc; /*XXX*/ + /* other parameters to look at? */ + } + } + if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) + algo = &ipcomp_algorithms[cpi]; + else + algo = NULL; + if (!algo) { + ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; " + "dropping the packet for simplicity\n", cpi)); + ipsec6stat.in_nosa++; + goto fail; + } + + newlen = m->m_pkthdr.len - off - sizeof(struct ipcomp); + error = (*algo->decompress)(m, md, &newlen); + if (error != 0) { + if (error == EINVAL) + ipsec6stat.in_inval++; + else if (error == ENOBUFS) + ipsec6stat.in_nomem++; + m = NULL; + goto fail; + } + ipsec6stat.in_comphist[cpi]++; + m->m_pkthdr.len = off + sizeof(struct ipcomp) + newlen; + + /* + * returning decompressed packet onto icmp is meaningless. + * mark it decrypted to prevent icmp from attaching original packet. + */ + m->m_flags |= M_DECRYPTED; + + { + char *prvnxtp; + + /* chop IPComp header */ + prvnxtp = ip6_get_prevhdr(m, off); + *prvnxtp = nxt; + ipcompm->m_len -= sizeof(struct ipcomp); + ipcompm->m_pkthdr.len -= sizeof(struct ipcomp); + + /* adjust payload length */ + ip6 = mtod(m, struct ip6_hdr *); + if (((m->m_pkthdr.len - sizeof(struct ip6_hdr)) & ~0xffff) != 0) + ip6->ip6_plen = 0; /*now a jumbogram*/ + else + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); + } + + if (sav) { + key_sa_recordxfer(sav, m); + key_freesav(sav); + sav = NULL; + } + *offp = off; + *mp = m; + ipsec6stat.in_success++; + return nxt; + +fail: + if (m) + m_freem(m); + if (sav) + key_freesav(sav); + return IPPROTO_DONE; +} +#endif /* INET6 */ diff --git a/sys/netinet6/ipcomp_output.c b/sys/netinet6/ipcomp_output.c new file mode 100644 index 0000000..265700e --- /dev/null +++ b/sys/netinet6/ipcomp_output.c @@ -0,0 +1,430 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_output.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <net/zlib.h> +#include <machine/cpu.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/ip_ecn.h> + +#ifdef INET6 +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#endif +#include <netinet6/ipcomp.h> +#ifdef INET6 +#include <netinet6/ipcomp6.h> +#endif + +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#include <netkey/key.h> +#include <netkey/keydb.h> + +#include <machine/stdarg.h> + +#include <net/net_osdep.h> + +static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *, int)); + +/* + * 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 NULL. + * + * 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(m, nexthdrp, md, isr, af) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; + int af; +{ + struct mbuf *n; + struct mbuf *md0; + struct mbuf *mprev; + struct ipcomp *ipcomp; + struct secasvar *sav = isr->sav; + 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; + + switch (af) { +#ifdef INET + case AF_INET: + afnumber = 4; + break; +#endif +#ifdef INET6 + case AF_INET6: + afnumber = 6; + break; +#endif + default: + ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af)); + return 0; /* no change at all */ + } + + /* grab parameters */ + if ((ntohl(sav->spi) & ~0xffff) != 0 || sav->alg_enc >= IPCOMP_MAX + || ipcomp_algorithms[sav->alg_enc].compress == NULL) { + ipsecstat.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; + algo = &ipcomp_algorithms[sav->alg_enc]; /*XXX*/ + + /* 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; + + /* + * keep the original data packet, so that we can backout + * our changes when compression is not necessary. + */ + md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); + if (md0 == NULL) { + 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)); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + m_freem(m); + m_freem(md0); + return EINVAL; + } + mprev->m_next = NULL; + if ((md = ipsec_copypkt(md)) == NULL) { + m_freem(m); + m_freem(md0); + 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); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + error = EINVAL; + goto fail; + } + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_comphist[sav->alg_enc]++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_comphist[sav->alg_enc]++; + break; +#endif + } + 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); + 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 +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; +#endif + size_t hlen = 0; /*ip header len*/ + size_t complen = sizeof(struct ipcomp); + + switch (af) { +#ifdef 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 +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof(*ip6); + 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, M_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 { + md->m_len += complen; + md->m_data -= complen; + m->m_pkthdr.len += complen; + ipcomp = mtod(md, struct ipcomp *); + } + + bzero(ipcomp, sizeof(*ipcomp)); + ipcomp->comp_nxt = *nexthdrp; + *nexthdrp = IPPROTO_IPCOMP; + ipcomp->comp_cpi = htons(cpi); + switch (af) { +#ifdef INET + case AF_INET: + if (compoff + complen + plen < IP_MAXPACKET) + ip->ip_len = htons(compoff + complen + plen); + else { + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); + ipsecstat.out_inval++; + m_freem(m); + error = EMSGSIZE; + goto fail; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + /* total packet length will be computed in ip6_output() */ + break; +#endif + } + } + + if (!m) { + ipseclog((LOG_DEBUG, + "NULL mbuf after compression in ipcomp%d_output", + afnumber)); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + } else { + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_success++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_success++; + break; +#endif + } + } +#if 0 + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_esphist[sav->alg_enc]++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_esphist[sav->alg_enc]++; + break; +#endif + } +#endif + key_sa_recordxfer(sav, m); + return 0; + +fail: +#if 1 + return error; +#else + panic("something bad in ipcomp_output"); +#endif +} + +#ifdef INET +int +ipcomp4_output(m, isr) + struct mbuf *m; + struct ipsecrequest *isr; +{ + struct ip *ip; + if (m->m_len < sizeof(struct ip)) { + ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n")); + ipsecstat.out_inval++; + m_freem(m); + return 0; + } + ip = mtod(m, struct ip *); + /* XXX assumes that m->m_next points to payload */ + return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET); +} +#endif /*INET*/ + +#ifdef INET6 +int +ipcomp6_output(m, nexthdrp, md, isr) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; +{ + if (m->m_len < sizeof(struct ip6_hdr)) { + ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n")); + ipsec6stat.out_inval++; + m_freem(m); + return 0; + } + return ipcomp_output(m, nexthdrp, md, isr, AF_INET6); +} +#endif /*INET6*/ diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c index a72fb6a..2e8a67e 100644 --- a/sys/netinet6/ipsec.c +++ b/sys/netinet6/ipsec.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.c,v 1.66 2000/06/15 04:08:54 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -58,7 +59,6 @@ #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> -#include <netinet/in_pcb.h> #include <netinet/ip_var.h> #include <netinet/in_var.h> #include <netinet/udp.h> @@ -69,16 +69,20 @@ #endif #ifdef INET6 -#include <netinet6/ip6.h> -#include <netinet6/in6_pcb.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> -#endif /*INET6*/ +#endif +#include <netinet/in_pcb.h> +#ifdef INET6 +#include <netinet/icmp6.h> +#endif #include <netinet6/ipsec.h> -#include <netinet6/ah.h> #ifdef INET6 #include <netinet6/ipsec6.h> +#endif +#include <netinet6/ah.h> +#ifdef INET6 #include <netinet6/ah6.h> #endif #ifdef IPSEC_ESP @@ -87,9 +91,11 @@ #include <netinet6/esp6.h> #endif #endif - +#include <netinet6/ipcomp.h> +#ifdef INET6 +#include <netinet6/ipcomp6.h> +#endif #include <netkey/key.h> -#include <netkey/key_var.h> #include <netkey/keydb.h> #ifdef IPSEC_DEBUG #include <netkey/key_debug.h> @@ -99,8 +105,24 @@ #include <machine/in_cksum.h> +#include <net/net_osdep.h> + +#ifdef HAVE_NRL_INPCB +#define in6pcb inpcb +#define in6p_sp inp_sp +#define in6p_fport inp_fport +#define in6p_lport inp_lport +#define in6p_socket inp_socket +#define sotoin6pcb(so) ((struct inpcb *)(so)->so_pcb) +#endif + +#ifdef IPSEC_DEBUG +int ipsec_debug = 1; +#else +int ipsec_debug = 0; +#endif + struct ipsecstat ipsecstat; -int ip4_inbound_call_ike = 0; int ip4_ah_cleartos = 1; int ip4_ah_offsetmask = 0; /* maybe IP_DF? */ int ip4_ipsec_dfbit = 0; /* DF bit on encap. 0: clear 1: set 2: copy */ @@ -111,6 +133,11 @@ int ip4_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip4_def_policy; int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ +SYSCTL_DECL(_net_inet_ipsec); +#ifdef INET6 +SYSCTL_DECL(_net_inet6_ipsec6); +#endif + /* net.inet.ipsec */ SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, stats, CTLFLAG_RD, &ipsecstat, ipsecstat, ""); @@ -124,8 +151,6 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, CTLFLAG_RW, &ip4_ah_trans_deflev, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, CTLFLAG_RW, &ip4_ah_net_deflev, 0, ""); -SYSCTL_INT(_net_inet_ipsec, IPSECCTL_INBOUND_CALL_IKE, - inbound_call_ike, CTLFLAG_RW, &ip4_inbound_call_ike, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos, CTLFLAG_RW, &ip4_ah_cleartos, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, @@ -134,10 +159,11 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit, CTLFLAG_RW, &ip4_ipsec_dfbit, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, + debug, CTLFLAG_RW, &ipsec_debug, 0, ""); #ifdef INET6 struct ipsecstat ipsec6stat; -int ip6_inbound_call_ike = 0; int ip6_esp_trans_deflev = IPSEC_LEVEL_USE; int ip6_esp_net_deflev = IPSEC_LEVEL_USE; int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; @@ -158,10 +184,10 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, CTLFLAG_RW, &ip6_ah_trans_deflev, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, CTLFLAG_RW, &ip6_ah_net_deflev, 0, ""); -SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_INBOUND_CALL_IKE, - inbound_call_ike, CTLFLAG_RW, &ip6_inbound_call_ike, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip6_ipsec_ecn, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, + debug, CTLFLAG_RW, &ipsec_debug, 0, ""); #endif /* INET6 */ static int ipsec_setspidx_mbuf @@ -169,13 +195,15 @@ static int ipsec_setspidx_mbuf static void ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); static void ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #ifdef INET6 -static u_int16_t ipsec6_get_ulp __P((struct mbuf *m)); +static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *)); static void ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); static void ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #endif +static struct inpcbpolicy *ipsec_newpcbpolicy __P((void)); +static void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); static int ipsec_set_policy __P((struct secpolicy **pcb_sp, - int optname, caddr_t request, int priv)); + int optname, caddr_t request, size_t len, int priv)); static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp)); static void vshiftl __P((unsigned char *, int, int)); static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *)); @@ -189,11 +217,6 @@ static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); #endif -#define KMALLOC(p, t, n) \ - ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) -#define KFREE(p) \ - free((caddr_t)(p), M_SECA); - /* * For OUTBOUND packet having a socket. Searching SPD for packet, * and return a pointer to SP. @@ -221,30 +244,27 @@ ipsec4_getpolicybysock(m, dir, so, error) if (m == NULL || so == NULL || error == NULL) panic("ipsec4_getpolicybysock: NULL pointer was passed.\n"); - if ((sotoinpcb(so)->inp_vflag & INP_IPV4) != 0) { + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: /* set spidx in pcb */ ipsec4_setspidx_inpcb(m, sotoinpcb(so)); pcbsp = sotoinpcb(so)->inp_sp; - } + break; #ifdef INET6 - else if ((sotoinpcb(so)->inp_vflag & INP_IPV6) != 0) { + case AF_INET6: /* set spidx in pcb */ ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); pcbsp = sotoin6pcb(so)->in6p_sp; - } + break; #endif - else + default: panic("ipsec4_getpolicybysock: unsupported address family\n"); + } /* sanity check */ if (pcbsp == NULL) panic("ipsec4_getpolicybysock: pcbsp is NULL.\n"); - KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("send: priv=%d ", pcbsp->priv); - if (so->so_cred) - printf("cr_uid=%d\n", so->so_cred->cr_uid); - ); switch (dir) { case IPSEC_DIR_INBOUND: currsp = pcbsp->sp_in; @@ -284,24 +304,23 @@ ipsec4_getpolicybysock(m, dir, so, error) /* no SP found */ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip4_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip4_def_policy.policy, IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; *error = 0; return &ip4_def_policy; - + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec4_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } @@ -324,18 +343,18 @@ ipsec4_getpolicybysock(m, dir, so, error) /* no SP found */ switch (currsp->policy) { case IPSEC_POLICY_BYPASS: - printf("ipsec4_getpolicybysock: " + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " "Illegal policy for non-priviliged defined %d\n", - currsp->policy); + currsp->policy)); *error = EINVAL; return NULL; case IPSEC_POLICY_ENTRUST: if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip4_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip4_def_policy.policy, IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; @@ -348,9 +367,8 @@ ipsec4_getpolicybysock(m, dir, so, error) return currsp; default: - printf("ipsec4_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } @@ -386,13 +404,7 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - if ((flag & IP_FORWARDING) == IP_FORWARDING) { - /* Case: IP forwarding */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); - } else { - /* Case: ICMP echo reply */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); - } + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); if (*error != 0) return NULL; @@ -412,9 +424,9 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) /* no SP found */ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", + ipseclog((LOG_INFO, "fixed system default policy:%d->%d\n", ip4_def_policy.policy, - IPSEC_POLICY_NONE); + IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; @@ -496,24 +508,23 @@ ipsec6_getpolicybysock(m, dir, so, error) /* no SP found */ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; *error = 0; return &ip6_def_policy; - + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec6_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec6_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } @@ -536,18 +547,18 @@ ipsec6_getpolicybysock(m, dir, so, error) /* no SP found */ switch (currsp->policy) { case IPSEC_POLICY_BYPASS: - printf("ipsec6_getpolicybysock: " - "Illegal policy for non-priviliged defined %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec6_getpolicybysock: " + "Illegal policy for non-priviliged defined %d\n", + currsp->policy)); *error = EINVAL; return NULL; case IPSEC_POLICY_ENTRUST: if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; @@ -560,9 +571,9 @@ ipsec6_getpolicybysock(m, dir, so, error) return currsp; default: - printf("ipsec6_policybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, + "ipsec6_policybysock: Invalid policy for PCB %d\n", + currsp->policy)); *error = EINVAL; return NULL; } @@ -582,7 +593,7 @@ ipsec6_getpolicybysock(m, dir, so, error) * others : error occured. */ #ifndef IP_FORWARDING -#define IP_FORWARDING 1 +#define IP_FORWARDING 1 #endif struct secpolicy * @@ -604,13 +615,7 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - if ((flag & IP_FORWARDING) == IP_FORWARDING) { - /* Case: IP forwarding */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); - } else { - /* Case: ICMP echo reply */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); - } + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); if (*error != 0) return NULL; @@ -630,9 +635,8 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) /* no SP found */ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; @@ -657,6 +661,7 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) u_int dir, family; struct mbuf *m; { + /* sanity check */ if (spidx == NULL || m == NULL) panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n"); @@ -668,9 +673,6 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) bzero(spidx, sizeof(*spidx)); spidx->dir = dir; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(family); - spidx->src.ss_family = spidx->dst.ss_family = family; - spidx->prefs = spidx->prefd = _INALENBYAF(family) << 3; { /* sanity check for packet length. */ @@ -695,6 +697,7 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) { struct ip *ip; struct ip ipbuf; + struct sockaddr_in *sin; /* sanity check 1 for minimum ip header length */ if (m->m_pkthdr.len < sizeof(struct ip)) { @@ -717,23 +720,32 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) ip = &ipbuf; } - /* some more checks on IPv4 header. */ - bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), - sizeof(ip->ip_src)); - bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip->ip_dst)); + /* XXX some more checks on IPv4 header. */ + + sin = (struct sockaddr_in *)&spidx->src; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + bcopy(&ip->ip_src, &sin->sin_addr, sizeof(sin->sin_addr)); + sin->sin_port = IPSEC_PORT_ANY; + + sin = (struct sockaddr_in *)&spidx->dst; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(sin->sin_addr)); + sin->sin_port = IPSEC_PORT_ANY; + + spidx->prefs = spidx->prefd = sizeof(struct in_addr) << 3; spidx->ul_proto = ip->ip_p; - _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; - _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; break; } #ifdef INET6 case AF_INET6: { - struct ip6_hdr *ip6_hdr; + struct ip6_hdr *ip6; struct ip6_hdr ip6buf; + struct sockaddr_in6 *sin6; /* sanity check 1 for minimum ip header length */ if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { @@ -749,15 +761,15 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) * get IPv6 header packet. usually the mbuf is contiguous * and we need no copies. */ - if (m->m_len >= sizeof(*ip6_hdr)) - ip6_hdr = mtod(m, struct ip6_hdr *); + if (m->m_len >= sizeof(*ip6)) + ip6 = mtod(m, struct ip6_hdr *); else { m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6_hdr = &ip6buf; + ip6 = &ip6buf; } /* some more checks on IPv4 header. */ - if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "wrong ip version on packet " @@ -765,14 +777,31 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) goto bad; } - bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), - sizeof(ip6_hdr->ip6_src)); - bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip6_hdr->ip6_dst)); + sin6 = (struct sockaddr_in6 *)&spidx->src; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + sin6->sin6_port = IPSEC_PORT_ANY; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } + + sin6 = (struct sockaddr_in6 *)&spidx->dst; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + sin6->sin6_port = IPSEC_PORT_ANY; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } + + spidx->prefs = spidx->prefd = sizeof(struct in6_addr) << 3; - spidx->ul_proto = ipsec6_get_ulp(m); - _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; - _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; + ipsec6_get_ulp(m, spidx); break; } #endif /* INET6 */ @@ -794,17 +823,17 @@ ipsec_setspidx_mbuf(spidx, dir, family, m) #ifdef INET6 /* - * Get the number of upper layer protocol. + * Get upper layer protocol number and port number if there. * Assumed all extension headers are in single mbuf. */ -static u_int16_t -ipsec6_get_ulp(m) +#include <netinet/tcp.h> +#include <netinet/udp.h> +static void +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) @@ -813,48 +842,45 @@ ipsec6_get_ulp(m) KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); - 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)) { - printf("ipsec6_get_ulp: all exthdr are not " - "in single mbuf.\n"); - return IPSEC_PORT_ANY; - } + /* set default */ + spidx->ul_proto = IPSEC_ULPROTO_ANY; + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; - switch(nxt) { - case IPPROTO_TCP: - case IPPROTO_UDP: - case IPPROTO_ICMPV6: - return nxt; - 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_ESP: - case IPPROTO_NONE: - case IPPROTO_DSTOPTS: - break; - default: - return nxt; /* XXX */ - } - off += (ip6e->ip6e_len + 1) << 3; - break; + 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; + if (off + sizeof(struct tcphdr) <= m->m_pkthdr.len) { + struct tcphdr th; + m_copydata(m, off, sizeof(th), (caddr_t)&th); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + th.th_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + th.th_dport; + } + 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); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + uh.uh_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + uh.uh_dport; } - nxt = ip6e->ip6e_nxt; + break; + case IPPROTO_ICMPV6: + spidx->ul_proto = nxt; + break; + default: + break; } - - return IPSEC_PORT_ANY; } #endif @@ -864,6 +890,7 @@ ipsec4_setspidx_inpcb(m, pcb) struct inpcb *pcb; { struct secpolicyindex *spidx; + struct sockaddr_in *sin1, *sin2; /* sanity check */ if (pcb == NULL) @@ -878,24 +905,28 @@ ipsec4_setspidx_inpcb(m, pcb) spidx = &pcb->inp_sp->sp_in->spidx; spidx->dir = IPSEC_DIR_INBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET; - spidx->prefs = _INALENBYAF(AF_INET) << 3; - spidx->prefd = _INALENBYAF(AF_INET) << 3; + sin1 = (struct sockaddr_in *)&spidx->src; + sin2 = (struct sockaddr_in *)&spidx->dst; + sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); + sin1->sin_family = sin2->sin_family = AF_INET; + spidx->prefs = sizeof(struct in_addr) << 3; + spidx->prefd = sizeof(struct in_addr) << 3; spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->inp_fport; - _INPORTBYSA(&spidx->dst) = pcb->inp_lport; + sin1->sin_port = pcb->inp_fport; + sin2->sin_port = pcb->inp_lport; ipsec4_setspidx_ipaddr(m, spidx); spidx = &pcb->inp_sp->sp_out->spidx; spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET; - spidx->prefs = _INALENBYAF(AF_INET) << 3; - spidx->prefd = _INALENBYAF(AF_INET) << 3; + sin1 = (struct sockaddr_in *)&spidx->src; + sin2 = (struct sockaddr_in *)&spidx->dst; + sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); + sin1->sin_family = sin2->sin_family = AF_INET; + spidx->prefs = sizeof(struct in_addr) << 3; + spidx->prefd = sizeof(struct in_addr) << 3; spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->inp_lport; - _INPORTBYSA(&spidx->dst) = pcb->inp_fport; + sin1->sin_port = pcb->inp_lport; + sin2->sin_port = pcb->inp_fport; ipsec4_setspidx_ipaddr(m, spidx); return; @@ -907,10 +938,11 @@ ipsec4_setspidx_ipaddr(m, spidx) struct secpolicyindex *spidx; { struct ip *ip = NULL; + struct ip ipbuf; /* 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, @@ -924,14 +956,14 @@ ipsec4_setspidx_ipaddr(m, spidx) if (m->m_len >= sizeof(*ip)) ip = mtod(m, struct ip *); else { - struct ip ipbuf; - m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); ip = &ipbuf; } - bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), sizeof(ip->ip_src)); - bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), sizeof(ip->ip_dst)); + bcopy(&ip->ip_src, &((struct sockaddr_in *)&spidx->src)->sin_addr, + sizeof(ip->ip_src)); + bcopy(&ip->ip_dst, &((struct sockaddr_in *)&spidx->dst)->sin_addr, + sizeof(ip->ip_dst)); return; } @@ -943,6 +975,7 @@ ipsec6_setspidx_in6pcb(m, pcb) struct in6pcb *pcb; { struct secpolicyindex *spidx; + struct sockaddr_in6 *sin1, *sin2; /* sanity check */ if (pcb == NULL) @@ -957,24 +990,28 @@ ipsec6_setspidx_in6pcb(m, pcb) spidx = &pcb->in6p_sp->sp_in->spidx; spidx->dir = IPSEC_DIR_INBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET6); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET6; - spidx->prefs = _INALENBYAF(AF_INET6) << 3; - spidx->prefd = _INALENBYAF(AF_INET6) << 3; + sin1 = (struct sockaddr_in6 *)&spidx->src; + sin2 = (struct sockaddr_in6 *)&spidx->dst; + sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); + sin1->sin6_family = sin2->sin6_family = AF_INET6; + spidx->prefs = sizeof(struct in6_addr) << 3; + spidx->prefd = sizeof(struct in6_addr) << 3; spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->in6p_fport; - _INPORTBYSA(&spidx->dst) = pcb->in6p_lport; + sin1->sin6_port = pcb->in6p_fport; + sin2->sin6_port = pcb->in6p_lport; ipsec6_setspidx_ipaddr(m, spidx); spidx = &pcb->in6p_sp->sp_out->spidx; spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET6); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET6; - spidx->prefs = _INALENBYAF(AF_INET6) << 3; - spidx->prefd = _INALENBYAF(AF_INET6) << 3; + sin1 = (struct sockaddr_in6 *)&spidx->src; + sin2 = (struct sockaddr_in6 *)&spidx->dst; + sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); + sin1->sin6_family = sin2->sin6_family = AF_INET6; + spidx->prefs = sizeof(struct in6_addr) << 3; + spidx->prefd = sizeof(struct in6_addr) << 3; spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->in6p_lport; - _INPORTBYSA(&spidx->dst) = pcb->in6p_fport; + sin1->sin6_port = pcb->in6p_lport; + sin2->sin6_port = pcb->in6p_fport; ipsec6_setspidx_ipaddr(m, spidx); return; @@ -985,7 +1022,9 @@ ipsec6_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; { - struct ip6_hdr *ip6_hdr = NULL; + struct ip6_hdr *ip6 = NULL; + struct ip6_hdr ip6buf; + struct sockaddr_in6 *sin6; /* sanity check 1 for minimum ip header length */ if (m == NULL) @@ -1000,16 +1039,14 @@ ipsec6_setspidx_ipaddr(m, spidx) return; } - if (m->m_len >= sizeof(*ip6_hdr)) - ip6_hdr = mtod(m, struct ip6_hdr *); + if (m->m_len >= sizeof(*ip6)) + ip6 = mtod(m, struct ip6_hdr *); else { - struct ip6_hdr ip6buf; - m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6_hdr = &ip6buf; + ip6 = &ip6buf; } - if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "wrong ip version on packet " @@ -1017,15 +1054,40 @@ ipsec6_setspidx_ipaddr(m, spidx) return; } - bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), - sizeof(ip6_hdr->ip6_src)); - bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip6_hdr->ip6_dst)); + sin6 = (struct sockaddr_in6 *)&spidx->src; + bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } + + sin6 = (struct sockaddr_in6 *)&spidx->dst; + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } return; } #endif +static struct inpcbpolicy * +ipsec_newpcbpolicy() +{ + struct inpcbpolicy *p; + + p = (struct inpcbpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + return p; +} + +static void +ipsec_delpcbpolicy(p) + struct inpcbpolicy *p; +{ + free(p, M_SECA); +} + /* initialize policy in PCB */ int ipsec_init_policy(so, pcb_sp) @@ -1038,9 +1100,9 @@ ipsec_init_policy(so, pcb_sp) if (so == NULL || pcb_sp == NULL) panic("ipsec_init_policy: NULL pointer was passed.\n"); - KMALLOC(new, struct inpcbpolicy *, sizeof(*new)); + new = ipsec_newpcbpolicy(); if (new == NULL) { - printf("ipsec_init_policy: No more memory.\n"); + ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n")); return ENOBUFS; } bzero(new, sizeof(*new)); @@ -1050,16 +1112,8 @@ ipsec_init_policy(so, pcb_sp) else new->priv = 0; - KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("init: priv=%d ", new->priv); - if (so->so_cred) - printf("cr_uid=%d\n", so->so_cred->cr_uid); - else - printf("so_cred is NULL\n"); - ); - if ((new->sp_in = key_newsp()) == NULL) { - KFREE(new); + ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_in->state = IPSEC_SPSTATE_ALIVE; @@ -1067,7 +1121,7 @@ ipsec_init_policy(so, pcb_sp) if ((new->sp_out = key_newsp()) == NULL) { key_freesp(new->sp_in); - KFREE(new); + ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_out->state = IPSEC_SPSTATE_ALIVE; @@ -1125,7 +1179,8 @@ ipsec_deepcopy_policy(src) */ q = &newchain; for (p = src->req; p; p = p->next) { - KMALLOC(*q, struct ipsecrequest *, sizeof(struct ipsecrequest)); + *q = (struct ipsecrequest *)malloc(sizeof(struct ipsecrequest), + M_SECA, M_NOWAIT); if (*q == NULL) goto fail; bzero(*q, sizeof(**q)); @@ -1134,6 +1189,7 @@ ipsec_deepcopy_policy(src) (*q)->saidx.proto = p->saidx.proto; (*q)->saidx.mode = p->saidx.mode; (*q)->level = p->level; + (*q)->saidx.reqid = p->saidx.reqid; bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); @@ -1154,7 +1210,7 @@ ipsec_deepcopy_policy(src) fail: for (p = newchain; p; p = r) { r = p->next; - KFREE(p); + free(p, M_SECA); p = NULL; } return NULL; @@ -1162,18 +1218,23 @@ fail: /* set policy and ipsec request if present. */ static int -ipsec_set_policy(pcb_sp, optname, request, priv) +ipsec_set_policy(pcb_sp, optname, request, len, priv) struct secpolicy **pcb_sp; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *newsp = NULL; + 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; + xpl = (struct sadb_x_policy *)request; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_set_policy: passed policy\n"); @@ -1190,8 +1251,8 @@ ipsec_set_policy(pcb_sp, optname, request, priv) return EACCES; /* allocation new SP entry */ - if ((newsp = key_msg2sp(xpl)) == NULL) - return EINVAL; /* maybe ENOBUFS */ + if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) + return error; newsp->state = IPSEC_SPSTATE_ALIVE; @@ -1210,42 +1271,42 @@ ipsec_get_policy(pcb_sp, mp) struct secpolicy *pcb_sp; struct mbuf **mp; { - struct sadb_x_policy *xpl; /* sanity check. */ if (pcb_sp == NULL || mp == NULL) return EINVAL; - if ((xpl = key_sp2msg(pcb_sp)) == NULL) { - printf("ipsec_get_policy: No more memory.\n"); + *mp = key_sp2msg(pcb_sp); + if (!*mp) { + ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n")); return ENOBUFS; } - *mp = m_get(M_WAIT, MT_DATA); - (*mp)->m_len = PFKEY_EXTLEN(xpl); - m_copyback((*mp), 0, PFKEY_EXTLEN(xpl), (caddr_t)xpl); + (*mp)->m_type = MT_DATA; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_get_policy:\n"); kdebug_mbuf(*mp)); - KFREE(xpl); - return 0; } int -ipsec4_set_policy(inp, optname, request, priv) +ipsec4_set_policy(inp, optname, request, len, priv) struct inpcb *inp; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy **pcb_sp; /* sanity check. */ if (inp == NULL || request == NULL) return EINVAL; + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { @@ -1256,26 +1317,32 @@ ipsec4_set_policy(inp, optname, request, priv) pcb_sp = &inp->inp_sp->sp_out; break; default: - printf("ipsec4_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, priv); + return ipsec_set_policy(pcb_sp, optname, request, len, priv); } int -ipsec4_get_policy(inp, request, mp) +ipsec4_get_policy(inp, request, len, mp) struct inpcb *inp; caddr_t request; + size_t len; struct mbuf **mp; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *pcb_sp; /* sanity check. */ if (inp == NULL || request == NULL || mp == NULL) return EINVAL; + if (inp->inp_sp == NULL) + panic("policy in PCB is NULL\n"); + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { @@ -1286,8 +1353,8 @@ ipsec4_get_policy(inp, request, mp) pcb_sp = inp->inp_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } @@ -1303,6 +1370,9 @@ ipsec4_delete_pcbpolicy(inp) if (inp == NULL) panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.\n"); + if (inp->inp_sp == NULL) + return 0; + if (inp->inp_sp->sp_in != NULL) { key_freesp(inp->inp_sp->sp_in); inp->inp_sp->sp_in = NULL; @@ -1313,7 +1383,7 @@ ipsec4_delete_pcbpolicy(inp) inp->inp_sp->sp_out = NULL; } - KFREE(inp->inp_sp); + ipsec_delpcbpolicy(inp->inp_sp); inp->inp_sp = NULL; return 0; @@ -1321,18 +1391,22 @@ ipsec4_delete_pcbpolicy(inp) #ifdef INET6 int -ipsec6_set_policy(in6p, optname, request, priv) +ipsec6_set_policy(in6p, optname, request, len, priv) struct in6pcb *in6p; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy **pcb_sp; /* sanity check. */ if (in6p == NULL || request == NULL) return EINVAL; + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { @@ -1343,26 +1417,32 @@ ipsec6_set_policy(in6p, optname, request, priv) pcb_sp = &in6p->in6p_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, priv); + return ipsec_set_policy(pcb_sp, optname, request, len, priv); } int -ipsec6_get_policy(in6p, request, mp) +ipsec6_get_policy(in6p, request, len, mp) struct in6pcb *in6p; caddr_t request; + size_t len; struct mbuf **mp; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *pcb_sp; /* sanity check. */ if (in6p == NULL || request == NULL || mp == NULL) return EINVAL; + if (in6p->in6p_sp == NULL) + panic("policy in PCB is NULL\n"); + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { @@ -1373,8 +1453,8 @@ ipsec6_get_policy(in6p, request, mp) pcb_sp = in6p->in6p_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } @@ -1389,6 +1469,9 @@ ipsec6_delete_pcbpolicy(in6p) if (in6p == NULL) panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.\n"); + if (in6p->in6p_sp == NULL) + return 0; + if (in6p->in6p_sp->sp_in != NULL) { key_freesp(in6p->in6p_sp->sp_in); in6p->in6p_sp->sp_in = NULL; @@ -1399,7 +1482,7 @@ ipsec6_delete_pcbpolicy(in6p) in6p->in6p_sp->sp_out = NULL; } - KFREE(in6p->in6p_sp); + ipsec_delpcbpolicy(in6p->in6p_sp); in6p->in6p_sp = NULL; return 0; @@ -1408,7 +1491,7 @@ ipsec6_delete_pcbpolicy(in6p) /* * return current level. - * IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. + * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. */ u_int ipsec_get_reqlevel(isr) @@ -1420,17 +1503,24 @@ ipsec_get_reqlevel(isr) /* sanity check */ if (isr == NULL || isr->sp == NULL) panic("ipsec_get_reqlevel: NULL pointer is passed.\n"); - if (isr->sp->spidx.src.ss_family != isr->sp->spidx.dst.ss_family) + if (((struct sockaddr *)&isr->sp->spidx.src)->sa_family + != ((struct sockaddr *)&isr->sp->spidx.dst)->sa_family) panic("ipsec_get_reqlevel: family mismatched.\n"); -#define IPSEC_CHECK_DEFAULT(lev) \ - (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE) \ - ? (printf("fixed system default level " #lev ":%d->%d\n", \ - (lev), IPSEC_LEVEL_USE), \ - (lev) = IPSEC_LEVEL_USE) : (lev)) +/* XXX note that we have ipseclog() expanded here - code sync issue */ +#define IPSEC_CHECK_DEFAULT(lev) \ + (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ + && (lev) != IPSEC_LEVEL_UNIQUE) \ + ? (ipsec_debug \ + ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ + (lev), IPSEC_LEVEL_REQUIRE) \ + : 0), \ + (lev) = IPSEC_LEVEL_REQUIRE, \ + (lev) \ + : (lev)) /* set default level */ - switch (isr->sp->spidx.src.ss_family) { + switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { #ifdef INET case AF_INET: esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev); @@ -1449,10 +1539,10 @@ ipsec_get_reqlevel(isr) #endif /* INET6 */ default: panic("key_get_reqlevel: Unknown family. %d\n", - isr->sp->spidx.src.ss_family); + ((struct sockaddr *)&isr->sp->spidx.src)->sa_family); } -#undef IPSEC_CHECK_DEFAULT(lev) +#undef IPSEC_CHECK_DEFAULT /* set level */ switch (isr->level) { @@ -1469,6 +1559,13 @@ ipsec_get_reqlevel(isr) level = ah_net_deflev; else level = ah_trans_deflev; + case IPPROTO_IPCOMP: + /* + * we don't really care, as IPcomp document says that + * we shouldn't compress small packets + */ + level = IPSEC_LEVEL_USE; + break; default: panic("ipsec_get_reqlevel: " "Illegal protocol defined %u\n", @@ -1480,6 +1577,9 @@ ipsec_get_reqlevel(isr) case IPSEC_LEVEL_REQUIRE: level = isr->level; break; + case IPSEC_LEVEL_UNIQUE: + level = IPSEC_LEVEL_REQUIRE; + break; default: panic("ipsec_get_reqlevel: Illegal IPsec level %u\n", @@ -1515,7 +1615,7 @@ ipsec_in_reject(sp, m) case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: return 0; - + case IPSEC_POLICY_IPSEC: break; @@ -1550,6 +1650,12 @@ ipsec_in_reject(sp, m) need_icv++; } break; + case IPPROTO_IPCOMP: + /* + * we don't really care, as IPcomp document says that + * we shouldn't compress small packets + */ + break; } } @@ -1696,7 +1802,7 @@ ipsec_hdrsiz(sp) case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: return 0; - + case IPSEC_POLICY_IPSEC: break; @@ -1722,10 +1828,13 @@ ipsec_hdrsiz(sp) case IPPROTO_AH: clen = ah_hdrsiz(isr); break; + case IPPROTO_IPCOMP: + clen = sizeof(struct ipcomp); + break; } if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { - switch (isr->saidx.dst.ss_family) { + switch (((struct sockaddr *)&isr->saidx.dst)->sa_family) { case AF_INET: clen += sizeof(struct ip); break; @@ -1735,9 +1844,9 @@ ipsec_hdrsiz(sp) break; #endif default: - printf("ipsec_hdrsiz: unknown AF %d " - "in IPsec tunnel SA\n", - isr->saidx.dst.ss_family); + ipseclog((LOG_ERR, "ipsec_hdrsiz: " + "unknown AF %d in IPsec tunnel SA\n", + ((struct sockaddr *)&isr->saidx.dst)->sa_family)); break; } } @@ -1842,11 +1951,22 @@ ipsec4_encapsulate(m, sav) size_t plen; /* can't tunnel between different AFs */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family - || sav->sah->saidx.src.ss_family != AF_INET) { + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family + || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) { m_freem(m); return EINVAL; } +#if 0 + /* XXX if the dst is myself, perform nothing. */ + if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) { + m_freem(m); + return EINVAL; + } +#endif + + if (m->m_len < sizeof(*ip)) + panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); ip = mtod(m, struct ip *); #ifdef _IP_VHL @@ -1855,6 +1975,9 @@ ipsec4_encapsulate(m, sav) hlen = ip->ip_hl << 2; #endif + if (m->m_len != hlen) + panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); + /* generate header checksum */ ip->ip_sum = 0; #ifdef _IP_VHL @@ -1872,8 +1995,6 @@ ipsec4_encapsulate(m, sav) * grow the mbuf to accomodate the new IPv4 header. * NOTE: IPv4 options will never be copied. */ - if (m->m_len != hlen) - panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); if (M_LEADINGSPACE(m->m_next) < hlen) { struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); @@ -1921,8 +2042,8 @@ ipsec4_encapsulate(m, sav) if (plen + sizeof(struct ip) < IP_MAXPACKET) ip->ip_len = htons(plen + sizeof(struct ip)); else { - printf("IPv4 ipsec: size exceeds limit: " - "leave ip_len as is (invalid packet)\n"); + ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: " + "leave ip_len as is (invalid packet)\n")); } ip->ip_id = htons(ip_id++); bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr, @@ -1947,11 +2068,19 @@ ipsec6_encapsulate(m, sav) size_t plen; /* can't tunnel between different AFs */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family - || sav->sah->saidx.src.ss_family != AF_INET6) { + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family + || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET6) { m_freem(m); return EINVAL; } +#if 0 + /* XXX if the dst is myself, perform nothing. */ + if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) { + m_freem(m); + return EINVAL; + } +#endif plen = m->m_pkthdr.len; @@ -2030,7 +2159,7 @@ ipsec_chkreplay(seq, sav) /* sanity check */ if (sav == NULL) - printf("ipsec_chkreplay: NULL pointer was passed.\n"); + panic("ipsec_chkreplay: NULL pointer was passed.\n"); replay = sav->replay; @@ -2071,6 +2200,11 @@ ipsec_chkreplay(seq, sav) } } +/* + * check replay counter whether to update or not. + * OUT: 0: OK + * 1: NG + */ int ipsec_updatereplay(seq, sav) u_int32_t seq; @@ -2084,7 +2218,7 @@ ipsec_updatereplay(seq, sav) /* sanity check */ if (sav == NULL) - printf("ipsec_chkreplay: NULL pointer was passed.\n"); + panic("ipsec_chkreplay: NULL pointer was passed.\n"); replay = sav->replay; @@ -2097,7 +2231,7 @@ ipsec_updatereplay(seq, sav) /* sequence number of 0 is invalid */ if (seq == 0) - return 0; + return 1; /* first time */ if (replay->count == 0) { @@ -2131,13 +2265,13 @@ ipsec_updatereplay(seq, sav) /* over range to check, i.e. too old or wrapped */ if (diff >= wsizeb) - return 0; + return 1; fr = frlast - diff / 8; /* this packet already seen ? */ if ((replay->bitmap)[fr] & (1 << (diff % 8))) - return 0; + return 1; /* mark as seen */ (replay->bitmap)[fr] |= (1 << (diff % 8)); @@ -2146,14 +2280,22 @@ ipsec_updatereplay(seq, sav) } ok: - if (replay->count == ~0 - && (sav->flags & SADB_X_EXT_CYCSEQ) == 0) { - return 1; /* don't increment, no more packets accepted */ + if (replay->count == ~0) { + + /* set overflow flag */ + replay->overflow++; + + /* don't increment, no more packets accepted */ + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) + return 1; + + ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n", + replay->overflow, ipsec_logsastr(sav))); } replay->count++; - return 1; + return 0; } /* @@ -2195,17 +2337,18 @@ ipsec4_logpacketstr(ip, spi) s = (u_int8_t *)(&ip->ip_src); d = (u_int8_t *)(&ip->ip_dst); + p = buf; snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); - for (p = buf; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d", s[0], s[1], s[2], s[3]); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%d.%d.%d.%d", d[0], d[1], d[2], d[3]); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; @@ -2220,17 +2363,18 @@ ipsec6_logpacketstr(ip6, spi) static char buf[256]; char *p; + p = buf; snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); - for (p = buf; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), "src=%s", ip6_sprintf(&ip6->ip6_src)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%s", ip6_sprintf(&ip6->ip6_dst)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; @@ -2246,13 +2390,15 @@ ipsec_logsastr(sav) struct secasindex *saidx = &sav->sah->saidx; /* validity check */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family) + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) panic("ipsec_logsastr: family mismatched.\n"); + p = buf; snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); - for (p = buf; p && *p; p++) - ; - if (saidx->src.ss_family == AF_INET) { + while (p && *p) + p++; + if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET) { u_int8_t *s, *d; s = (u_int8_t *)&((struct sockaddr_in *)&saidx->src)->sin_addr; d = (u_int8_t *)&((struct sockaddr_in *)&saidx->dst)->sin_addr; @@ -2261,19 +2407,19 @@ ipsec_logsastr(sav) s[0], s[1], s[2], s[3], d[0], d[1], d[2], d[3]); } #ifdef INET6 - else if (saidx->src.ss_family == AF_INET6) { + else if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET6) { snprintf(p, sizeof(buf) - (p - buf), "src=%s", ip6_sprintf(&((struct sockaddr_in6 *)&saidx->src)->sin6_addr)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%s", ip6_sprintf(&((struct sockaddr_in6 *)&saidx->dst)->sin6_addr)); } #endif - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; @@ -2315,12 +2461,14 @@ ipsec4_output(state, sp, flags) { struct ip *ip = NULL; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int s; int error; #ifdef IPSEC_SRCSEL struct in_ifaddr *ia; #endif struct sockaddr_in *dst4; + struct sockaddr_in *sin; if (!state) panic("state == NULL in ipsec4_output"); @@ -2331,15 +2479,44 @@ ipsec4_output(state, sp, flags) if (!state->dst) panic("state->dst == NULL in ipsec4_output"); - ip = mtod(state->m, struct ip *); - KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec4_output: applyed SP\n"); kdebug_secpolicy(sp)); for (isr = sp->req; isr != NULL; isr = isr->next) { - if ((error = key_checkrequest(isr)) != 0) { +#if 0 /* give up to check restriction of transport mode */ + /* XXX but should be checked somewhere */ + /* + * some of the IPsec operation must be performed only in + * originating case. + */ + if (isr->saidx.mode == IPSEC_MODE_TRANSPORT + && (flags & IP_FORWARDING)) + continue; +#endif + + /* make SA index for search proper SA */ + ip = mtod(state->m, struct ip *); + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + sin = (struct sockaddr_in *)&saidx.src; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_src, &sin->sin_addr, + sizeof(sin->sin_addr)); + } + sin = (struct sockaddr_in *)&saidx.dst; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_dst, &sin->sin_addr, + sizeof(sin->sin_addr)); + } + + if ((error = key_checkrequest(isr, &saidx)) != 0) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2362,9 +2539,15 @@ ipsec4_output(state, sp, flags) } } + /* + * If there is no valid SA, we give up to process any + * more. In such a case, the SA's status is changed + * from DYING to DEAD after allocating. If a packet + * send to the receiver by dead SA, the receiver can + * not decode a packet because SA has been dead. + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsecstat.out_nosa++; error = EINVAL; goto bad; @@ -2381,17 +2564,15 @@ ipsec4_output(state, sp, flags) * build IPsec tunnel. */ /* XXX should be processed with other familiy */ - if (isr->sav->sah->saidx.src.ss_family != AF_INET) { - printf("ipsec4_output: family mismatched " - "between inner and outer spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); + if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET) { + ipseclog((LOG_ERR, "ipsec4_output: " + "family mismatched between inner and outer spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi))); splx(s); error = EAFNOSUPPORT; goto bad; } - ip = mtod(state->m, struct ip *); - state->m = ipsec4_splithdr(state->m); if (!state->m) { splx(s); @@ -2468,9 +2649,16 @@ ipsec4_output(state, sp, flags) goto bad; } break; + case IPPROTO_IPCOMP: + if ((error = ipcomp4_output(state->m, isr)) != 0) { + state->m = NULL; + goto bad; + } + break; default: - printf("ipsec4_output: unknown ipsec protocol %d\n", - isr->saidx.proto); + ipseclog((LOG_ERR, + "ipsec4_output: unknown ipsec protocol %d\n", + isr->saidx.proto)); m_freem(state->m); state->m = NULL; error = EINVAL; @@ -2507,8 +2695,10 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int error = 0; int plen; + struct sockaddr_in6 *sin6; if (!state) panic("state == NULL in ipsec6_output"); @@ -2534,7 +2724,37 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) break; } - if (key_checkrequest(isr) == ENOENT) { + /* make SA index for search proper SA */ + ip6 = mtod(state->m, struct ip6_hdr *); + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + sin6 = (struct sockaddr_in6 *)&saidx.src; + if (sin6->sin6_len == 0) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + bcopy(&ip6->ip6_src, &sin6->sin6_addr, + sizeof(ip6->ip6_src)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } + } + sin6 = (struct sockaddr_in6 *)&saidx.dst; + if (sin6->sin6_len == 0) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, + sizeof(ip6->ip6_dst)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } + } + + if (key_checkrequest(isr, &saidx) == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2558,9 +2778,12 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) } } + /* + * If there is no valid SA, we give up to process. + * see same place at ipsec4_output(). + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsec6stat.out_nosa++; error = EINVAL; goto bad; @@ -2578,9 +2801,14 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) case IPPROTO_AH: error = ah6_output(state->m, nexthdrp, mprev->m_next, isr); break; + case IPPROTO_IPCOMP: + error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr); + break; default: - printf("ipsec6_output_trans: unknown ipsec protocol %d\n", isr->saidx.proto); + ipseclog((LOG_ERR, "ipsec6_output_trans: " + "unknown ipsec protocol %d\n", isr->saidx.proto)); m_freem(state->m); + ipsec6stat.out_inval++; error = EINVAL; break; } @@ -2590,7 +2818,8 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) } plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); if (plen > IPV6_MAXPACKET) { - printf("ipsec6_output: IPsec with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, "ipsec6_output_trans: " + "IPsec with IPv6 jumbogram is not supported\n")); ipsec6stat.out_inval++; error = EINVAL; /*XXX*/ goto bad; @@ -2622,6 +2851,7 @@ ipsec6_output_tunnel(state, sp, flags) { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int error = 0; int plen; #ifdef IPSEC_SRCSEL @@ -2651,7 +2881,9 @@ ipsec6_output_tunnel(state, sp, flags) } for (/*already initialized*/; isr; isr = isr->next) { - if (key_checkrequest(isr) == ENOENT) { + /* When tunnel mode, SA peers must be specified. */ + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + if (key_checkrequest(isr, &saidx) == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called @@ -2675,9 +2907,12 @@ ipsec6_output_tunnel(state, sp, flags) } } + /* + * If there is no valid SA, we give up to process. + * see same place at ipsec4_output(). + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsec6stat.out_nosa++; error = EINVAL; goto bad; @@ -2694,23 +2929,20 @@ ipsec6_output_tunnel(state, sp, flags) * build IPsec tunnel. */ /* XXX should be processed with other familiy */ - if (isr->sav->sah->saidx.src.ss_family != AF_INET6) { - printf("ipsec4_output: family mismatched " - "between inner and outer, spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); - printf("ipsec6_output_tunnel: family mismatched " - "between inner and outer, spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); + if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) { + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "family mismatched between inner and outer, spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi))); splx(s); + ipsec6stat.out_inval++; error = EAFNOSUPPORT; goto bad; } - ip6 = mtod(state->m, struct ip6_hdr *); - state->m = ipsec6_splithdr(state->m); if (!state->m) { splx(s); + ipsec6stat.out_nomem++; error = ENOMEM; goto bad; } @@ -2740,9 +2972,16 @@ ipsec6_output_tunnel(state, sp, flags) } if (state->ro->ro_rt == 0) { ip6stat.ip6s_noroute++; + ipsec6stat.out_noroute++; error = EHOSTUNREACH; goto bad; } +#if 0 /* XXX Is the following need ? */ + if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { + state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; + dst6 = (struct sockaddr_in6 *)state->dst; + } +#endif #ifdef IPSEC_SRCSEL /* * Which address in SA or in routing table should I @@ -2752,8 +2991,11 @@ ipsec6_output_tunnel(state, sp, flags) ia6 = in6_selectsrc(dst6, NULL, NULL, (struct route_in6 *)state->ro, NULL, &error); - if (ia6 == NULL) + if (ia6 == NULL) { + ip6stat.ip6s_noroute++; + ipsec6stat.out_noroute++; goto bad; + } ip6->ip6_src = *ia6; #endif } else @@ -2761,6 +3003,7 @@ ipsec6_output_tunnel(state, sp, flags) state->m = ipsec6_splithdr(state->m); if (!state->m) { + ipsec6stat.out_nomem++; error = ENOMEM; goto bad; } @@ -2777,9 +3020,14 @@ ipsec6_output_tunnel(state, sp, flags) case IPPROTO_AH: error = ah6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr); break; + case IPPROTO_IPCOMP: + /* XXX code should be here */ + /*FALLTHROUGH*/ default: - printf("ipsec6_output_tunnel: unknown ipsec protocol %d\n", isr->saidx.proto); + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "unknown ipsec protocol %d\n", isr->saidx.proto)); m_freem(state->m); + ipsec6stat.out_inval++; error = EINVAL; break; } @@ -2789,7 +3037,8 @@ ipsec6_output_tunnel(state, sp, flags) } plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); if (plen > IPV6_MAXPACKET) { - printf("ipsec6_output_tunnel: IPsec with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "IPsec with IPv6 jumbogram is not supported\n")); ipsec6stat.out_inval++; error = EINVAL; /*XXX*/ goto bad; @@ -2906,7 +3155,7 @@ ipsec4_tunnel_validate(ip, nxt0, sav) #endif if (hlen != sizeof(struct ip)) return 0; - switch (sav->sah->saidx.dst.ss_family) { + switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { case AF_INET: sin = (struct sockaddr_in *)&sav->sah->saidx.dst; if (bcmp(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)) != 0) @@ -2937,7 +3186,7 @@ ipsec6_tunnel_validate(ip6, nxt0, sav) if (nxt != IPPROTO_IPV6) return 0; - switch (sav->sah->saidx.dst.ss_family) { + switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { case AF_INET6: sin6 = ((struct sockaddr_in6 *)&sav->sah->saidx.dst); if (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6->sin6_addr)) @@ -2987,6 +3236,14 @@ ipsec_copypkt(m) if (mnew == NULL) goto fail; mnew->m_pkthdr = n->m_pkthdr; +#if 0 + if (n->m_pkthdr.aux) { + mnew->m_pkthdr.aux = + m_copym(n->m_pkthdr.aux, + 0, M_COPYALL, M_DONTWAIT); + } +#endif + M_COPY_PKTHDR(mnew, n); mnew->m_flags = n->m_flags & M_COPYFLAGS; } else { @@ -3059,3 +3316,39 @@ ipsec_copypkt(m) m_freem(m); return(NULL); } + +void +ipsec_setsocket(m, so) + struct mbuf *m; + struct socket *so; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (so && !n) + n = m_aux_add(m, AF_INET, IPPROTO_ESP); + if (n) { + if (so) { + *mtod(n, struct socket **) = so; + /* + * XXX think again about it when we put decryption + * histrory into aux mbuf + */ + n->m_len = sizeof(struct socket *); + } else + m_aux_delete(m, n); + } +} + +struct socket * +ipsec_getsocket(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (n && n->m_len >= sizeof(struct socket *)) + return *mtod(n, struct socket **); + else + return NULL; +} diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index df68bd7..80be10c 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -43,59 +44,79 @@ /* * Security Policy Index - * NOTE: Encure to be same address family and upper layer protocol. + * NOTE: Ensure to be same address family and upper layer protocol. * NOTE: ul_proto, port number, uid, gid: * ANY: reserved for waldcard. * 0 to (~0 - 1): is one of the number of each value. */ struct secpolicyindex { - u_int8_t dir; /* direction of packet flow, see blow */ - struct sockaddr_storage src; /* IP src address for SP */ - struct sockaddr_storage dst; /* IP dst address for SP */ - u_int8_t prefs; /* prefix length in bits for src */ - u_int8_t prefd; /* prefix length in bits for dst */ - u_int16_t ul_proto; /* upper layer Protocol */ + u_int8_t dir; /* direction of packet flow, see blow */ + struct sockaddr_storage src; /* IP src address for SP */ + struct sockaddr_storage dst; /* IP dst address for SP */ + u_int8_t prefs; /* prefix length in bits for src */ + u_int8_t prefd; /* prefix length in bits for dst */ + u_int16_t ul_proto; /* upper layer Protocol */ +#ifdef notyet + uid_t uids; + uid_t uidd; + gid_t gids; + gid_t gidd; +#endif }; /* Security Policy Data Base */ struct secpolicy { LIST_ENTRY(secpolicy) chain; - int refcnt; /* reference count */ + int refcnt; /* reference count */ struct secpolicyindex spidx; /* selector */ - u_int state; /* 0: dead, others: alive */ -#define IPSEC_SPSTATE_DEAD 0 -#define IPSEC_SPSTATE_ALIVE 1 + u_int32_t id; /* It's unique number on the system. */ + u_int state; /* 0: dead, others: alive */ +#define IPSEC_SPSTATE_DEAD 0 +#define IPSEC_SPSTATE_ALIVE 1 - u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ - struct ipsecrequest *req; + u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ + struct ipsecrequest *req; /* pointer to the ipsec request tree, */ /* if policy == IPSEC else this value == NULL.*/ }; /* Request for IPsec */ struct ipsecrequest { - struct ipsecrequest *next; + struct ipsecrequest *next; /* pointer to next structure */ /* If NULL, it means the end of chain. */ - struct secasindex saidx; - u_int level; /* IPsec level defined below. */ + struct secasindex saidx;/* hint for search proper SA */ + /* if __ss_len == 0 then no address specified.*/ + u_int level; /* IPsec level defined below. */ - struct secasvar *sav; /* place holder of SA for use */ - struct secpolicy *sp; /* back pointer to SP */ + struct secasvar *sav; /* place holder of SA for use */ + struct secpolicy *sp; /* back pointer to SP */ }; /* security policy in PCB */ struct inpcbpolicy { - struct secpolicy *sp_in; - struct secpolicy *sp_out; - int priv; /* privileged socket ? */ + struct secpolicy *sp_in; + struct secpolicy *sp_out; + int priv; /* privileged socket ? */ +}; + +/* SP acquiring list table. */ +struct secspacq { + LIST_ENTRY(secspacq) chain; + + struct secpolicyindex spidx; + + u_int32_t tick; /* for lifetime */ + int count; /* for lifetime */ + /* XXX: here is mbuf place holder to be sent ? */ }; #endif /*_KERNEL*/ -#define IPSEC_PORT_ANY 65535 -#define IPSEC_ULPROTO_ANY 255 -#define IPSEC_PROTO_ANY 65535 +/* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */ +#define IPSEC_PORT_ANY 0 +#define IPSEC_ULPROTO_ANY 255 +#define IPSEC_PROTO_ANY 255 /* mode of security protocol */ /* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */ @@ -108,11 +129,11 @@ struct inpcbpolicy { * NOTE: Since INVALID is used just as flag. * The other are used for loop counter too. */ -#define IPSEC_DIR_ANY 0 -#define IPSEC_DIR_INBOUND 1 -#define IPSEC_DIR_OUTBOUND 2 -#define IPSEC_DIR_MAX 3 -#define IPSEC_DIR_INVALID 4 +#define IPSEC_DIR_ANY 0 +#define IPSEC_DIR_INBOUND 1 +#define IPSEC_DIR_OUTBOUND 2 +#define IPSEC_DIR_MAX 3 +#define IPSEC_DIR_INVALID 4 /* Policy level */ /* @@ -120,11 +141,11 @@ struct inpcbpolicy { * DISCARD, IPSEC and NONE are allowd for setkey() in SPD. * DISCARD and NONE are allowd for system default. */ -#define IPSEC_POLICY_DISCARD 0 /* discarding packet */ -#define IPSEC_POLICY_NONE 1 /* through IPsec engine */ -#define IPSEC_POLICY_IPSEC 2 /* do IPsec */ -#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */ -#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */ +#define IPSEC_POLICY_DISCARD 0 /* discarding packet */ +#define IPSEC_POLICY_NONE 1 /* through IPsec engine */ +#define IPSEC_POLICY_IPSEC 2 /* do IPsec */ +#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */ +#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */ /* Security protocol level */ #define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */ @@ -132,30 +153,47 @@ struct inpcbpolicy { #define IPSEC_LEVEL_REQUIRE 2 /* require SA. */ #define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */ +#define IPSEC_MANUAL_REQID_MAX 0x3fff + /* + * if security policy level == unique, this id + * indicate to a relative SA for use, else is + * zero. + * 1 - 0x3fff are reserved for manual keying. + * 0 are reserved for above reason. Others is + * for kernel use. + * Note that this id doesn't identify SA + * by only itself. + */ #define IPSEC_REPLAYWSIZE 32 /* statistics for ipsec processing */ struct ipsecstat { - u_long in_success; /* succeeded inbound process */ - u_long in_polvio; /* security policy violation for inbound process */ - u_long in_nosa; /* inbound SA is unavailable */ - u_long in_inval; /* inbound processing failed due to EINVAL */ - u_long in_badspi; /* failed getting a SPI */ - u_long in_ahreplay; /* AH replay check failed */ - u_long in_espreplay; /* ESP replay check failed */ - u_long in_ahauthsucc; /* AH authentication success */ - u_long in_ahauthfail; /* AH authentication failure */ - u_long in_espauthsucc; /* ESP authentication success */ - u_long in_espauthfail; /* ESP authentication failure */ - u_long in_esphist[SADB_EALG_MAX]; - u_long in_ahhist[SADB_AALG_MAX]; - u_long out_success; /* succeeded outbound process */ - u_long out_polvio; /* security policy violation for outbound process */ - u_long out_nosa; /* outbound SA is unavailable */ - u_long out_inval; /* outbound process failed due to EINVAL */ - u_long out_noroute; /* there is no route */ - u_long out_esphist[SADB_EALG_MAX]; - u_long out_ahhist[SADB_AALG_MAX]; + u_quad_t in_success; /* succeeded inbound process */ + u_quad_t in_polvio; + /* security policy violation for inbound process */ + u_quad_t in_nosa; /* inbound SA is unavailable */ + u_quad_t in_inval; /* inbound processing failed due to EINVAL */ + u_quad_t in_nomem; /* inbound processing failed due to ENOBUFS */ + u_quad_t in_badspi; /* failed getting a SPI */ + u_quad_t in_ahreplay; /* AH replay check failed */ + u_quad_t in_espreplay; /* ESP replay check failed */ + u_quad_t in_ahauthsucc; /* AH authentication success */ + u_quad_t in_ahauthfail; /* AH authentication failure */ + u_quad_t in_espauthsucc; /* ESP authentication success */ + u_quad_t in_espauthfail; /* ESP authentication failure */ + u_quad_t in_esphist[256]; + u_quad_t in_ahhist[256]; + u_quad_t in_comphist[256]; + u_quad_t out_success; /* succeeded outbound process */ + u_quad_t out_polvio; + /* security policy violation for outbound process */ + u_quad_t out_nosa; /* outbound SA is unavailable */ + u_quad_t out_inval; /* outbound process failed due to EINVAL */ + u_quad_t out_nomem; /* inbound processing failed due to ENOBUFS */ + u_quad_t out_noroute; /* there is no route */ + u_quad_t out_esphist[256]; + u_quad_t out_ahhist[256]; + u_quad_t out_comphist[256]; }; /* @@ -164,18 +202,21 @@ struct ipsecstat { /* * Names for IPsec & Key sysctl objects */ -#define IPSECCTL_STATS 1 /* stats */ -#define IPSECCTL_DEF_POLICY 2 -#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */ -#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */ -#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */ -#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */ -#define IPSECCTL_INBOUND_CALL_IKE 7 +#define IPSECCTL_STATS 1 /* stats */ +#define IPSECCTL_DEF_POLICY 2 +#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */ +#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */ +#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */ +#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */ +#if 0 /*obsolete, do not reuse*/ +#define IPSECCTL_INBOUND_CALL_IKE 7 +#endif #define IPSECCTL_AH_CLEARTOS 8 #define IPSECCTL_AH_OFFSETMASK 9 #define IPSECCTL_DFBIT 10 #define IPSECCTL_ECN 11 -#define IPSECCTL_MAXID 12 +#define IPSECCTL_DEBUG 12 +#define IPSECCTL_MAXID 13 #define IPSECCTL_NAMES { \ { 0, 0 }, \ @@ -185,11 +226,12 @@ struct ipsecstat { { "esp_net_deflev", CTLTYPE_INT }, \ { "ah_trans_deflev", CTLTYPE_INT }, \ { "ah_net_deflev", CTLTYPE_INT }, \ - { "inbound_call_ike", CTLTYPE_INT }, \ + { 0, 0 }, \ { "ah_cleartos", CTLTYPE_INT }, \ { "ah_offsetmask", CTLTYPE_INT }, \ { "dfbit", CTLTYPE_INT }, \ { "ecn", CTLTYPE_INT }, \ + { "debug", CTLTYPE_INT }, \ } #define IPSEC6CTL_NAMES { \ @@ -200,121 +242,83 @@ struct ipsecstat { { "esp_net_deflev", CTLTYPE_INT }, \ { "ah_trans_deflev", CTLTYPE_INT }, \ { "ah_net_deflev", CTLTYPE_INT }, \ - { "inbound_call_ike", CTLTYPE_INT }, \ + { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "ecn", CTLTYPE_INT }, \ -} - -#define IPSECCTL_VARS { \ - 0, \ - 0, \ - &ip4_def_policy.policy, \ - &ip4_esp_trans_deflev, \ - &ip4_esp_net_deflev, \ - &ip4_ah_trans_deflev, \ - &ip4_ah_net_deflev, \ - &ip4_inbound_call_ike, \ - &ip4_ah_cleartos, \ - &ip4_ah_offsetmask, \ - &ip4_ipsec_dfbit, \ - &ip4_ipsec_ecn, \ -} - -#define IPSEC6CTL_VARS { \ - 0, \ - 0, \ - &ip6_def_policy.policy, \ - &ip6_esp_trans_deflev, \ - &ip6_esp_net_deflev, \ - &ip6_ah_trans_deflev, \ - &ip6_ah_net_deflev, \ - &ip6_inbound_call_ike, \ - 0, \ - 0, \ - 0, \ - &ip6_ipsec_ecn, \ + { "debug", CTLTYPE_INT }, \ } #ifdef _KERNEL - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet_ipsec); -#endif - struct ipsec_output_state { - struct mbuf *m; - struct route *ro; - struct sockaddr *dst; + struct mbuf *m; + struct route *ro; + struct sockaddr *dst; }; -extern struct ipsecstat ipsecstat; -extern struct secpolicy ip4_def_policy; -extern int ip4_esp_trans_deflev; -extern int ip4_esp_net_deflev; -extern int ip4_ah_trans_deflev; -extern int ip4_ah_net_deflev; -extern int ip4_inbound_call_ike; -extern int ip4_ah_cleartos; -extern int ip4_ah_offsetmask; -extern int ip4_ipsec_dfbit; -extern int ip4_ipsec_ecn; - -extern struct secpolicy *ipsec4_getpolicybysock +extern int ipsec_debug; + +extern struct ipsecstat ipsecstat; +extern struct secpolicy ip4_def_policy; +extern int ip4_esp_trans_deflev; +extern int ip4_esp_net_deflev; +extern int ip4_ah_trans_deflev; +extern int ip4_ah_net_deflev; +extern int ip4_ah_cleartos; +extern int ip4_ah_offsetmask; +extern int ip4_ipsec_dfbit; +extern int ip4_ipsec_ecn; + +#define ipseclog(x) do { if (ipsec_debug) log x; } while (0) + +extern struct secpolicy *ipsec4_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); -extern struct secpolicy *ipsec4_getpolicybyaddr +extern struct secpolicy *ipsec4_getpolicybyaddr __P((struct mbuf *, u_int, int, int *)); -struct inpcb; - -extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **)); -extern int ipsec_copy_policy +struct inpcb; +extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **)); +extern int ipsec_copy_policy __P((struct inpcbpolicy *, struct inpcbpolicy *)); -extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); +extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); -extern int ipsec4_set_policy __P((struct inpcb *inp, int optname, - caddr_t request, int priv)); -extern int ipsec4_get_policy - __P((struct inpcb *inpcb, caddr_t request, struct mbuf **mp)); -extern int ipsec4_delete_pcbpolicy __P((struct inpcb *)); -extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *)); -extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); +extern int ipsec4_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, size_t len, int priv)); +extern int ipsec4_get_policy __P((struct inpcb *inpcb, caddr_t request, + size_t len, struct mbuf **mp)); +extern int ipsec4_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); -struct secas; -struct tcpcb; -struct tcp6cb; -extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); -extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); +struct secas; +struct tcpcb; +extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); +extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); -extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); -extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *)); +extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); +extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *)); -struct ip; +struct ip; +extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t)); +extern const char *ipsec_logsastr __P((struct secasvar *)); -extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t)); -extern const char *ipsec_logsastr __P((struct secasvar *)); +extern void ipsec_dumpmbuf __P((struct mbuf *)); -extern void ipsec_dumpmbuf __P((struct mbuf *)); - -extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, +extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, int)); - -extern int ipsec4_tunnel_validate __P((struct ip *, u_int, - struct secasvar *)); - -extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); - +extern int ipsec4_tunnel_validate __P((struct ip *, u_int, struct secasvar *)); +extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); +extern void ipsec_setsocket __P((struct mbuf *, struct socket *)); +extern struct socket *ipsec_getsocket __P((struct mbuf *)); #endif /*_KERNEL*/ #ifndef _KERNEL +extern caddr_t ipsec_set_policy __P((char *, int)); +extern int ipsec_get_policylen __P((caddr_t)); +extern char *ipsec_dump_policy __P((caddr_t, char *)); -extern caddr_t ipsec_set_policy __P((char *policy, int buflen)); -extern int ipsec_get_policylen __P((caddr_t buf)); -extern char *ipsec_dump_policy __P((caddr_t buf, char *delimiter)); - -extern char *ipsec_strerror __P((void)); +extern char *ipsec_strerror __P((void)); #endif /*!_KERNEL*/ #endif /*_NETINET6_IPSEC_H_*/ - diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h index 0572592..383c125 100644 --- a/sys/netinet6/ipsec6.h +++ b/sys/netinet6/ipsec6.h @@ -1,5 +1,8 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ + /* - * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,57 +28,53 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* - * IPsec controller part, only IPv6 related + * IPsec controller part. */ #ifndef _NETINET6_IPSEC6_H_ #define _NETINET6_IPSEC6_H_ -#ifdef _KERNEL - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_ipsec6); -#endif +#include <net/pfkeyv2.h> +#include <netkey/keydb.h> -extern struct ipsecstat ipsec6stat; -extern struct secpolicy ip6_def_policy; -extern int ip6_esp_trans_deflev; -extern int ip6_esp_net_deflev; -extern int ip6_ah_trans_deflev; -extern int ip6_ah_net_deflev; -extern int ip6_inbound_call_ike; -extern int ip6_ipsec_ecn; +#ifdef _KERNEL +extern struct ipsecstat ipsec6stat; +extern struct secpolicy ip6_def_policy; +extern int ip6_esp_trans_deflev; +extern int ip6_esp_net_deflev; +extern int ip6_ah_trans_deflev; +extern int ip6_ah_net_deflev; +extern int ip6_ipsec_ecn; -extern struct secpolicy *ipsec6_getpolicybysock +extern struct secpolicy *ipsec6_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); -extern struct secpolicy *ipsec6_getpolicybyaddr +extern struct secpolicy *ipsec6_getpolicybyaddr __P((struct mbuf *, u_int, int, int *)); -extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); -extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); -extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, - caddr_t request, int priv)); -extern int ipsec6_get_policy - __P((struct inpcb *inp, caddr_t request, struct mbuf **mp)); -extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); +struct inpcb; -extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); +extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, size_t len, int priv)); +extern int ipsec6_get_policy + __P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp)); +extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); -struct ip6_hdr; +extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); -extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t)); -extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, - struct mbuf *, struct secpolicy *, - int, int *)); -extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, - struct secpolicy *, int)); -extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, - struct secasvar *)); +struct ip6_hdr; +extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t)); +extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, + struct mbuf *, struct secpolicy *, int, int *)); +extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, + struct secpolicy *, int)); +extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, + struct secasvar *)); #endif /*_KERNEL*/ -#endif /* _NETINET6_IPSEC6_H_ */ + +#endif /*_NETINET6_IPSEC6_H_*/ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index f09b38a..c655bb5 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: mld6.c,v 1.19 2000/05/05 11:01:03 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -68,7 +69,8 @@ * @(#)igmp.c 8.1 (Berkeley) 7/19/93 */ -#include "opt_ipsec.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/systm.h> @@ -81,10 +83,9 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/in6.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet6/mld6_var.h> #include <net/net_osdep.h> @@ -94,23 +95,20 @@ */ /* denotes that the MLD max response delay field specifies time in milliseconds */ -#define MLD6_TIMER_SCALE 1000 +#define MLD6_TIMER_SCALE 1000 /* * time between repetitions of a node's initial report of interest in a * multicast address(in seconds) */ -#define MLD6_UNSOLICITED_REPORT_INTERVAL 10 +#define MLD6_UNSOLICITED_REPORT_INTERVAL 10 -static struct ip6_pktopts ip6_opts; -static int mld6_timers_are_running; +static struct ip6_pktopts ip6_opts; +static int mld6_timers_are_running; /* XXX: These are necessary for KAME's link-local hack */ -static struct in6_addr mld6_all_nodes_linklocal = - IN6ADDR_LINKLOCAL_ALLNODES_INIT; -static struct in6_addr mld6_all_routers_linklocal = - IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; +static struct in6_addr mld6_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT; +static struct in6_addr mld6_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; -static void mld6_sendpkt __P((struct in6_multi *, int, - const struct in6_addr *)); +static void mld6_sendpkt __P((struct in6_multi *, int, const struct in6_addr *)); void mld6_init() @@ -122,7 +120,7 @@ mld6_init() mld6_timers_are_running = 0; /* ip6h_nxt will be fill in later */ - hbh->ip6h_len = 0; /* (8 >> 3) - 1*/ + hbh->ip6h_len = 0; /* (8 >> 3) - 1 */ /* XXX: grotty hard coding... */ hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ @@ -143,7 +141,7 @@ mld6_start_listening(in6m) int s = splnet(); /* - * (draft-ietf-ipngwg-mld, page 10) + * RFC2710 page 10: * The node never sends a Report or Done for the link-scope all-nodes * address. * MLD messages are never sent for multicast addresses whose scope is 0 @@ -187,7 +185,7 @@ mld6_input(m, off) int off; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); + struct mld6_hdr *mldh; struct ifnet *ifp = m->m_pkthdr.rcvif; struct in6_multi *in6m; struct in6_ifaddr *ia; @@ -200,13 +198,25 @@ mld6_input(m, off) "mld6_input: src %s is not link-local\n", ip6_sprintf(&ip6->ip6_src)); /* - * spec(draft-ietf-ipngwg-mld) does not explicitly + * spec (RFC2710) does not explicitly * specify to discard the packet from a non link-local * source address. But we believe it's expected to do so. */ + m_freem(m); return; } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); + mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); + if (mldh == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + /* * In the MLD6 specification, there are 3 states and a flag. * @@ -231,25 +241,25 @@ mld6_input(m, off) htons(ifp->if_index); /* XXX */ /* - * - Start the timers in all of our membership records - * that the query applies to for the interface on - * which the query arrived excl. those that belong - * to the "all-nodes" group (ff02::1). - * - Restart any timer that is already running but has - * A value longer than the requested timeout. - * - Use the value specified in the query message as - * the maximum timeout. - */ + * - Start the timers in all of our membership records + * that the query applies to for the interface on + * which the query arrived excl. those that belong + * to the "all-nodes" group (ff02::1). + * - Restart any timer that is already running but has + * A value longer than the requested timeout. + * - Use the value specified in the query message as + * the maximum timeout. + */ IFP_TO_IA6(ifp, ia); if (ia == NULL) break; /* - * XXX: System timer resolution is too low to handle Max - * Response Delay, so set 1 to the internal timer even if - * the calculated value equals to zero when Max Response - * Delay is positive. - */ + * XXX: System timer resolution is too low to handle Max + * Response Delay, so set 1 to the internal timer even if + * the calculated value equals to zero when Max Response + * Delay is positive. + */ timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; if (timer == 0 && mldh->mld6_maxdelay) timer = 1; @@ -277,7 +287,8 @@ mld6_input(m, off) NULL); in6m->in6m_timer = 0; /* reset timer */ in6m->in6m_state = MLD6_IREPORTEDLAST; - } else if (in6m->in6m_timer == 0 || /*idle state*/ + } + else if (in6m->in6m_timer == 0 || /*idle state*/ in6m->in6m_timer > timer) { in6m->in6m_timer = MLD6_RANDOM_DELAY(timer); @@ -291,14 +302,14 @@ mld6_input(m, off) break; case MLD6_LISTENER_REPORT: /* - * For fast leave to work, we have to know that we are the - * last person to send a report for this group. Reports - * can potentially get looped back if we are a multicast - * router, so discard reports sourced by me. - * Note that it is impossible to check IFF_LOOPBACK flag of - * ifp for this purpose, since ip6_mloopback pass the physical - * interface to looutput. - */ + * For fast leave to work, we have to know that we are the + * last person to send a report for this group. Reports + * can potentially get looped back if we are a multicast + * router, so discard reports sourced by me. + * Note that it is impossible to check IFF_LOOPBACK flag of + * ifp for this purpose, since ip6_mloopback pass the physical + * interface to looutput. + */ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ break; @@ -309,9 +320,9 @@ mld6_input(m, off) mldh->mld6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ /* - * If we belong to the group being reported, stop - * our timer for that group. - */ + * If we belong to the group being reported, stop + * our timer for that group. + */ IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); if (in6m) { in6m->in6m_timer = 0; /* transit to idle state */ @@ -325,6 +336,8 @@ mld6_input(m, off) log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); break; } + + m_freem(m); } void @@ -376,7 +389,8 @@ mld6_sendpkt(in6m, type, dst) * At first, find a link local address on the outgoing interface * to use as the source address of the MLD packet. */ - if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL) + if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST)) + == NULL) return; /* @@ -393,6 +407,7 @@ mld6_sendpkt(in6m, type, dst) return; } mh->m_next = md; + mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); mh->m_len = sizeof(struct ip6_hdr); MH_ALIGN(mh, sizeof(struct ip6_hdr)); @@ -400,7 +415,8 @@ mld6_sendpkt(in6m, type, dst) /* fill in the ip6 header */ ip6 = mtod(mh, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; /* ip6_hlim will be set by im6o.im6o_multicast_hlim */ @@ -431,7 +447,7 @@ mld6_sendpkt(in6m, type, dst) * Request loopback of the report if we are acting as a multicast * router, so that the process-level routing daemon can hear it. */ - im6o.im6o_multicast_loop = 0; + im6o.im6o_multicast_loop = (ip6_mrouter != NULL); /* increment output statictics */ icmp6stat.icp6s_outhist[type]++; diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h index ab50c59..e58560e 100644 --- a/sys/netinet6/mld6_var.h +++ b/sys/netinet6/mld6_var.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: mld6_var.h,v 1.4 2000/03/25 07:23:54 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,22 +28,20 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_MLD6_VAR_H_ -#define _NETINET6_MLD6_VAR_H_ +#define _NETINET6_MLD6_VAR_H_ #ifdef _KERNEL -#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1) +#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1) /* * States for MLD stop-listening processing */ -#define MLD6_OTHERLISTENER 0 -#define MLD6_IREPORTEDLAST 1 +#define MLD6_OTHERLISTENER 0 +#define MLD6_IREPORTEDLAST 1 void mld6_init __P((void)); void mld6_input __P((struct mbuf *, int)); diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 31e975c..0a56a91 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: nd6.c,v 1.68 2000/07/02 14:48:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -37,6 +38,9 @@ * I left the code mostly as it was in 970310. -- itojun */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -45,6 +49,7 @@ #include <sys/sockio.h> #include <sys/time.h> #include <sys/kernel.h> +#include <sys/protosw.h> #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> @@ -59,21 +64,21 @@ #include <netinet/if_ether.h> #include <netinet/if_fddi.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include "loop.h" #include <net/net_osdep.h> -#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ -#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ +#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ +#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ -#define SIN6(s) ((struct sockaddr_in6 *)s) -#define SDL(s) ((struct sockaddr_dl *)s) +#define SIN6(s) ((struct sockaddr_in6 *)s) +#define SDL(s) ((struct sockaddr_dl *)s) /* timer values */ int nd6_prune = 1; /* walk list every 1 seconds */ @@ -81,20 +86,25 @@ int nd6_delay = 5; /* delay first probe time 5 second */ int nd6_umaxtries = 3; /* maximum unicast query */ int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ -int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */ + +/* preventing too many loops in ND option parsing */ +int nd6_maxndopt = 10; /* max # of ND options allowed */ + +int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ /* for debugging? */ -static int nd6_inuse, nd6_allocated; +static int nd6_inuse, nd6_allocated; -struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; -struct nd_ifinfo *nd_ifinfo = NULL; -struct nd_drhead nd_defrouter = { 0 }; -struct nd_prhead nd_prefix = { 0 }; +struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; +static size_t nd_ifinfo_indexlim = 8; +struct nd_ifinfo *nd_ifinfo = NULL; +struct nd_drhead nd_defrouter; +struct nd_prhead nd_prefix = { 0 }; -int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; -static struct sockaddr_in6 all1_sa; +int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; +static struct sockaddr_in6 all1_sa; -static void nd6_slowtimo __P((void *)); +static void nd6_slowtimo __P((void *)); void nd6_init() @@ -112,6 +122,9 @@ nd6_init() for (i = 0; i < sizeof(all1_sa.sin6_addr); i++) all1_sa.sin6_addr.s6_addr[i] = 0xff; + /* initialization of the default router list */ + TAILQ_INIT(&nd_defrouter); + nd6_init_done = 1; /* start timer */ @@ -122,21 +135,20 @@ void nd6_ifattach(ifp) struct ifnet *ifp; { - static size_t if_indexlim = 8; /* * We have some arrays that should be indexed by if_index. * since if_index will grow dynamically, they should grow too. */ - if (nd_ifinfo == NULL || if_index >= if_indexlim) { + if (nd_ifinfo == NULL || if_index >= nd_ifinfo_indexlim) { size_t n; caddr_t q; - while (if_index >= if_indexlim) - if_indexlim <<= 1; + while (if_index >= nd_ifinfo_indexlim) + nd_ifinfo_indexlim <<= 1; /* grow nd_ifinfo */ - n = if_indexlim * sizeof(struct nd_ifinfo); + n = nd_ifinfo_indexlim * sizeof(struct nd_ifinfo); q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK); bzero(q, n); if (nd_ifinfo) { @@ -147,12 +159,18 @@ nd6_ifattach(ifp) } #define ND nd_ifinfo[ifp->if_index] + + /* don't initialize if called twice */ + if (ND.linkmtu) + return; + ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu; ND.chlim = IPV6_DEFHLIM; ND.basereachable = REACHABLE_TIME; ND.reachable = ND_COMPUTE_RTIME(ND.basereachable); ND.retrans = RETRANS_TIMER; ND.receivedra = 0; + ND.flags = ND6_IFF_PERFORMNUD; nd6_setmtu(ifp); #undef ND } @@ -330,14 +348,15 @@ nd6_options(ndopts) * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ - log(LOG_INFO, + log(LOG_DEBUG, "nd6_options: unsupported option %d - " "option ignored\n", nd_opt->nd_opt_type); } skip1: i++; - if (i > 10) { + if (i > nd6_maxndopt) { + icmp6stat.icp6s_nd_toomanyopt++; printf("too many loop in nd opt\n"); break; } @@ -371,6 +390,8 @@ nd6_timer(ignored_arg) struct ifnet *ifp; struct sockaddr_in6 *dst; struct llinfo_nd6 *next = ln->ln_next; + /* XXX: used for the DELAY case only: */ + struct nd_ifinfo *ndi = NULL; if ((rt = ln->ln_rt) == NULL) { ln = next; @@ -380,6 +401,7 @@ nd6_timer(ignored_arg) ln = next; continue; } + ndi = &nd_ifinfo[ifp->if_index]; dst = (struct sockaddr_in6 *)rt_key(rt); if (ln->ln_expire > time_second) { @@ -390,6 +412,9 @@ nd6_timer(ignored_arg) /* sanity check */ if (!rt) panic("rt=0 in nd6_timer(ln=%p)\n", ln); + if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) + panic("rt_llinfo(%p) is not equal to ln(%p)\n", + rt->rt_llinfo, ln); if (!dst) panic("dst=0 in nd6_timer(ln=%p)\n", ln); @@ -422,23 +447,26 @@ nd6_timer(ignored_arg) } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) { + if (ln->ln_expire) ln->ln_state = ND6_LLINFO_STALE; - } break; /* * ND6_LLINFO_STALE state requires nothing for timer * routine. */ case ND6_LLINFO_DELAY: - ln->ln_asked = 1; - ln->ln_state = ND6_LLINFO_PROBE; - ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].retrans / 1000; - nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, - ln, 0); + if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { + /* We need NUD */ + ln->ln_asked = 1; + ln->ln_state = ND6_LLINFO_PROBE; + ln->ln_expire = time_second + + ndi->retrans / 1000; + nd6_ns_output(ifp, &dst->sin6_addr, + &dst->sin6_addr, + ln, 0); + } else + ln->ln_state = ND6_LLINFO_STALE; /* XXX */ break; - case ND6_LLINFO_PROBE: if (ln->ln_asked < nd6_umaxtries) { ln->ln_asked++; @@ -458,17 +486,18 @@ nd6_timer(ignored_arg) } /* expire */ - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr) { if (dr->expire && dr->expire < time_second) { struct nd_defrouter *t; - t = LIST_NEXT(dr, dr_entry); + t = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); dr = t; - } else - dr = LIST_NEXT(dr, dr_entry); + } else { + dr = TAILQ_NEXT(dr, dr_entry); + } } - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr) { struct in6_ifaddr *ia6; struct in6_addrlifetime *lt6; @@ -503,7 +532,7 @@ nd6_timer(ignored_arg) if (pr->ndpr_expire && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { struct nd_prefix *t; - t = LIST_NEXT(pr, ndpr_entry); + t = pr->ndpr_next; /* * address expiration and prefix expiration are @@ -513,11 +542,104 @@ nd6_timer(ignored_arg) prelist_remove(pr); pr = t; } else - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } splx(s); } +/* + * Nuke neighbor cache/prefix/default router management table, right before + * ifp goes away. + */ +void +nd6_purge(ifp) + struct ifnet *ifp; +{ + struct llinfo_nd6 *ln, *nln; + struct nd_defrouter *dr, *ndr, drany; + struct nd_prefix *pr, *npr; + + /* Nuke default router list entries toward ifp */ + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { + /* + * The first entry of the list may be stored in + * the routing table, so we'll delete it later. + */ + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) { + ndr = TAILQ_NEXT(dr, dr_entry); + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + dr = TAILQ_FIRST(&nd_defrouter); + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + + /* Nuke prefix list entries toward ifp */ + for (pr = nd_prefix.lh_first; pr; pr = npr) { + npr = pr->ndpr_next; + if (pr->ndpr_ifp == ifp) { + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + prelist_remove(pr); + } + } + + /* cancel default outgoing interface setting */ + if (nd6_defifindex == ifp->if_index) + nd6_setdefaultiface(0); + + /* refresh default router list */ + bzero(&drany, sizeof(drany)); + defrouter_delreq(&drany, 0); + defrouter_select(); + + /* + * Nuke neighbor cache entries for the ifp. + * Note that rt->rt_ifp may not be the same as ifp, + * due to KAME goto ours hack. See RTM_RESOLVE case in + * nd6_rtrequest(), and ip6_input(). + */ + ln = llinfo_nd6.ln_next; + while (ln && ln != &llinfo_nd6) { + struct rtentry *rt; + struct sockaddr_dl *sdl; + + nln = ln->ln_next; + rt = ln->ln_rt; + if (rt && rt->rt_gateway && + rt->rt_gateway->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)rt->rt_gateway; + if (sdl->sdl_index == ifp->if_index) + nd6_free(rt); + } + ln = nln; + } + + /* + * Neighbor cache entry for interface route will be retained + * with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it. + */ + ln = llinfo_nd6.ln_next; + while (ln && ln != &llinfo_nd6) { + struct rtentry *rt; + struct sockaddr_dl *sdl; + + nln = ln->ln_next; + rt = ln->ln_rt; + if (rt && rt->rt_gateway && + rt->rt_gateway->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)rt->rt_gateway; + if (sdl->sdl_index == ifp->if_index) { + rtrequest(RTM_DELETE, rt_key(rt), + (struct sockaddr *)0, rt_mask(rt), 0, + (struct rtentry **)0); + } + } + ln = nln; + } +} + struct rtentry * nd6_lookup(addr6, create, ifp) struct in6_addr *addr6; @@ -531,6 +653,9 @@ nd6_lookup(addr6, create, ifp) sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; +#ifdef SCOPEDROUTING + sin6.sin6_scope_id = in6_addr2scopeid(ifp, addr6); +#endif rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL); if (rt && (rt->rt_flags & RTF_LLINFO) == 0) { /* @@ -546,6 +671,8 @@ nd6_lookup(addr6, create, ifp) } if (!rt) { if (create && ifp) { + int e; + /* * If no route is available and create is set, * we allocate a host route for the destination @@ -564,15 +691,17 @@ nd6_lookup(addr6, create, ifp) * destination in nd6_rtrequest which will be * called in rtequest via ifa->ifa_rtrequest. */ - if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6, - ifa->ifa_addr, - (struct sockaddr *)&all1_sa, - (ifa->ifa_flags | - RTF_HOST | RTF_LLINFO) & ~RTF_CLONING, - &rt)) + if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, + ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + (ifa->ifa_flags | + RTF_HOST | RTF_LLINFO) & + ~RTF_CLONING, + &rt)) != 0) log(LOG_ERR, "nd6_lookup: failed to add route for a " - "neighbor(%s)\n", ip6_sprintf(addr6)); + "neighbor(%s), errno=%d\n", + ip6_sprintf(addr6), e); if (rt == NULL) return(NULL); if (rt->rt_llinfo) { @@ -610,7 +739,7 @@ nd6_lookup(addr6, create, ifp) */ int nd6_is_addr_neighbor(addr, ifp) - struct in6_addr *addr; + struct sockaddr_in6 *addr; struct ifnet *ifp; { register struct ifaddr *ifa; @@ -619,21 +748,29 @@ nd6_is_addr_neighbor(addr, ifp) #define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) #define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr) - /* A link-local address is always a neighbor. */ - if (IN6_IS_ADDR_LINKLOCAL(addr)) + /* + * A link-local address is always a neighbor. + * XXX: we should use the sin6_scope_id field rather than the embedded + * interface index. + */ + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && + ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) return(1); /* * If the address matches one of our addresses, * it should be a neighbor. */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_INET6) next: continue; for (i = 0; i < 4; i++) { - if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) & + if ((IFADDR6(ifa).s6_addr32[i] ^ + addr->sin6_addr.s6_addr32[i]) & IFMASK6(ifa).s6_addr32[i]) goto next; } @@ -644,7 +781,7 @@ nd6_is_addr_neighbor(addr, ifp) * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ - if (nd6_lookup(addr, 0, ifp)) + if (nd6_lookup(&addr->sin6_addr, 0, ifp)) return(1); return(0); @@ -661,40 +798,74 @@ nd6_free(rt) { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; struct sockaddr_dl *sdl; + struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + struct nd_defrouter *dr; - if (ln->ln_router) { - /* remove from default router list */ - struct nd_defrouter *dr; - struct in6_addr *in6; - int s; - in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + /* + * Clear all destination cache entries for the neighbor. + * XXX: is it better to restrict this to hosts? + */ + pfctlinput(PRC_HOSTDEAD, rt_key(rt)); + if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ + int s; s = splnet(); - dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))-> - sin6_addr, + dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, rt->rt_ifp); - if (dr) - defrtrlist_del(dr); - else if (!ip6_forwarding && ip6_accept_rtadv) { + if (ln->ln_router || dr) { + /* + * rt6_flush must be called whether or not the neighbor + * is in the Default Router List. + * See a corresponding comment in nd6_na_input(). + */ + rt6_flush(&in6, rt->rt_ifp); + } + + if (dr) { /* - * rt6_flush must be called in any case. - * see the comment in nd6_na_input(). + * Unreachablity of a router might affect the default + * router selection and on-link detection of advertised + * prefixes. */ - rt6_flush(in6, rt->rt_ifp); + + /* + * Temporarily fake the state to choose a new default + * router and to perform on-link determination of + * prefixes coreectly. + * Below the state will be set correctly, + * or the entry itself will be deleted. + */ + ln->ln_state = ND6_LLINFO_INCOMPLETE; + + if (dr == TAILQ_FIRST(&nd_defrouter)) { + /* + * It is used as the current default router, + * so we have to move it to the end of the + * list and choose a new one. + * XXX: it is not very efficient if this is + * the only router. + */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry); + + defrouter_select(); + } + pfxlist_onlink_check(); } splx(s); } - + if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && - sdl->sdl_family == AF_LINK) { + sdl->sdl_family == AF_LINK) { sdl->sdl_alen = 0; ln->ln_state = ND6_LLINFO_WAITDELETE; ln->ln_asked = 0; rt->rt_flags &= ~RTF_REJECT; return; } - rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), - 0, (struct rtentry **)0); + + rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, + rt_mask(rt), 0, (struct rtentry **)0); } /* @@ -703,9 +874,10 @@ nd6_free(rt) * XXX cost-effective metods? */ void -nd6_nud_hint(rt, dst6) +nd6_nud_hint(rt, dst6, force) struct rtentry *rt; struct in6_addr *dst6; + int force; { struct llinfo_nd6 *ln; @@ -720,11 +892,10 @@ nd6_nud_hint(rt, dst6) return; } - if ((rt->rt_flags & RTF_GATEWAY) - || (rt->rt_flags & RTF_LLINFO) == 0 - || !rt->rt_llinfo - || !rt->rt_gateway - || rt->rt_gateway->sa_family != AF_LINK) { + if ((rt->rt_flags & RTF_GATEWAY) != 0 || + (rt->rt_flags & RTF_LLINFO) == 0 || + !rt->rt_llinfo || !rt->rt_gateway || + rt->rt_gateway->sa_family != AF_LINK) { /* This is not a host route. */ return; } @@ -733,12 +904,117 @@ nd6_nud_hint(rt, dst6) if (ln->ln_state < ND6_LLINFO_REACHABLE) return; + /* + * if we get upper-layer reachability confirmation many times, + * it is possible we have false information. + */ + if (!force) { + ln->ln_byhint++; + if (ln->ln_byhint > nd6_maxnudhint) + return; + } + ln->ln_state = ND6_LLINFO_REACHABLE; if (ln->ln_expire) ln->ln_expire = time_second + nd_ifinfo[rt->rt_ifp->if_index].reachable; } +#ifdef OLDIP6OUTPUT +/* + * Resolve an IP6 address into an ethernet address. If success, + * desten is filled in. If there is no entry in ndptab, + * set one up and multicast a solicitation for the IP6 address. + * Hold onto this mbuf and resend it once the address + * is finally resolved. A return value of 1 indicates + * that desten has been filled in and the packet should be sent + * normally; a 0 return indicates that the packet has been + * taken over here, either now or for later transmission. + */ +int +nd6_resolve(ifp, rt, m, dst, desten) + struct ifnet *ifp; + struct rtentry *rt; + struct mbuf *m; + struct sockaddr *dst; + u_char *desten; +{ + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; + struct sockaddr_dl *sdl; + + if (m->m_flags & M_MCAST) { + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, + desten); + return(1); + break; + case IFT_ARCNET: + *desten = 0; + return(1); + break; + default: + return(0); + } + } + if (rt && (rt->rt_flags & RTF_LLINFO) != 0) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + else { + if ((rt = nd6_lookup(&(SIN6(dst)->sin6_addr), 1, ifp)) != NULL) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + } + if (!ln || !rt) { + log(LOG_DEBUG, "nd6_resolve: can't allocate llinfo for %s\n", + ip6_sprintf(&(SIN6(dst)->sin6_addr))); + m_freem(m); + return(0); + } + sdl = SDL(rt->rt_gateway); + /* + * Ckeck the address family and length is valid, the address + * is resolved; otherwise, try to resolve. + */ + if (ln->ln_state >= ND6_LLINFO_REACHABLE + && sdl->sdl_family == AF_LINK + && sdl->sdl_alen != 0) { + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (ln->ln_state == ND6_LLINFO_STALE) { + ln->ln_asked = 0; + ln->ln_state = ND6_LLINFO_DELAY; + ln->ln_expire = time_second + nd6_delay; + } + return(1); + } + /* + * There is an ndp entry, but no ethernet address + * response yet. Replace the held mbuf with this + * latest one. + * + * XXX Does the code conform to rate-limiting rule? + * (RFC 2461 7.2.2) + */ + if (ln->ln_state == ND6_LLINFO_WAITDELETE || + ln->ln_state == ND6_LLINFO_NOSTATE) + ln->ln_state = ND6_LLINFO_INCOMPLETE; + if (ln->ln_hold) + m_freem(ln->ln_hold); + ln->ln_hold = m; + if (ln->ln_expire) { + rt->rt_flags &= ~RTF_REJECT; + if (ln->ln_asked < nd6_mmaxtries && + ln->ln_expire < time_second) { + ln->ln_asked++; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, NULL, &(SIN6(dst)->sin6_addr), + ln, 0); + } + } + return(0); +} +#endif /* OLDIP6OUTPUT */ + void nd6_rtrequest(req, rt, sa) int req; @@ -763,7 +1039,7 @@ nd6_rtrequest(req, rt, sa) * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * rt->rt_flags |= RTF_CLONING; */ - if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) { + if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) { /* * Case 1: This route should come from * a route to interface. RTF_LLINFO flag is set @@ -777,30 +1053,63 @@ nd6_rtrequest(req, rt, sa) SDL(gate)->sdl_index = ifp->if_index; if (ln) ln->ln_expire = time_second; +#if 1 if (ln && ln->ln_expire == 0) { /* cludge for desktops */ +#if 0 + printf("nd6_request: time.tv_sec is zero; " + "treat it as 1\n"); +#endif ln->ln_expire = 1; } +#endif if (rt->rt_flags & RTF_CLONING) break; } - /* Announce a new entry if requested. */ + /* + * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here. + * We don't do that here since llinfo is not ready yet. + * + * There are also couple of other things to be discussed: + * - unsolicited NA code needs improvement beforehand + * - RFC2461 says we MAY send multicast unsolicited NA + * (7.2.6 paragraph 4), however, it also says that we + * SHOULD provide a mechanism to prevent multicast NA storm. + * we don't have anything like it right now. + * note that the mechanism need a mutual agreement + * between proxies, which means that we need to implement + * a new protocol, or new kludge. + * - from RFC2461 6.2.4, host MUST NOT send unsolicited NA. + * we need to check ip6forwarding before sending it. + * (or should we allow proxy ND configuration only for + * routers? there's no mention about proxy ND from hosts) + */ +#if 0 + /* XXX it does not work */ if (rt->rt_flags & RTF_ANNOUNCE) nd6_na_output(ifp, - &SIN6(rt_key(rt))->sin6_addr, - &SIN6(rt_key(rt))->sin6_addr, - ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1, NULL); +#endif /* FALLTHROUGH */ case RTM_RESOLVE: - if (gate->sa_family != AF_LINK || - gate->sa_len < sizeof(null_sdl)) { - log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n"); - break; + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + /* + * Address resolution isn't necessary for a point to + * point link, so we can skip this test for a p2p link. + */ + if (gate->sa_family != AF_LINK || + gate->sa_len < sizeof(null_sdl)) { + log(LOG_DEBUG, + "nd6_rtrequest: bad gateway value\n"); + break; + } + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; } - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - if (ln != 0) + if (ln != NULL) break; /* This happens on a route change */ /* * Case 2: This route may come from cloning, or a manual route @@ -824,12 +1133,13 @@ nd6_rtrequest(req, rt, sa) * which is specified by ndp command. */ ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; } else { /* * When req == RTM_RESOLVE, rt is created and * initialized in rtrequest(), so rt_expire is 0. */ - ln->ln_state = ND6_LLINFO_INCOMPLETE; + ln->ln_state = ND6_LLINFO_NOSTATE; ln->ln_expire = time_second; } rt->rt_flags |= RTF_LLINFO; @@ -848,6 +1158,7 @@ nd6_rtrequest(req, rt, sa) caddr_t macp = nd6_ifptomac(ifp); ln->ln_expire = 0; ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (macp) { Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen); SDL(gate)->sdl_alen = ifp->if_addrlen; @@ -863,17 +1174,56 @@ nd6_rtrequest(req, rt, sa) * of the loopback address. */ if (ifa != rt->rt_ifa) { - rt->rt_ifa->ifa_refcnt--; + IFAFREE(rt->rt_ifa); ifa->ifa_refcnt++; rt->rt_ifa = ifa; } } + } else if (rt->rt_flags & RTF_ANNOUNCE) { + ln->ln_expire = 0; + ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; + + /* join solicited node multicast for proxy ND */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in6_addr llsol; + int error; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + (void)in6_addmulti(&llsol, ifp, &error); + if (error) + printf( +"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error); + } } break; case RTM_DELETE: if (!ln) break; + /* leave from solicited node multicast for proxy ND */ + if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + struct in6_addr llsol; + struct in6_multi *in6m; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } nd6_inuse--; ln->ln_next->ln_prev = ln->ln_prev; ln->ln_prev->ln_next = ln->ln_next; @@ -927,7 +1277,7 @@ nd6_p2p_rtrequest(req, rt, sa) &SIN6(rt_key(rt))->sin6_addr, &SIN6(rt_key(rt))->sin6_addr, ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + 1, NULL); /* FALLTHROUGH */ case RTM_RESOLVE: /* @@ -955,6 +1305,7 @@ nd6_ioctl(cmd, data, ifp) struct in6_prlist *prl = (struct in6_prlist *)data; struct in6_ndireq *ndi = (struct in6_ndireq *)data; struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; + struct in6_ndifreq *ndif = (struct in6_ndifreq *)data; struct nd_defrouter *dr, any; struct nd_prefix *pr; struct rtentry *rt; @@ -965,7 +1316,7 @@ nd6_ioctl(cmd, data, ifp) case SIOCGDRLST_IN6: bzero(drl, sizeof(*drl)); s = splnet(); - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr && i < DRLSTSIZ) { drl->defrouter[i].rtaddr = dr->rtaddr; if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) { @@ -982,14 +1333,19 @@ nd6_ioctl(cmd, data, ifp) drl->defrouter[i].expire = dr->expire; drl->defrouter[i].if_index = dr->ifp->if_index; i++; - dr = LIST_NEXT(dr, dr_entry); + dr = TAILQ_NEXT(dr, dr_entry); } splx(s); break; case SIOCGPRLST_IN6: + /* + * XXX meaning of fields, especialy "raflags", is very + * differnet between RA prefix list and RR/static prefix list. + * how about separating ioctls into two? + */ bzero(prl, sizeof(*prl)); s = splnet(); - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr && i < PRLSTSIZ) { struct nd_pfxrouter *pfr; int j; @@ -1002,7 +1358,7 @@ nd6_ioctl(cmd, data, ifp) prl->prefix[i].if_index = pr->ndpr_ifp->if_index; prl->prefix[i].expire = pr->ndpr_expire; - pfr = LIST_FIRST(&pr->ndpr_advrtrs); + pfr = pr->ndpr_advrtrs.lh_first; j = 0; while(pfr) { if (j < DRLSTSIZ) { @@ -1020,14 +1376,14 @@ nd6_ioctl(cmd, data, ifp) #undef RTRADDR } j++; - pfr = LIST_NEXT(pfr, pfr_entry); + pfr = pfr->pfr_next; } prl->prefix[i].advrtrs = j; + prl->prefix[i].origin = PR_ORIG_RA; i++; - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } - splx(s); { struct rr_prefix *rpp; @@ -1043,15 +1399,29 @@ nd6_ioctl(cmd, data, ifp) prl->prefix[i].if_index = rpp->rp_ifp->if_index; prl->prefix[i].expire = rpp->rp_expire; prl->prefix[i].advrtrs = 0; + prl->prefix[i].origin = rpp->rp_origin; i++; } } + splx(s); break; case SIOCGIFINFO_IN6: + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } ndi->ndi = nd_ifinfo[ifp->if_index]; break; - case SIOCSNDFLUSH_IN6: + case SIOCSIFINFO_FLAGS: + /* XXX: almost all other fields of ndi->ndi is unused */ + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } + nd_ifinfo[ifp->if_index].flags = ndi->ndi.flags; + break; + case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ /* flush default router list */ /* * xxx sumikawa: should not delete route if default @@ -1059,6 +1429,7 @@ nd6_ioctl(cmd, data, ifp) */ bzero(&any, sizeof(any)); defrouter_delreq(&any, 0); + defrouter_select(); /* xxx sumikawa: flush prefix list */ break; case SIOCSPFXFLUSH_IN6: @@ -1067,8 +1438,8 @@ nd6_ioctl(cmd, data, ifp) struct nd_prefix *pr, *next; s = splnet(); - for (pr = LIST_FIRST(&nd_prefix); pr; pr = next) { - next = LIST_NEXT(pr, ndpr_entry); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); prelist_remove(pr); @@ -1082,16 +1453,16 @@ nd6_ioctl(cmd, data, ifp) struct nd_defrouter *dr, *next; s = splnet(); - if ((dr = LIST_FIRST(&nd_defrouter)) != NULL) { + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { /* * The first entry of the list may be stored in * the routing table, so we'll delete it later. */ - for (dr = LIST_NEXT(dr, dr_entry); dr; dr = next) { - next = LIST_NEXT(dr, dr_entry); + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) { + next = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); } - defrtrlist_del(LIST_FIRST(&nd_defrouter)); + defrtrlist_del(TAILQ_FIRST(&nd_defrouter)); } splx(s); break; @@ -1116,6 +1487,7 @@ nd6_ioctl(cmd, data, ifp) s = splnet(); if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { error = EINVAL; + splx(s); break; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; @@ -1127,6 +1499,12 @@ nd6_ioctl(cmd, data, ifp) break; } + case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + ndif->ifindex = nd6_defifindex; + break; + case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + return(nd6_setdefaultiface(ndif->ifindex)); + break; } return(error); } @@ -1174,6 +1552,12 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) rt = nd6_lookup(from, 0, ifp); if (!rt) { +#if 0 + /* nothing must be done if there's no lladdr */ + if (!lladdr || !lladdrlen) + return NULL; +#endif + rt = nd6_lookup(from, 1, ifp); is_newentry = 1; } else @@ -1248,9 +1632,18 @@ fail: if (ln->ln_state == ND6_LLINFO_STALE) { rt->rt_flags &= ~RTF_REJECT; if (ln->ln_hold) { - nd6_output(ifp, ln->ln_hold, +#ifdef OLDIP6OUTPUT + (*ifp->if_output)(ifp, ln->ln_hold, + rt_key(rt), rt); +#else + /* + * we assume ifp is not a p2p here, so just + * set the 2nd argument as the 1st one. + */ + nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); +#endif ln->ln_hold = 0; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { @@ -1301,7 +1694,6 @@ fail: * If the icmp is a redirect to a better router, always set the * is_router flag. Otherwise, if the entry is newly created, * clear the flag. [RFC 2461, sec 8.3] - * */ if (code == ND_REDIRECT_ROUTER) ln->ln_router = 1; @@ -1338,6 +1730,8 @@ nd6_slowtimo(ignored_arg) timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); for (i = 1; i < if_index + 1; i++) { + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) + continue; nd6if = &nd_ifinfo[i]; if (nd6if->basereachable && /* already initialized */ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { @@ -1356,14 +1750,16 @@ nd6_slowtimo(ignored_arg) #define senderr(e) { error = (e); goto bad;} int -nd6_output(ifp, m0, dst, rt0) +nd6_output(ifp, origifp, m0, dst, rt0) register struct ifnet *ifp; + struct ifnet *origifp; struct mbuf *m0; struct sockaddr_in6 *dst; struct rtentry *rt0; { register struct mbuf *m = m0; register struct rtentry *rt = rt0; + struct sockaddr_in6 *gw6 = NULL; struct llinfo_nd6 *ln = NULL; int error = 0; @@ -1372,12 +1768,16 @@ nd6_output(ifp, m0, dst, rt0) /* * XXX: we currently do not make neighbor cache on any interface - * other than ARCnet, Ethernet and FDDI. + * other than ARCnet, Ethernet, FDDI and GIF. + * + * draft-ietf-ngtrans-mech-06.txt says: + * - unidirectional tunnels needs no ND */ switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_GIF: /* XXX need more cases? */ break; default: goto sendpkt; @@ -1392,12 +1792,43 @@ nd6_output(ifp, m0, dst, rt0) NULL) { rt->rt_refcnt--; - if (rt->rt_ifp != ifp) - return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */ + if (rt->rt_ifp != ifp) { + /* XXX: loop care? */ + return nd6_output(ifp, origifp, m0, + dst, rt); + } } else senderr(EHOSTUNREACH); } + if (rt->rt_flags & RTF_GATEWAY) { + gw6 = (struct sockaddr_in6 *)rt->rt_gateway; + + /* + * We skip link-layer address resolution and NUD + * if the gateway is not a neighbor from ND point + * of view, regardless the value of the value of + * nd_ifinfo.flags. + * The second condition is a bit tricky: we skip + * if the gateway is our own address, which is + * sometimes used to install a route to a p2p link. + */ + if (!nd6_is_addr_neighbor(gw6, ifp) || + in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) { + if (rt->rt_flags & RTF_REJECT) + senderr(EHOSTDOWN); + + /* + * We allow this kind of tricky route only + * when the outgoing interface is p2p. + * XXX: we may need a more generic rule here. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) + senderr(EHOSTUNREACH); + + goto sendpkt; + } + if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { @@ -1422,16 +1853,32 @@ nd6_output(ifp, m0, dst, rt0) if (rt && (rt->rt_flags & RTF_LLINFO) != 0) ln = (struct llinfo_nd6 *)rt->rt_llinfo; else { - if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) + /* + * Since nd6_is_addr_neighbor() internally calls nd6_lookup(), + * the condition below is not very efficient. But we believe + * it is tolerable, because this should be a rare case. + */ + if (nd6_is_addr_neighbor(dst, ifp) && + (rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) ln = (struct llinfo_nd6 *)rt->rt_llinfo; } if (!ln || !rt) { - log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " - "(ln=%p, rt=%p)\n", - ip6_sprintf(&dst->sin6_addr), ln, rt); - senderr(EIO); /* XXX: good error? */ + if ((ifp->if_flags & IFF_POINTOPOINT) == 0 && + !(nd_ifinfo[ifp->if_index].flags & ND6_IFF_PERFORMNUD)) { + log(LOG_DEBUG, + "nd6_output: can't allocate llinfo for %s " + "(ln=%p, rt=%p)\n", + ip6_sprintf(&dst->sin6_addr), ln, rt); + senderr(EIO); /* XXX: good error? */ + } + + goto sendpkt; /* send anyway */ } + /* We don't have to do link-layer address resolution on a p2p link. */ + if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && + ln->ln_state < ND6_LLINFO_REACHABLE) + ln->ln_state = ND6_LLINFO_STALE; /* * The first time we send a packet to a neighbor whose entry is @@ -1481,8 +1928,15 @@ nd6_output(ifp, m0, dst, rt0) return(0); sendpkt: + +#ifdef FAKE_LOOPBACK_IF + if (ifp->if_flags & IFF_LOOPBACK) { + return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, + rt)); + } +#endif return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); - + bad: if (m) m_freem(m); @@ -1522,8 +1976,12 @@ nd6_storelladdr(ifp, rt, m, dst, desten) return(0); } sdl = SDL(rt->rt_gateway); - if (sdl->sdl_alen != 0) - bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (sdl->sdl_alen == 0) { + /* this should be impossible, but we bark here for debugging */ + printf("nd6_storelladdr: sdl_alen == 0\n"); + return(0); + } + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 756f495..be1264d 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: nd6.h,v 1.23 2000/06/04 12:54:57 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,12 +28,15 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_ND6_H_ -#define _NETINET6_ND6_H_ +#define _NETINET6_ND6_H_ + +/* see net/route.h, or net/if_inarp.h */ +#ifndef RTF_ANNOUNCE +#define RTF_ANNOUNCE RTF_PROTO2 +#endif #include <sys/queue.h> @@ -43,128 +49,147 @@ struct llinfo_nd6 { u_long ln_expire; /* lifetime for NDP state transition */ short ln_state; /* reachability state */ short ln_router; /* 2^0: ND6 router bit */ + int ln_byhint; /* # of times we made it reachable by UL hint */ }; -#define ND6_LLINFO_NOSTATE -2 -#define ND6_LLINFO_WAITDELETE -1 -#define ND6_LLINFO_INCOMPLETE 0 -#define ND6_LLINFO_REACHABLE 1 -#define ND6_LLINFO_STALE 2 -#define ND6_LLINFO_DELAY 3 -#define ND6_LLINFO_PROBE 4 +#define ND6_LLINFO_NOSTATE -2 +#define ND6_LLINFO_WAITDELETE -1 +#define ND6_LLINFO_INCOMPLETE 0 +#define ND6_LLINFO_REACHABLE 1 +#define ND6_LLINFO_STALE 2 +#define ND6_LLINFO_DELAY 3 +#define ND6_LLINFO_PROBE 4 + +#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) struct nd_ifinfo { - u_int32_t linkmtu; /* LinkMTU */ - u_int32_t maxmtu; /* Upper bound of LinkMTU */ - u_int32_t basereachable; /* BaseReachableTime */ - u_int32_t reachable; /* Reachable Time */ - u_int32_t retrans; /* Retrans Timer */ - int recalctm; /* BaseReacable re-calculation timer */ - u_int8_t chlim; /* CurHopLimit */ - u_int8_t receivedra; + u_int32_t linkmtu; /* LinkMTU */ + u_int32_t maxmtu; /* Upper bound of LinkMTU */ + u_int32_t basereachable; /* BaseReachableTime */ + u_int32_t reachable; /* Reachable Time */ + u_int32_t retrans; /* Retrans Timer */ + u_int32_t flags; /* Flags */ + int recalctm; /* BaseReacable re-calculation timer */ + u_int8_t chlim; /* CurHopLimit */ + u_int8_t receivedra; }; +#define ND6_IFF_PERFORMNUD 0x1 + struct in6_nbrinfo { - char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ - struct in6_addr addr; /* IPv6 address of the neighbor */ + char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct in6_addr addr; /* IPv6 address of the neighbor */ long asked; /* number of queries already sent for this addr */ int isrouter; /* if it acts as a router */ int state; /* reachability state */ int expire; /* lifetime for NDP state transition */ }; -#define DRLSTSIZ 10 -#define PRLSTSIZ 10 +#define DRLSTSIZ 10 +#define PRLSTSIZ 10 struct in6_drlist { - char ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ]; struct { struct in6_addr rtaddr; u_char flags; u_short rtlifetime; u_long expire; - u_short if_index; + u_short if_index; } defrouter[DRLSTSIZ]; }; struct in6_prlist { - char ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ]; struct { struct in6_addr prefix; - struct prf_ra raflags; + struct prf_ra raflags; u_char prefixlen; + u_char origin; u_long vltime; u_long pltime; u_long expire; - u_short if_index; - u_short advrtrs; /* number of advertisement routers */ + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ } prefix[PRLSTSIZ]; }; struct in6_ndireq { - char ifname[IFNAMSIZ]; - struct nd_ifinfo ndi; + char ifname[IFNAMSIZ]; + struct nd_ifinfo ndi; }; +struct in6_ndifreq { + char ifname[IFNAMSIZ]; + u_long ifindex; +}; + + /* protocol constants */ -#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ -#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/ -#define MAX_RTR_SOLICITATIONS 3 +#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ +#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/ +#define MAX_RTR_SOLICITATIONS 3 -#define ND6_INFINITE_LIFETIME 0xffffffff +#define ND6_INFINITE_LIFETIME 0xffffffff #ifdef _KERNEL /* node constants */ -#define MAX_REACHABLE_TIME 3600000 /* msec */ -#define REACHABLE_TIME 30000 /* msec */ -#define RETRANS_TIMER 1000 /* msec */ -#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ -#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ -#define ND_COMPUTE_RTIME(x) \ +#define MAX_REACHABLE_TIME 3600000 /* msec */ +#define REACHABLE_TIME 30000 /* msec */ +#define RETRANS_TIMER 1000 /* msec */ +#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ +#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) +TAILQ_HEAD(nd_drhead, nd_defrouter); struct nd_defrouter { - LIST_ENTRY(nd_defrouter) dr_entry; + TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; u_char flags; u_short rtlifetime; u_long expire; - struct ifnet *ifp; + u_long advint; /* Mobile IPv6 addition (milliseconds) */ + u_long advint_expire; /* Mobile IPv6 addition */ + int advints_lost; /* Mobile IPv6 addition */ + struct ifnet *ifp; }; struct nd_prefix { - struct ifnet *ndpr_ifp; + struct ifnet *ndpr_ifp; LIST_ENTRY(nd_prefix) ndpr_entry; - struct sockaddr_in6 ndpr_prefix; /* prefix */ - struct in6_addr ndpr_mask; /* netmask derived from the prefix */ - struct in6_addr ndpr_addr; /* address that is derived from the prefix */ - u_int32_t ndpr_vltime; /* advertised valid lifetime */ - u_int32_t ndpr_pltime; /* advertised preferred lifetime */ - time_t ndpr_expire; /* expiration time of the prefix */ - time_t ndpr_preferred; /* preferred time of the prefix */ - struct prf_ra ndpr_flags; + struct sockaddr_in6 ndpr_prefix; /* prefix */ + struct in6_addr ndpr_mask; /* netmask derived from the prefix */ + struct in6_addr ndpr_addr; /* address that is derived from the prefix */ + u_int32_t ndpr_vltime; /* advertised valid lifetime */ + u_int32_t ndpr_pltime; /* advertised preferred lifetime */ + time_t ndpr_expire; /* expiration time of the prefix */ + time_t ndpr_preferred; /* preferred time of the prefix */ + struct prf_ra ndpr_flags; /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; struct ndpr_stateflags { /* if this prefix can be regarded as on-link */ - u_char onlink : 1; + u_char onlink : 1; } ndpr_stateflags; }; -#define ndpr_raf ndpr_flags -#define ndpr_raf_onlink ndpr_flags.onlink -#define ndpr_raf_auto ndpr_flags.autonomous +#define ndpr_next ndpr_entry.le_next + +#define ndpr_raf ndpr_flags +#define ndpr_raf_onlink ndpr_flags.onlink +#define ndpr_raf_auto ndpr_flags.autonomous -#define ndpr_statef_onlink ndpr_stateflags.onlink -#define ndpr_statef_addmark ndpr_stateflags.addmark +#define ndpr_statef_onlink ndpr_stateflags.onlink +#define ndpr_statef_addmark ndpr_stateflags.addmark /* * We keep expired prefix for certain amount of time, for validation purposes. * 1800s = MaxRtrAdvInterval */ -#define NDPR_KEEP_EXPIRED (1800 * 2) +#define NDPR_KEEP_EXPIRED (1800 * 2) /* * Message format for use in obtaining information about prefixes @@ -174,124 +199,136 @@ struct inet6_ndpr_msghdr { u_short inpm_msglen; /* to skip over non-understood messages */ u_char inpm_version; /* future binary compatability */ u_char inpm_type; /* message type */ - struct in6_addr inpm_prefix; + struct in6_addr inpm_prefix; u_long prm_vltim; u_long prm_pltime; u_long prm_expire; u_long prm_preferred; - struct in6_prflags prm_flags; + struct in6_prflags prm_flags; u_short prm_index; /* index for associated ifp */ u_char prm_plen; /* length of prefix in bits */ }; -#define prm_raf_onlink prm_flags.prf_ra.onlink -#define prm_raf_auto prm_flags.prf_ra.autonomous +#define prm_raf_onlink prm_flags.prf_ra.onlink +#define prm_raf_auto prm_flags.prf_ra.autonomous -#define prm_statef_onlink prm_flags.prf_state.onlink +#define prm_statef_onlink prm_flags.prf_state.onlink -#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid -#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd +#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid +#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd -#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) -#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) +#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) +#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; - struct nd_defrouter *router; +#define pfr_next pfr_entry.le_next + struct nd_defrouter *router; }; -LIST_HEAD(nd_drhead, nd_defrouter); LIST_HEAD(nd_prhead, nd_prefix); /* nd6.c */ -extern int nd6_prune; -extern int nd6_delay; -extern int nd6_umaxtries; -extern int nd6_mmaxtries; -extern int nd6_useloopback; -extern int nd6_proxyall; -extern struct llinfo_nd6 llinfo_nd6; -extern struct nd_ifinfo *nd_ifinfo; -extern struct nd_drhead nd_defrouter; -extern struct nd_prhead nd_prefix; +extern int nd6_prune; +extern int nd6_delay; +extern int nd6_umaxtries; +extern int nd6_mmaxtries; +extern int nd6_useloopback; +extern int nd6_maxnudhint; +extern struct llinfo_nd6 llinfo_nd6; +extern struct nd_ifinfo *nd_ifinfo; +extern struct nd_drhead nd_defrouter; +extern struct nd_prhead nd_prefix; + +/* nd6_rtr.c */ +extern int nd6_defifindex; union nd_opts { - struct nd_opt_hdr *nd_opt_array[9]; + struct nd_opt_hdr *nd_opt_array[9]; /*max = home agent info*/ struct { - struct nd_opt_hdr *zero; - struct nd_opt_hdr *src_lladdr; - struct nd_opt_hdr *tgt_lladdr; - struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */ - struct nd_opt_rd_hdr *rh; - struct nd_opt_mtu *mtu; - struct nd_opt_hdr *search; /* multiple opts */ - struct nd_opt_hdr *last; /* multiple opts */ - int done; - struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */ + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + struct nd_opt_hdr *six; + struct nd_opt_advint *adv; + struct nd_opt_hai *hai; + struct nd_opt_hdr *search; /* multiple opts */ + struct nd_opt_hdr *last; /* multiple opts */ + int done; + struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ } nd_opt_each; }; -#define nd_opts_src_lladdr nd_opt_each.src_lladdr -#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr -#define nd_opts_pi nd_opt_each.pi_beg -#define nd_opts_pi_end nd_opt_each.pi_end -#define nd_opts_rh nd_opt_each.rh -#define nd_opts_mtu nd_opt_each.mtu -#define nd_opts_search nd_opt_each.search -#define nd_opts_last nd_opt_each.last -#define nd_opts_done nd_opt_each.done +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi_beg +#define nd_opts_pi_end nd_opt_each.pi_end +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_adv nd_opt_each.adv +#define nd_opts_hai nd_opt_each.hai +#define nd_opts_search nd_opt_each.search +#define nd_opts_last nd_opt_each.last +#define nd_opts_done nd_opt_each.done /* XXX: need nd6_var.h?? */ /* nd6.c */ -void nd6_init __P((void)); -void nd6_ifattach __P((struct ifnet *)); -int nd6_is_addr_neighbor __P((struct in6_addr *, struct ifnet *)); -void nd6_option_init __P((void *, int, union nd_opts *)); -struct nd_opt_hdr *nd6_option __P((union nd_opts *)); -int nd6_options __P((union nd_opts *)); +void nd6_init __P((void)); +void nd6_ifattach __P((struct ifnet *)); +int nd6_is_addr_neighbor __P((struct sockaddr_in6 *, struct ifnet *)); +void nd6_option_init __P((void *, int, union nd_opts *)); +struct nd_opt_hdr *nd6_option __P((union nd_opts *)); +int nd6_options __P((union nd_opts *)); struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); -void nd6_setmtu __P((struct ifnet *)); -void nd6_timer __P((void *)); -void nd6_free __P((struct rtentry *)); -void nd6_nud_hint __P((struct rtentry *, struct in6_addr *)); -int nd6_resolve __P((struct ifnet *, struct rtentry *, - struct mbuf *, struct sockaddr *, u_char *)); -void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); -struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, - char *, int, int, int)); +void nd6_setmtu __P((struct ifnet *)); +void nd6_timer __P((void *)); +void nd6_purge __P((struct ifnet *)); +void nd6_free __P((struct rtentry *)); +void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int)); +int nd6_resolve __P((struct ifnet *, struct rtentry *, + struct mbuf *, struct sockaddr *, u_char *)); +void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); +struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, + char *, int, int, int)); /* for test */ -int nd6_output __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *, - struct rtentry *)); -int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, - struct sockaddr *, u_char *)); +int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *, struct rtentry *)); +int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, + struct sockaddr *, u_char *)); /* nd6_nbr.c */ -void nd6_na_input __P((struct mbuf *, int, int)); -void nd6_na_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, u_long, int)); -void nd6_ns_input __P((struct mbuf *, int, int)); -void nd6_ns_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, struct llinfo_nd6 *, int)); -caddr_t nd6_ifptomac __P((struct ifnet *)); -void nd6_dad_start __P((struct ifaddr *, int *)); -void nd6_dad_duplicated __P((struct ifaddr *)); +void nd6_na_input __P((struct mbuf *, int, int)); +void nd6_na_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, u_long, int, struct sockaddr *)); +void nd6_ns_input __P((struct mbuf *, int, int)); +void nd6_ns_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, struct llinfo_nd6 *, int)); +caddr_t nd6_ifptomac __P((struct ifnet *)); +void nd6_dad_start __P((struct ifaddr *, int *)); +void nd6_dad_duplicated __P((struct ifaddr *)); /* nd6_rtr.c */ -void nd6_rs_input __P((struct mbuf *, int, int)); -void nd6_ra_input __P((struct mbuf *, int, int)); -void prelist_del __P((struct nd_prefix *)); -void defrouter_addreq __P((struct nd_defrouter *)); -void defrouter_delreq __P((struct nd_defrouter *, int)); -void defrtrlist_del __P((struct nd_defrouter *)); -void prelist_remove __P((struct nd_prefix *)); -int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, - struct mbuf *)); -struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, - struct ifnet *)); -int in6_ifdel __P((struct ifnet *, struct in6_addr *)); -int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); -void rt6_flush __P((struct in6_addr *, struct ifnet *)); +void nd6_rs_input __P((struct mbuf *, int, int)); +void nd6_ra_input __P((struct mbuf *, int, int)); +void prelist_del __P((struct nd_prefix *)); +void defrouter_addreq __P((struct nd_defrouter *)); +void defrouter_delreq __P((struct nd_defrouter *, int)); +void defrouter_select __P((void)); +void defrtrlist_del __P((struct nd_defrouter *)); +void prelist_remove __P((struct nd_prefix *)); +int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, + struct mbuf *)); +void pfxlist_onlink_check __P((void)); +struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, + struct ifnet *)); +int in6_ifdel __P((struct ifnet *, struct in6_addr *)); +int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); +void rt6_flush __P((struct in6_addr *, struct ifnet *)); +int nd6_setdefaultiface __P((int)); #endif /* _KERNEL */ diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 58f0a52..d3fa831 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: nd6_nbr.c,v 1.37 2000/06/04 12:46:13 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,10 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -51,31 +54,37 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> + +#ifdef IPSEC +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#endif #include <net/net_osdep.h> -#define SDL(s) ((struct sockaddr_dl *)s) +#define SDL(s) ((struct sockaddr_dl *)s) -struct dadq; -static struct dadq *nd6_dad_find __P((struct ifaddr *)); -static void nd6_dad_timer __P((struct ifaddr *)); -static void nd6_dad_ns_input __P((struct ifaddr *)); -static void nd6_dad_na_input __P((struct ifaddr *)); +struct dadq; +static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static void nd6_dad_timer __P((struct ifaddr *)); +static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *)); +static void nd6_dad_ns_input __P((struct ifaddr *)); +static void nd6_dad_na_input __P((struct ifaddr *)); -/* ignore NS in DAD - specwise incorrect, */ -int dad_ignore_ns = 0; +static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/ +static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ /* * Input an Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) - * - * XXX proxy advertisement */ void nd6_ns_input(m, off, icmp6len) @@ -84,11 +93,10 @@ nd6_ns_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_solicit *nd_ns - = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); + struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_ns->nd_ns_target; + struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa; @@ -96,11 +104,12 @@ nd6_ns_input(m, off, icmp6len) int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; + struct sockaddr_dl *proxydl = NULL; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { @@ -118,6 +127,18 @@ nd6_ns_input(m, off, icmp6len) } } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); + if (nd_ns == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + taddr6 = nd_ns->nd_ns_target; + if (IN6_IS_ADDR_MULTICAST(&taddr6)) { log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); goto bad; @@ -154,6 +175,12 @@ nd6_ns_input(m, off, icmp6len) * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ +#if 0 /* too much! */ + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); + if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) + tlladdr = 0; + else +#endif if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else @@ -169,7 +196,7 @@ nd6_ns_input(m, off, icmp6len) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ - if (!ifa && nd6_proxyall) { + if (!ifa) { struct rtentry *rt; struct sockaddr_in6 tsin6; @@ -179,16 +206,20 @@ nd6_ns_input(m, off, icmp6len) tsin6.sin6_addr = taddr6; rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); - if (rt && rt->rt_ifp != ifp) { + if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && + rt->rt_gateway->sa_family == AF_LINK) { /* - * search link local addr for ifp, and use it for - * proxy NA. + * proxy NDP for single entry */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); - if (ifa) + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) { proxy = 1; + proxydl = SDL(rt->rt_gateway); + } } - rtfree(rt); + if (rt) + rtfree(rt); } if (!ifa) { /* @@ -196,13 +227,13 @@ nd6_ns_input(m, off, icmp6len) * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ - return; + goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) - return; + goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, @@ -215,7 +246,7 @@ nd6_ns_input(m, off, icmp6len) log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6)); - return; + goto freeit; } /* @@ -241,7 +272,7 @@ nd6_ns_input(m, off, icmp6len) if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); - return; + goto freeit; } /* @@ -259,8 +290,8 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr); - return; + tlladdr, (struct sockaddr *)proxydl); + goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); @@ -269,14 +300,16 @@ nd6_ns_input(m, off, icmp6len) ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, - tlladdr); + tlladdr, (struct sockaddr *)proxydl); + freeit: + m_freem(m); return; bad: log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); - return; + m_freem(m); } /* @@ -301,13 +334,33 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) struct in6_ifaddr *ia = NULL; struct ip6_moptions im6o; int icmp6len; + int maxlen; caddr_t mac; struct ifnet *outif = NULL; if (IN6_IS_ADDR_MULTICAST(taddr6)) return; - if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + /* estimate the size of message */ + maxlen = sizeof(*ip6) + sizeof(*nd_ns); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); +#endif + return; + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) return; if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { @@ -319,12 +372,13 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) icmp6len = sizeof(*nd_ns); m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; - MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enought. but just in case */ + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ /* fill neighbor solicitation packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; @@ -339,7 +393,20 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) ip6->ip6_dst.s6_addr8[12] = 0xff; } if (!dad) { - /* spec-wise correct, scope match */ +#if 0 /* KAME way, exact address scope match */ + /* + * Select a source whose scope is the same as that of the dest. + * Typically, the dest is link-local solicitation multicast + * (i.e. neighbor discovery) or link-local/global unicast + * (i.e. neighbor un-reachability detection). + */ + ia = in6_ifawithifp(ifp, &ip6->ip6_dst); + if (ia == NULL) { + m_freem(m); + return; + } + ip6->ip6_src = ia->ia_addr.sin6_addr; +#else /* spec-wise correct */ /* * RFC2461 7.2.2: * "If the source address of the packet prompting the @@ -377,6 +444,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } ip6->ip6_src = ia->ia_addr.sin6_addr; } +#endif } else { /* * Source address for DAD packet must always be IPv6 @@ -425,6 +493,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) nd_ns->nd_ns_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); @@ -438,6 +510,10 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) + * + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void nd6_na_input(m, off, icmp6len) @@ -446,14 +522,16 @@ nd6_na_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_advert *nd_na - = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); + struct nd_neighbor_advert *nd_na; +#if 0 + struct in6_addr saddr6 = ip6->ip6_src; +#endif struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_na->nd_na_target; - int flags = nd_na->nd_na_flags_reserved; - int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); - int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); - int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); + struct in6_addr taddr6; + int flags; + int is_router; + int is_solicited; + int is_override; char *lladdr = NULL; int lladdrlen = 0; struct ifaddr *ifa; @@ -465,8 +543,24 @@ nd6_na_input(m, off, icmp6len) if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); + if (nd_na == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif + taddr6 = nd_na->nd_na_target; + flags = nd_na->nd_na_flags_reserved; + is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); + is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); + is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); @@ -475,20 +569,20 @@ nd6_na_input(m, off, icmp6len) log(LOG_ERR, "nd6_na_input: invalid target address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { log(LOG_ERR, "nd6_na_input: a solicited adv is multicasted\n"); - return; + goto freeit; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { @@ -510,7 +604,7 @@ nd6_na_input(m, off, icmp6len) if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { nd6_dad_na_input(ifa); - return; + goto freeit; } /* Just for safety, maybe unnecessery. */ @@ -518,7 +612,7 @@ nd6_na_input(m, off, icmp6len) log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { @@ -535,7 +629,7 @@ nd6_na_input(m, off, icmp6len) if ((rt == NULL) || ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || ((sdl = SDL(rt->rt_gateway)) == NULL)) - return; + goto freeit; if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* @@ -543,7 +637,7 @@ nd6_na_input(m, off, icmp6len) * discard the packet. */ if (ifp->if_addrlen && !lladdr) - return; + goto freeit; /* * Record link-layer address, and update the state. @@ -552,6 +646,7 @@ nd6_na_input(m, off, icmp6len) bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (ln->ln_expire) ln->ln_expire = time_second + nd_ifinfo[rt->rt_ifp->if_index].reachable; @@ -602,7 +697,7 @@ nd6_na_input(m, off, icmp6len) */ if (ln->ln_state == ND6_LLINFO_REACHABLE) ln->ln_state = ND6_LLINFO_STALE; - return; + goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ || !lladdr) { /* (2c) */ @@ -621,6 +716,7 @@ nd6_na_input(m, off, icmp6len) */ if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (ln->ln_expire) { ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].reachable; @@ -663,10 +759,21 @@ nd6_na_input(m, off, icmp6len) rt->rt_flags &= ~RTF_REJECT; ln->ln_asked = 0; if (ln->ln_hold) { - nd6_output(ifp, ln->ln_hold, +#ifdef OLDIP6OUTPUT + (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt); +#else + /* + * we assume ifp is not a p2p here, so just set the 2nd + * argument as the 1st one. + */ + nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); +#endif ln->ln_hold = 0; } + + freeit: + m_freem(m); } /* @@ -674,16 +781,17 @@ nd6_na_input(m, off, icmp6len) * * Based on RFC 2461 * - * XXX NA delay for anycast address is not implemented yet - * (RFC 2461 7.2.7) - * XXX proxy advertisement? + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void -nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) +nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ifnet *ifp; struct in6_addr *daddr6, *taddr6; u_long flags; - int tlladdr; /* 1 if include target link-layer address */ + int tlladdr; /* 1 if include target link-layer address */ + struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ { struct mbuf *m; struct ip6_hdr *ip6; @@ -691,10 +799,30 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) struct in6_ifaddr *ia = NULL; struct ip6_moptions im6o; int icmp6len; + int maxlen; caddr_t mac; - struct ifnet *outif; - - if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + struct ifnet *outif = NULL; + + /* estimate the size of message */ + maxlen = sizeof(*ip6) + sizeof(*nd_na); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); +#endif + return; + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) return; if (IN6_IS_ADDR_MULTICAST(daddr6)) { @@ -706,12 +834,13 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) icmp6len = sizeof(*nd_na); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; - MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enough. but just in case */ + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) { @@ -747,7 +876,23 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) * Basically, if NS packet is sent to unicast/anycast addr, * target lladdr option SHOULD NOT be included. */ - if (tlladdr && (mac = nd6_ifptomac(ifp))) { + if (tlladdr) { + mac = NULL; + /* + * sdl0 != NULL indicates proxy NA. If we do proxy, use + * lladdr in sdl0. If we are not proxying (sending NA for + * my address) use lladdr configured for the interface. + */ + if (sdl0 == NULL) + mac = nd6_ifptomac(ifp); + else if (sdl0->sa_family == AF_LINK) { + struct sockaddr_dl *sdl; + sdl = (struct sockaddr_dl *)sdl0; + if (sdl->sdl_alen == ifp->if_addrlen) + mac = LLADDR(sdl); + } + } + if (tlladdr && mac) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); @@ -771,8 +916,9 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); #ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); @@ -801,6 +947,7 @@ struct dadq { TAILQ_ENTRY(dadq) dad_list; struct ifaddr *dad_ifa; int dad_count; /* max NS to send */ + int dad_ns_tcount; /* # of trials to send NS */ int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; @@ -815,7 +962,7 @@ nd6_dad_find(ifa) { struct dadq *dp; - TAILQ_FOREACH(dp, &dadq, dad_list) { + for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { if (dp->dad_ifa == ifa) return dp; } @@ -846,7 +993,8 @@ nd6_dad_start(ifa, tick) * - the interface address is anycast */ if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { - printf("nd6_dad_start: called with non-tentative address " + log(LOG_DEBUG, + "nd6_dad_start: called with non-tentative address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); @@ -871,7 +1019,7 @@ nd6_dad_start(ifa, tick) dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); if (dp == NULL) { - printf("nd6_dad_start: memory allocation failed for " + log(LOG_ERR, "nd6_dad_start: memory allocation failed for " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); @@ -881,7 +1029,7 @@ nd6_dad_start(ifa, tick) TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); #ifdef ND6_DEBUG - printf("%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); #endif @@ -895,11 +1043,9 @@ nd6_dad_start(ifa, tick) ifa->ifa_refcnt++; /*just for safety*/ dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; - dp->dad_ns_ocount = 0; + dp->dad_ns_ocount = dp->dad_ns_tcount = 0; if (!tick) { - dp->dad_ns_ocount++; - nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, - NULL, 1); + nd6_dad_ns_output(dp, ifa); dp->dad_timer = timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); @@ -929,37 +1075,47 @@ nd6_dad_timer(ifa) /* Sanity check */ if (ia == NULL) { - printf("nd6_dad_timer: called with null parameter\n"); + log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); goto done; } dp = nd6_dad_find(ifa); if (dp == NULL) { - printf("nd6_dad_timer: DAD structure not found\n"); + log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n"); goto done; } if (ia->ia6_flags & IN6_IFF_DUPLICATED) { - printf("nd6_dad_timer: called with duplicated address " + log(LOG_ERR, "nd6_dad_timer: called with duplicated address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto done; } if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { - printf("nd6_dad_timer: called with non-tentative address " + log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto done; } + /* timeouted with IFF_{RUNNING,UP} check */ + if (dp->dad_ns_tcount > dad_maxtry) { + log(LOG_ERR, "%s: could not run DAD, driver problem?\n", + if_name(ifa->ifa_ifp)); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + IFAFREE(ifa); + goto done; + } + /* Need more checks? */ if (dp->dad_ns_ocount < dp->dad_count) { /* * We have more NS to go. Send NS packet for DAD. */ - dp->dad_ns_ocount++; - nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, - NULL, 1); + nd6_dad_ns_output(dp, ifa); dp->dad_timer = timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); @@ -981,8 +1137,33 @@ nd6_dad_timer(ifa) } if (dp->dad_ns_icount) { +#if 0 /*heuristics*/ + /* + * if + * - we have sent many(?) DAD NS, and + * - the number of NS we sent equals to the + * number of NS we've got, and + * - we've got no NA + * we may have a faulty network card/driver which + * loops back multicasts to myself. + */ + if (3 < dp->dad_count + && dp->dad_ns_icount == dp->dad_count + && dp->dad_na_icount == 0) { + log(LOG_INFO, "DAD questionable for %s(%s): " + "network card loops back multicast?\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + if_name(ifa->ifa_ifp)); + /* XXX consider it a duplicate or not? */ + /* duplicate++; */ + } else { + /* We've seen NS, means DAD has failed. */ + duplicate++; + } +#else /* We've seen NS, means DAD has failed. */ duplicate++; +#endif } if (duplicate) { @@ -997,15 +1178,16 @@ nd6_dad_timer(ifa) ia->ia6_flags &= ~IN6_IFF_TENTATIVE; #ifdef ND6_DEBUG - printf("%s: DAD complete for %s - no duplicates " - "found\n", if_name(ifa->ifa_ifp), + log(LOG_INFO, + "%s: DAD complete for %s - no duplicates found\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); #endif TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); dp = NULL; - ifa->ifa_refcnt--; + IFAFREE(ifa); } } @@ -1022,7 +1204,7 @@ nd6_dad_duplicated(ifa) dp = nd6_dad_find(ifa); if (dp == NULL) { - printf("nd6_dad_duplicated: DAD structure not found\n"); + log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n"); return; } @@ -1036,19 +1218,47 @@ nd6_dad_duplicated(ifa) /* We are done with DAD, with duplicated address found. (failure) */ untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa - , dp->dad_timer); + , dp->dad_timer + ); - printf("%s: DAD complete for %s - duplicate found\n", + log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); - printf("%s: manual intervention required\n", if_name(ifa->ifa_ifp)); + log(LOG_ERR, "%s: manual intervention required\n", + if_name(ifa->ifa_ifp)); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); dp = NULL; - ifa->ifa_refcnt--; + IFAFREE(ifa); } -void +static void +nd6_dad_ns_output(dp, ifa) + struct dadq *dp; + struct ifaddr *ifa; +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; + struct ifnet *ifp = ifa->ifa_ifp; + + dp->dad_ns_tcount++; + if ((ifp->if_flags & IFF_UP) == 0) { +#if 0 + printf("%s: interface down?\n", if_name(ifp)); +#endif + return; + } + if ((ifp->if_flags & IFF_RUNNING) == 0) { +#if 0 + printf("%s: interface not running?\n", if_name(ifp)); +#endif + return; + } + + dp->dad_ns_ocount++; + nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); +} + +static void nd6_dad_ns_input(ifa) struct ifaddr *ifa; { @@ -1103,7 +1313,7 @@ nd6_dad_ns_input(ifa) } } -void +static void nd6_dad_na_input(ifa) struct ifaddr *ifa; { diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 2501a2d6..f403371 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: nd6_rtr.c,v 1.43 2000/07/02 23:19:59 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> @@ -47,34 +51,44 @@ #include <netinet/in.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> +#include <netinet6/scope6_var.h> #include <net/net_osdep.h> -#define SDL(s) ((struct sockaddr_dl *)s) +#define SDL(s) ((struct sockaddr_dl *)s) + +static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); +static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); +static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); +static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, int)); +static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, + struct nd_defrouter *)); +static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); +static void pfxrtr_del __P((struct nd_pfxrouter *)); +static struct nd_pfxrouter *find_pfxlist_reachable_router + __P((struct nd_prefix *)); +static void nd6_detach_prefix __P((struct nd_prefix *)); +static void nd6_attach_prefix __P((struct nd_prefix *)); +static void defrouter_addifreq __P((struct ifnet *)); +#ifdef ND6_USE_RTSOCK +static void defrouter_msg __P((int, struct rtentry *)); +#endif -static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); -static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); -static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); -static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, int)); -static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, - struct nd_defrouter *)); -static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); -static void pfxrtr_del __P((struct nd_pfxrouter *)); -static void pfxlist_onlink_check __P((void)); -static void nd6_detach_prefix __P((struct nd_prefix *)); -static void nd6_attach_prefix __P((struct nd_prefix *)); +static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, + struct in6_addrlifetime *lt6, + int update_vltime)); -static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, - struct in6_addrlifetime *lt6)); +static int rt6_deleteroute __P((struct radix_node *, void *)); -static int rt6_deleteroute __P((struct radix_node *, void *)); +extern int nd6_recalc_reachtm_interval; -extern int nd6_recalc_reachtm_interval; +struct ifnet *nd6_defifp; +int nd6_defifindex; /* * Receive Router Solicitation Message - just for routers. @@ -90,22 +104,30 @@ nd6_rs_input(m, off, icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_solicit *nd_rs - = (struct nd_router_solicit *)((caddr_t)ip6 + off); + struct nd_router_solicit *nd_rs; struct in6_addr saddr6 = ip6->ip6_src; +#if 0 + struct in6_addr daddr6 = ip6->ip6_dst; +#endif char *lladdr = NULL; int lladdrlen = 0; +#if 0 + struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL; + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; + struct rtentry *rt = NULL; + int is_newentry; +#endif union nd_opts ndopts; /* If I'm not a router, ignore it. */ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1) - return; + goto freeit; /* Sanity checks */ if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } /* @@ -113,13 +135,24 @@ nd6_rs_input(m, off, icmp6len) * This indicates that the src has no IP address assigned yet. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) + goto freeit; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len); + if (nd_rs == NULL) { + icmp6stat.icp6s_tooshort++; return; + } +#endif icmp6len -= sizeof(*nd_rs); nd6_option_init(nd_rs + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_src_lladdr) { @@ -135,6 +168,9 @@ nd6_rs_input(m, off, icmp6len) } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); + + freeit: + m_freem(m); } /* @@ -152,33 +188,49 @@ nd6_ra_input(m, off, icmp6len) struct ifnet *ifp = m->m_pkthdr.rcvif; struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_advert *nd_ra = - (struct nd_router_advert *)((caddr_t)ip6 + off); + struct nd_router_advert *nd_ra; struct in6_addr saddr6 = ip6->ip6_src; +#if 0 + struct in6_addr daddr6 = ip6->ip6_dst; + int flags; /* = nd_ra->nd_ra_flags_reserved; */ + int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); + int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); +#endif union nd_opts ndopts; struct nd_defrouter *dr; if (ip6_accept_rtadv == 0) - return; + goto freeit; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { log(LOG_ERR, "nd6_ra_input: src %s is not link-local\n", ip6_sprintf(&saddr6)); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); + if (nd_ra == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif icmp6len -= sizeof(*nd_ra); nd6_option_init(nd_ra + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); - return; + goto freeit; } { @@ -190,6 +242,9 @@ nd6_ra_input(m, off, icmp6len) dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); dr0.expire = time_second + dr0.rtlifetime; dr0.ifp = ifp; + dr0.advint = 0; /* Mobile IPv6 */ + dr0.advint_expire = 0; /* Mobile IPv6 */ + dr0.advints_lost = 0; /* Mobile IPv6 */ /* unspecified or not? (RFC 2461 6.3.4) */ if (advreachable) { NTOHL(advreachable); @@ -336,17 +391,47 @@ nd6_ra_input(m, off, icmp6len) } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); + + /* + * Installing a link-layer address might change the state of the + * router's neighbor cache, which might also affect our on-link + * detection of adveritsed prefixes. + */ + pfxlist_onlink_check(); } + +freeit: + m_freem(m); } /* * default router list proccessing sub routines */ + +#ifdef ND6_USE_RTSOCK +/* tell the change to user processes watching the routing socket. */ +static void +defrouter_msg(cmd, rt) + int cmd; + struct rtentry *rt; +{ + struct rt_addrinfo info; + + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = rt_key(rt); + info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info.rti_info[RTAX_NETMASK] = rt_mask(rt); + + rt_missmsg(cmd, &info, rt->rt_flags, 0); +} +#endif + void defrouter_addreq(new) struct nd_defrouter *new; { struct sockaddr_in6 def, mask, gate; + struct rtentry *newrt = NULL; int s; Bzero(&def, sizeof(def)); @@ -361,11 +446,68 @@ defrouter_addreq(new) s = splnet(); (void)rtrequest(RTM_ADD, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, NULL); + RTF_GATEWAY, &newrt); + if (newrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_ADD, newrt); /* tell user process */ +#endif + newrt->rt_refcnt--; + } splx(s); return; } +/* Add a route to a given interface as default */ +void +defrouter_addifreq(ifp) + struct ifnet *ifp; +{ + struct sockaddr_in6 def, mask; + struct ifaddr *ifa; + struct rtentry *newrt = NULL; + int error, flags; + + bzero(&def, sizeof(def)); + bzero(&mask, sizeof(mask)); + + def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6); + def.sin6_family = mask.sin6_family = AF_INET6; + + /* + * Search for an ifaddr beloging to the specified interface. + * XXX: An IPv6 address are required to be assigned on the interface. + */ + if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { + log(LOG_ERR, /* better error? */ + "defrouter_addifreq: failed to find an ifaddr " + "to install a route to interface %s\n", + if_name(ifp)); + return; + } + + flags = ifa->ifa_flags; + if ((ifp->if_flags & IFF_POINTOPOINT) != 0) + flags &= ~RTF_CLONING; + if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def, + ifa->ifa_addr, (struct sockaddr *)&mask, + flags, &newrt)) != 0) { + log(LOG_ERR, + "defrouter_addifreq: failed to install a route to " + "interface %s (errno = %d)\n", + if_name(ifp), error); + + if (newrt) /* maybe unnecessary, but do it for safety */ + newrt->rt_refcnt--; + } else { + if (newrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_ADD, newrt); +#endif + newrt->rt_refcnt--; + } + } +} + struct nd_defrouter * defrouter_lookup(addr, ifp) struct in6_addr *addr; @@ -373,9 +515,11 @@ defrouter_lookup(addr, ifp) { struct nd_defrouter *dr; - LIST_FOREACH(dr, &nd_defrouter, dr_entry) + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) return(dr); + } return(NULL); /* search failed */ } @@ -386,6 +530,7 @@ defrouter_delreq(dr, dofree) int dofree; { struct sockaddr_in6 def, mask, gate; + struct rtentry *oldrt = NULL; Bzero(&def, sizeof(def)); Bzero(&mask, sizeof(mask)); @@ -399,18 +544,18 @@ defrouter_delreq(dr, dofree) rtrequest(RTM_DELETE, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, (struct rtentry **)0); + RTF_GATEWAY, &oldrt); + if (oldrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_DELETE, oldrt); +#endif + if (oldrt->rt_refcnt <= 0) + oldrt->rt_refcnt++; /* XXX */ + rtfree(oldrt); + } - if (dofree) + if (dofree) /* XXX: necessary? */ free(dr, M_IP6NDP); - - if (!LIST_EMPTY(&nd_defrouter)) - defrouter_addreq(LIST_FIRST(&nd_defrouter)); - - /* - * xxx update the Destination Cache entries for all - * destinations using that neighbor as a router (7.2.5) - */ } void @@ -429,15 +574,15 @@ defrtrlist_del(dr) rt6_flush(&dr->rtaddr, dr->ifp); } - if (dr == LIST_FIRST(&nd_defrouter)) + if (dr == TAILQ_FIRST(&nd_defrouter)) deldr = dr; /* The router is primary. */ - LIST_REMOVE(dr, dr_entry); + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); /* * Also delete all the pointers to the router in each prefix lists. */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { struct nd_pfxrouter *pfxrtr; if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) pfxrtr_del(pfxrtr); @@ -445,14 +590,96 @@ defrtrlist_del(dr) pfxlist_onlink_check(); /* - * If the router is the primary one, delete the default route - * entry in the routing table. + * If the router is the primary one, choose a new one. + * Note that defrouter_select() will remove the current gateway + * from the routing table. */ if (deldr) - defrouter_delreq(deldr, 0); + defrouter_select(); + free(dr, M_IP6NDP); } +/* + * Default Router Selection according to Section 6.3.6 of RFC 2461: + * 1) Routers that are reachable or probably reachable should be + * preferred. + * 2) When no routers on the list are known to be reachable or + * probably reachable, routers SHOULD be selected in a round-robin + * fashion. + * 3) If the Default Router List is empty, assume that all + * destinations are on-link. + */ +void +defrouter_select() +{ + int s = splnet(); + struct nd_defrouter *dr, anydr; + struct rtentry *rt = NULL; + struct llinfo_nd6 *ln = NULL; + + /* + * Search for a (probably) reachable router from the list. + */ + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln)) { + /* Got it, and move it to the head */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); + break; + } + } + + if ((dr = TAILQ_FIRST(&nd_defrouter))) { + /* + * De-install the previous default gateway and install + * a new one. + * Note that if there is no reachable router in the list, + * the head entry will be used anyway. + * XXX: do we have to check the current routing table entry? + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + defrouter_addreq(dr); + } + else { + /* + * The Default Router List is empty, so install the default + * route to an inteface. + * XXX: The specification does not say this mechanism should + * be restricted to hosts, but this would be not useful + * (even harmful) for routers. + */ + if (!ip6_forwarding) { + /* + * De-install the current default route + * in advance. + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + if (nd6_defifp) { + /* + * Install a route to the default interface + * as default route. + */ + defrouter_addifreq(nd6_defifp); + } +#ifdef ND6_DEBUG + else /* noisy log? */ + log(LOG_INFO, "defrouter_select: " + "there's no default router and no default" + " interface\n"); +#endif + } + } + + splx(s); + return; +} + static struct nd_defrouter * defrtrlist_update(new) struct nd_defrouter *new; @@ -488,13 +715,15 @@ defrtrlist_update(new) } bzero(n, sizeof(*n)); *n = *new; - if (LIST_EMPTY(&nd_defrouter)) { - LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry); - defrouter_addreq(n); - } else { - LIST_INSERT_AFTER(LIST_FIRST(&nd_defrouter), n, dr_entry); - defrouter_addreq(n); - } + + /* + * Insert the new router at the end of the Default Router List. + * If there is no other router, install it anyway. Otherwise, + * just continue to use the current default router. + */ + TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); + if (TAILQ_FIRST(&nd_defrouter) == n) + defrouter_select(); splx(s); return(n); @@ -506,8 +735,8 @@ pfxrtr_lookup(pr, dr) struct nd_defrouter *dr; { struct nd_pfxrouter *search; - - LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { + + for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { if (search->router == dr) break; } @@ -547,7 +776,7 @@ prefix_lookup(pr) { struct nd_prefix *search; - LIST_FOREACH(search, &nd_prefix, ndpr_entry) { + for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { if (pr->ndpr_ifp == search->ndpr_ifp && pr->ndpr_plen == search->ndpr_plen && in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, @@ -591,8 +820,9 @@ prelist_add(pr, dr) LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); splx(s); - if (dr) + if (dr) { pfxrtr_add(new, dr); + } return 0; } @@ -610,8 +840,8 @@ prelist_remove(pr) splx(s); /* free list of routers that adversed the prefix */ - for (pfr = LIST_FIRST(&pr->ndpr_advrtrs); pfr; pfr = next) { - next = LIST_NEXT(pfr, pfr_entry); + for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { + next = pfr->pfr_next; free(pfr, M_IP6NDP); } @@ -638,6 +868,7 @@ prelist_update(new, dr, m) int error = 0; int auth; struct in6_addrlifetime *lt6; + u_char onlink; /* Mobile IPv6 */ auth = 0; if (m) { @@ -656,6 +887,7 @@ prelist_update(new, dr, m) error = EADDRNOTAVAIL; goto end; } + /* update prefix information */ pr->ndpr_flags = new->ndpr_flags; pr->ndpr_vltime = new->ndpr_vltime; @@ -707,9 +939,9 @@ prelist_update(new, dr, m) /* address lifetime <= prefix lifetime */ lt6->ia6t_vltime = new->ndpr_vltime; lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6); + in6_init_address_ltimes(new, lt6, 1); } else { -#define TWOHOUR (120*60) +#define TWOHOUR (120*60) /* * We have seen the prefix before, and we have added * interface address in the past. We still have @@ -722,10 +954,26 @@ prelist_update(new, dr, m) lt6 = &ia6->ia6_lifetime; - /* - * update to RFC 2462 5.5.3 (e) from Jim Bound, - * (ipng 6712) - */ +#if 0 /* RFC 2462 5.5.3 (e) */ + lt6->ia6t_pltime = new->ndpr_pltime; + if (TWOHOUR < new->ndpr_vltime + || lt6pr->nd < new->ndpr_vltime) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } else if (auth + && lt6->ia6t_vltime <= TWOHOUR0 + && new->ndpr_vltime <= lt6->ia6t_vltime) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } else { + lt6->ia6t_vltime = TWOHOUR; + update++; + } + + /* 2 hour rule is not imposed for pref lifetime */ + new->ndpr_apltime = new->ndpr_pltime; + lt6->ia6t_pltime = new->ndpr_pltime; +#else /* update from Jim Bound, (ipng 6712) */ if (TWOHOUR < new->ndpr_vltime || lt6->ia6t_vltime < new->ndpr_vltime) { lt6->ia6t_vltime = new->ndpr_vltime; @@ -737,16 +985,28 @@ prelist_update(new, dr, m) /* jim bound rule is not imposed for pref lifetime */ lt6->ia6t_pltime = new->ndpr_pltime; - - update++; - if (update) - in6_init_address_ltimes(new, lt6); +#endif + in6_init_address_ltimes(new, lt6, update); } noautoconf1: +#if 0 + /* address lifetime expire processing, RFC 2462 5.5.4. */ + if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { + struct in6_ifaddr *ia6; + + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (ia6) + ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; + } +#endif + + onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */ + if (dr && pfxrtr_lookup(pr, dr) == NULL) pfxrtr_add(pr, dr); + } else { int error_tmp; @@ -779,7 +1039,7 @@ prelist_update(new, dr, m) /* address lifetime <= prefix lifetime */ lt6->ia6t_vltime = new->ndpr_vltime; lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6); + in6_init_address_ltimes(new, lt6, 1); noautoconf2: error_tmp = prelist_add(new, dr); @@ -792,49 +1052,82 @@ prelist_update(new, dr, m) } /* + * A supplement function used in the on-link detection below; + * detect if a given prefix has a (probably) reachable advertising router. + * XXX: lengthy function name... + */ +struct nd_pfxrouter * +find_pfxlist_reachable_router(pr) + struct nd_prefix *pr; +{ + struct nd_pfxrouter *pfxrtr; + struct rtentry *rt; + struct llinfo_nd6 *ln; + + for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr; + pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { + if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0, + pfxrtr->router->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln)) + break; /* found */ + } + + return(pfxrtr); + +} + +/* * Check if each prefix in the prefix list has at least one available router - * that advertised the prefix. - * If the check fails, the prefix may be off-link because, for example, + * that advertised the prefix (A router is "available" if its neighbor cache + * entry has reachable or probably reachable). + * If the check fails, the prefix may be off-link, because, for example, * we have moved from the network but the lifetime of the prefix has not * been expired yet. So we should not use the prefix if there is another * prefix that has an available router. - * But if there is no prefix that has an availble router, we still regards + * But if there is no prefix that has an available router, we still regards * all the prefixes as on-link. This is because we can't tell if all the * routers are simply dead or if we really moved from the network and there * is no router around us. */ -static void +void pfxlist_onlink_check() { struct nd_prefix *pr; - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) - if (!LIST_EMPTY(&pr->ndpr_advrtrs)) /* pr has an available router */ + /* + * Check if there is a prefix that has a reachable advertising + * router. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr)) break; + } if (pr) { /* - * There is at least one prefix that has a router. First, - * detach prefixes which has no advertising router and then - * attach other prefixes. The order is important since an - * attached prefix and a detached prefix may have a same - * interface route. + * There is at least one prefix that has a reachable router. + * First, detach prefixes which has no reachable advertising + * router and then attach other prefixes. + * The order is important since an attached prefix and a + * detached prefix may have a same interface route. */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (LIST_EMPTY(&pr->ndpr_advrtrs) && + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr) == NULL && pr->ndpr_statef_onlink) { pr->ndpr_statef_onlink = 0; nd6_detach_prefix(pr); } } - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (!LIST_EMPTY(&pr->ndpr_advrtrs) && - pr->ndpr_statef_onlink == 0) + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr) && + pr->ndpr_statef_onlink == 0) nd6_attach_prefix(pr); } - } else { - /* there is no prefix that has a router */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) + } + else { + /* there is no prefix that has a reachable router */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) if (pr->ndpr_statef_onlink == 0) nd6_attach_prefix(pr); } @@ -904,7 +1197,8 @@ nd6_attach_prefix(pr) "nd6_attach_prefix: failed to find any ifaddr" " to add route for a prefix(%s/%d)\n", ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen); - } else { + } + else { int e; struct sockaddr_in6 mask6; @@ -955,12 +1249,20 @@ in6_ifadd(ifp, in6, addr, prefixlen) in6_len2mask(&mask, prefixlen); /* find link-local address (will be interface ID) */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; else return NULL; +#if 0 /* don't care link local addr state, and always do DAD */ + /* if link-local address is not eligible, do not autoconfigure. */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { + printf("in6_ifadd: link-local address not ready\n"); + return NULL; + } +#endif + /* prefixlen + ifidlen must be equal to 128 */ if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s" @@ -979,7 +1281,10 @@ in6_ifadd(ifp, in6, addr, prefixlen) bzero((caddr_t)ia, sizeof(*ia)); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + if (ifp->if_flags & IFF_POINTOPOINT) + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + else + ia->ia_ifa.ifa_dstaddr = NULL; ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; @@ -988,14 +1293,22 @@ in6_ifadd(ifp, in6, addr, prefixlen) for( ; oia->ia_next; oia = oia->ia_next) continue; oia->ia_next = ia; - } else + } else { + /* + * This should be impossible, since we have at least one + * link-local address (see the beginning of this function). + * XXX: should we rather panic here? + */ + printf("in6_ifadd: in6_ifaddr is NULL (impossible!)\n"); in6_ifaddr = ia; + } + /* gain a refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; /* link to if_addrlist */ - if (!TAILQ_EMPTY(&ifp->if_addrlist)) { - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, - ifa_list); - } + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + /* gain another refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; /* new address */ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); @@ -1113,6 +1426,7 @@ in6_ifdel(ifp, in6) } TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); /* lladdr is never deleted */ oia = ia; @@ -1163,16 +1477,26 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) } static void -in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) +in6_init_address_ltimes(struct nd_prefix *new, + struct in6_addrlifetime *lt6, + int update_vltime) { - /* init ia6t_expire */ - if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_expire = 0; - else { - lt6->ia6t_expire = time_second; - lt6->ia6t_expire += lt6->ia6t_vltime; + /* Valid lifetime must not be updated unless explicitly specified. */ + if (update_vltime) { + /* init ia6t_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; + } + /* Ensure addr lifetime <= prefix lifetime. */ + if (new->ndpr_expire && lt6->ia6t_expire && + new->ndpr_expire < lt6->ia6t_expire) + lt6->ia6t_expire = new->ndpr_expire; } + /* init ia6t_preferred */ if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) lt6->ia6t_preferred = 0; @@ -1180,10 +1504,7 @@ in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) lt6->ia6t_preferred = time_second; lt6->ia6t_preferred += lt6->ia6t_pltime; } - /* ensure addr lifetime <= prefix lifetime */ - if (new->ndpr_expire && lt6->ia6t_expire && - new->ndpr_expire < lt6->ia6t_expire) - lt6->ia6t_expire = new->ndpr_expire; + /* Ensure addr lifetime <= prefix lifetime. */ if (new->ndpr_preferred && lt6->ia6t_preferred && new->ndpr_preferred < lt6->ia6t_preferred) lt6->ia6t_preferred = new->ndpr_preferred; @@ -1240,3 +1561,41 @@ rt6_deleteroute(rn, arg) rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0)); #undef SIN6 } + +int +nd6_setdefaultiface(ifindex) + int ifindex; +{ + int error = 0; + + if (ifindex < 0 || if_index < ifindex) + return(EINVAL); + + if (nd6_defifindex != ifindex) { + nd6_defifindex = ifindex; + if (nd6_defifindex > 0) + nd6_defifp = ifindex2ifnet[nd6_defifindex]; + else + nd6_defifp = NULL; + + /* + * If the Default Router List is empty, install a route + * to the specified interface as default or remove the default + * route when the default interface becomes canceled. + * The check for the queue is actually redundant, but + * we do this here to avoid re-install the default route + * if the list is NOT empty. + */ + if (TAILQ_FIRST(&nd_defrouter) == NULL) + defrouter_select(); + + /* + * Our current implementation assumes one-to-one maping between + * interfaces and links, so it would be natural to use the + * default interface as the default link. + */ + scope6_setdefault(nd6_defifp); + } + + return(error); +} diff --git a/sys/netinet6/pim6.h b/sys/netinet6/pim6.h index 1e47785..d420db9 100644 --- a/sys/netinet6/pim6.h +++ b/sys/netinet6/pim6.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: pim6.h,v 1.3 2000/03/25 07:23:58 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Protocol Independent Multicast (PIM) definitions @@ -39,7 +40,7 @@ /* * PIM packet header */ -#define PIM_VERSION 2 +#define PIM_VERSION 2 struct pim { #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) u_char pim_type:4, /* the PIM message type, currently they are: @@ -52,17 +53,17 @@ struct pim { u_char pim_ver:4, /* PIM version */ pim_type:4; /* PIM type */ #endif - u_char pim_rsv; /* Reserved */ + u_char pim_rsv; /* Reserved */ u_short pim_cksum; /* IP style check sum */ }; -#define PIM_MINLEN 8 /* The header min. length is 8 */ -#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */ +#define PIM_MINLEN 8 /* The header min. length is 8 */ +#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */ /* * Message types */ -#define PIM_REGISTER 1 /* PIM Register type is 1 */ +#define PIM_REGISTER 1 /* PIM Register type is 1 */ /* second bit in reg_head is the null bit */ -#define PIM_NULL_REGISTER 0x40000000 +#define PIM_NULL_REGISTER 0x40000000 diff --git a/sys/netinet6/pim6_var.h b/sys/netinet6/pim6_var.h index 9423b7f..358a195d 100644 --- a/sys/netinet6/pim6_var.h +++ b/sys/netinet6/pim6_var.h @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: pim6_var.h,v 1.8 2000/06/06 08:07:43 jinmei Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: pim6_var.h,v 1.2 1999/08/01 15:58:13 itojun Exp $ */ #ifndef _NETINET6_PIM6_VAR_H_ #define _NETINET6_PIM6_VAR_H_ @@ -42,30 +42,29 @@ */ struct pim6stat { - u_int pim6s_rcv_total; /* total PIM messages received */ - u_int pim6s_rcv_tooshort; /* received with too few bytes */ - u_int pim6s_rcv_badsum; /* received with bad checksum */ - u_int pim6s_rcv_badversion; /* received bad PIM version */ - u_int pim6s_rcv_registers; /* received registers */ - u_int pim6s_rcv_badregisters; /* received invalid registers */ - u_int pim6s_snd_registers; /* sent registers */ + u_quad_t pim6s_rcv_total; /* total PIM messages received */ + u_quad_t pim6s_rcv_tooshort; /* received with too few bytes */ + u_quad_t pim6s_rcv_badsum; /* received with bad checksum */ + u_quad_t pim6s_rcv_badversion; /* received bad PIM version */ + u_quad_t pim6s_rcv_registers; /* received registers */ + u_quad_t pim6s_rcv_badregisters; /* received invalid registers */ + u_quad_t pim6s_snd_registers; /* sent registers */ }; -#ifdef _KERNEL -extern struct pim6stat pim6stat; +#if (defined(KERNEL)) || (defined(_KERNEL)) +extern struct pim6stat pim6stat; -int pim6_input __P((struct mbuf **, int*, int)); -#endif +int pim6_input __P((struct mbuf **, int*, int)); +#endif /* KERNEL */ /* * Names for PIM sysctl objects */ -#define PIMCTL_STATS 1 /* statistics (read-only) */ -#define PIMCTL_MAXID 2 +#define PIM6CTL_STATS 1 /* statistics (read-only) */ +#define PIM6CTL_MAXID 2 -#define PIMCTL_NAMES { \ +#define PIM6CTL_NAMES { \ { 0, 0 }, \ { 0, 0 }, \ } - #endif /* _NETINET6_PIM6_VAR_H_ */ diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 9fb94d5..a846b3c 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -85,13 +85,17 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/in_systm.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/ip6_mroute.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet/in_pcb.h> #include <netinet6/in6_pcb.h> #include <netinet6/nd6.h> +#include <netinet6/ip6protosw.h> +#ifdef ENABLE_DEFAULT_SCOPE +#include <netinet6/scope6_var.h> +#endif #ifdef IPSEC #include <netinet6/ipsec.h> @@ -209,6 +213,66 @@ rip6_input(mp, offp, proto) return IPPROTO_DONE; } +void +rip6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + struct sockaddr_in6 sa6; + struct ip6_hdr *ip6; + struct mbuf *m; + int off = 0; + void (*notify) __P((struct inpcb *, int)) = in6_rtchange; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + + if ((unsigned)cmd >= PRC_NCMDS) + return; + if (PRC_IS_REDIRECT(cmd)) + notify = in6_rtchange, d = NULL; + else if (cmd == PRC_HOSTDEAD) + d = NULL; + else if (inet6ctlerrmap[cmd] == 0) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + /* translate addresses into internal form */ + sa6 = *(struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) + sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + + if (ip6) { + /* + * XXX: We assume that when IPV6 is non NULL, + * M and OFF are valid. + */ + struct in6_addr s; + + /* translate addresses into internal form */ + memcpy(&s, &ip6->ip6_src, sizeof(s)); + if (IN6_IS_ADDR_LINKLOCAL(&s)) + s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + + (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, + 0, &s, 0, cmd, notify); + } else + (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, 0, + &zeroin6_addr, 0, cmd, notify); +} + /* * Generate IPv6 header and pass packet to ip6_output. * Tack on options user may have setup with control call. @@ -371,10 +435,10 @@ rip6_output(m, va_alist) } #ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)so; + ipsec_setsocket(m, so); #endif /*IPSEC*/ - error = ip6_output(m, optp, &in6p->in6p_route, IPV6_SOCKINMRCVIF, + error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions, &oifp); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (oifp) @@ -543,6 +607,11 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) return EADDRNOTAVAIL; +#ifdef ENABLE_DEFAULT_SCOPE + if (addr->sin6_scope_id == 0) { /* not change if specified */ + addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); + } +#endif if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) return EADDRNOTAVAIL; @@ -563,6 +632,9 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct in6_addr *in6a = NULL; int error = 0; +#ifdef ENABLE_DEFAULT_SCOPE + struct sockaddr_in6 tmp; +#endif if (nam->sa_len != sizeof(*addr)) return EINVAL; @@ -570,7 +642,14 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) return EADDRNOTAVAIL; if (addr->sin6_family != AF_INET6) return EAFNOSUPPORT; - +#ifdef ENABLE_DEFAULT_SCOPE + if (addr->sin6_scope_id == 0) { /* not change if specified */ + /* avoid overwrites */ + tmp = *addr; + addr = &tmp; + addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); + } +#endif /* Source address selection. XXX: need pcblookup? */ in6a = in6_selectsrc(addr, inp->in6p_outputopts, inp->in6p_moptions, &inp->in6p_route, @@ -598,6 +677,7 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct sockaddr_in6 tmp; struct sockaddr_in6 *dst; + /* always copy sockaddr to avoid overwrites */ if (so->so_state & SS_ISCONNECTED) { if (nam) { m_freem(m); @@ -615,8 +695,14 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, m_freem(m); return ENOTCONN; } - dst = (struct sockaddr_in6 *)nam; + tmp = *(struct sockaddr_in6 *)nam; + dst = &tmp; + } +#ifdef ENABLE_DEFAULT_SCOPE + if (dst->sin6_scope_id == 0) { /* not change if specified */ + dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); } +#endif return rip6_output(m, so, dst, control); } diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c index d50cf90..cbba9db 100644 --- a/sys/netinet6/route6.c +++ b/sys/netinet6/route6.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: route6.c,v 1.15 2000/06/23 16:18:20 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,25 +28,27 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include <sys/param.h> #include <sys/mbuf.h> #include <sys/socket.h> +#include <sys/systm.h> #include <net/if.h> #include <netinet/in.h> -#include <netinet6/in6.h> #include <netinet6/in6_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> -static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, struct ip6_rthdr0 *)); +static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, + struct ip6_rthdr0 *)); int route6_input(mp, offp, proto) @@ -55,27 +60,57 @@ route6_input(mp, offp, proto) register struct ip6_rthdr *rh; int off = *offp, rhlen; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); rh = (struct ip6_rthdr *)((caddr_t)ip6 + off); +#else + ip6 = mtod(m, struct ip6_hdr *); + IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); + if (rh == NULL) { + ip6stat.ip6s_tooshort++; + return IPPROTO_DONE; + } +#endif - switch(rh->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - rhlen = (rh->ip6r_len + 1) << 3; - IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); - if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) - return(IPPROTO_DONE); - break; - default: - /* unknown routing type */ - if (rh->ip6r_segleft == 0) { - rhlen = (rh->ip6r_len + 1) << 3; - break; /* Final dst. Just ignore the header. */ - } - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&rh->ip6r_type - (caddr_t)ip6); - return(IPPROTO_DONE); + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rhlen = (rh->ip6r_len + 1) << 3; +#ifndef PULLDOWN_TEST + /* + * note on option length: + * due to IP6_EXTHDR_CHECK assumption, we cannot handle + * very big routing header (max rhlen == 2048). + */ + IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); +#else + /* + * note on option length: + * maximum rhlen: 2048 + * max mbuf m_pulldown can handle: MCLBYTES == usually 2048 + * so, here we are assuming that m_pulldown can handle + * rhlen == 2048 case. this may not be a good thing to + * assume - we may want to avoid pulling it up altogether. + */ + IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen); + if (rh == NULL) { + ip6stat.ip6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) + return(IPPROTO_DONE); + break; + default: + /* unknown routing type */ + if (rh->ip6r_segleft == 0) { + rhlen = (rh->ip6r_len + 1) << 3; + break; /* Final dst. Just ignore the header. */ + } + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + (caddr_t)&rh->ip6r_type - (caddr_t)ip6); + return(IPPROTO_DONE); } *offp += rhlen; @@ -122,10 +157,25 @@ ip6_rthdr0(m, ip6, rh0) index = addrs - rh0->ip6r0_segleft; rh0->ip6r0_segleft--; - nextaddr = rh0->ip6r0_addr + index; + nextaddr = ((struct in6_addr *)(rh0 + 1)) + index; + /* + * reject invalid addresses. be proactive about malicious use of + * IPv4 mapped/compat address. + * XXX need more checks? + */ if (IN6_IS_ADDR_MULTICAST(nextaddr) || - IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + IN6_IS_ADDR_UNSPECIFIED(nextaddr) || + IN6_IS_ADDR_V4MAPPED(nextaddr) || + IN6_IS_ADDR_V4COMPAT(nextaddr)) { + ip6stat.ip6s_badoptions++; + m_freem(m); + return(-1); + } + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || + IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || + IN6_IS_ADDR_V4COMPAT(nextaddr)) { ip6stat.ip6s_badoptions++; m_freem(m); return(-1); diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c new file mode 100644 index 0000000..43a6fb7 --- /dev/null +++ b/sys/netinet6/scope6.c @@ -0,0 +1,302 @@ +/* $FreeBSD$ */ +/* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <net/route.h> +#include <net/if.h> + +#include <netinet/in.h> + +#include <netinet6/in6_var.h> +#include <netinet6/scope6_var.h> + +struct scope6_id { + /* + * 16 is correspondent to 4bit multicast scope field. + * i.e. from node-local to global with some reserved/unassigned types. + */ + u_int32_t s6id_list[16]; +}; +static size_t if_indexlim = 8; +struct scope6_id *scope6_ids = NULL; + +void +scope6_ifattach(ifp) + struct ifnet *ifp; +{ + int s = splnet(); + + /* + * We have some arrays that should be indexed by if_index. + * since if_index will grow dynamically, they should grow too. + */ + if (scope6_ids == NULL || if_index >= if_indexlim) { + size_t n; + caddr_t q; + + while (if_index >= if_indexlim) + if_indexlim <<= 1; + + /* grow scope index array */ + n = if_indexlim * sizeof(struct scope6_id); + /* XXX: need new malloc type? */ + q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); + if (scope6_ids) { + bcopy((caddr_t)scope6_ids, q, n/2); + free((caddr_t)scope6_ids, M_IFADDR); + } + scope6_ids = (struct scope6_id *)q; + } + +#define SID scope6_ids[ifp->if_index] + + /* don't initialize if called twice */ + if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { + splx(s); + return; + } + + /* + * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. + * Should we rather hardcode here? + */ + SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; +#ifdef MULTI_SCOPE + /* by default, we don't care about scope boundary for these scopes. */ + SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; + SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; +#endif +#undef SID + + splx(s); +} + +int +scope6_set(ifp, idlist) + struct ifnet *ifp; + u_int32_t *idlist; +{ + int i, s; + int error = 0; + + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + /* + * XXX: We need more consistency checks of the relationship among + * scopes (e.g. an organization should be larger than a site). + */ + + /* + * TODO(XXX): after setting, we should reflect the changes to + * interface addresses, routing table entries, PCB entries... + */ + + s = splnet(); + + for (i = 0; i < 16; i++) { + if (idlist[i] && + idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { + if (i == IPV6_ADDR_SCOPE_LINKLOCAL && + idlist[i] > if_index) { + /* + * XXX: theoretically, there should be no + * relationship between link IDs and interface + * IDs, but we check the consistency for + * safety in later use. + */ + splx(s); + return(EINVAL); + } + + /* + * XXX: we must need lots of work in this case, + * but we simply set the new value in this initial + * implementation. + */ + scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; + } + } + splx(s); + + return(error); +} + +int +scope6_get(ifp, idlist) + struct ifnet *ifp; + u_int32_t *idlist; +{ + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, + sizeof(scope6_ids[ifp->if_index].s6id_list)); + + return(0); +} + + +/* + * Get a scope of the address. Node-local, link-local, site-local or global. + */ +int +in6_addrscope(addr) +struct in6_addr *addr; +{ + int scope; + + if (addr->s6_addr8[0] == 0xfe) { + scope = addr->s6_addr8[1] & 0xc0; + + switch (scope) { + case 0x80: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case 0xc0: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ + break; + } + } + + + if (addr->s6_addr8[0] == 0xff) { + scope = addr->s6_addr8[1] & 0x0f; + + /* + * due to other scope such as reserved, + * return scope doesn't work. + */ + switch (scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return IPV6_ADDR_SCOPE_NODELOCAL; + break; + case IPV6_ADDR_SCOPE_LINKLOCAL: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; + break; + } + } + + if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { + if (addr->s6_addr8[15] == 1) /* loopback */ + return IPV6_ADDR_SCOPE_NODELOCAL; + if (addr->s6_addr8[15] == 0) /* unspecified */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } + + return IPV6_ADDR_SCOPE_GLOBAL; +} + +int +in6_addr2scopeid(ifp, addr) + struct ifnet *ifp; /* must not be NULL */ + struct in6_addr *addr; /* must not be NULL */ +{ + int scope = in6_addrscope(addr); + + if (scope6_ids == NULL) /* paranoid? */ + return(0); /* XXX */ + if (ifp->if_index >= if_indexlim) + return(0); /* XXX */ + +#define SID scope6_ids[ifp->if_index] + switch(scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return(-1); /* XXX: is this an appropriate value? */ + + case IPV6_ADDR_SCOPE_LINKLOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); + + case IPV6_ADDR_SCOPE_SITELOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); + + case IPV6_ADDR_SCOPE_ORGLOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); + + default: + return(0); /* XXX: treat as global. */ + } +#undef SID +} + +void +scope6_setdefault(ifp) + struct ifnet *ifp; /* note that this might be NULL */ +{ + /* + * Currently, this function just set the default "link" according to + * the given interface. + * We might eventually have to separate the notion of "link" from + * "interface" and provide a user interface to set the default. + */ + if (ifp) { + scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = + ifp->if_index; + } + else + scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; +} + +int +scope6_get_default(idlist) + u_int32_t *idlist; +{ + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + bcopy(scope6_ids[0].s6id_list, idlist, + sizeof(scope6_ids[0].s6id_list)); + + return(0); +} + +u_int32_t +scope6_addr2default(addr) + struct in6_addr *addr; +{ + return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); +} diff --git a/sys/netinet6/scope6_var.h b/sys/netinet6/scope6_var.h new file mode 100644 index 0000000..6e107d7 --- /dev/null +++ b/sys/netinet6/scope6_var.h @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ +/* $KAME: scope6_var.h,v 1.4 2000/05/18 15:03:27 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NETINET6_SCOPE6_VAR_H_ +#define _NETINET6_SCOPE6_VAR_H_ + +#ifdef _KERNEL +void scope6_ifattach __P((struct ifnet *)); +int scope6_set __P((struct ifnet *, u_int32_t *)); +int scope6_get __P((struct ifnet *, u_int32_t *)); +void scope6_setdefault __P((struct ifnet *)); +int scope6_get_default __P((u_int32_t *)); +u_int32_t scope6_in6_addrscope __P((struct in6_addr *)); +u_int32_t scope6_addr2default __P((struct in6_addr *)); +#endif /* _KERNEL */ + +#endif /* _NETINET6_SCOPE6_VAR_H_ */ diff --git a/sys/netinet6/tcp6_var.h b/sys/netinet6/tcp6_var.h index 820c49b..e283212 100644 --- a/sys/netinet6/tcp6_var.h +++ b/sys/netinet6/tcp6_var.h @@ -66,7 +66,7 @@ */ #ifndef _NETINET_TCP6_VAR_H_ -#define _NETINET_TCP6_VAR_H_ +#define _NETINET_TCP6_VAR_H_ #ifdef _KERNEL #ifdef SYSCTL_DECL diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c new file mode 100644 index 0000000..c6d831f --- /dev/null +++ b/sys/netinet6/udp6_output.c @@ -0,0 +1,285 @@ +/* $FreeBSD$ */ +/* $KAME: udp6_output.c,v 1.14 2000/06/13 10:31:23 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 + */ + +#include "opt_ipsec.h" +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/syslog.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/in_pcb.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#include <netinet6/in6_pcb.h> +#include <netinet6/udp6_var.h> +#include <netinet/icmp6.h> +#include <netinet6/ip6protosw.h> + +#ifdef IPSEC +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#endif /*IPSEC*/ + +#include "faith.h" + +#include <net/net_osdep.h> + +/* + * UDP protocol inplementation. + * Per RFC 768, August, 1980. + */ + +#define in6pcb inpcb +#define udp6stat udpstat +#define udp6s_opackets udps_opackets + +int +udp6_output(in6p, m, addr6, control, p) + register struct in6pcb *in6p; + register struct mbuf *m; + struct mbuf *control; + struct sockaddr *addr6; + struct proc *p; +{ + register u_int32_t ulen = m->m_pkthdr.len; + u_int32_t plen = sizeof(struct udphdr) + ulen; + struct ip6_hdr *ip6; + struct udphdr *udp6; + struct in6_addr *laddr, *faddr; + u_short fport; + int error = 0; + struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; + int priv; + int af, hlen; + int flags; + struct sockaddr_in6 tmp; + + priv = 0; + if (p && !suser(p)) + priv = 1; + if (control) { + if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + goto release; + in6p->in6p_outputopts = &opt; + } + + if (addr6) { + /* + * IPv4 version of udp_output calls in_pcbconnect in this case, + * which needs splnet and affects performance. + * Since we saw no essential reason for calling in_pcbconnect, + * we get rid of such kind of logic, and call in6_selectsrc + * and in6_pcbsetport in order to fill in the local address + * and the local port. + */ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6; + if (sin6->sin6_port == 0) { + error = EADDRNOTAVAIL; + goto release; + } + + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + error = EISCONN; + goto release; + } + + /* protect *sin6 from overwrites */ + tmp = *sin6; + sin6 = &tmp; + + faddr = &sin6->sin6_addr; + fport = sin6->sin6_port; /* allow 0 port */ + + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { + error = EINVAL; + goto release; + } + + if (!IN6_IS_ADDR_V4MAPPED(faddr)) { + laddr = in6_selectsrc(sin6, in6p->in6p_outputopts, + in6p->in6p_moptions, + &in6p->in6p_route, + &in6p->in6p_laddr, &error); + } else + laddr = &in6p->in6p_laddr; /*XXX*/ + if (laddr == NULL) { + if (error == 0) + error = EADDRNOTAVAIL; + goto release; + } + if (in6p->in6p_lport == 0 && + (error = in6_pcbsetport(laddr, in6p, p)) != 0) + goto release; + } else { + if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + error = ENOTCONN; + goto release; + } + laddr = &in6p->in6p_laddr; + faddr = &in6p->in6p_faddr; + fport = in6p->in6p_fport; + } + + if (!IN6_IS_ADDR_V4MAPPED(faddr)) { + af = AF_INET6; + hlen = sizeof(struct ip6_hdr); + } else { + af = AF_INET; + hlen = sizeof(struct ip); + } + + /* + * Calculate data length and get a mbuf + * for UDP and IP6 headers. + */ + M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT); + if (m == 0) { + error = ENOBUFS; + goto release; + } + + /* + * Stuff checksum and output datagram. + */ + udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); + udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */ + udp6->uh_dport = fport; + if (plen <= 0xffff) + udp6->uh_ulen = htons((u_short)plen); + else + udp6->uh_ulen = 0; + udp6->uh_sum = 0; + + switch (af) { + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; +#if 0 /* ip6_plen will be filled in ip6_output. */ + ip6->ip6_plen = htons((u_short)plen); +#endif + ip6->ip6_nxt = IPPROTO_UDP; + ip6->ip6_hlim = in6_selecthlim(in6p, + in6p->in6p_route.ro_rt ? + in6p->in6p_route.ro_rt->rt_ifp : NULL); + ip6->ip6_src = *laddr; + ip6->ip6_dst = *faddr; + + if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, + sizeof(struct ip6_hdr), plen)) == 0) { + udp6->uh_sum = 0xffff; + } + + flags = 0; + + udp6stat.udp6s_opackets++; +#ifdef IPSEC + ipsec_setsocket(m, in6p->in6p_socket); +#endif /*IPSEC*/ + error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, + flags, in6p->in6p_moptions, NULL); + break; + case AF_INET: + error = EAFNOSUPPORT; + goto release; + } + goto releaseopt; + +release: + m_freem(m); + +releaseopt: + if (control) { + in6p->in6p_outputopts = stickyopt; + m_freem(control); + } + return(error); +} diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 99032b9..beda1e9 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: udp6_usrreq.c,v 1.11 2000/06/18 06:23:06 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -60,9 +63,10 @@ * SUCH DAMAGE. * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -90,10 +94,10 @@ #include <netinet/ip_var.h> #include <netinet/udp.h> #include <netinet/udp_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/in6_pcb.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet6/udp6_var.h> #include <netinet6/ip6protosw.h> @@ -402,15 +406,20 @@ udp6_ctlinput(cmd, sa, d) struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; - int off; + int off = 0; + void (*notify) __P((struct inpcb *, int)) = udp_notify; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) return; - off = 0; - if (!PRC_IS_REDIRECT(cmd) && - ((unsigned)cmd >= PRC_NCMDS || inet6ctlerrmap[cmd] == 0)) + if ((unsigned)cmd >= PRC_NCMDS) + return; + if (PRC_IS_REDIRECT(cmd)) + notify = in6_rtchange, d = NULL; + else if (cmd == PRC_HOSTDEAD) + d = NULL; + else if (inet6ctlerrmap[cmd] == 0) return; /* if the parameter is from icmp6, decode it. */ @@ -426,7 +435,7 @@ udp6_ctlinput(cmd, sa, d) /* translate addresses into internal form */ sa6 = *(struct sockaddr_in6 *)sa; - if (m != NULL && IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (ip6) { @@ -452,10 +461,10 @@ udp6_ctlinput(cmd, sa, d) uhp = (struct udphdr *)(mtod(m, caddr_t) + off); (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, uhp->uh_dport, &s, - uhp->uh_sport, cmd, udp_notify); + uhp->uh_sport, cmd, notify); } else (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, udp_notify); + &zeroin6_addr, 0, cmd, notify); } static int @@ -468,6 +477,11 @@ udp6_getcred(SYSCTL_HANDLER_ARGS) error = suser(req->p); if (error) return (error); + + if (req->newlen != sizeof(addrs)) + return (EINVAL); + if (req->oldlen != sizeof(struct ucred)) + return (EINVAL); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); @@ -492,121 +506,6 @@ SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, udp6_getcred, "S,ucred", "Get the ucred of a UDP6 connection"); -int -udp6_output(in6p, m, addr6, control, p) - register struct inpcb *in6p; - struct mbuf *m; - struct sockaddr *addr6; - struct mbuf *control; - struct proc *p; -{ - register int ulen = m->m_pkthdr.len; - int plen = sizeof(struct udphdr) + ulen; - struct ip6_hdr *ip6; - struct udphdr *udp6; - struct in6_addr laddr6; - int s = 0, error = 0; - struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; - - if (control) { - if ((error = ip6_setpktoptions(control, &opt, suser(p))) != 0) - goto release; - in6p->in6p_outputopts = &opt; - } - - if (addr6) { - laddr6 = in6p->in6p_laddr; - if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = EISCONN; - goto release; - } - /* - * Must block input while temporarily connected. - */ - s = splnet(); - /* - * XXX: the user might want to overwrite the local address - * via an ancillary data. - */ - bzero(&in6p->in6p_laddr, sizeof(struct in6_addr)); - error = in6_pcbconnect(in6p, addr6, p); - if (error) { - splx(s); - goto release; - } - } else { - if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = ENOTCONN; - goto release; - } - } - /* - * Calculate data length and get a mbuf - * for UDP and IP6 headers. - */ - M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct udphdr), - M_DONTWAIT); - if (m == 0) { - error = ENOBUFS; - if (addr6) - splx(s); - goto release; - } - - /* - * Stuff checksum and output datagram. - */ - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | - (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); - ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | - (IPV6_VERSION & IPV6_VERSION_MASK); - /* ip6_plen will be filled in ip6_output. */ - ip6->ip6_nxt = IPPROTO_UDP; - ip6->ip6_hlim = in6_selecthlim(in6p, - in6p->in6p_route.ro_rt ? - in6p->in6p_route.ro_rt->rt_ifp : - NULL); - ip6->ip6_src = in6p->in6p_laddr; - ip6->ip6_dst = in6p->in6p_faddr; - - udp6 = (struct udphdr *)(ip6 + 1); - udp6->uh_sport = in6p->in6p_lport; - udp6->uh_dport = in6p->in6p_fport; - udp6->uh_ulen = htons((u_short)plen); - udp6->uh_sum = 0; - - if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, - sizeof(struct ip6_hdr), plen)) == 0) { - udp6->uh_sum = 0xffff; - } - - udpstat.udps_opackets++; - -#ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)in6p->in6p_socket; -#endif /*IPSEC*/ - error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - IPV6_SOCKINMRCVIF, in6p->in6p_moptions, NULL); - - if (addr6) { - in6_pcbdisconnect(in6p); - in6p->in6p_laddr = laddr6; - splx(s); - } - goto releaseopt; - -release: - m_freem(m); - -releaseopt: - if (control) { - in6p->in6p_outputopts = stickyopt; - m_freem(control); - } - return(error); -} - static int udp6_abort(struct socket *so) { @@ -732,10 +631,16 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) return error; } } + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) return EISCONN; s = splnet(); error = in6_pcbconnect(inp, nam, p); + if (ip6_auto_flowlabel) { + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; + inp->in6p_flowinfo |= + (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); + } splx(s); if (error == 0) { inp->inp_vflag &= ~INP_IPV4; @@ -793,14 +698,26 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct inpcb *inp; + int error = 0; inp = sotoinpcb(so); if (inp == 0) { - m_freem(m); - return EINVAL; + error = EINVAL; + goto bad; } - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + if (addr) { + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + error = EINVAL; + goto bad; + } + if (addr->sa_family != AF_INET6) { + error = EAFNOSUPPORT; + goto bad; + } + } + + if (ip6_mapped_addr_on) { int hasv4addr; struct sockaddr_in6 *sin6 = 0; @@ -813,7 +730,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } if (hasv4addr) { struct pr_usrreqs *pru; - int error; if (sin6) in6_sin6_2_sin_in_sock(addr); @@ -826,6 +742,10 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } return udp6_output(inp, m, addr, control, p); + + bad: + m_freem(m); + return(error); } struct pr_usrreqs udp6_usrreqs = { diff --git a/sys/netinet6/udp6_var.h b/sys/netinet6/udp6_var.h index efadb7f..5c3efbc 100644 --- a/sys/netinet6/udp6_var.h +++ b/sys/netinet6/udp6_var.h @@ -65,7 +65,7 @@ */ #ifndef _NETINET6_UDP6_VAR_H_ -#define _NETINET6_UDP6_VAR_H_ +#define _NETINET6_UDP6_VAR_H_ #ifdef _KERNEL SYSCTL_DECL(_net_inet6_udp6); |