diff options
author | Hajimu UMEMOTO <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
---|---|---|
committer | Hajimu UMEMOTO <ume@FreeBSD.org> | 2001-06-11 12:39:29 +0000 |
commit | 33841545909f4a4ee94aa148b3a9cbcdc1abb02a (patch) | |
tree | a79fc7ad2b97862c4a404f352f0211ad93a7b5f1 /sys/netinet6 | |
parent | 52ebde4fbaab8a8b79de6b17892943783abec7be (diff) |
Sync with recent KAME.
This work was based on kame-20010528-freebsd43-snap.tgz and some
critical problem after the snap was out were fixed.
There are many many changes since last KAME merge.
TODO:
- The definitions of SADB_* in sys/net/pfkeyv2.h are still different
from RFC2407/IANA assignment because of binary compatibility
issue. It should be fixed under 5-CURRENT.
- ip6po_m member of struct ip6_pktopts is no longer used. But, it
is still there because of binary compatibility issue. It should
be removed under 5-CURRENT.
Reviewed by: itojun
Obtained from: KAME
MFC after: 3 weeks
Notes
Notes:
svn path=/head/; revision=78064
Diffstat (limited to 'sys/netinet6')
58 files changed, 8603 insertions, 5329 deletions
diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h index 3c7d0a671ae2..084646677f28 100644 --- a/sys/netinet6/ah.h +++ b/sys/netinet6/ah.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,7 +37,9 @@ #ifndef _NETINET6_AH_H_ #define _NETINET6_AH_H_ -struct secasvar; +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif struct ah { u_int8_t ah_nxt; /* Next Header */ @@ -56,6 +58,9 @@ struct newah { /* variable size, 32bit bound*/ /* Authentication data */ }; +#ifdef _KERNEL +struct secasvar; + struct ah_algorithm_state { struct secasvar *sav; void* foo; /*per algorithm data - maybe*/ @@ -74,8 +79,7 @@ struct ah_algorithm { #define AH_MAXSUMSIZE 16 -#ifdef _KERNEL -extern struct ah_algorithm ah_algorithms[]; +extern const struct ah_algorithm *ah_algorithm_lookup __P((int)); /* cksum routines */ extern int ah_hdrlen __P((struct secasvar *)); @@ -84,7 +88,7 @@ extern size_t ah_hdrsiz __P((struct ipsecrequest *)); 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, size_t, - struct ah_algorithm *, struct secasvar *)); + const struct ah_algorithm *, struct secasvar *)); #endif /*_KERNEL*/ #endif /*_NETINET6_AH_H_*/ diff --git a/sys/netinet6/ah6.h b/sys/netinet6/ah6.h index 4010f1335cb8..ead07bfdadb0 100644 --- a/sys/netinet6/ah6.h +++ b/sys/netinet6/ah6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -44,7 +44,9 @@ 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, size_t, - struct ah_algorithm *, struct secasvar *)); + const struct ah_algorithm *, struct secasvar *)); + +extern void ah6_ctlinput __P((int, struct sockaddr *, void *)); #endif #endif /*_NETINET6_AH6_H_*/ diff --git a/sys/netinet6/ah_core.c b/sys/netinet6/ah_core.c index 477de5117b71..5c7afaf8d5fe 100644 --- a/sys/netinet6/ah_core.c +++ b/sys/netinet6/ah_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_core.c,v 1.35 2000/06/14 11:14:03 itojun Exp $ */ +/* $KAME: ah_core.c,v 1.44 2001/03/12 11:24:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -34,6 +34,8 @@ * RFC1826/2402 authentication header. */ +/* TODO: have shared routines for hmac-* algorithms */ + #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" @@ -82,6 +84,7 @@ #include <netkey/keydb.h> #include <sys/md5.h> #include <crypto/sha1.h> +#include <crypto/sha2/sha2.h> #include <net/net_osdep.h> @@ -90,8 +93,7 @@ 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 int ah_none_init __P((struct ah_algorithm_state *, - struct secasvar *)); +static int ah_none_init __P((struct ah_algorithm_state *, struct secasvar *)); 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 *)); @@ -118,25 +120,84 @@ static int ah_hmac_sha1_init __P((struct ah_algorithm_state *, 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 int ah_hmac_sha2_256_mature __P((struct secasvar *)); +static int ah_hmac_sha2_256_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_256_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_256_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha2_384_mature __P((struct secasvar *)); +static int ah_hmac_sha2_384_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_384_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_384_result __P((struct ah_algorithm_state *, caddr_t)); +static int ah_hmac_sha2_512_mature __P((struct secasvar *)); +static int ah_hmac_sha2_512_init __P((struct ah_algorithm_state *, + struct secasvar *)); +static void ah_hmac_sha2_512_loop __P((struct ah_algorithm_state *, caddr_t, + size_t)); +static void ah_hmac_sha2_512_result __P((struct ah_algorithm_state *, caddr_t)); + +static void ah_update_mbuf __P((struct mbuf *, int, int, + const struct ah_algorithm *, struct ah_algorithm_state *)); + +const struct ah_algorithm * +ah_algorithm_lookup(idx) + int idx; +{ + /* checksum algorithms */ + static struct ah_algorithm ah_algorithms[] = { + { 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, "hmac-sha1", + ah_hmac_sha1_init, ah_hmac_sha1_loop, + ah_hmac_sha1_result, }, + { 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, "keyed-sha1", + ah_keyed_sha1_init, ah_keyed_sha1_loop, + ah_keyed_sha1_result, }, + { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", + ah_none_init, ah_none_loop, ah_none_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_256_mature, 256, 256, + "hmac-sha2-256", + ah_hmac_sha2_256_init, ah_hmac_sha2_256_loop, + ah_hmac_sha2_256_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_384_mature, 384, 384, + "hmac-sha2-384", + ah_hmac_sha2_384_init, ah_hmac_sha2_384_loop, + ah_hmac_sha2_384_result, }, + { ah_sumsiz_1216, ah_hmac_sha2_512_mature, 512, 512, + "hmac-sha2-512", + ah_hmac_sha2_512_init, ah_hmac_sha2_512_loop, + ah_hmac_sha2_512_result, }, + }; + + switch (idx) { + case SADB_AALG_MD5HMAC: + return &ah_algorithms[0]; + case SADB_AALG_SHA1HMAC: + return &ah_algorithms[1]; + case SADB_X_AALG_MD5: + return &ah_algorithms[2]; + case SADB_X_AALG_SHA: + return &ah_algorithms[3]; + case SADB_X_AALG_NULL: + return &ah_algorithms[4]; + case SADB_X_AALG_SHA2_256: + return &ah_algorithms[5]; + case SADB_X_AALG_SHA2_384: + return &ah_algorithms[6]; + case SADB_X_AALG_SHA2_512: + return &ah_algorithms[7]; + default: + return NULL; + } +} -static void ah_update_mbuf __P((struct mbuf *, int, int, struct ah_algorithm *, - struct ah_algorithm_state *)); - -/* checksum algorithms */ -/* NOTE: The order depends on SADB_AALG_x in net/pfkeyv2.h */ -struct ah_algorithm ah_algorithms[] = { - { 0, 0, 0, 0, 0, 0, }, - { 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, "hmac-sha1", - ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, }, - { 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, "keyed-sha1", - ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, }, - { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", - ah_none_init, ah_none_loop, ah_none_result, }, -}; static int ah_sumsiz_1216(sav) @@ -297,13 +358,19 @@ static int ah_keyed_sha1_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_keyed_sha1_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -385,7 +452,7 @@ ah_keyed_sha1_loop(state, addr, len) panic("ah_keyed_sha1_loop: what?"); ctxt = (SHA1_CTX *)state->foo; - SHA1Update(ctxt, (u_int8_t *)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void @@ -414,13 +481,19 @@ static int ah_hmac_md5_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_hmac_md5_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -532,13 +605,19 @@ static int ah_hmac_sha1_mature(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; if (!sav->key_auth) { ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n")); return 1; } - algo = &ah_algorithms[sav->alg_auth]; + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah_hmac_sha1_mature: unsupported algorithm.\n")); + return 1; + } + if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { ipseclog((LOG_ERR, @@ -579,7 +658,7 @@ ah_hmac_sha1_init(state, sav) /* compress the key if necessery */ if (64 < _KEYLEN(state->sav->key_auth)) { SHA1Init(ctxt); - SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), + SHA1Update(ctxt, _KEYBUF(state->sav->key_auth), _KEYLEN(state->sav->key_auth)); SHA1Final(&tk[0], ctxt); key = &tk[0]; @@ -616,7 +695,7 @@ ah_hmac_sha1_loop(state, addr, len) panic("ah_hmac_sha1_loop: what?"); ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128); - SHA1Update(ctxt, (u_int8_t *)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void @@ -640,7 +719,7 @@ ah_hmac_sha1_result(state, addr) SHA1Init(ctxt); SHA1Update(ctxt, opad, 64); - SHA1Update(ctxt, (u_int8_t *)&digest[0], sizeof(digest)); + SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); SHA1Final((caddr_t)&digest[0], ctxt); bcopy(&digest[0], (void *)addr, HMACSIZE); @@ -648,6 +727,404 @@ ah_hmac_sha1_result(state, addr) free(state->foo, M_TEMP); } +static int +ah_hmac_sha2_256_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_256_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_256_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA256_CTX *ctxt; + u_char tk[SHA256_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_256_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA256_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA256_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA256_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_256_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA256_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_256_loop: what?"); + + ctxt = (SHA256_CTX *)(((u_char *)state->foo) + 128); + SHA256_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_256_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA256_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA256_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_256_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA256_CTX *)(opad + 64); + + SHA256_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA256_Init(ctxt); + SHA256_Update(ctxt, opad, 64); + SHA256_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA256_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_sha2_384_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_384_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_384_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA384_CTX *ctxt; + u_char tk[SHA384_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_384_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA384_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + bzero(state->foo, 64 + 64 + sizeof(SHA384_CTX)); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA384_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA384_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_384_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA384_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_384_loop: what?"); + + ctxt = (SHA384_CTX *)(((u_char *)state->foo) + 128); + SHA384_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_384_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA384_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA384_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_384_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA384_CTX *)(opad + 64); + + SHA384_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA384_Init(ctxt); + SHA384_Update(ctxt, opad, 64); + SHA384_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA384_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + +static int +ah_hmac_sha2_512_mature(sav) + struct secasvar *sav; +{ + const struct ah_algorithm *algo; + + if (!sav->key_auth) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: no key is given.\n")); + return 1; + } + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: unsupported algorithm.\n")); + return 1; + } + + if (sav->key_auth->sadb_key_bits < algo->keymin || + algo->keymax < sav->key_auth->sadb_key_bits) { + ipseclog((LOG_ERR, + "ah_hmac_sha2_512_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); + return 1; + } + + return 0; +} + +static int +ah_hmac_sha2_512_init(state, sav) + struct ah_algorithm_state *state; + struct secasvar *sav; +{ + u_char *ipad; + u_char *opad; + SHA512_CTX *ctxt; + u_char tk[SHA512_DIGEST_LENGTH]; + u_char *key; + size_t keylen; + size_t i; + + if (!state) + panic("ah_hmac_sha2_512_init: what?"); + + state->sav = sav; + state->foo = (void *)malloc(64 + 64 + sizeof(SHA512_CTX), + M_TEMP, M_NOWAIT); + if (!state->foo) + return ENOBUFS; + bzero(state->foo, 64 + 64 + sizeof(SHA512_CTX)); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA512_CTX *)(opad + 64); + + /* compress the key if necessery */ + if (64 < _KEYLEN(state->sav->key_auth)) { + bzero(tk, sizeof(tk)); + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, _KEYBUF(state->sav->key_auth), + _KEYLEN(state->sav->key_auth)); + SHA512_Final(&tk[0], ctxt); + key = &tk[0]; + keylen = sizeof(tk) < 64 ? sizeof(tk) : 64; + } else { + key = _KEYBUF(state->sav->key_auth); + keylen = _KEYLEN(state->sav->key_auth); + } + + bzero(ipad, 64); + bzero(opad, 64); + bcopy(key, ipad, keylen); + bcopy(key, opad, keylen); + for (i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, ipad, 64); + + return 0; +} + +static void +ah_hmac_sha2_512_loop(state, addr, len) + struct ah_algorithm_state *state; + caddr_t addr; + size_t len; +{ + SHA512_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_512_loop: what?"); + + ctxt = (SHA512_CTX *)(((u_char *)state->foo) + 128); + SHA512_Update(ctxt, (caddr_t)addr, (size_t)len); +} + +static void +ah_hmac_sha2_512_result(state, addr) + struct ah_algorithm_state *state; + caddr_t addr; +{ + u_char digest[SHA512_DIGEST_LENGTH]; + u_char *ipad; + u_char *opad; + SHA512_CTX *ctxt; + + if (!state || !state->foo) + panic("ah_hmac_sha2_512_result: what?"); + + ipad = (u_char *)state->foo; + opad = (u_char *)(ipad + 64); + ctxt = (SHA512_CTX *)(opad + 64); + + SHA512_Final((caddr_t)&digest[0], ctxt); + + bzero(ctxt, sizeof(*ctxt)); + SHA512_Init(ctxt); + SHA512_Update(ctxt, opad, 64); + SHA512_Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); + SHA512_Final((caddr_t)&digest[0], ctxt); + + bcopy(&digest[0], (void *)addr, HMACSIZE); + + free(state->foo, M_TEMP); +} + /*------------------------------------------------------------*/ /* @@ -658,7 +1135,7 @@ ah_update_mbuf(m, off, len, algo, algos) struct mbuf *m; int off; int len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct ah_algorithm_state *algos; { struct mbuf *n; @@ -695,6 +1172,7 @@ ah_update_mbuf(m, off, len, algo, algos) } } +#ifdef INET /* * Go generate the checksum. This function won't modify the mbuf chain * except AH itself. @@ -707,7 +1185,7 @@ ah4_calccksum(m, ahdat, len, algo, sav) struct mbuf *m; caddr_t ahdat; size_t len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct secasvar *sav; { int off; @@ -935,6 +1413,7 @@ fail: m_free(n); return error; } +#endif #ifdef INET6 /* @@ -949,7 +1428,7 @@ ah6_calccksum(m, ahdat, len, algo, sav) struct mbuf *m; caddr_t ahdat; size_t len; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; struct secasvar *sav; { int newoff, off; diff --git a/sys/netinet6/ah_input.c b/sys/netinet6/ah_input.c index b0489d1d5e35..070883e7a624 100644 --- a/sys/netinet6/ah_input.c +++ b/sys/netinet6/ah_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_input.c,v 1.29 2000/05/29 08:33:53 itojun Exp $ */ +/* $KAME: ah_input.c,v 1.59 2001/05/16 04:01:27 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,7 +66,9 @@ #ifdef INET6 #include <netinet/ip6.h> #include <netinet6/ip6_var.h> +#include <netinet6/in6_pcb.h> #include <netinet/icmp6.h> +#include <netinet6/ip6protosw.h> #endif #include <netinet6/ipsec.h> @@ -107,16 +109,16 @@ ah4_input(m, va_alist) struct ip *ip; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; size_t hlen; - int s; int off, proto; va_list ap; + size_t stripsiz = 0; va_start(ap, m); off = va_arg(ap, int); @@ -175,16 +177,16 @@ ah4_input(m, va_alist) ipsecstat.in_badspi++; goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv4 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -284,29 +286,23 @@ ah4_input(m, va_alist) goto fail; } - { -#if 1 /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. */ ip->ip_len = htons(ip->ip_len + hlen); ip->ip_off = htons(ip->ip_off); -#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_off = ntohs(ip->ip_off); -#endif - } { caddr_t sumpos = NULL; @@ -397,7 +393,14 @@ ah4_input(m, va_alist) } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav)) { /* * strip off all the headers that precedes AH. * IP xx AH IP' payload -> IP' payload @@ -405,17 +408,9 @@ ah4_input(m, va_alist) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; u_int8_t tos; tos = ip->ip_tos; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); @@ -469,6 +464,11 @@ ah4_input(m, va_alist) #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) { + ipsecstat.in_nomem++; + goto fail; + } if (! IF_HANDOFF(&ipintrq, m, NULL)) { ipsecstat.in_inval++; @@ -482,15 +482,6 @@ ah4_input(m, va_alist) /* * strip off AH. */ - size_t stripsiz = 0; - - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } ip = mtod(m, struct ip *); #ifndef PULLDOWN_TEST @@ -548,10 +539,19 @@ ah4_input(m, va_alist) /* forget about IP hdr checksum, the check has already been passed */ key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + ipsecstat.in_nomem++; + goto fail; + } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto fail; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; } @@ -587,12 +587,13 @@ ah6_input(mp, offp, proto) struct ip6_hdr *ip6; struct ah *ah; u_int32_t spi; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; + size_t stripsiz = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); @@ -601,7 +602,7 @@ ah6_input(mp, offp, proto) 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++; + ipsec6stat.in_inval++; return IPPROTO_DONE; } #endif @@ -637,16 +638,16 @@ ah6_input(mp, offp, proto) ipsec6stat.in_badspi++; goto fail; } - if (sav->alg_auth == SADB_AALG_NONE) { + + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_DEBUG, "IPv6 AH input: " - "unspecified authentication algorithm for spi %u\n", + "unsupported authentication algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } - algo = &ah_algorithms[sav->alg_auth]; - siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); @@ -685,7 +686,7 @@ ah6_input(mp, offp, proto) sizeof(struct ah) + sizoff + siz1); if (ah == NULL) { ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); - ipsecstat.in_inval++; + ipsec6stat.in_inval++; m = NULL; goto fail; } @@ -808,7 +809,14 @@ ah6_input(mp, offp, proto) } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + stripsiz = sizeof(struct ah) + siz1; + } else { + /* RFC 2402 */ + stripsiz = sizeof(struct newah) + siz1; + } + if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) { /* * strip off all the headers that precedes AH. * IP6 xx AH IP6' payload -> IP6' payload @@ -816,17 +824,9 @@ ah6_input(mp, offp, proto) * XXX more sanity checks * XXX relationship with gif? */ - size_t stripsiz = 0; u_int32_t flowinfo; /*net endian*/ flowinfo = ip6->ip6_flow; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip6)) { /* @@ -870,6 +870,11 @@ ah6_input(mp, offp, proto) #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } if (! IF_HANDOFF(&ip6intrq, m, NULL)) { ipsec6stat.in_inval++; @@ -883,7 +888,6 @@ ah6_input(mp, offp, proto) /* * strip off AH. */ - size_t stripsiz = 0; char *prvnxtp; /* @@ -894,14 +898,6 @@ ah6_input(mp, offp, proto) prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ *prvnxtp = nxt; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - stripsiz = sizeof(struct ah) + siz1; - } else { - /* RFC 2402 */ - stripsiz = sizeof(struct newah) + siz1; - } - ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST /* @@ -945,6 +941,10 @@ ah6_input(mp, offp, proto) ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } } *offp = off; @@ -968,4 +968,92 @@ fail: m_freem(m); return IPPROTO_DONE; } + +void +ah6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + const struct newah *ahp; + struct newah ah; + struct secasvar *sav; + struct ip6_hdr *ip6; + struct mbuf *m; + struct ip6ctlparam *ip6cp = NULL; + int off; + struct sockaddr_in6 sa6_src, sa6_dst; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if ((unsigned)cmd >= PRC_NCMDS) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + if (ip6) { + /* + * XXX: We assume that when ip6 is non NULL, + * M and OFF are valid. + */ + + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(ah)) + return; + + if (m->m_len < off + sizeof(ah)) { + /* + * this should be rare case, + * so we compromise on this copy... + */ + m_copydata(m, off, sizeof(ah), (caddr_t)&ah); + ahp = &ah; + } else + ahp = (struct newah *)(mtod(m, caddr_t) + off); + + if (cmd == PRC_MSGSIZE) { + int valid = 0; + + /* + * Check to see if we have a valid SA corresponding to + * the address in the ICMP message payload. + */ + sav = key_allocsa(AF_INET6, + (caddr_t)&sa6_src.sin6_addr, + (caddr_t)&sa6_dst.sin6_addr, + IPPROTO_AH, ahp->ah_spi); + if (sav) { + if (sav->state == SADB_SASTATE_MATURE || + sav->state == SADB_SASTATE_DYING) + valid++; + key_freesav(sav); + } + + /* XXX Further validation? */ + + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + } + + /* we normally notify single pcb here */ + } else { + /* we normally notify any pcb here */ + } +} #endif /* INET6 */ diff --git a/sys/netinet6/ah_output.c b/sys/netinet6/ah_output.c index 59263cd1b05e..983ecab95196 100644 --- a/sys/netinet6/ah_output.c +++ b/sys/netinet6/ah_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ah_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -76,7 +76,9 @@ #include <net/net_osdep.h> +#ifdef INET static struct in_addr *ah4_finaldst __P((struct mbuf *)); +#endif /* * compute AH header size. @@ -87,7 +89,7 @@ size_t ah_hdrsiz(isr) struct ipsecrequest *isr; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t hdrsiz; /* sanity check */ @@ -104,7 +106,7 @@ ah_hdrsiz(isr) goto estimate; /* we need transport mode AH. */ - algo = &ah_algorithms[isr->sav->alg_auth]; + algo = ah_algorithm_lookup(isr->sav->alg_auth); if (!algo) goto estimate; @@ -131,6 +133,7 @@ ah_hdrsiz(isr) return sizeof(struct newah) + 16; } +#ifdef INET /* * Modify the packet so that it includes the authentication data. * The mbuf passed must start with IPv4 header. @@ -144,7 +147,7 @@ ah4_output(m, isr) struct ipsecrequest *isr; { struct secasvar *sav = isr->sav; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; u_int32_t spi; u_char *ahdrpos; u_char *ahsumpos = NULL; @@ -171,7 +174,14 @@ ah4_output(m, isr) return EINVAL; } - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } spi = sav->spi; /* @@ -314,16 +324,19 @@ ah4_output(m, isr) return 0; } +#endif /* Calculate AH length */ int ah_hdrlen(sav) struct secasvar *sav; { - struct ah_algorithm *algo; + const struct ah_algorithm *algo; int plen, ahlen; - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) + return 0; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ @@ -351,7 +364,7 @@ ah6_output(m, nexthdrp, md, isr) struct mbuf *mprev; struct mbuf *mah; struct secasvar *sav = isr->sav; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; u_int32_t spi; u_char *ahsumpos = NULL; size_t plen; /*AH payload size in bytes*/ @@ -414,7 +427,14 @@ ah6_output(m, nexthdrp, md, isr) return EINVAL; } - algo = &ah_algorithms[sav->alg_auth]; + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { + ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + ipsec6stat.out_inval++; + m_freem(m); + return EINVAL; + } spi = sav->spi; /* @@ -447,7 +467,7 @@ ah6_output(m, nexthdrp, md, isr) ipseclog((LOG_WARNING, "replay counter overflowed. %s\n", ipsec_logsastr(sav))); - ipsecstat.out_inval++; + ipsec6stat.out_inval++; m_freem(m); return EINVAL; } @@ -479,6 +499,7 @@ ah6_output(m, nexthdrp, md, isr) } #endif +#ifdef INET /* * Find the final destination if there is loose/strict source routing option. * Returns NULL if there's no source routing options. @@ -564,3 +585,4 @@ ah4_finaldst(m) } return NULL; } +#endif diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c index 8d3987cb5476..8ecde86f1ac6 100644 --- a/sys/netinet6/dest6.c +++ b/sys/netinet6/dest6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: dest6.c,v 1.12 2000/05/05 11:00:57 sumikawa Exp $ */ +/* $KAME: dest6.c,v 1.27 2001/03/29 05:34:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,12 +35,14 @@ #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/kernel.h> #include <net/if.h> #include <net/route.h> @@ -59,10 +61,13 @@ dest6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { - register struct mbuf *m = *mp; + struct mbuf *m = *mp; int off = *offp, dstoptlen, optlen; struct ip6_dest *dstopts; u_int8_t *opt; + struct ip6_hdr *ip6; + + ip6 = mtod(m, struct ip6_hdr *); /* validation of the length of the header */ #ifndef PULLDOWN_TEST @@ -102,19 +107,21 @@ dest6_input(mp, offp, proto) case IP6OPT_PADN: optlen = *(opt + 1) + 2; break; - default: /* unknown option */ - if ((optlen = ip6_unknown_opt(opt, m, - opt-mtod(m, u_int8_t *))) == -1) - return(IPPROTO_DONE); - optlen += 2; - break; + + default: /* unknown option */ + optlen = ip6_unknown_opt(opt, m, + opt - mtod(m, u_int8_t *)); + if (optlen == -1) + return (IPPROTO_DONE); + optlen += 2; + break; } } *offp = off; - return(dstopts->ip6d_nxt); + return (dstopts->ip6d_nxt); bad: m_freem(m); - return(IPPROTO_DONE); + return (IPPROTO_DONE); } diff --git a/sys/netinet6/esp.h b/sys/netinet6/esp.h index 95deec32a8ff..6f794a8df78f 100644 --- a/sys/netinet6/esp.h +++ b/sys/netinet6/esp.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,7 +37,9 @@ #ifndef _NETINET6_ESP_H_ #define _NETINET6_ESP_H_ -struct secasvar; +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif struct esp { u_int32_t esp_spi; /* ESP */ @@ -67,35 +69,41 @@ struct esptail { /*variable size, 32bit bound*/ /* Authentication data (new IPsec)*/ }; -struct esp_algorithm_state { - struct secasvar *sav; - void* foo; /*per algorithm data - maybe*/ -}; +#ifdef _KERNEL +struct secasvar; -/* XXX yet to be defined */ struct esp_algorithm { size_t padbound; /* pad boundary, in byte */ + int ivlenval; /* iv length, in byte */ int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ + int (*schedlen) __P((const struct esp_algorithm *)); const char *name; - int (*ivlen) __P((struct secasvar *)); + int (*ivlen) __P((const struct esp_algorithm *, struct secasvar *)); int (*decrypt) __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); int (*encrypt) __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); + /* not supposed to be called directly */ + int (*schedule) __P((const struct esp_algorithm *, struct secasvar *)); + int (*blockdecrypt) __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); + int (*blockencrypt) __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); }; -#ifdef _KERNEL -extern struct esp_algorithm esp_algorithms[]; +extern const struct esp_algorithm *esp_algorithm_lookup __P((int)); +extern int esp_max_ivlen __P((void)); /* crypt routines */ extern int esp4_output __P((struct mbuf *, struct ipsecrequest *)); extern void esp4_input __P((struct mbuf *, ...)); extern size_t esp_hdrsiz __P((struct ipsecrequest *)); -#endif /*_KERNEL*/ +extern int esp_schedule __P((const struct esp_algorithm *, struct secasvar *)); extern int esp_auth __P((struct mbuf *, size_t, size_t, struct secasvar *, u_char *)); +#endif /*_KERNEL*/ #endif /*_NETINET6_ESP_H_*/ diff --git a/sys/netinet6/esp6.h b/sys/netinet6/esp6.h index 5d2f984b75cd..d774d2470652 100644 --- a/sys/netinet6/esp6.h +++ b/sys/netinet6/esp6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ +/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -41,6 +41,8 @@ extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); extern int esp6_input __P((struct mbuf **, int *, int)); + +extern void esp6_ctlinput __P((int, struct sockaddr *, void *)); #endif /*_KERNEL*/ #endif /*_NETINET6_ESP6_H_*/ diff --git a/sys/netinet6/esp_core.c b/sys/netinet6/esp_core.c index e5f8ec5d2a04..0319255d6904 100644 --- a/sys/netinet6/esp_core.c +++ b/sys/netinet6/esp_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_core.c,v 1.15 2000/06/14 10:41:18 itojun Exp $ */ +/* $KAME: esp_core.c,v 1.50 2000/11/02 12:27:38 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -35,6 +35,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> @@ -66,87 +67,181 @@ #ifdef INET6 #include <netinet6/esp6.h> #endif +#include <netinet6/esp_rijndael.h> #include <net/pfkeyv2.h> #include <netkey/keydb.h> +#include <netkey/key.h> #include <crypto/des/des.h> #include <crypto/blowfish/blowfish.h> #include <crypto/cast128/cast128.h> -#include <crypto/rc5/rc5.h> #include <net/net_osdep.h> static int esp_null_mature __P((struct secasvar *)); -static int esp_null_ivlen __P((struct secasvar *)); static int esp_null_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); static int esp_null_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); + struct secasvar *, const struct esp_algorithm *, int)); static int esp_descbc_mature __P((struct secasvar *)); -static int esp_descbc_ivlen __P((struct secasvar *)); -static int esp_descbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_descbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); +static int esp_descbc_ivlen __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_des_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_des_schedlen __P((const struct esp_algorithm *)); +static int esp_des_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_des_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); static int esp_cbc_mature __P((struct secasvar *)); -static int esp_blowfish_cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_blowfish_cbc_encrypt __P((struct mbuf *, size_t, - size_t, struct secasvar *, struct esp_algorithm *, int)); -static int esp_blowfish_cbc_ivlen __P((struct secasvar *)); -static int esp_cast128cbc_ivlen __P((struct secasvar *)); -static int esp_cast128cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_cast128cbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_3descbc_ivlen __P((struct secasvar *)); -static int esp_3descbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_3descbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_rc5cbc_ivlen __P((struct secasvar *)); -static int esp_rc5cbc_decrypt __P((struct mbuf *, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static int esp_rc5cbc_encrypt __P((struct mbuf *, size_t, size_t, - struct secasvar *, struct esp_algorithm *, int)); -static void esp_increment_iv __P((struct secasvar *)); -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, "des-cbc", - esp_descbc_ivlen, esp_descbc_decrypt, esp_descbc_encrypt, }, - { 8, esp_cbc_mature, 192, 192, "3des-cbc", - esp_3descbc_ivlen, esp_3descbc_decrypt, esp_3descbc_encrypt, }, - { 1, esp_null_mature, 0, 2048, "null", - esp_null_ivlen, esp_null_decrypt, esp_null_encrypt, }, - { 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, "cast128-cbc", - esp_cast128cbc_ivlen, esp_cast128cbc_decrypt, - esp_cast128cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 2040, "rc5-cbc", - esp_rc5cbc_ivlen, esp_rc5cbc_decrypt, esp_rc5cbc_encrypt, }, +static int esp_blowfish_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_blowfish_schedlen __P((const struct esp_algorithm *)); +static int esp_blowfish_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_blowfish_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_cast128_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_cast128_schedlen __P((const struct esp_algorithm *)); +static int esp_cast128_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_cast128_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_3des_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_3des_schedlen __P((const struct esp_algorithm *)); +static int esp_3des_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_3des_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +static int esp_common_ivlen __P((const struct esp_algorithm *, + struct secasvar *)); +static int esp_cbc_decrypt __P((struct mbuf *, size_t, + struct secasvar *, const struct esp_algorithm *, int)); +static int esp_cbc_encrypt __P((struct mbuf *, size_t, size_t, + struct secasvar *, const struct esp_algorithm *, int)); + +#define MAXIVLEN 16 + +static const struct esp_algorithm esp_algorithms[] = { + { 8, -1, esp_descbc_mature, 64, 64, esp_des_schedlen, + "des-cbc", + esp_descbc_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_des_schedule, + esp_des_blockdecrypt, esp_des_blockencrypt, }, + { 8, 8, esp_cbc_mature, 192, 192, esp_3des_schedlen, + "3des-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_3des_schedule, + esp_3des_blockdecrypt, esp_3des_blockencrypt, }, + { 1, 0, esp_null_mature, 0, 2048, 0, "null", + esp_common_ivlen, esp_null_decrypt, + esp_null_encrypt, NULL, }, + { 8, 8, esp_cbc_mature, 40, 448, esp_blowfish_schedlen, "blowfish-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_blowfish_schedule, + esp_blowfish_blockdecrypt, esp_blowfish_blockencrypt, }, + { 8, 8, esp_cbc_mature, 40, 128, esp_cast128_schedlen, + "cast128-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_cast128_schedule, + esp_cast128_blockdecrypt, esp_cast128_blockencrypt, }, + { 16, 16, esp_cbc_mature, 128, 256, esp_rijndael_schedlen, + "rijndael-cbc", + esp_common_ivlen, esp_cbc_decrypt, + esp_cbc_encrypt, esp_rijndael_schedule, + esp_rijndael_blockdecrypt, esp_rijndael_blockencrypt }, }; -/* - * mbuf assumption: foo_encrypt() assumes that IV part is placed in a single - * mbuf, not across multiple mbufs. - */ +const struct esp_algorithm * +esp_algorithm_lookup(idx) + int idx; +{ -static int -esp_null_mature(sav) + switch (idx) { + case SADB_EALG_DESCBC: + return &esp_algorithms[0]; + case SADB_EALG_3DESCBC: + return &esp_algorithms[1]; + case SADB_EALG_NULL: + return &esp_algorithms[2]; + case SADB_X_EALG_BLOWFISHCBC: + return &esp_algorithms[3]; + case SADB_X_EALG_CAST128CBC: + return &esp_algorithms[4]; + case SADB_X_EALG_RIJNDAELCBC: + return &esp_algorithms[5]; + default: + return NULL; + } +} + +int +esp_max_ivlen() +{ + int idx; + int ivlen; + + ivlen = 0; + for (idx = 0; idx < sizeof(esp_algorithms)/sizeof(esp_algorithms[0]); + idx++) { + if (esp_algorithms[idx].ivlenval > ivlen) + ivlen = esp_algorithms[idx].ivlenval; + } + + return ivlen; +} + +int +esp_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - /* anything is okay */ - return 0; + int error; + + /* check for key length */ + if (_KEYBITS(sav->key_enc) < algo->keymin || + _KEYBITS(sav->key_enc) > algo->keymax) { + ipseclog((LOG_ERR, + "esp_schedule %s: unsupported key length %d: " + "needs %d to %d bits\n", algo->name, _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); + return EINVAL; + } + + /* already allocated */ + if (sav->sched && sav->schedlen != 0) + return 0; + /* no schedule necessary */ + if (!algo->schedule || !algo->schedlen) + return 0; + + sav->schedlen = (*algo->schedlen)(algo); + if (sav->schedlen < 0) + return EINVAL; + sav->sched = malloc(sav->schedlen, M_SECA, M_DONTWAIT); + if (!sav->sched) { + sav->schedlen = 0; + return ENOBUFS; + } + + error = (*algo->schedule)(algo, sav); + if (error) { + ipseclog((LOG_ERR, "esp_schedule %s: error %d\n", + algo->name, error)); + free(sav->sched, M_SECA); + sav->sched = NULL; + sav->schedlen = 0; + } + return error; } static int -esp_null_ivlen(sav) +esp_null_mature(sav) struct secasvar *sav; { + + /* anything is okay */ return 0; } @@ -155,9 +250,10 @@ esp_null_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { + return 0; /* do nothing */ } @@ -167,9 +263,10 @@ esp_null_encrypt(m, off, plen, sav, algo, ivlen) size_t off; /* offset to ESP header */ size_t plen; /* payload length (to be encrypted) */ struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { + return 0; /* do nothing */ } @@ -177,7 +274,7 @@ static int esp_descbc_mature(sav) struct secasvar *sav; { - struct esp_algorithm *algo; + const struct esp_algorithm *algo; if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) { ipseclog((LOG_ERR, "esp_cbc_mature: " @@ -189,9 +286,16 @@ esp_descbc_mature(sav) 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)) { + + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { + ipseclog((LOG_ERR, + "esp_descbc_mature: unsupported algorithm.\n")); + return 1; + } + + if (_KEYBITS(sav->key_enc) < algo->keymin || + _KEYBITS(sav->key_enc) > algo->keymax) { ipseclog((LOG_ERR, "esp_descbc_mature: invalid key length %d.\n", _KEYBITS(sav->key_enc))); @@ -199,7 +303,7 @@ esp_descbc_mature(sav) } /* weak key check */ - if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc))) { + if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc))) { ipseclog((LOG_ERR, "esp_descbc_mature: weak key was passed.\n")); return 1; @@ -209,246 +313,69 @@ esp_descbc_mature(sav) } static int -esp_descbc_ivlen(sav) +esp_descbc_ivlen(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - if (sav && (sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) - return 4; - if (sav && !(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) - return 4; - else + if (!sav) return 8; + if ((sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) + return 4; + if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) + return 4; + return 8; } static int -esp_descbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_des_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff = 0; - size_t bodyoff = 0; - u_int8_t *iv; - size_t plen; - u_int8_t tiv[8]; - int derived; - int error; - - derived = 0; - /* sanity check */ - if (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)) { - ipseclog((LOG_ERR, "esp_descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); - return EINVAL; - } - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1827 */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + ivlen; - derived = 0; - } else { - /* RFC 2406 */ - if (sav->flags & SADB_X_EXT_DERIV) { - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, but you can get from - * ftp://ftp.kame.net/pub/internet-drafts/. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); - ivlen = sizeof(u_int32_t); - derived = 1; - } else { - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - derived = 0; - } - } - if (ivlen == 4) { - iv = &tiv[0]; - m_copydata(m, ivoff, 4, &tiv[0]); - m_copydata(m, ivoff, 4, &tiv[4]); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - } else if (ivlen == 8) { - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - } else { - ipseclog((LOG_ERR, "esp_descbc_decrypt: unsupported ivlen %d\n", - ivlen)); - return EINVAL; - } - - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_descbc_decrypt: too short packet: len=%lu", - (u_long)plen); - plen -= bodyoff; - - if (plen % 8) { - ipseclog((LOG_ERR, "esp_descbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - - { - int deserr; - des_key_schedule ks; - - deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); - if (deserr != 0) { - ipseclog((LOG_ERR, - "esp_descbc_decrypt: key error %d\n", deserr)); - return EINVAL; - } - - error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, - DES_DECRYPT); - - /* for safety */ - bzero(&ks, sizeof(des_key_schedule)); - } - - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return error; + return sizeof(des_key_schedule); } static int -esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - size_t plen; /* payload length (to be decrypted) */ +esp_des_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff = 0; - size_t bodyoff = 0; - u_int8_t *iv; - u_int8_t tiv[8]; - int derived; - int error; - - derived = 0; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_descbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != 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)) { - ipseclog((LOG_ERR, "esp_descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); + if (des_key_sched((des_cblock *)_KEYBUF(sav->key_enc), + *(des_key_schedule *)sav->sched)) return EINVAL; - } - - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1827 */ - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, see above. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + ivlen; - derived = 0; - } else { - /* RFC 2406 */ - if (sav->flags & SADB_X_EXT_DERIV) { - /* - * draft-ietf-ipsec-ciph-des-derived-00.txt - * uses sequence number field as IV field. - * This draft has been deleted, see above. - */ - ivoff = off + sizeof(struct esp); - bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); - ivlen = sizeof(u_int32_t); - derived = 1; - } else { - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - derived = 0; - } - } - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - if (ivlen == 4) { - if (!derived) { - bcopy(sav->iv, &tiv[0], 4); - bcopy(sav->iv, &tiv[4], 4); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - bcopy(&tiv[0], iv, 4); - iv = &tiv[0]; - } else { - bcopy(iv, &tiv[0], 4); - bcopy(iv, &tiv[4], 4); - tiv[4] ^= 0xff; - tiv[5] ^= 0xff; - tiv[6] ^= 0xff; - tiv[7] ^= 0xff; - iv = &tiv[0]; - } - } else if (ivlen == 8) - bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); - else { - ipseclog((LOG_ERR, - "esp_descbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - { - int deserr; - des_key_schedule ks; - - deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); - if (deserr != 0) { - ipseclog((LOG_ERR, - "esp_descbc_encrypt: key error %d\n", deserr)); - return EINVAL; - } - - error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, - DES_ENCRYPT); + else + return 0; +} - /* for safety */ - bzero(&ks, sizeof(des_key_schedule)); - } +static int +esp_des_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - esp_increment_iv(sav); + /* assumption: d has a good alignment */ + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, + *(des_key_schedule *)sav->sched, DES_DECRYPT); + return 0; +} - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); +static int +esp_des_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - return error; + /* assumption: d has a good alignment */ + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, + *(des_key_schedule *)sav->sched, DES_ENCRYPT); + return 0; } static int @@ -456,7 +383,7 @@ esp_cbc_mature(sav) struct secasvar *sav; { int keylen; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; if (sav->flags & SADB_X_EXT_OLD) { ipseclog((LOG_ERR, @@ -470,31 +397,47 @@ esp_cbc_mature(sav) } if (!sav->key_enc) { + ipseclog((LOG_ERR, "esp_cbc_mature: no key is given.\n")); + return 1; + } + + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_ERR, - "esp_cbc_mature: no key is given.\n")); + "esp_cbc_mature %s: unsupported algorithm.\n", algo->name)); return 1; } - algo = &esp_algorithms[sav->alg_enc]; + keylen = sav->key_enc->sadb_key_bits; if (keylen < algo->keymin || algo->keymax < keylen) { - ipseclog((LOG_ERR, "esp_cbc_mature: invalid key length %d.\n", - sav->key_enc->sadb_key_bits)); + ipseclog((LOG_ERR, + "esp_cbc_mature %s: invalid key length %d.\n", + algo->name, sav->key_enc->sadb_key_bits)); return 1; } switch (sav->alg_enc) { case SADB_EALG_3DESCBC: /* weak key check */ - 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))) { + if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc)) || + des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 8)) || + des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 16))) { ipseclog((LOG_ERR, - "esp_cbc_mature: weak key was passed.\n")); + "esp_cbc_mature %s: weak key was passed.\n", + algo->name)); return 1; } break; - case SADB_EALG_BLOWFISHCBC: - case SADB_EALG_CAST128CBC: - case SADB_EALG_RC5CBC: + case SADB_X_EALG_BLOWFISHCBC: + case SADB_X_EALG_CAST128CBC: + break; + case SADB_X_EALG_RIJNDAELCBC: + /* allows specific key sizes only */ + if (!(keylen == 128 || keylen == 192 || keylen == 256)) { + ipseclog((LOG_ERR, + "esp_cbc_mature %s: invalid key length %d.\n", + algo->name, keylen)); + return 1; + } break; } @@ -502,694 +445,596 @@ esp_cbc_mature(sav) } static int -esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_blowfish_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - u_int8_t tiv[8]; - size_t plen; - static BF_KEY key; /* made static to avoid kernel stack overflow */ - int s; - int error; - - /* sanity check */ - if (sav->ivlen != 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)) { - 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) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_blowfish_cbc_decrypt: too short packet: len=%lu", - (u_long)plen); - plen -= bodyoff; - - if (plen % 8) { - 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)); - error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); - - /* for safety */ - bzero(&key, sizeof(BF_KEY)); - splx(s); - - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return error; + return sizeof(BF_KEY); } static int -esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; /* offset to ESP header */ - size_t plen; /* payload length (to be decrypted) */ +esp_blowfish_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - static BF_KEY key; /* made static to avoid kernel stack overflow */ - int s; - int error; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_blowfish_cbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != 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)) { - 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) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_blowfish_cbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - 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)); - error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); - - /* for safety */ - bzero(&key, sizeof(BF_KEY)); - - splx(s); - - esp_increment_iv(sav); - - return error; + BF_set_key((BF_KEY *)sav->sched, _KEYLEN(sav->key_enc), + _KEYBUF(sav->key_enc)); + return 0; } static int -esp_blowfish_cbc_ivlen(sav) +esp_blowfish_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; { - return 8; + /* HOLY COW! BF_encrypt() takes values in host byteorder */ + BF_LONG t[2]; + + bcopy(s, t, sizeof(t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + BF_encrypt(t, (BF_KEY *)sav->sched, BF_DECRYPT); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + bcopy(t, d, sizeof(t)); + return 0; } static int -esp_cast128cbc_ivlen(sav) +esp_blowfish_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; { - return 8; + /* HOLY COW! BF_encrypt() takes values in host byteorder */ + BF_LONG t[2]; + + bcopy(s, t, sizeof(t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + BF_encrypt(t, (BF_KEY *)sav->sched, BF_ENCRYPT); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + bcopy(t, d, sizeof(t)); + return 0; } static int -esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; +esp_cast128_schedlen(algo) + const struct esp_algorithm *algo; { - size_t ivoff; - size_t bodyoff; - u_int8_t iv[8]; - size_t plen; - int error; - /* sanity check */ - if (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) { - 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) { - ipseclog((LOG_ERR, - "esp_cast128cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_cast128cbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + return sizeof(u_int32_t) * 32; +} - /* copy mbuf's IV into iv */ - m_copydata(m, ivoff, 8, iv); +static int +esp_cast128_schedule(algo, sav) + const struct esp_algorithm *algo; + struct secasvar *sav; +{ - plen = m->m_pkthdr.len; - if (plen < bodyoff) { - panic("esp_cast128cbc_decrypt: too short packet: len=%lu\n", - (u_long)plen); - } - plen -= bodyoff; + set_cast128_subkey((u_int32_t *)sav->sched, _KEYBUF(sav->key_enc)); + return 0; +} - if (plen % 8) { - ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } +static int +esp_cast128_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - /* decrypt */ - { - u_int8_t key[16]; - u_int32_t subkey[32]; + if (_KEYLEN(sav->key_enc) <= 80 / 8) + cast128_decrypt_round12(d, s, (u_int32_t *)sav->sched); + else + cast128_decrypt_round16(d, s, (u_int32_t *)sav->sched); + return 0; +} - bzero(key, sizeof(key)); - bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); +static int +esp_cast128_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ - set_cast128_subkey(subkey, key); - error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); + if (_KEYLEN(sav->key_enc) <= 80 / 8) + cast128_encrypt_round12(d, s, (u_int32_t *)sav->sched); + else + cast128_encrypt_round16(d, s, (u_int32_t *)sav->sched); + return 0; +} - /* for safety */ - bzero(subkey, sizeof(subkey)); - bzero(key, sizeof(key)); - } +static int +esp_3des_schedlen(algo) + const struct esp_algorithm *algo; +{ - return error; + return sizeof(des_key_schedule) * 3; } static int -esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; - size_t plen; +esp_3des_schedule(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; int error; + des_key_schedule *p; + int i; + char *k; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); - return EINVAL; + p = (des_key_schedule *)sav->sched; + k = _KEYBUF(sav->key_enc); + for (i = 0; i < 3; i++) { + error = des_key_sched((des_cblock *)(k + 8 * i), p[i]); + if (error) + return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || _KEYBITS(sav->key_enc) > 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) { - ipseclog((LOG_ERR, - "esp_cast128cbc_encrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_cast128cbc_encrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - bcopy(sav->iv, iv, ivlen); - - /* encrypt */ - { - u_int8_t key[16]; - u_int32_t subkey[32]; - - bzero(key, sizeof(key)); - bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); - - set_cast128_subkey(subkey, key); - error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); - - /* for safety */ - bzero(subkey, sizeof(subkey)); - bzero(key, sizeof(key)); - } + return 0; +} - esp_increment_iv(sav); +static int +esp_3des_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + des_key_schedule *p; + + /* assumption: d has a good alignment */ + p = (des_key_schedule *)sav->sched; + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[2], DES_DECRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[1], DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[0], DES_DECRYPT); + return 0; +} - return error; +static int +esp_3des_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + des_key_schedule *p; + + /* assumption: d has a good alignment */ + p = (des_key_schedule *)sav->sched; + bcopy(s, d, sizeof(DES_LONG) * 2); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[0], DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[1], DES_DECRYPT); + des_ecb_encrypt((des_cblock *)d, (des_cblock *)d, p[2], DES_ENCRYPT); + return 0; } static int -esp_3descbc_ivlen(sav) +esp_common_ivlen(algo, sav) + const struct esp_algorithm *algo; struct secasvar *sav; { - return 8; + + if (!algo) + panic("esp_common_ivlen: unknown algorithm"); + return algo->ivlenval; } static int -esp_3descbc_decrypt(m, off, sav, algo, ivlen) +esp_cbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - size_t plen; - u_int8_t tiv[8]; + struct mbuf *s; + struct mbuf *d, *d0, *dp; + int soff, doff; /*offset from the head of chain, to head of this mbuf */ + int sn, dn; /*offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + u_int8_t iv[MAXIVLEN], *ivp; + u_int8_t sbuf[MAXIVLEN], *sp; + u_int8_t *p, *q; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + int derived; - /* sanity check */ - if (ivlen != sav->ivlen) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); + if (ivlen != sav->ivlen || ivlen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + m_freem(m); return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || algo->keymax < _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) { - ipseclog((LOG_ERR, - "esp_3descbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_3descbc_decrypt: unsupported ivlen %d\n", ivlen)); - return EINVAL; - } - - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; - iv = &tiv[0]; - m_copydata(m, ivoff, 8, &tiv[0]); - plen = m->m_pkthdr.len; - if (plen < bodyoff) - panic("esp_3descbc_decrypt: too short packet: len=%lu", - (u_long)plen); + /* assumes blocklen == padbound */ + blocklen = algo->padbound; - plen -= bodyoff; - - if (plen % 8) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: " - "payload length must be multiple of 8\n")); +#ifdef DIAGNOSTIC + if (blocklen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "unsupported blocklen %d\n", algo->name, blocklen)); + m_freem(m); return EINVAL; } +#endif - /* decrypt packet */ - { - int deserr[3]; - des_key_schedule ks[3]; - - deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc),ks[0]); - 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)) { - ipseclog((LOG_ERR, "esp_3descbc_decrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2])); - return EINVAL; + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } } - des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); - - /* for safety */ - bzero(ks[0], sizeof(des_key_schedule)*3); - } + /* grab iv */ + m_copydata(m, ivoff, ivlen, iv); - /* for safety */ - bzero(&tiv[0], sizeof(tiv)); - - return 0; -} - -static int -esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) - struct mbuf *m; - size_t off; - size_t plen; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; -{ - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; - } - if (sav->ivlen != 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)) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc))); + /* extend iv */ + if (ivlen == blocklen) + ; + else if (ivlen == 4 && blocklen == 8) { + bcopy(&iv[0], &iv[4], 4); + iv[4] ^= 0xff; + iv[5] ^= 0xff; + iv[6] ^= 0xff; + iv[7] ^= 0xff; + } else { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen/blocklen: %d %d\n", + algo->name, ivlen, blocklen)); + m_freem(m); return EINVAL; } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_3descbc_encrypt: unsupported ESP version\n")); + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + m_freem(m); return EINVAL; } - if (ivlen != 8) { - ipseclog((LOG_ERR, - "esp_3descbc_encrypt: unsupported ivlen %d\n", ivlen)); + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " + "payload length must be multiple of %d\n", + algo->name, blocklen)); + m_freem(m); return EINVAL; } - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + ivp = sp = NULL; - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); - - bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); - - /* encrypt packet */ - { - int deserr[3]; - des_key_schedule ks[3]; + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } - deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks[0]); - 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)) { - ipseclog((LOG_ERR, "esp_3descbc_encrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2])); - return EINVAL; + soff += s->m_len; + s = s->m_next; } + scut = s; + scutoff = sn; - des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); - - /* for safety */ - bzero(ks[0], sizeof(des_key_schedule)*3); - } + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; - esp_increment_iv(sav); - - return 0; -} + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, sbuf); + sp = sbuf; + } -static int -esp_rc5cbc_ivlen(sav) - struct secasvar *sav; -{ - return 8; -} + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } -static int -esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) - struct mbuf *m; - size_t off; - struct secasvar *sav; - struct esp_algorithm *algo; - int ivlen; -{ - size_t ivoff; - size_t bodyoff; - u_int8_t iv[8]; - size_t plen; - int error; + /* decrypt */ + (*algo->blockdecrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn); - /* sanity check */ - if (sav->ivlen != 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)) { - 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) { - ipseclog((LOG_ERR, - "esp_rc5cbc_decrypt: unsupported ESP version\n")); - return EINVAL; - } - if (ivlen != 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: unsupported ivlen %d\n", - ivlen)); - return EINVAL; - } + /* xor */ + p = ivp ? ivp : iv; + q = mtod(d, u_int8_t *) + dn; + for (i = 0; i < blocklen; i++) + q[i] ^= p[i]; - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + /* next iv */ + if (sp == sbuf) { + bcopy(sbuf, iv, blocklen); + ivp = NULL; + } else + ivp = sp; - /* copy mbuf's IV into iv */ - m_copydata(m, ivoff, 8, iv); + sn += blocklen; + dn += blocklen; - plen = m->m_pkthdr.len; - if (plen < bodyoff) { - panic("esp_rc5cbc_decrypt: too short packet: len=%lu", - (u_long)plen); - } - plen -= bodyoff; + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; + } - if (plen % 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: " - "payload length must be multiple of 8\n")); - return EINVAL; + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; } - /* decrypt */ - { - RC5_WORD e_key[34]; + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; - set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), - _KEYBITS(sav->key_enc) / 8, 16); - error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); + /* just in case */ + bzero(iv, sizeof(iv)); + bzero(sbuf, sizeof(sbuf)); - /* for safety */ - bzero(e_key, sizeof(e_key)); - } - - return error; + return 0; } static int -esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) +esp_cbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; size_t plen; struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; { - size_t ivoff; - size_t bodyoff; - u_int8_t *iv; - int error; + struct mbuf *s; + struct mbuf *d, *d0, *dp; + int soff, doff; /*offset from the head of chain, to head of this mbuf */ + int sn, dn; /*offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + u_int8_t iv[MAXIVLEN], *ivp; + u_int8_t sbuf[MAXIVLEN], *sp; + u_int8_t *p, *q; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + int derived; - /* sanity check */ - if (plen % 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: " - "payload length must be multiple of 8\n")); + if (ivlen != sav->ivlen || ivlen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + m_freem(m); return EINVAL; } - if (sav->ivlen != ivlen) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen)); + + /* assumes blocklen == padbound */ + blocklen = algo->padbound; + +#ifdef DIAGNOSTIC + if (blocklen > sizeof(iv)) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported blocklen %d\n", algo->name, blocklen)); + m_freem(m); return EINVAL; } - if (_KEYBITS(sav->key_enc) < algo->keymin - || _KEYBITS(sav->key_enc) > 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)); +#endif + + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1827 */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + ivlen; + derived = 0; + } else { + /* RFC 2406 */ + if (sav->flags & SADB_X_EXT_DERIV) { + /* + * draft-ietf-ipsec-ciph-des-derived-00.txt + * uses sequence number field as IV field. + */ + ivoff = off + sizeof(struct esp); + bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); + ivlen = sizeof(u_int32_t); + derived = 1; + } else { + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + derived = 0; + } + } + + /* put iv into the packet. if we are in derived mode, use seqno. */ + if (derived) + m_copydata(m, ivoff, ivlen, iv); + else { + bcopy(sav->iv, iv, ivlen); + /* maybe it is better to overwrite dest, not source */ + m_copyback(m, ivoff, ivlen, iv); + } + + /* extend iv */ + if (ivlen == blocklen) + ; + else if (ivlen == 4 && blocklen == 8) { + bcopy(&iv[0], &iv[4], 4); + iv[4] ^= 0xff; + iv[5] ^= 0xff; + iv[6] ^= 0xff; + iv[7] ^= 0xff; + } else { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "unsupported ivlen/blocklen: %d %d\n", + algo->name, ivlen, blocklen)); + m_freem(m); return EINVAL; } - if (sav->flags & SADB_X_EXT_OLD) { - ipseclog((LOG_ERR, - "esp_rc5cbc_encrypt: unsupported ESP version\n")); + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + m_freem(m); return EINVAL; } - if (ivlen != 8) { - ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: unsupported ivlen %d\n", - ivlen)); + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " + "payload length must be multiple of %lu\n", + algo->name, (unsigned long)algo->padbound)); + m_freem(m); return EINVAL; } - ivoff = off + sizeof(struct newesp); - bodyoff = off + sizeof(struct newesp) + ivlen; + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + ivp = sp = NULL; - if (m->m_pkthdr.len < bodyoff) - panic("assumption failed: mbuf too short"); - iv = mbuf_find_offset(m, ivoff, ivlen); - if (!iv) - panic("assumption failed: bad mbuf chain"); + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } - bcopy(sav->iv, iv, ivlen); + soff += s->m_len; + s = s->m_next; + } + scut = s; + scutoff = sn; - /* encrypt */ - { - RC5_WORD e_key[34]; + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; - set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), - _KEYBITS(sav->key_enc) / 8, 16); - error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, sbuf); + sp = sbuf; + } - /* for safety */ - bzero(e_key, sizeof(e_key)); - } + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } - esp_increment_iv(sav); + /* xor */ + p = ivp ? ivp : iv; + q = sp; + for (i = 0; i < blocklen; i++) + q[i] ^= p[i]; - return error; -} + /* encrypt */ + (*algo->blockencrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn); -/* - * increment iv. - */ -static void -esp_increment_iv(sav) - struct secasvar *sav; -{ - u_int8_t *x; - u_int8_t y; - int i; + /* next iv */ + ivp = mtod(d, u_int8_t *) + dn; -#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++) { - *x = (*x + y) & 0xff; - x++; - } -} + sn += blocklen; + dn += blocklen; -static caddr_t -mbuf_find_offset(m, off, len) - struct mbuf *m; - size_t off; - size_t len; -{ - struct mbuf *n; - size_t cnt; - - if (m->m_pkthdr.len < off || m->m_pkthdr.len < off + len) - return (caddr_t)NULL; - cnt = 0; - for (n = m; n; n = n->m_next) { - if (cnt + n->m_len <= off) { - cnt += n->m_len; - continue; + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; } - if (cnt <= off && off < cnt + n->m_len - && cnt <= off + len && off + len <= cnt + n->m_len) { - return mtod(n, caddr_t) + off - cnt; - } else - return (caddr_t)NULL; + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; } - return (caddr_t)NULL; + + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; + + /* just in case */ + bzero(iv, sizeof(iv)); + bzero(sbuf, sizeof(sbuf)); + + key_sa_stir_iv(sav); + + return 0; } /*------------------------------------------------------------*/ @@ -1207,7 +1052,7 @@ esp_auth(m0, skip, length, sav, sum) size_t off; struct ah_algorithm_state s; u_char sumbuf[AH_MAXSUMSIZE]; - struct ah_algorithm *algo; + const struct ah_algorithm *algo; size_t siz; int error; @@ -1233,7 +1078,8 @@ esp_auth(m0, skip, length, sav, sum) ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n")); return EINVAL; } - if (!sav->alg_auth) { + algo = ah_algorithm_lookup(sav->alg_auth); + if (!algo) { ipseclog((LOG_ERR, "esp_auth: bad ESP auth algorithm passed: %d\n", sav->alg_auth)); @@ -1243,7 +1089,6 @@ esp_auth(m0, skip, length, sav, sum) m = m0; off = 0; - algo = &ah_algorithms[sav->alg_auth]; siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1)); if (sizeof(sumbuf) < siz) { ipseclog((LOG_DEBUG, diff --git a/sys/netinet6/esp_input.c b/sys/netinet6/esp_input.c index 4d4344b974f6..4542f135d560 100644 --- a/sys/netinet6/esp_input.c +++ b/sys/netinet6/esp_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_input.c,v 1.25 2000/05/08 08:04:30 itojun Exp $ */ +/* $KAME: esp_input.c,v 1.55 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -64,8 +64,10 @@ #ifdef INET6 #include <netinet/ip6.h> +#include <netinet6/in6_pcb.h> #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> +#include <netinet6/ip6protosw.h> #endif #include <netinet6/ipsec.h> @@ -82,11 +84,7 @@ #endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #include <machine/stdarg.h> @@ -94,14 +92,14 @@ #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)) +#ifdef INET +#include <netinet/ipprotosw.h> +extern struct ipprotosw inetsw[]; + void #if __STDC__ esp4_input(struct mbuf *m, ...) @@ -118,7 +116,7 @@ esp4_input(m, va_alist) struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; size_t hlen; size_t esplen; @@ -178,16 +176,15 @@ esp4_input(m, va_alist) ipsecstat.in_badspi++; goto bad; } - if (sav->alg_enc == SADB_EALG_NONE) { + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_DEBUG, "IPv4 ESP input: " - "unspecified encryption algorithm for spi %u\n", + "unsupported encryption algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ - /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { @@ -201,7 +198,8 @@ esp4_input(m, va_alist) && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; - if (sav->alg_auth == SADB_AALG_NULL) + if (sav->alg_auth == SADB_X_AALG_NULL || + sav->alg_auth == SADB_AALG_NONE) goto noreplaycheck; /* @@ -221,10 +219,12 @@ esp4_input(m, va_alist) { u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; - struct ah_algorithm *sumalgo; + const struct ah_algorithm *sumalgo; size_t siz; - sumalgo = &ah_algorithms[sav->alg_auth]; + sumalgo = ah_algorithm_lookup(sav->alg_auth); + if (!sumalgo) + goto noreplaycheck; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { ipseclog((LOG_DEBUG, @@ -303,22 +303,30 @@ noreplaycheck: } } - { + /* + * pre-compute and cache intermediate key + */ + if (esp_schedule(algo, sav) != 0) { + ipsecstat.in_inval++; + goto bad; + } + /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); + /* m is already freed */ + m = NULL; + ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s\n", + ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } ipsecstat.in_esphist[sav->alg_enc]++; m->m_flags |= M_DECRYPTED; - } /* * find the trailer of the ESP. @@ -347,7 +355,7 @@ noreplaycheck: #endif /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav)) { + if (ipsec4_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP4 xx ESP IP4' payload -> IP4' payload @@ -387,6 +395,11 @@ noreplaycheck: #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) { + ipsecstat.in_nomem++; + goto bad; + } if (! IF_HANDOFF(&ipintrq, m, NULL)) { ipsecstat.in_inval++; @@ -421,10 +434,19 @@ noreplaycheck: ip->ip_p = nxt; key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) { + ipsecstat.in_nomem++; + goto bad; + } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; } @@ -464,10 +486,9 @@ esp6_input(mp, offp, proto) struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; int ivlen; size_t esplen; - int s; /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { @@ -518,16 +539,15 @@ esp6_input(mp, offp, proto) ipsec6stat.in_badspi++; goto bad; } - if (sav->alg_enc == SADB_EALG_NONE) { + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { ipseclog((LOG_DEBUG, "IPv6 ESP input: " - "unspecified encryption algorithm for spi %u\n", + "unsupported encryption algorithm for spi %u\n", (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ - /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { @@ -541,7 +561,8 @@ esp6_input(mp, offp, proto) && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; - if (sav->alg_auth == SADB_AALG_NULL) + if (sav->alg_auth == SADB_X_AALG_NULL || + sav->alg_auth == SADB_AALG_NONE) goto noreplaycheck; /* @@ -561,10 +582,12 @@ esp6_input(mp, offp, proto) { u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; - struct ah_algorithm *sumalgo; + const struct ah_algorithm *sumalgo; size_t siz; - sumalgo = &ah_algorithms[sav->alg_auth]; + sumalgo = ah_algorithm_lookup(sav->alg_auth); + if (!sumalgo) + goto noreplaycheck; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { ipseclog((LOG_DEBUG, @@ -643,13 +666,23 @@ noreplaycheck: ip6 = mtod(m, struct ip6_hdr *); /*set it again just in case*/ /* + * pre-compute and cache intermediate key + */ + if (esp_schedule(algo, sav) != 0) { + ipsec6stat.in_inval++; + goto bad; + } + + /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); + /* m is already freed */ + m = NULL; + ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s\n", + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } @@ -680,7 +713,7 @@ noreplaycheck: ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen); /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav)) { + if (ipsec6_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP6 xx ESP IP6' payload -> IP6' payload @@ -728,6 +761,11 @@ noreplaycheck: #endif key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 || + ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) { + ipsec6stat.in_nomem++; + goto bad; + } if (! IF_HANDOFF(&ip6intrq, m, NULL)) { ipsec6stat.in_inval++; @@ -778,10 +816,60 @@ noreplaycheck: m->m_pkthdr.len += n->m_pkthdr.len; } +#ifndef PULLDOWN_TEST + /* + * KAME requires that the packet to be contiguous on the + * mbuf. We need to make that sure. + * this kind of code should be avoided. + * XXX other conditions to avoid running this part? + */ + if (m->m_len != m->m_pkthdr.len) { + struct mbuf *n = NULL; + int maxlen; + + MGETHDR(n, M_DONTWAIT, MT_HEADER); + maxlen = MHLEN; + if (n) + M_COPY_PKTHDR(n, m); + if (n && m->m_pkthdr.len > maxlen) { + MCLGET(n, M_DONTWAIT); + maxlen = MCLBYTES; + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (!n) { + printf("esp6_input: mbuf allocation failed\n"); + goto bad; + } + + if (m->m_pkthdr.len <= maxlen) { + m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); + n->m_len = m->m_pkthdr.len; + n->m_pkthdr.len = m->m_pkthdr.len; + n->m_next = NULL; + m_freem(m); + } else { + m_copydata(m, 0, maxlen, mtod(n, caddr_t)); + m_adj(m, maxlen); + n->m_len = maxlen; + n->m_pkthdr.len = m->m_pkthdr.len; + n->m_next = m; + m->m_flags &= ~M_PKTHDR; + } + m = n; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) { + ipsec6stat.in_nomem++; + goto bad; + } } *offp = off; @@ -805,4 +893,108 @@ bad: m_freem(m); return IPPROTO_DONE; } + +void +esp6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + const struct newesp *espp; + struct newesp esp; + struct ip6ctlparam *ip6cp = NULL, ip6cp1; + struct secasvar *sav; + struct ip6_hdr *ip6; + struct mbuf *m; + int off; + struct sockaddr_in6 sa6_src, sa6_dst; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + if ((unsigned)cmd >= PRC_NCMDS) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + if (ip6) { + /* + * Notify the error to all possible sockets via pfctlinput2. + * Since the upper layer information (such as protocol type, + * source and destination ports) is embedded in the encrypted + * data and might have been cut, we can't directly call + * an upper layer ctlinput function. However, the pcbnotify + * function will consider source and destination addresses + * as well as the flow info value, and may be able to find + * some PCB that should be notified. + * Although pfctlinput2 will call esp6_ctlinput(), there is + * no possibility of an infinite loop of function calls, + * because we don't pass the inner IPv6 header. + */ + bzero(&ip6cp1, sizeof(ip6cp1)); + ip6cp1.ip6c_src = ip6cp->ip6c_src; + pfctlinput2(cmd, sa, (void *)&ip6cp1); + + /* + * Then go to special cases that need ESP header information. + * XXX: We assume that when ip6 is non NULL, + * M and OFF are valid. + */ + + /* check if we can safely examine src and dst ports */ + if (m->m_pkthdr.len < off + sizeof(esp)) + return; + + if (m->m_len < off + sizeof(esp)) { + /* + * this should be rare case, + * so we compromise on this copy... + */ + m_copydata(m, off, sizeof(esp), (caddr_t)&esp); + espp = &esp; + } else + espp = (struct newesp*)(mtod(m, caddr_t) + off); + + if (cmd == PRC_MSGSIZE) { + int valid = 0; + + /* + * Check to see if we have a valid SA corresponding to + * the address in the ICMP message payload. + */ + sav = key_allocsa(AF_INET6, + (caddr_t)&sa6_src.sin6_addr, + (caddr_t)&sa6_dst, IPPROTO_ESP, + espp->esp_spi); + if (sav) { + if (sav->state == SADB_SASTATE_MATURE || + sav->state == SADB_SASTATE_DYING) + valid++; + key_freesav(sav); + } + + /* XXX Further validation? */ + + /* + * Depending on the value of "valid" and routing table + * size (mtudisc_{hi,lo}wat), we will: + * - recalcurate the new MTU and create the + * corresponding routing entry, or + * - ignore the MTU change notification. + */ + icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); + } + } else { + /* we normally notify any pcb here */ + } +} #endif /* INET6 */ diff --git a/sys/netinet6/esp_output.c b/sys/netinet6/esp_output.c index 8d505ae0a72a..7770b787cdaa 100644 --- a/sys/netinet6/esp_output.c +++ b/sys/netinet6/esp_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: esp_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: esp_output.c,v 1.43 2001/03/01 07:10:45 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -90,7 +90,8 @@ esp_hdrsiz(isr) struct ipsecrequest *isr; { struct secasvar *sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; + const struct ah_algorithm *aalgo; size_t ivlen; size_t authlen; size_t hdrsiz; @@ -111,7 +112,7 @@ esp_hdrsiz(isr) goto estimate; /* we need transport mode ESP. */ - algo = &esp_algorithms[sav->alg_enc]; + algo = esp_algorithm_lookup(sav->alg_enc); if (!algo) goto estimate; ivlen = sav->ivlen; @@ -130,8 +131,9 @@ esp_hdrsiz(isr) hdrsiz = sizeof(struct esp) + ivlen + 9; } else { /* RFC 2406 */ - if (sav->replay && sav->alg_auth && sav->key_auth) - authlen = (*ah_algorithms[sav->alg_auth].sumsiz)(sav); + aalgo = ah_algorithm_lookup(sav->alg_auth); + if (aalgo && sav->replay && sav->key_auth) + authlen = (aalgo->sumsiz)(sav); else authlen = 0; hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen; @@ -143,12 +145,12 @@ esp_hdrsiz(isr) /* * ASSUMING: * sizeof(struct newesp) > sizeof(struct esp). - * 8 = ivlen for CBC mode (RFC2451). + * esp_max_ivlen() = max ivlen for CBC mode * 9 = (maximum padding length without random padding length) * + (Pad Length field) + (Next Header field). * 16 = maximum ICV we support. */ - return sizeof(struct newesp) + 8 + 9 + 16; + return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16; } /* @@ -184,7 +186,7 @@ esp_output(m, nexthdrp, md, isr, af) struct esp *esp; struct esptail *esptail; struct secasvar *sav = isr->sav; - struct esp_algorithm *algo; + const struct esp_algorithm *algo; u_int32_t spi; u_int8_t nxt = 0; size_t plen; /*payload length to be encrypted*/ @@ -193,16 +195,19 @@ esp_output(m, nexthdrp, md, isr, af) int afnumber; size_t extendsiz; int error = 0; + struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; + stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; + stat = &ipsec6stat; break; #endif default: @@ -225,28 +230,31 @@ esp_output(m, nexthdrp, md, isr, af) (u_int32_t)ntohl(ip->ip_dst.s_addr), (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; - m_freem(m); - return EINVAL; + break; } #endif /*INET*/ #ifdef INET6 case AF_INET6: - { - struct ip6_hdr *ip6; - - ip6 = mtod(m, struct ip6_hdr *); ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; - m_freem(m); - return EINVAL; - } + break; #endif /*INET6*/ + default: + panic("esp_output: should not reach here"); } + m_freem(m); + return EINVAL; } - algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { + ipseclog((LOG_ERR, "esp_output: unsupported algorithm: " + "SPI=%u\n", (u_int32_t)ntohl(sav->spi))); + m_freem(m); + return EINVAL; + } spi = sav->spi; ivlen = sav->ivlen; /* should be okey */ @@ -331,7 +339,7 @@ esp_output(m, nexthdrp, md, isr, af) * before: IP ... payload * after: IP ... ESP IV payload */ - if (M_LEADINGSPACE(md) < esphlen) { + if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) { MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); @@ -386,7 +394,7 @@ esp_output(m, nexthdrp, md, isr, af) ipseclog((LOG_WARNING, "replay counter overflowed. %s\n", ipsec_logsastr(sav))); - ipsecstat.out_inval++; + stat->out_inval++; m_freem(m); return EINVAL; } @@ -402,7 +410,6 @@ esp_output(m, nexthdrp, md, isr, af) { /* * find the last mbuf. make some room for ESP trailer. - * XXX new-esp authentication data */ #ifdef INET struct ip *ip = NULL; @@ -410,6 +417,7 @@ esp_output(m, nexthdrp, md, isr, af) size_t padbound; u_char *extend; int i; + int randpadmax; if (algo->padbound) padbound = algo->padbound; @@ -423,13 +431,62 @@ esp_output(m, nexthdrp, md, isr, af) if (extendsiz == 1) extendsiz = padbound + 1; + /* random padding */ + switch (af) { +#ifdef INET + case AF_INET: + randpadmax = ip4_esp_randpad; + break; +#endif +#ifdef INET6 + case AF_INET6: + randpadmax = ip6_esp_randpad; + break; +#endif + default: + randpadmax = -1; + break; + } + if (randpadmax < 0 || plen + extendsiz >= randpadmax) + ; + else { + int n; + + /* round */ + randpadmax = (randpadmax / padbound) * padbound; + n = (randpadmax - plen + extendsiz) / padbound; + + if (n > 0) + n = (random() % n) * padbound; + else + n = 0; + + /* + * make sure we do not pad too much. + * MLEN limitation comes from the trailer attachment + * code below. + * 256 limitation comes from sequential padding. + * also, the 1-octet length field in ESP trailer imposes + * limitation (but is less strict than sequential padding + * as length field do not count the last 2 octets). + */ + if (extendsiz + n <= MLEN && extendsiz + n < 256) + extendsiz += n; + } + +#ifdef DIAGNOSTIC + if (extendsiz > MLEN || extendsiz >= 256) + panic("extendsiz too big in esp_output"); +#endif + n = m; while (n->m_next) n = n->m_next; /* - * if M_EXT, the external part may be shared among - * two consequtive TCP packets. + * if M_EXT, the external mbuf data may be shared among + * two consequtive TCP packets, and it may be unsafe to use the + * trailing space. */ if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { extend = mtod(n, u_char *) + n->m_len; @@ -455,8 +512,7 @@ esp_output(m, nexthdrp, md, isr, af) } switch (sav->flags & SADB_X_EXT_PMASK) { case SADB_X_EXT_PRAND: - for (i = 0; i < extendsiz; i++) - extend[i] = random() & 0xff; + key_randomfill(extend, extendsiz); break; case SADB_X_EXT_PZERO: bzero(extend, extendsiz); @@ -499,26 +555,25 @@ esp_output(m, nexthdrp, md, isr, af) } /* + * pre-compute and cache intermediate key + */ + error = esp_schedule(algo, sav); + if (error) { + m_freem(m); + stat->out_inval++; + goto fail; + } + + /* * encrypt the packet, based on security association * and the algorithm specified. */ if (!algo->encrypt) panic("internal error: no encrypt function"); if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { + /* m is already freed */ ipseclog((LOG_ERR, "packet encryption failure\n")); - m_freem(m); - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_inval++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_inval++; - break; -#endif - } + stat->out_inval++; error = EINVAL; goto fail; } @@ -530,16 +585,23 @@ esp_output(m, nexthdrp, md, isr, af) goto noantireplay; if (!sav->key_auth) goto noantireplay; - if (!sav->alg_auth) + if (sav->key_auth == SADB_AALG_NONE) goto noantireplay; + { + const struct ah_algorithm *aalgo; u_char authbuf[AH_MAXSUMSIZE]; struct mbuf *n; u_char *p; size_t siz; +#ifdef INET struct ip *ip; +#endif - siz = (((*ah_algorithms[sav->alg_auth].sumsiz)(sav) + 3) & ~(4 - 1)); + aalgo = ah_algorithm_lookup(sav->alg_auth); + if (!aalgo) + goto noantireplay; + siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1); if (AH_MAXSUMSIZE < siz) panic("assertion failed for AH_MAXSUMSIZE"); @@ -547,18 +609,7 @@ esp_output(m, nexthdrp, md, isr, af) 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 - } + stat->out_inval++; goto fail; } @@ -619,32 +670,9 @@ noantireplay: if (!m) { ipseclog((LOG_ERR, "NULL mbuf after encryption in esp%d_output", afnumber)); - } else { - switch (af) { -#ifdef INET - case AF_INET: - ipsecstat.out_success++; - break; -#endif -#ifdef INET6 - case AF_INET6: - ipsec6stat.out_success++; - break; -#endif - } - } - 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 - } + } else + stat->out_success++; + stat->out_esphist[sav->alg_enc]++; key_sa_recordxfer(sav, m); return 0; diff --git a/sys/netinet6/esp_rijndael.c b/sys/netinet6/esp_rijndael.c new file mode 100644 index 000000000000..5b0e5fab47b1 --- /dev/null +++ b/sys/netinet6/esp_rijndael.c @@ -0,0 +1,116 @@ +/* $FreeBSD$ */ +/* $KAME: esp_rijndael.c,v 1.4 2001/03/02 05:53:05 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. + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet6/ipsec.h> +#include <netinet6/esp.h> +#include <netinet6/esp_rijndael.h> + +#include <crypto/rijndael/rijndael.h> + +#include <net/net_osdep.h> + +/* as rijndael uses assymetric scheduled keys, we need to do it twice. */ +int +esp_rijndael_schedlen(algo) + const struct esp_algorithm *algo; +{ + + return sizeof(keyInstance) * 2; +} + +int +esp_rijndael_schedule(algo, sav) + const struct esp_algorithm *algo; + struct secasvar *sav; +{ + keyInstance *k; + + k = (keyInstance *)sav->sched; + if (rijndael_makeKey(&k[0], DIR_DECRYPT, _KEYLEN(sav->key_enc) * 8, + _KEYBUF(sav->key_enc)) < 0) + return -1; + if (rijndael_makeKey(&k[1], DIR_ENCRYPT, _KEYLEN(sav->key_enc) * 8, + _KEYBUF(sav->key_enc)) < 0) + return -1; + return 0; +} + +int +esp_rijndael_blockdecrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + cipherInstance c; + keyInstance *p; + + /* does not take advantage of CBC mode support */ + bzero(&c, sizeof(c)); + if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0) + return -1; + p = (keyInstance *)sav->sched; + if (rijndael_blockDecrypt(&c, &p[0], s, algo->padbound * 8, d) < 0) + return -1; + return 0; +} + +int +esp_rijndael_blockencrypt(algo, sav, s, d) + const struct esp_algorithm *algo; + struct secasvar *sav; + u_int8_t *s; + u_int8_t *d; +{ + cipherInstance c; + keyInstance *p; + + /* does not take advantage of CBC mode support */ + bzero(&c, sizeof(c)); + if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0) + return -1; + p = (keyInstance *)sav->sched; + if (rijndael_blockEncrypt(&c, &p[1], s, algo->padbound * 8, d) < 0) + return -1; + return 0; +} diff --git a/sys/netinet6/esp_rijndael.h b/sys/netinet6/esp_rijndael.h new file mode 100644 index 000000000000..0c40d78c5363 --- /dev/null +++ b/sys/netinet6/esp_rijndael.h @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ +/* $KAME: esp_rijndael.h,v 1.1 2000/09/20 18:15:22 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. + */ + +int esp_rijndael_schedlen __P((const struct esp_algorithm *)); +int esp_rijndael_schedule __P((const struct esp_algorithm *, + struct secasvar *)); +int esp_rijndael_blockdecrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); +int esp_rijndael_blockencrypt __P((const struct esp_algorithm *, + struct secasvar *, u_int8_t *, u_int8_t *)); diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c index ea4e6ca55089..dbf9279771c2 100644 --- a/sys/netinet6/frag6.c +++ b/sys/netinet6/frag6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: frag6.c,v 1.24 2000/03/25 07:23:41 sumikawa Exp $ */ +/* $KAME: frag6.c,v 1.31 2001/05/17 13:45:34 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,6 +66,7 @@ static void frag6_insque __P((struct ip6q *, struct ip6q *)); static void frag6_remque __P((struct ip6q *)); static void frag6_freef __P((struct ip6q *)); +/* XXX we eventually need splreass6, or some real semaphore */ int frag6_doing_reass; u_int frag6_nfragpackets; struct ip6q ip6q; /* ip6 reassemble queue */ @@ -206,6 +207,8 @@ frag6_input(mp, offp, proto) /* offset now points to data portion */ offset += sizeof(struct ip6_frag); + frag6_doing_reass = 1; + for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && @@ -217,7 +220,6 @@ frag6_input(mp, offp, proto) * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; - frag6_nfragpackets++; /* * Enforce upper bound on number of fragmented packets @@ -225,11 +227,11 @@ frag6_input(mp, offp, proto) * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ - if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) { - ip6stat.ip6s_fragoverflow++; - in6_ifstat_inc(dstifp, ifs6_reass_fail); - frag6_freef(ip6q.ip6q_prev); - } + if (ip6_maxfragpackets < 0) + ; + else if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) + goto dropfrag; + frag6_nfragpackets++; q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, M_DONTWAIT); if (q6 == NULL) @@ -274,6 +276,7 @@ frag6_input(mp, offp, proto) icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); + frag6_doing_reass = 0; return(IPPROTO_DONE); } } @@ -281,6 +284,7 @@ frag6_input(mp, offp, proto) icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset - sizeof(struct ip6_frag) + offsetof(struct ip6_frag, ip6f_offlg)); + frag6_doing_reass = 0; return(IPPROTO_DONE); } /* @@ -531,6 +535,7 @@ insert: in6_ifstat_inc(dstifp, ifs6_reass_fail); ip6stat.ip6s_fragdropped++; m_freem(m); + frag6_doing_reass = 0; return IPPROTO_DONE; } @@ -620,7 +625,7 @@ frag6_remque(p6) } /* - * IP timer processing; + * IPv6 reassembling timer processing; * if a timer expires on a reassembly * queue, discard it. */ @@ -629,9 +634,6 @@ 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; @@ -650,7 +652,8 @@ frag6_slowtimo() * (due to the limit being lowered), drain off * enough to get down to the new limit. */ - while (frag6_nfragpackets > (u_int)ip6_maxfragpackets) { + while (frag6_nfragpackets > (u_int)ip6_maxfragpackets && + ip6q.ip6q_prev) { ip6stat.ip6s_fragoverflow++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_prev); diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 9dca71e71902..4ea9a3ac2d1d 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: icmp6.c,v 1.119 2000/07/03 14:16:46 itojun Exp $ */ +/* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -71,6 +71,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -99,16 +100,45 @@ #ifdef IPSEC #include <netinet6/ipsec.h> -#ifdef INET6 -#include <netinet6/ipsec6.h> -#endif #include <netkey/key.h> #endif #include "faith.h" +#if defined(NFAITH) && 0 < NFAITH +#include <net/if_faith.h> +#endif #include <net/net_osdep.h> +#ifdef HAVE_NRL_INPCB +/* inpcb members */ +#define in6pcb inpcb +#define in6p_laddr inp_laddr6 +#define in6p_faddr inp_faddr6 +#define in6p_icmp6filt inp_icmp6filt +#define in6p_route inp_route +#define in6p_socket inp_socket +#define in6p_flags inp_flags +#define in6p_moptions inp_moptions6 +#define in6p_outputopts inp_outputopts6 +#define in6p_ip6 inp_ipv6 +#define in6p_flowinfo inp_flowinfo +#define in6p_sp inp_sp +#define in6p_next inp_next +#define in6p_prev inp_prev +/* macro names */ +#define sotoin6pcb sotoinpcb +/* function names */ +#define in6_pcbdetach in_pcbdetach +#define in6_rtchange in_rtchange + +/* + * for KAME src sync over BSD*'s. XXX: FreeBSD (>=3) are VERY different from + * others... + */ +#define in6p_ip6_nxt inp_ipv6.ip6_nxt +#endif + extern struct domain inet6domain; extern struct ip6protosw inet6sw[]; extern u_char ip6_protox[]; @@ -116,34 +146,33 @@ 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; +static struct timeval icmp6errppslim_last; 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 *)); +#ifndef HAVE_PPSRATECHECK +static int ppsratecheck __P((struct timeval *, int *, int)); #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 **)); + struct ifnet **, char *)); static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int)); +static int icmp6_notify_error __P((struct mbuf *, int, int, int)); #ifdef COMPAT_RFC1885 static struct route_in6 icmp6_reflect_rt; #endif + void icmp6_init() { @@ -155,7 +184,7 @@ icmp6_errcount(stat, type, code) struct icmp6errstat *stat; int type, code; { - switch(type) { + switch (type) { case ICMP6_DST_UNREACH: switch (code) { case ICMP6_DST_UNREACH_NOROUTE: @@ -179,7 +208,7 @@ icmp6_errcount(stat, type, code) stat->icp6errs_packet_too_big++; return; case ICMP6_TIME_EXCEEDED: - switch(code) { + switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: stat->icp6errs_time_exceed_transit++; return; @@ -189,7 +218,7 @@ icmp6_errcount(stat, type, code) } break; case ICMP6_PARAM_PROB: - switch(code) { + switch (code) { case ICMP6_PARAMPROB_HEADER: stat->icp6errs_paramprob_header++; return; @@ -318,7 +347,7 @@ icmp6_error(m, type, code, param) if (m && m->m_len < preplen) m = m_pullup(m, preplen); if (m == NULL) { - printf("ENOBUFS in icmp6_error %d\n", __LINE__); + nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__)); return; } @@ -336,6 +365,15 @@ icmp6_error(m, type, code, param) icmp6->icmp6_code = code; icmp6->icmp6_pptr = htonl((u_int32_t)param); + /* + * icmp6_reflect() is designed to be in the input path. + * icmp6_error() can be called from both input and outut path, + * and if we are in output path rcvif could contain bogus value. + * clear m->m_pkthdr.rcvif for safety, we should have enough scope + * information in ip header (nip6). + */ + m->m_pkthdr.rcvif = NULL; + icmp6stat.icp6s_outhist[type]++; icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ @@ -362,7 +400,6 @@ icmp6_input(mp, offp, proto) int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; int code, sum, noff; - struct sockaddr_in6 icmp6src; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); @@ -395,17 +432,15 @@ icmp6_input(mp, offp, proto) code = icmp6->icmp6_code; if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n", - icmp6->icmp6_type, - sum, - ip6_sprintf(&ip6->ip6_src)); + icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src))); icmp6stat.icp6s_checksum++; goto freeit; } #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { + if (faithprefix(&ip6->ip6_dst)) { /* * Deliver very specific ICMP6 type only. * This is important to deilver TOOBIG. Otherwise PMTUD @@ -422,21 +457,12 @@ icmp6_input(mp, offp, proto) } #endif -#ifdef IPSEC - /* drop it if it does not match the default policy */ - if (ipsec6_in_reject(m, NULL)) { - ipsecstat.in_polvio++; - goto freeit; - } -#endif - icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); switch (icmp6->icmp6_type) { - case ICMP6_DST_UNREACH: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); switch (code) { @@ -530,9 +556,6 @@ icmp6_input(mp, offp, proto) * always copy the length we specified. */ if (maxlen >= MCLBYTES) { -#ifdef DIAGNOSTIC - printf("MCLBYTES too small\n"); -#endif /* Give up remote */ m_freem(n0); break; @@ -648,13 +671,13 @@ icmp6_input(mp, offp, proto) u_char *p; int maxlen, maxhlen; + if ((icmp6_nodeinfo & 5) != 5) + break; + 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; } @@ -670,6 +693,7 @@ icmp6_input(mp, offp, proto) /* Give up remote */ break; } + n->m_pkthdr.rcvif = NULL; n->m_len = 0; maxhlen = M_TRAILINGSPACE(n) - maxlen; if (maxhlen > hostnamelen) @@ -794,10 +818,11 @@ icmp6_input(mp, offp, proto) break; default: - printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", - icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), - ip6_sprintf(&ip6->ip6_dst), - m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); + nd6log((LOG_DEBUG, + "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", + icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0)); if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { /* ICMPv6 error: MUST deliver it by spec... */ code = PRC_NCMDS; @@ -807,32 +832,63 @@ icmp6_input(mp, offp, proto) break; } deliver: - if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { - icmp6stat.icp6s_tooshort++; - goto freeit; + if (icmp6_notify_error(m, off, icmp6len, code)) { + /* In this case, m should've been freed. */ + return(IPPROTO_DONE); } + break; + + badcode: + icmp6stat.icp6s_badcode++; + break; + + badlen: + icmp6stat.icp6s_badlen++; + break; + } + + /* deliver the packet to appropriate sockets */ + icmp6_rip6_input(&m, *offp); + + return IPPROTO_DONE; + + freeit: + m_freem(m); + return IPPROTO_DONE; +} + +static int +icmp6_notify_error(m, off, icmp6len, code) + struct mbuf *m; + int off, icmp6len; +{ + struct icmp6_hdr *icmp6; + struct ip6_hdr *eip6; + u_int32_t notifymtu; + struct sockaddr_in6 icmp6src, icmp6dst; + + if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { + 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); + IP6_EXTHDR_CHECK(m, off, + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), + -1); + 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; - } + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return(-1); + } #endif - bzero(&icmp6src, sizeof(icmp6src)); - icmp6src.sin6_len = sizeof(struct sockaddr_in6); - icmp6src.sin6_family = AF_INET6; - icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + eip6 = (struct ip6_hdr *)(icmp6 + 1); - /* Detect the upper level protocol */ - { + /* Detect the upper level protocol */ + { void (*ctlfunc) __P((int, struct sockaddr *, void *)); - struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); u_int8_t nxt = eip6->ip6_nxt; int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); @@ -847,22 +903,22 @@ icmp6_input(mp, offp, proto) while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; - switch(nxt) { + switch (nxt) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_AH: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), - IPPROTO_DONE); + -1); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(eh, struct ip6_ext *, m, - eoff, sizeof(*eh)); + eoff, sizeof(*eh)); if (eh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif @@ -883,15 +939,15 @@ icmp6_input(mp, offp, proto) */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), - IPPROTO_DONE); + -1); rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, - eoff, sizeof(*rth)); + eoff, sizeof(*rth)); if (rth == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif rthlen = (rth->ip6r_len + 1) << 3; @@ -909,7 +965,7 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, - IPPROTO_DONE); + -1); rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth0, @@ -917,7 +973,7 @@ icmp6_input(mp, offp, proto) eoff, rthlen); if (rth0 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* just ignore a bogus header */ @@ -932,15 +988,15 @@ icmp6_input(mp, offp, proto) #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_frag), - IPPROTO_DONE); + -1); fh = (struct ip6_frag *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(fh, struct ip6_frag *, m, - eoff, sizeof(*fh)); + eoff, sizeof(*fh)); if (fh == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); } #endif /* @@ -967,69 +1023,114 @@ icmp6_input(mp, offp, proto) goto notify; } } - 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)); + sizeof(*icmp6) + sizeof(struct ip6_hdr)); if (icmp6 == NULL) { icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; + return(-1); + } +#endif + + eip6 = (struct ip6_hdr *)(icmp6 + 1); + bzero(&icmp6dst, sizeof(icmp6dst)); + icmp6dst.sin6_len = sizeof(struct sockaddr_in6); + icmp6dst.sin6_family = AF_INET6; + if (finaldst == NULL) + icmp6dst.sin6_addr = eip6->ip6_dst; + else + icmp6dst.sin6_addr = *finaldst; + icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6dst.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; + } +#endif + + /* + * retrieve parameters from the inner IPv6 header, and convert + * them into sockaddr structures. + */ + bzero(&icmp6src, sizeof(icmp6src)); + icmp6src.sin6_len = sizeof(struct sockaddr_in6); + icmp6src.sin6_family = AF_INET6; + icmp6src.sin6_addr = eip6->ip6_src; + icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &icmp6src.sin6_addr); +#ifndef SCOPEDROUTING + if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, + NULL, NULL)) { + /* should be impossbile */ + nd6log((LOG_DEBUG, + "icmp6_notify_error: in6_embedscope failed\n")); + goto freeit; } #endif + icmp6src.sin6_flowinfo = + (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); + + if (finaldst == NULL) + finaldst = &eip6->ip6_dst; + ip6cp.ip6c_m = m; + ip6cp.ip6c_icmp6 = icmp6; + ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); + ip6cp.ip6c_off = eoff; + ip6cp.ip6c_finaldst = finaldst; + ip6cp.ip6c_src = &icmp6src; + ip6cp.ip6c_nxt = nxt; + if (icmp6type == ICMP6_PACKET_TOO_BIG) { - if (finaldst == NULL) - finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; - icmp6_mtudisc_update(finaldst, icmp6, m); + notifymtu = ntohl(icmp6->icmp6_mtu); + ip6cp.ip6c_cmdarg = (void *)¬ifymtu; + icmp6_mtudisc_update(&ip6cp, 1); /*XXX*/ } ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { - ip6cp.ip6c_m = m; - ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); - ip6cp.ip6c_off = eoff; - (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); + (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst, + &ip6cp); } - } - break; - - badcode: - icmp6stat.icp6s_badcode++; - break; - - badlen: - icmp6stat.icp6s_badlen++; - break; } + return(0); -#ifdef HAVE_NRL_INPCB - rip6_input(&m, offp, IPPROTO_ICMPV6); -#else - icmp6_rip6_input(&m, *offp); -#endif - return IPPROTO_DONE; - - freeit: + freeit: m_freem(m); - return IPPROTO_DONE; + return(-1); } -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 */ +void +icmp6_mtudisc_update(ip6cp, validated) + struct ip6ctlparam *ip6cp; + int validated; { + struct in6_addr *dst = ip6cp->ip6c_finaldst; + struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; + struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ u_int mtu = ntohl(icmp6->icmp6_mtu); struct rtentry *rt = NULL; struct sockaddr_in6 sin6; + if (!validated) + return; + bzero(&sin6, sizeof(sin6)); sin6.sin6_family = PF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_addr = *dst; + /* XXX normally, this won't happen */ + if (IN6_IS_ADDR_LINKLOCAL(dst)) { + sin6.sin6_addr.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + } /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING); @@ -1041,6 +1142,7 @@ icmp6_mtudisc_update(dst, icmp6, m) rt->rt_rmx.rmx_locks |= RTV_MTU; } else if (mtu < rt->rt_ifp->if_mtu && rt->rt_rmx.rmx_mtu > mtu) { + icmp6stat.icp6s_pmtuchg++; rt->rt_rmx.rmx_mtu = mtu; } } @@ -1049,19 +1151,17 @@ icmp6_mtudisc_update(dst, icmp6, m) } /* - * Process a Node Information Query packet, (roughly) based on - * draft-ietf-ipngwg-icmp-name-lookups-05. + * Process a Node Information Query packet, based on + * draft-ietf-ipngwg-icmp-name-lookups-07. * * 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) - static struct mbuf * ni6_input(m, off) struct mbuf *m; @@ -1075,10 +1175,12 @@ ni6_input(m, off) struct ni_reply_fqdn *fqdn; int addrs; /* for NI_QTYPE_NODEADDR */ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ - struct sockaddr_in6 sin6; + struct sockaddr_in6 sin6; /* double meaning; ip6_dst and subjectaddr */ + struct sockaddr_in6 sin6_d; /* XXX: we should retrieve this from m_aux */ struct ip6_hdr *ip6; int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ - char *subj; + char *subj = NULL; + struct in6_ifaddr *ia6 = NULL; ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST @@ -1094,81 +1196,40 @@ ni6_input(m, off) /* * 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. + * The Responder must discard the Query without further processing + * unless it is one of the Responder's unicast or anycast addresses, or + * a link-local scope multicast address which the Responder has joined. + * [icmp-name-lookups-07, Section 4.] */ 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*/ + if ((ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)&sin6)) != NULL) { + /* unicast/anycast, fine */ + if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + nd6log((LOG_DEBUG, "ni6_input: ignore node info to " + "a temporary address in %s:%d", + __FILE__, __LINE__)); + goto bad; + } + } else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) + ; /* link-local multicast, fine */ 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. */ + qtype = ntohs(ni6->ni_qtype); 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; - + /* 07 draft */ + if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0) + break; + /* FALLTHROUGH */ case NI_QTYPE_FQDN: case NI_QTYPE_NODEADDR: switch (ni6->ni_code) { @@ -1180,10 +1241,15 @@ ni6_input(m, off) * backward compatibility - try to accept 03 draft * format, where no Subject is present. */ - if (subjlen == 0) { + if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 && + subjlen == 0) { oldfqdn++; break; } +#if ICMP6_NI_SUBJ_IPV6 != 0 + if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6) + goto bad; +#endif if (subjlen != sizeof(sin6.sin6_addr)) goto bad; @@ -1191,8 +1257,8 @@ ni6_input(m, off) /* * Validate Subject address. * - * Not sure what exactly does "address belongs to the - * node" mean in the spec, is it just unicast, or what? + * Not sure what exactly "address belongs to the node" + * means 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 @@ -1205,23 +1271,24 @@ ni6_input(m, off) /* 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]; - } + sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &sin6.sin6_addr); +#ifndef SCOPEDROUTING + in6_embedscope(&sin6.sin6_addr, &sin6, NULL, NULL); #endif - } - if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr)) + bzero(&sin6_d, sizeof(sin6_d)); + sin6_d.sin6_family = AF_INET6; /* not used, actually */ + sin6_d.sin6_len = sizeof(sin6_d); /* ditto */ + sin6_d.sin6_addr = ip6->ip6_dst; + sin6_d.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, + &ip6->ip6_dst); +#ifndef SCOPEDROUTING + in6_embedscope(&sin6_d.sin6_addr, &sin6_d, NULL, NULL); +#endif + subj = (char *)&sin6; + if (SA6_ARE_ADDR_EQUAL(&sin6, &sin6_d)) break; + /* * XXX if we are to allow other cases, we should really * be careful about scope here. @@ -1259,18 +1326,60 @@ ni6_input(m, off) n = NULL; break; - case ICMP6_NI_SUBJ_IPV4: /* xxx: to be implemented? */ + case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */ default: goto bad; } break; + } + + /* refuse based on configuration. XXX ICMP6_NI_REFUSED? */ + switch (qtype) { + case NI_QTYPE_FQDN: + if ((icmp6_nodeinfo & 1) == 0) + goto bad; + break; + case NI_QTYPE_NODEADDR: + if ((icmp6_nodeinfo & 2) == 0) + goto bad; + break; + } + /* guess reply length */ + switch (qtype) { + case NI_QTYPE_NOOP: + break; /* no reply data */ + case NI_QTYPE_SUPTYPES: + replylen += sizeof(u_int32_t); + break; + case NI_QTYPE_FQDN: + /* XXX will append an mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + break; + case NI_QTYPE_NODEADDR: + addrs = ni6_addrs(ni6, m, &ifp, subj); + if ((replylen += addrs * (sizeof(struct in6_addr) + + sizeof(u_int32_t))) > MCLBYTES) + replylen = MCLBYTES; /* XXX: will truncate pkt later */ + break; default: - /* should never be here due to "switch (qtype)" above */ - goto bad; + /* + * 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 an mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + oldfqdn++; + break; } - /* allocate a mbuf to reply. */ + /* allocate an mbuf to reply. */ MGETHDR(n, M_DONTWAIT, m->m_type); if (n == NULL) { m_freem(m); @@ -1279,10 +1388,10 @@ ni6_input(m, off) M_COPY_PKTHDR(n, m); /* just for recvif */ if (replylen > MHLEN) { if (replylen > MCLBYTES) { - /* - * XXX: should we try to allocate more? But MCLBYTES is - * probably much larger than IPV6_MMTU... - */ + /* + * XXX: should we try to allocate more? But MCLBYTES + * is probably much larger than IPV6_MMTU... + */ goto bad; } MCLGET(n, M_DONTWAIT); @@ -1300,12 +1409,21 @@ ni6_input(m, off) /* qtype dependent procedure */ switch (qtype) { case NI_QTYPE_NOOP: + nni6->ni_code = ICMP6_NI_SUCCESS; nni6->ni_flags = 0; break; case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ + { + u_int32_t v; + nni6->ni_code = ICMP6_NI_SUCCESS; + nni6->ni_flags = htons(0x0000); /* raw bitmap */ + /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */ + v = (u_int32_t)htonl(0x0000000f); + bcopy(&v, nni6 + 1, sizeof(u_int32_t)); break; + } case NI_QTYPE_FQDN: + nni6->ni_code = ICMP6_NI_SUCCESS; fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo)); @@ -1326,12 +1444,10 @@ ni6_input(m, off) { 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); + nni6->ni_code = ICMP6_NI_SUCCESS; + n->m_pkthdr.len = n->m_len = + sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); + lenlim = M_TRAILINGSPACE(n); copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); /* XXX: reset mbuf length */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + @@ -1343,7 +1459,6 @@ ni6_input(m, off) } nni6->ni_type = ICMP6_NI_REPLY; - nni6->ni_code = ICMP6_NI_SUCESS; m_freem(m); return(n); @@ -1455,6 +1570,7 @@ ni6_nametodns(name, namelen, old) /* * check if two DNS-encoded string matches. takes care of truncated * form (with \0\0 at the end). no compression support. + * XXX upper/lowercase match (see RFC2065) */ static int ni6_dnsmatch(a, alen, b, blen) @@ -1521,16 +1637,34 @@ ni6_dnsmatch(a, alen, b, blen) * calculate the number of addresses to be returned in the node info reply. */ static int -ni6_addrs(ni6, m, ifpp) +ni6_addrs(ni6, m, ifpp, subj) struct icmp6_nodeinfo *ni6; struct mbuf *m; struct ifnet **ifpp; + char *subj; { - register struct ifnet *ifp; - register struct in6_ifaddr *ifa6; - register struct ifaddr *ifa; - struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct ifnet *ifp; + struct in6_ifaddr *ifa6; + struct ifaddr *ifa; + struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */ int addrs = 0, addrsofif, iffound = 0; + int niflags = ni6->ni_flags; + + if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { + switch (ni6->ni_code) { + case ICMP6_NI_SUBJ_IPV6: + if (subj == NULL) /* must be impossible... */ + return(0); + subj_ip6 = (struct sockaddr_in6 *)subj; + break; + default: + /* + * XXX: we only support IPv6 subject address for + * this Qtype. + */ + return(0); + } + } for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { @@ -1542,8 +1676,8 @@ ni6_addrs(ni6, m, ifpp) continue; ifa6 = (struct in6_ifaddr *)ifa; - if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && - IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 && + IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr, &ifa6->ia_addr.sin6_addr)) iffound = 1; @@ -1552,36 +1686,41 @@ ni6_addrs(ni6, m, ifpp) * Node Information proxy, since they represent * addresses of IPv4-only nodes, which perforce do * not implement this protocol. - * [icmp-name-lookups-05] + * [icmp-name-lookups-07, Section 5.4] * 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 | - NI_NODEADDR_FLAG_GLOBAL)) == 0) - continue; - /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { - case IPV6_ADDR_SCOPE_LINKLOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) - addrsofif++; + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_SITELOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) - addrsofif++; + case IPV6_ADDR_SCOPE_SITELOCAL: + if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) + continue; + break; + case IPV6_ADDR_SCOPE_GLOBAL: + if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_GLOBAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) - addrsofif++; - break; - default: - continue; + default: + continue; + } + + /* + * check if anycast is okay. + * XXX: just experimental. not in the spec. + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && + (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) + continue; /* we need only unicast addresses */ + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + continue; } + addrsofif++; /* count the address */ } if (iffound) { *ifpp = ifp; @@ -1600,82 +1739,139 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) struct ifnet *ifp0; int resid; { - register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); - register struct in6_ifaddr *ifa6; - register struct ifaddr *ifa; - int docopy, copied = 0; + struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); + struct in6_ifaddr *ifa6; + struct ifaddr *ifa; + struct ifnet *ifp_dep = NULL; + int copied = 0, allow_deprecated = 0; u_char *cp = (u_char *)(nni6 + 1); + int niflags = ni6->ni_flags; + u_int32_t ltime; - if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) + if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) return(0); /* needless to copy */ + again: + for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) { for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { - docopy = 0; - if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; - if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { - /* just experimental. not in the spec. */ - if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - docopy = 1; - else - continue; - } - else { /* unicast address */ - if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - continue; - else - docopy = 1; + if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 && + allow_deprecated == 0) { + /* + * prefererred address should be put before + * deprecated addresses. + */ + + /* record the interface for later search */ + if (ifp_dep == NULL) + ifp_dep = ifp; + + continue; } + else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 && + allow_deprecated != 0) + continue; /* we now collect deprecated addrs */ /* What do we have to do about ::1? */ - switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { - case IPV6_ADDR_SCOPE_LINKLOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) - docopy = 1; + switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { + case IPV6_ADDR_SCOPE_LINKLOCAL: + if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) + continue; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_SITELOCAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) - docopy = 1; + case IPV6_ADDR_SCOPE_GLOBAL: + if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) + continue; break; - case IPV6_ADDR_SCOPE_GLOBAL: - if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) - docopy = 1; - break; - default: - continue; + default: + continue; } - if (docopy) { - if (resid < sizeof(struct in6_addr)) { - /* - * We give up much more copy. - * Set the truncate flag and return. - */ - nni6->ni_flags |= - NI_NODEADDR_FLAG_TRUNCATE; - return(copied); - } - bcopy(&ifa6->ia_addr.sin6_addr, cp, - sizeof(struct in6_addr)); - /* XXX: KAME link-local hack; remove ifindex */ - if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) - ((struct in6_addr *)cp)->s6_addr16[1] = 0; - cp += sizeof(struct in6_addr); - resid -= sizeof(struct in6_addr); - copied += sizeof(struct in6_addr); + /* + * check if anycast is okay. + * XXX: just experimental. not in the spec. + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && + (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) + continue; + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (icmp6_nodeinfo & 4) == 0) { + continue; } + + /* now we can copy the address */ + if (resid < sizeof(struct in6_addr) + + sizeof(u_int32_t)) { + /* + * We give up much more copy. + * Set the truncate flag and return. + */ + nni6->ni_flags |= + NI_NODEADDR_FLAG_TRUNCATE; + return(copied); + } + + /* + * Set the TTL of the address. + * The TTL value should be one of the following + * according to the specification: + * + * 1. The remaining lifetime of a DHCP lease on the + * address, or + * 2. The remaining Valid Lifetime of a prefix from + * which the address was derived through Stateless + * Autoconfiguration. + * + * Note that we currently do not support stateful + * address configuration by DHCPv6, so the former + * case can't happen. + */ + if (ifa6->ia6_lifetime.ia6t_expire == 0) + ltime = ND6_INFINITE_LIFETIME; + else { + if (ifa6->ia6_lifetime.ia6t_expire > + time_second) + ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - time_second); + else + ltime = 0; + } + + bcopy(<ime, cp, sizeof(u_int32_t)); + cp += sizeof(u_int32_t); + + /* copy the address itself */ + bcopy(&ifa6->ia_addr.sin6_addr, cp, + sizeof(struct in6_addr)); + /* XXX: KAME link-local hack; remove ifindex */ + if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) + ((struct in6_addr *)cp)->s6_addr16[1] = 0; + cp += sizeof(struct in6_addr); + + resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); + copied += (sizeof(struct in6_addr) + + sizeof(u_int32_t)); } if (ifp0) /* we need search only on the specified IF */ break; } + if (allow_deprecated == 0 && ifp_dep != NULL) { + ifp = ifp_dep; + allow_deprecated = 1; + + goto again; + } + return(copied); } @@ -1688,8 +1884,8 @@ icmp6_rip6_input(mp, off) int off; { struct mbuf *m = *mp; - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct in6pcb *in6p; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct in6pcb *in6p; struct in6pcb *last = NULL; struct sockaddr_in6 rip6src; struct icmp6_hdr *icmp6; @@ -1714,8 +1910,12 @@ icmp6_rip6_input(mp, off) LIST_FOREACH(in6p, &ripcb, inp_list) { - if ((in6p->inp_vflag & INP_IPV6) == NULL) + if ((in6p->inp_vflag & INP_IPV6) == 0) + continue; +#ifdef HAVE_NRL_INPCB + if (!(in6p->in6p_flags & INP_IPV6)) continue; +#endif if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && @@ -1740,8 +1940,9 @@ icmp6_rip6_input(mp, off) n, opts) == 0) { /* should notify about lost packet */ m_freem(n); - if (opts) + if (opts) { m_freem(opts); + } } else sorwakeup(last->in6p_socket); opts = NULL; @@ -1755,7 +1956,7 @@ icmp6_rip6_input(mp, off) /* strip intermediate headers */ m_adj(m, off); if (sbappendaddr(&last->in6p_socket->so_rcv, - (struct sockaddr *)&rip6src, m, opts) == 0) { + (struct sockaddr *)&rip6src, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); @@ -1784,6 +1985,7 @@ icmp6_reflect(m, off) int plen; int type, code; struct ifnet *outif = NULL; + struct sockaddr_in6 sa6_src, sa6_dst; #ifdef COMPAT_RFC1885 int mtu = IPV6_MMTU; struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; @@ -1791,9 +1993,10 @@ icmp6_reflect(m, off) /* 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__); + nd6log((LOG_DEBUG, + "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", + (u_long)off, (u_long)sizeof(struct ip6_hdr), + __FILE__, __LINE__)); goto bad; } @@ -1840,12 +2043,24 @@ icmp6_reflect(m, off) */ ip6->ip6_dst = ip6->ip6_src; - /* XXX hack for link-local addresses */ - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = - htons(m->m_pkthdr.rcvif->if_index); - if (IN6_IS_ADDR_LINKLOCAL(&t)) - t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + /* + * XXX: make sure to embed scope zone information, using + * already embedded IDs or the received interface (if any). + * Note that rcvif may be NULL. + * TODO: scoped routing case (XXX). + */ + bzero(&sa6_src, sizeof(sa6_src)); + sa6_src.sin6_family = AF_INET6; + sa6_src.sin6_len = sizeof(sa6_src); + sa6_src.sin6_addr = ip6->ip6_dst; + in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif); + in6_embedscope(&ip6->ip6_dst, &sa6_src, NULL, NULL); + bzero(&sa6_dst, sizeof(sa6_dst)); + sa6_dst.sin6_family = AF_INET6; + sa6_dst.sin6_len = sizeof(sa6_dst); + sa6_dst.sin6_addr = t; + in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif); + in6_embedscope(&t, &sa6_dst, NULL, NULL); #ifdef COMPAT_RFC1885 /* @@ -1902,19 +2117,27 @@ icmp6_reflect(m, off) src = &t; } - if (src == 0) + if (src == 0) { + int e; + struct route_in6 ro; + /* * 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. + * that we do not own. Select a source address based on the + * source address of the erroneous packet. */ - if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) - src = &IA6_SIN6(ia)->sin6_addr; - - if (src == 0) - goto bad; + bzero(&ro, sizeof(ro)); + src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &e); + if (ro.ro_rt) + RTFREE(ro.ro_rt); /* XXX: we could use this */ + if (src == NULL) { + nd6log((LOG_DEBUG, + "icmp6_reflect: source can't be determined: " + "dst=%s, error=%d\n", + ip6_sprintf(&sa6_src.sin6_addr), e)); + goto bad; + } + } ip6->ip6_src = *src; @@ -1925,20 +2148,21 @@ icmp6_reflect(m, off) if (m->m_pkthdr.rcvif) { /* XXX: This may not be the outgoing interface */ ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; - } + } else + ip6->ip6_hlim = ip6_defhlim; icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), plen); /* - * xxx option handling + * XXX option handling */ m->m_flags &= ~(M_BCAST|M_MCAST); #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif /*IPSEC*/ #ifdef COMPAT_RFC1885 @@ -1961,9 +2185,6 @@ icmp6_fasttimo() { mld6_fasttimeo(); - - /* reset ICMPv6 pps limit */ - icmp6errpps_count = 0; } static const char * @@ -1980,7 +2201,7 @@ icmp6_redirect_diag(src6, dst6, tgt6) void icmp6_redirect_input(m, off) - register struct mbuf *m; + struct mbuf *m; int off; { struct ifnet *ifp = m->m_pkthdr.rcvif; @@ -2028,17 +2249,17 @@ icmp6_redirect_input(m, off) /* validation */ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " - "must be from linklocal\n", ip6_sprintf(&src6)); - goto freeit; + "must be from linklocal\n", ip6_sprintf(&src6))); + goto bad; } if (ip6->ip6_hlim != 255) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", - ip6_sprintf(&src6), ip6->ip6_hlim); - goto freeit; + ip6_sprintf(&src6), ip6->ip6_hlim)); + goto bad; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ @@ -2053,41 +2274,41 @@ icmp6_redirect_input(m, off) if (rt) { if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; no route " "with inet6 gateway found for redirect dst: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); RTFREE(rt); - goto freeit; + goto bad; } gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "not equal to gw-for-src=%s (must be same): " "%s\n", ip6_sprintf(gw6), - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); RTFREE(rt); - goto freeit; + goto bad; } } else { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } RTFREE(rt); rt = NULL; } if (IN6_IS_ADDR_MULTICAST(&reddst6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } is_router = is_onlink = 0; @@ -2096,20 +2317,21 @@ icmp6_redirect_input(m, off) if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) is_onlink = 1; /* on-link destination case */ if (!is_router && !is_onlink) { - log(LOG_ERR, + nd6log((LOG_ERR, "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - goto freeit; + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } /* validation passed */ icmp6len -= sizeof(*nd_rd); nd6_option_init(nd_rd + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "icmp6_redirect_input: " + nd6log((LOG_INFO, "icmp6_redirect_input: " "invalid ND option, rejected: %s\n", - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + /* nd6_options have incremented stats */ goto freeit; } @@ -2124,11 +2346,12 @@ icmp6_redirect_input(m, off) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "icmp6_redirect_input: lladdrlen mismatch for %s " "(if %d, icmp6 packet %d): %s\n", ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, - icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); + icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + goto bad; } /* RFC 2461 8.3 */ @@ -2171,6 +2394,11 @@ icmp6_redirect_input(m, off) freeit: m_freem(m); + return; + + bad: + icmp6stat.icp6s_badredirect++; + m_freem(m); } void @@ -2235,7 +2463,9 @@ icmp6_redirect_output(m0, rt) MCLGET(m, M_DONTWAIT); if (!m) goto fail; - maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; + m->m_pkthdr.rcvif = NULL; + m->m_len = 0; + maxlen = M_TRAILINGSPACE(m); maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + @@ -2440,7 +2670,7 @@ noredhdropt:; /* send the packet to outside... */ #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif /*IPSEC*/ ip6_output(m, NULL, NULL, 0, NULL, &outif); if (outif) { @@ -2458,11 +2688,13 @@ fail: m_freem(m0); } +#ifdef HAVE_NRL_INPCB +#define sotoin6pcb sotoinpcb +#define in6pcb inpcb +#define in6p_icmp6filt inp_icmp6filt +#endif /* * 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) @@ -2471,7 +2703,7 @@ icmp6_ctloutput(so, sopt) { int error = 0; int optlen; - register struct inpcb *inp = sotoinpcb(so); + struct inpcb *inp = sotoinpcb(so); int level, op, optname; if (sopt) { @@ -2481,11 +2713,12 @@ icmp6_ctloutput(so, sopt) optlen = sopt->sopt_valsize; } else level = op = optname = optlen = 0; + if (level != IPPROTO_ICMPV6) { return EINVAL; } - switch(op) { + switch (op) { case PRCO_SETOPT: switch (optname) { case ICMP6_FILTER: @@ -2533,42 +2766,79 @@ icmp6_ctloutput(so, sopt) return(error); } +#ifdef HAVE_NRL_INPCB +#undef sotoin6pcb +#undef in6pcb +#undef in6p_icmp6filt +#endif + +#ifndef HAVE_PPSRATECHECK +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif -#ifndef HAVE_RATECHECK /* - * ratecheck() returns true if it is okay to send. We return - * true if it is not okay to send. + * ppsratecheck(): packets (or events) per second limitation. */ static int -ratecheck(last, limit) - struct timeval *last; - struct timeval *limit; +ppsratecheck(lasttime, curpps, maxpps) + struct timeval *lasttime; + int *curpps; + int maxpps; /* maximum pps allowed */ { - struct timeval tp; - struct timeval nextsend; + struct timeval tv, delta; + int s, rv; - microtime(&tp); - tp.tv_sec = time_second; + s = splclock(); + microtime(&tv); + splx(s); - /* 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; - } - } + timersub(&tv, lasttime, &delta); + + /* + * check for 0,0 is so that the message will be seen at least once. + * if more than one second have passed since the last update of + * lasttime, reset the counter. + * + * we do increment *curpps even in *curpps < maxpps case, as some may + * try to use *curpps for stat purposes as well. + */ + if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || + delta.tv_sec >= 1) { + *lasttime = tv; + *curpps = 0; + rv = 1; + } else if (maxpps < 0) + rv = 1; + else if (*curpps < maxpps) + rv = 1; + else + rv = 0; + +#if 1 /*DIAGNOSTIC?*/ + /* be careful about wrap-around */ + if (*curpps + 1 > *curpps) + *curpps = *curpps + 1; +#else + /* + * assume that there's not too many calls to this function. + * not sure if the assumption holds, as it depends on *caller's* + * behavior, not the behavior of this function. + * IMHO it is wrong to make assumption on the caller's behavior, + * so the above #if is #if 1, not #ifdef DIAGNOSTIC. + */ + *curpps = *curpps + 1; +#endif - *last = tp; - return 1; + return (rv); } #endif @@ -2578,14 +2848,6 @@ ratecheck(last, limit) * 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 @@ -2599,13 +2861,8 @@ icmp6_ratelimit(dst, type, code) ret = 0; /*okay to send*/ /* PPS limit */ - icmp6errpps_count++; - if (icmp6errppslim && icmp6errpps_count > icmp6errppslim / 5) { - /* The packet is subject to pps limit */ - ret++; - } - - if (!ratecheck(&icmp6errratelim_last, &icmp6errratelim)) { + if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, + icmp6errppslim)) { /* The packet is subject to rate limit */ ret++; } diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 5f51cef11179..1fd566b42106 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6.c,v 1.99 2000/07/11 17:00:58 jinmei Exp $ */ +/* $KAME: in6.c,v 1.187 2001/05/24 07:43:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -88,6 +88,11 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/if_ether.h> +#ifndef SCOPEDROUTING +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> +#endif #include <netinet6/nd6.h> #include <netinet/ip6.h> @@ -96,6 +101,9 @@ #include <netinet6/ip6_mroute.h> #include <netinet6/in6_ifattach.h> #include <netinet6/scope6_var.h> +#ifndef SCOPEDROUTING +#include <netinet6/in6_pcb.h> +#endif #include "gif.h" #if NGIF > 0 @@ -124,103 +132,105 @@ const struct in6_addr in6mask64 = IN6MASK64; const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; +const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6, + 0, 0, IN6ADDR_ANY_INIT, 0}; + static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); +static int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, + struct sockaddr_in6 *, int)); +static void in6_unlink_ifa __P((struct in6_ifaddr *, struct ifnet *)); struct in6_multihead in6_multihead; /* XXX BSS initialization */ /* - * Check if the loopback entry will be automatically generated. - * if 0 returned, will not be automatically generated. - * if 1 returned, will be automatically generated. - */ -static int -in6_is_ifloop_auto(struct ifaddr *ifa) -{ -#define SIN6(s) ((struct sockaddr_in6 *)s) - /* - * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), - * or netmask is all0 or all1, then cloning will not happen, - * then we can't rely on its loopback entry generation. - */ - if ((ifa->ifa_flags & RTF_CLONING) == 0 || - (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || - (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) - && - IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, - &in6mask128)) || - ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) - return 0; - else - return 1; -#undef SIN6 -} - -/* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ static void in6_ifloop_request(int cmd, struct ifaddr *ifa) { - struct sockaddr_in6 lo_sa; struct sockaddr_in6 all1_sa; - struct rtentry *nrt = NULL, **nrtp = NULL; + struct rtentry *nrt = NULL; + int e; - bzero(&lo_sa, sizeof(lo_sa)); bzero(&all1_sa, sizeof(all1_sa)); - lo_sa.sin6_family = AF_INET6; - lo_sa.sin6_len = sizeof(struct sockaddr_in6); - all1_sa = lo_sa; - lo_sa.sin6_addr = in6addr_loopback; + all1_sa.sin6_family = AF_INET6; + all1_sa.sin6_len = sizeof(struct sockaddr_in6); all1_sa.sin6_addr = in6mask128; - + /* - * 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. + * We specify the address itself as the gateway, and set the + * RTF_LLINFO flag, so that the corresponding host route would have + * the flag, and thus applications that assume traditional behavior + * would be happy. Note that we assume the caller of the function + * (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest, + * which changes the outgoing interface to the loopback interface. */ - if (cmd == RTM_ADD) - nrtp = &nrt; - rtrequest(cmd, ifa->ifa_addr, - (struct sockaddr *)&lo_sa, - (struct sockaddr *)&all1_sa, - RTF_UP|RTF_HOST, nrtp); + e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + RTF_UP|RTF_HOST|RTF_LLINFO, &nrt); + if (e != 0) { + log(LOG_ERR, "in6_ifloop_request: " + "%s operation failed for %s (errno=%d)\n", + cmd == RTM_ADD ? "ADD" : "DELETE", + ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr), + e); + } /* * Make sure rt_ifa be equal to IFA, the second argument of the * function. - * We need this because when we refer rt_ifa->ia6_flags in ip6_input, - * we assume that the rt_ifa points to the address instead of the - * loopback address. + * We need this because when we refer to rt_ifa->ia6_flags in + * ip6_input, we assume that the rt_ifa points to the address instead + * of the loopback address. */ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { IFAFREE(nrt->rt_ifa); - ifa->ifa_refcnt++; + IFAREF(ifa); nrt->rt_ifa = ifa; } - if (nrt) - nrt->rt_refcnt--; + + /* + * Report the addition/removal of the address to the routing socket. + * XXX: since we called rtinit for a p2p interface with a destination, + * we end up reporting twice in such a case. Should we rather + * omit the second report? + */ + if (nrt) { + rt_newaddrmsg(cmd, ifa, e, nrt); + if (cmd == RTM_DELETE) { + if (nrt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + nrt->rt_refcnt++; + rtfree(nrt); + } + } else { + /* the cmd must be RTM_ADD here */ + nrt->rt_refcnt--; + } + } } /* - * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). - * Because, KAME needs loopback rtentry for ownaddr check in - * ip6_input(). + * Add ownaddr as loopback rtentry. We previously add the route only if + * necessary (ex. on a p2p link). However, since we now manage addresses + * separately from prefixes, we should always add the route. We can't + * rely on the cloning mechanism from the corresponding interface route + * any more. */ static void in6_ifaddloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct rtentry *rt; - - /* If there is no loopback entry, allocate one. */ - rt = rtalloc1(ifa->ifa_addr, 0, 0); - if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) - in6_ifloop_request(RTM_ADD, ifa); - if (rt) - rt->rt_refcnt--; - } + struct rtentry *rt; + + /* If there is no loopback entry, allocate one. */ + rt = rtalloc1(ifa->ifa_addr, 0, 0); + if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 || + (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) + in6_ifloop_request(RTM_ADD, ifa); + if (rt) + rt->rt_refcnt--; } /* @@ -231,28 +241,48 @@ static void in6_ifremloop(struct ifaddr *ifa) { struct in6_ifaddr *ia; + struct rtentry *rt; int ia_count = 0; /* - * All BSD variants except BSD/OS do not remove cloned routes + * Some of BSD variants 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 + * (see comments in net/net_osdep.h). Even for variants that do remove + * cloned routes, they could fail to remove the cloned routes when + * we handle multple addresses that share a common prefix. + * 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), - &ia->ia_addr.sin6_addr)) { - ia_count++; - if (ia_count > 1) - break; - } + + /* + * Delete the entry only if exact one ifa exists. More than one ifa + * can exist if we assign a same single address to multiple + * (probably p2p) interfaces. + * XXX: we should avoid such a configuration in IPv6... + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) { + ia_count++; + if (ia_count > 1) + break; } - if (ia_count == 1) + } + + if (ia_count == 1) { + /* + * Before deleting, check if a corresponding loopbacked host + * route surely exists. With this check, we can avoid to + * delete an interface direct route whose destination is same + * as the address being removed. This can happen when remofing + * a subnet-router anycast address on an interface attahced + * to a shared medium. + */ + rt = rtalloc1(ifa->ifa_addr, 0, 0); + if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 && + (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + rt->rt_refcnt--; in6_ifloop_request(RTM_DELETE, ifa); + } } } @@ -281,22 +311,40 @@ in6_ifindex2scopeid(idx) } int -in6_mask2len(mask) +in6_mask2len(mask, lim0) struct in6_addr *mask; + u_char *lim0; { - int x, y; - - for (x = 0; x < sizeof(*mask); x++) { - if (mask->s6_addr8[x] != 0xff) + int x = 0, y; + u_char *lim = lim0, *p; + + if (lim0 == NULL || + lim0 - (u_char *)mask > sizeof(*mask)) /* ignore the scope_id part */ + lim = (u_char *)mask + sizeof(*mask); + for (p = (u_char *)mask; p < lim; x++, p++) { + if (*p != 0xff) break; } y = 0; - if (x < sizeof(*mask)) { + if (p < lim) { for (y = 0; y < 8; y++) { - if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) + if ((*p & (0x80 >> y)) == 0) break; } } + + /* + * when the limit pointer is given, do a stricter check on the + * remaining bits. + */ + if (p < lim) { + if (y != 0 && (*p & (0x00ff >> y)) != 0) + return(-1); + for (p = p + 1; p < lim; p++) + if (*p != 0) + return(-1); + } + return x * 8 + y; } @@ -326,36 +374,14 @@ in6_control(so, cmd, data, ifp, p) struct proc *p; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia = NULL, *oia; + struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; - struct sockaddr_in6 oldaddr; -#ifdef COMPAT_IN6IFIOCTL - struct sockaddr_in6 net; -#endif - int error = 0, hostIsNew, prefixIsNew; - int newifaddr; int privileged; privileged = 0; if (p == NULL || !suser(p)) privileged++; - /* - * xxx should prevent processes for link-local addresses? - */ -#if NGIF > 0 - if (ifp && ifp->if_type == IFT_GIF) { - switch (cmd) { - case SIOCSIFPHYADDR_IN6: - if (!privileged) - return(EPERM); - /*fall through*/ - case SIOCGIFPSRCADDR_IN6: - case SIOCGIFPDSTADDR_IN6: - return gif_ioctl(ifp, cmd, data); - } - } -#endif switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: @@ -374,6 +400,7 @@ in6_control(so, cmd, data, ifp, p) if (!privileged) return(EPERM); /*fall through*/ + case OSIOCGIFINFO_IN6: case SIOCGIFINFO_IN6: case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: @@ -388,13 +415,11 @@ in6_control(so, cmd, data, ifp, p) case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: - if (!privileged) - return(EPERM); - /*fall through*/ case SIOCGIFPREFIX_IN6: - if (ip6_forwarding == 0) - return(EPERM); - return(in6_prefix_ioctl(so, cmd, data, ifp)); + log(LOG_NOTICE, + "prefix ioctls are now invalidated. " + "please use ifconfig.\n"); + return(EOPNOTSUPP); } switch(cmd) { @@ -430,12 +455,12 @@ in6_control(so, cmd, data, ifp, p) if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_addr.s6_addr16[1] == 0) { - /* interface ID is not embedded by the user */ + /* link 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 */ + return(EINVAL); /* link ID contradicts */ } if (sa6->sin6_scope_id) { if (sa6->sin6_scope_id != @@ -448,92 +473,39 @@ in6_control(so, cmd, data, ifp, p) } switch (cmd) { + case SIOCSIFADDR_IN6: + 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. + */ + /* we decided to obsolete this command (20000704) */ + return(EINVAL); case SIOCDIFADDR_IN6: /* - * for IPv4, we look for existing in6_ifaddr here to allow + * for IPv4, we look for existing in_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. + * semantics to be not preferable. */ if (ia == NULL) return(EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: - case SIOCSIFADDR_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. + * We always require users to specify a valid IPv6 address for + * the corresponding operation. */ -#endif - if (ifra->ifra_addr.sin6_family != AF_INET6) + if (ifra->ifra_addr.sin6_family != AF_INET6 || + ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) return(EAFNOSUPPORT); if (!privileged) return(EPERM); - 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_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; - - ia->ia_ifp = ifp; - if ((oia = in6_ifaddr) != NULL) { - for ( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; - /* 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 */ - struct in6_addrlifetime *lt; - lt = &ifra->ifra_lifetime; - if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME - && lt->ia6t_vltime + time_second < time_second) { - return EINVAL; - } - if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME - && lt->ia6t_pltime + time_second < time_second) { - return EINVAL; - } - } break; case SIOCGIFADDR_IN6: @@ -618,42 +590,6 @@ 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); - oldaddr = ia->ia_dstaddr; - ia->ia_dstaddr = ifr->ifr_dstaddr; - - /* link-local index check */ - if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { - /* 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] != - htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } - } - - if (ifp->if_ioctl && (error = (ifp->if_ioctl) - (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { - 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); - ia->ia_ifa.ifa_dstaddr = - (struct sockaddr *)&ia->ia_dstaddr; - rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); - } - break; - -#endif case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; break; @@ -673,129 +609,400 @@ in6_control(so, cmd, data, ifp, p) ia->ia6_lifetime.ia6t_preferred = 0; break; - case SIOCSIFADDR_IN6: - error = in6_ifinit(ifp, ia, &ifr->ifr_addr, 1); -#if 0 + case SIOCAIFADDR_IN6: + { + int i, error = 0; + struct nd_prefix pr0, *pr; + + /* + * first, make or update the interface address structure, + * and link it to the list. + */ + if ((error = in6_update_ifa(ifp, ifra, ia)) != 0) + return(error); + /* - * 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(). + * then, make the prefix on-link on the interface. + * XXX: we'd rather create the prefix before the address, but + * we need at least one address to install the corresponding + * interface route, so we configure the address first. */ - 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"); + + /* + * convert mask to prefix length (prefixmask has already + * been validated in in6_update_ifa(). + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) + break; /* we don't need to install a host route. */ + pr0.ndpr_prefix = ifra->ifra_addr; + pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr; + /* apply the mask for safety. */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; + } + /* + * XXX: since we don't have enough APIs, we just set inifinity + * to lifetimes. They can be overridden by later advertised + * RAs (when accept_rtadv is non 0), but we'd rather intend + * such a behavior. + */ + pr0.ndpr_raf_onlink = 1; /* should be configurable? */ + pr0.ndpr_raf_auto = + ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + + /* add the prefix if there's one. */ + if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { + /* + * nd6_prelist_add will install the corresponding + * interface route. + */ + if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) + return(error); + if (pr == NULL) { + log(LOG_ERR, "nd6_prelist_add succedded but " + "no prefix\n"); + return(EINVAL); /* XXX panic here? */ + } + } + if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) + == NULL) { + /* XXX: this should not happen! */ + log(LOG_ERR, "in6_control: addition succeeded, but" + " no ifaddr\n"); + } else { + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && + ia->ia6_ndpr == NULL) { /* new autoconfed addr */ + ia->ia6_ndpr = pr; + pr->ndpr_refcnt++; + + /* + * If this is the first autoconf address from + * the prefix, create a temporary address + * as well (when specified). + */ + if (ip6_use_tempaddr && + pr->ndpr_refcnt == 1) { + int e; + if ((e = in6_tmpifadd(ia, 1)) != 0) { + log(LOG_NOTICE, "in6_control: " + "failed to create a " + "temporary address, " + "errno=%d\n", + e); + } } } - /* release another refcnt for the link from in6_ifaddr */ - IFAFREE(&oia->ia_ifa); + + /* + * this might affect the status of autoconfigured + * addresses, that is, this address might make + * other addresses detached. + */ + pfxlist_onlink_check(); } -#endif - return error; + break; + } -#ifdef COMPAT_IN6IFIOCTL /* XXX should be unused */ - case SIOCSIFNETMASK_IN6: - ia->ia_prefixmask = ifr->ifr_addr; - bzero(&net, sizeof(net)); - net.sin6_len = sizeof(struct sockaddr_in6); - net.sin6_family = AF_INET6; - net.sin6_port = htons(0); - net.sin6_flowinfo = htonl(0); - net.sin6_addr.s6_addr32[0] - = ia->ia_addr.sin6_addr.s6_addr32[0] & - ia->ia_prefixmask.sin6_addr.s6_addr32[0]; - net.sin6_addr.s6_addr32[1] - = ia->ia_addr.sin6_addr.s6_addr32[1] & - ia->ia_prefixmask.sin6_addr.s6_addr32[1]; - net.sin6_addr.s6_addr32[2] - = ia->ia_addr.sin6_addr.s6_addr32[2] & - ia->ia_prefixmask.sin6_addr.s6_addr32[2]; - net.sin6_addr.s6_addr32[3] - = ia->ia_addr.sin6_addr.s6_addr32[3] & - ia->ia_prefixmask.sin6_addr.s6_addr32[3]; - ia->ia_net = net; + case SIOCDIFADDR_IN6: + { + int i = 0; + struct nd_prefix pr0, *pr; + + /* + * If the address being deleted is the only one that owns + * the corresponding prefix, expire the prefix as well. + * XXX: theoretically, we don't have to warry about such + * relationship, since we separate the address management + * and the prefix management. We do this, however, to provide + * as much backward compatibility as possible in terms of + * the ioctl operation. + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + pr0.ndpr_plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, + NULL); + if (pr0.ndpr_plen == 128) + goto purgeaddr; + pr0.ndpr_prefix = ia->ia_addr; + pr0.ndpr_mask = ia->ia_prefixmask.sin6_addr; + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + ia->ia_prefixmask.sin6_addr.s6_addr32[i]; + } + /* + * The logic of the following condition is a bit complicated. + * We expire the prefix when + * 1. the address obeys autoconfiguration and it is the + * only owner of the associated prefix, or + * 2. the address does not obey autoconf and there is no + * other owner of the prefix. + */ + if ((pr = nd6_prefix_lookup(&pr0)) != NULL && + (((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 && + pr->ndpr_refcnt == 1) || + ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0 && + pr->ndpr_refcnt == 0))) { + pr->ndpr_expire = 1; /* XXX: just for expiration */ + } + + purgeaddr: + in6_purgeaddr(&ia->ia_ifa); break; -#endif + } - case SIOCAIFADDR_IN6: - prefixIsNew = 0; - hostIsNew = 1; + default: + if (ifp == NULL || ifp->if_ioctl == 0) + return(EOPNOTSUPP); + return((*ifp->if_ioctl)(ifp, cmd, data)); + } + + return(0); +} + +/* + * Update parameters of an IPv6 interface address. + * If necessary, a new entry is created and linked into address chains. + * This function is separated from in6_control(). + * XXX: should this be performed under splnet()? + */ +int +in6_update_ifa(ifp, ifra, ia) + struct ifnet *ifp; + struct in6_aliasreq *ifra; + struct in6_ifaddr *ia; +{ + int error = 0, hostIsNew = 0, plen = -1; + struct in6_ifaddr *oia; + struct sockaddr_in6 dst6; + struct in6_addrlifetime *lt; - if (ifra->ifra_addr.sin6_len == 0) { - ifra->ifra_addr = ia->ia_addr; - hostIsNew = 0; - } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, - &ia->ia_addr.sin6_addr)) - hostIsNew = 0; + /* Validate parameters */ + if (ifp == NULL || ifra == NULL) /* this maybe redundant */ + return(EINVAL); - /* 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); + /* + * validate ifra_prefixmask. don't check sin6_family, netmask + * does not carry fields other than sin6_len. + */ + if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) + return(EINVAL); + /* + * Because the IPv6 address architecture is classless, we require + * users to specify a (non 0) prefix length (mask) for a new address. + * We also require the prefix (when specified) mask is valid, and thus + * reject a non-consecutive mask. + */ + if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0) + return(EINVAL); + if (ifra->ifra_prefixmask.sin6_len != 0) { + plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + (u_char *)&ifra->ifra_prefixmask + + ifra->ifra_prefixmask.sin6_len); + if (plen <= 0) + return(EINVAL); + } + else { /* - * The destination address for a p2p link must have a family - * of AF_UNSPEC or AF_INET6. + * In this case, ia must not be NULL. We just use its prefix + * length. */ - if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - ifra->ifra_dstaddr.sin6_family != AF_INET6 && - ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) - return(EAFNOSUPPORT); + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); + } + /* + * If the destination address on a p2p interface is specified, + * and the address is a scoped one, validate/set the scope + * zone identifier. + */ + dst6 = ifra->ifra_dstaddr; + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) && + (dst6.sin6_family == AF_INET6)) { + int scopeid; + +#ifndef SCOPEDROUTING + if ((error = in6_recoverscope(&dst6, + &ifra->ifra_dstaddr.sin6_addr, + ifp)) != 0) + return(error); +#endif + scopeid = in6_addr2scopeid(ifp, &dst6.sin6_addr); + if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */ + dst6.sin6_scope_id = scopeid; + else if (dst6.sin6_scope_id != scopeid) + return(EINVAL); /* scope ID mismatch. */ +#ifndef SCOPEDROUTING + if ((error = in6_embedscope(&dst6.sin6_addr, &dst6, NULL, NULL)) + != 0) + return(error); + dst6.sin6_scope_id = 0; /* XXX */ +#endif + } + /* + * The destination address can be specified only for a p2p or a + * loopback interface. If specified, the corresponding prefix length + * must be 128. + */ + if (ifra->ifra_dstaddr.sin6_family == AF_INET6) { + if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { + /* XXX: noisy message */ + log(LOG_INFO, "in6_update_ifa: a destination can be " + "specified for a p2p or a loopback IF only\n"); + return(EINVAL); + } + if (plen != 128) { + /* + * The following message seems noisy, but we dare to + * add it for diagnosis. + */ + log(LOG_INFO, "in6_update_ifa: prefixlen must be 128 " + "when dstaddr is specified\n"); + return(EINVAL); + } + } + /* lifetime consistency check */ + lt = &ifra->ifra_lifetime; + if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME + && lt->ia6t_vltime + time_second < time_second) { + return EINVAL; + } + if (lt->ia6t_vltime == 0) { /* - * The prefixmask must have a family of AF_UNSPEC or AF_INET6. + * the following log might be noisy, but this is a typical + * configuration mistake or a tool's bug. */ - if (ifra->ifra_prefixmask.sin6_family != AF_INET6 && - ifra->ifra_prefixmask.sin6_family != AF_UNSPEC) - return(EAFNOSUPPORT); + log(LOG_INFO, + "in6_update_ifa: valid lifetime is 0 for %s\n", + ip6_sprintf(&ifra->ifra_addr.sin6_addr)); + } + if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME + && lt->ia6t_pltime + time_second < time_second) { + return EINVAL; + } - if (ifra->ifra_prefixmask.sin6_len) { - in6_ifscrub(ifp, ia); - ia->ia_prefixmask = ifra->ifra_prefixmask; - prefixIsNew = 1; + /* + * If this is a new address, allocate a new ifaddr and link it + * into chains. + */ + if (ia == NULL) { + hostIsNew = 1; + 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_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_len = sizeof(ia->ia_addr); + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { + /* + * XXX: some functions expect that ifa_dstaddr is not + * NULL for p2p interfaces. + */ + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + } else { + ia->ia_ifa.ifa_dstaddr = NULL; } - 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)) { - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { - /* - * 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] != - htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } - } - prefixIsNew = 1; /* We lie; but effect's the same */ + ia->ia_ifa.ifa_netmask + = (struct sockaddr *)&ia->ia_prefixmask; + + ia->ia_ifp = ifp; + if ((oia = in6_ifaddr) != NULL) { + for ( ; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + + TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, + ifa_list); + } + + /* set prefix mask */ + if (ifra->ifra_prefixmask.sin6_len) { + /* + * We prohibit changing the prefix length of an existing + * address, because + * + such an operation should be rare in IPv6, and + * + the operation would confuse prefix management. + */ + if (ia->ia_prefixmask.sin6_len && + in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { + log(LOG_INFO, "in6_update_ifa: the prefix length of an" + " existing (%s) address should not be changed\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + error = EINVAL; + goto unlink; } - if (hostIsNew || prefixIsNew) { - error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); -#if 0 - if (error) - goto undo; -#endif + ia->ia_prefixmask = ifra->ifra_prefixmask; + } + + /* + * If a new destination address is specified, scrub the old one and + * install the new destination. Note that the interface must be + * p2p or loopback (see the check above.) + */ + if (dst6.sin6_family == AF_INET6 && + !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, + &ia->ia_dstaddr.sin6_addr)) { + int e; + + if ((ia->ia_flags & IFA_ROUTE) != 0 && + (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) + != 0) { + log(LOG_ERR, "in6_update_ifa: failed to remove " + "a route to the old destination: %s\n", + ip6_sprintf(&ia->ia_addr.sin6_addr)); + /* proceed anyway... */ } - if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { - int error_local = 0; + else + ia->ia_flags &= ~IFA_ROUTE; + ia->ia_dstaddr = dst6; + } + /* reset the interface and routing table appropriately. */ + if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) + goto unlink; + + /* + * Beyond this point, we should call in6_purgeaddr upon an error, + * not just go to unlink. + */ + +#if 0 /* disable this mechanism for now */ + /* update prefix list */ + if (hostIsNew && + (ifra->ifra_flags & IN6_IFF_NOPFX) == 0) { /* XXX */ + int iilen; + + iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - plen; + if ((error = in6_prefix_add_ifid(iilen, ia)) != 0) { + in6_purgeaddr((struct ifaddr *)ia); + return(error); + } + } +#endif + + if ((ifp->if_flags & IFF_MULTICAST) != 0) { + struct sockaddr_in6 mltaddr, mltmask; + struct in6_multi *in6m; + + if (hostIsNew) { /* * join solicited multicast addr for new host id */ @@ -808,98 +1015,182 @@ in6_control(so, cmd, data, ifp, p) llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error_local); - if (error == 0) - error = error_local; + (void)in6_addmulti(&llsol, ifp, &error); + if (error != 0) { + log(LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&llsol), if_name(ifp), + error); + in6_purgeaddr((struct ifaddr *)ia); + return(error); + } } - ia->ia6_flags = ifra->ifra_flags; - ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ - - ia->ia6_lifetime = ifra->ifra_lifetime; - /* for sanity */ - if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_expire = - time_second + ia->ia6_lifetime.ia6t_vltime; - } else - ia->ia6_lifetime.ia6t_expire = 0; - if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { - ia->ia6_lifetime.ia6t_preferred = - time_second + ia->ia6_lifetime.ia6t_pltime; - } else - ia->ia6_lifetime.ia6t_preferred = 0; + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; /* - * make sure to initialize ND6 information. this is to - * workaround issues with interfaces with IPv6 addresses, - * which have never brought # up. we are assuming that it is - * safe to nd6_ifattach multiple times. + * join link-local all-nodes address */ - nd6_ifattach(ifp); + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL) { + 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 (error != 0) { + log(LOG_WARNING, + "in6_update_ifa: addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } + } /* - * Perform DAD, if needed. - * XXX It may be of use, if we can administratively - * disable DAD. + * join node information group address */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: -#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); +#define hostnamelen strlen(hostname) + if (in6_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 (error != 0) { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for " + "%s on %s (errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } } - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_FAITH: - case IFT_GIF: - case IFT_LOOP: - default: - break; } +#undef hostnamelen - if (hostIsNew) { - int iilen; - int error_local = 0; - - iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&ia->ia_prefixmask.sin6_addr); - error_local = in6_prefix_add_ifid(iilen, ia); - if (error == 0) - error = error_local; + /* + * join node-local all-nodes address, on loopback. + * XXX: since "node-local" is obsoleted by interface-local, + * we have to join the group on every interface with + * some interface-boundary restriction. + */ + if (ifp->if_flags & IFF_LOOPBACK) { + struct in6_addr loop6 = in6addr_loopback; + ia = in6ifa_ifpwithaddr(ifp, &loop6); + + 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); + if (error != 0) { + log(LOG_WARNING, "in6_update_ifa: " + "addmulti failed for %s on %s " + "(errno=%d)\n", + ip6_sprintf(&mltaddr.sin6_addr), + if_name(ifp), error); + } + } } + } - return(error); + ia->ia6_flags = ifra->ifra_flags; + ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ + ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */ + + ia->ia6_lifetime = ifra->ifra_lifetime; + /* for sanity */ + if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_expire = + time_second + ia->ia6_lifetime.ia6t_vltime; + } else + ia->ia6_lifetime.ia6t_expire = 0; + if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { + ia->ia6_lifetime.ia6t_preferred = + time_second + ia->ia6_lifetime.ia6t_pltime; + } else + ia->ia6_lifetime.ia6t_preferred = 0; - case SIOCDIFADDR_IN6: - in6_purgeaddr(&ia->ia_ifa, ifp); - break; + /* + * make sure to initialize ND6 information. this is to workaround + * issues with interfaces with IPv6 addresses, which have never brought + * up. We are assuming that it is safe to nd6_ifattach multiple times. + */ + nd6_ifattach(ifp); - default: - if (ifp == NULL || ifp->if_ioctl == 0) - return(EOPNOTSUPP); - return((*ifp->if_ioctl)(ifp, cmd, data)); + /* + * Perform DAD, if needed. + * XXX It may be of use, if we can administratively + * disable DAD. + */ + if (in6if_do_dad(ifp) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0) { + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); } - return(0); + + return(error); + + unlink: + /* + * XXX: if a change of an existing address failed, keep the entry + * anyway. + */ + if (hostIsNew) + in6_unlink_ifa(ia, ifp); + return(error); } void -in6_purgeaddr(ifa, ifp) +in6_purgeaddr(ifa) struct ifaddr *ifa; - struct ifnet *ifp; { - struct in6_ifaddr *oia, *ia = (void *) ifa; - int plen; + struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; - in6_ifscrub(ifp, ia); + /* stop DAD processing */ + nd6_dad_stop(ifa); + + /* + * delete route to the destination of the address being purged. + * The interface must be p2p or loopback in this case. + */ + if ((ia->ia_flags & IFA_ROUTE) != 0 && ia->ia_dstaddr.sin6_len != 0) { + int e; + + if ((e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) + != 0) { + log(LOG_ERR, "in6_purgeaddr: failed to remove " + "a route to the p2p destination: %s on %s, " + "errno=%d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), if_name(ifp), + e); + /* proceed anyway... */ + } + else + ia->ia_flags &= ~IFA_ROUTE; + } + + /* Remove ownaddr's loopback rtentry, if it exists. */ + in6_ifremloop(&(ia->ia_ifa)); if (ifp->if_flags & IFF_MULTICAST) { /* @@ -921,9 +1212,19 @@ in6_purgeaddr(ifa, ifp) in6_delmulti(in6m); } + in6_unlink_ifa(ia, ifp); +} + +static void +in6_unlink_ifa(ia, ifp) + struct in6_ifaddr *ia; + struct ifnet *ifp; +{ + int plen, iilen; + struct in6_ifaddr *oia; + int s = splnet(); + 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)) @@ -933,45 +1234,61 @@ in6_purgeaddr(ifa, ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; - else - printf("Didn't unlink in6_ifaddr from list\n"); + else { + /* search failed */ + printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); + } } - { - int iilen; - plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr); + if (oia->ia6_ifpr) { /* check for safety */ + plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr, NULL); 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. + * When an autoconfigured address is being removed, release the + * reference to the base prefix. Also, since the release might + * affect the status of other (detached) addresses, call + * pfxlist_onlink_check(). */ - 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 */ - } + if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) { + if (oia->ia6_ndpr == NULL) { + log(LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " + "%p has no prefix\n", oia); + } else { + oia->ia6_ndpr->ndpr_refcnt--; + oia->ia6_flags &= ~IN6_IFF_AUTOCONF; + oia->ia6_ndpr = NULL; } + + pfxlist_onlink_check(); } - - /* release another refcnt for the link from in6_ifaddr */ + + /* + * release another refcnt for the link from in6_ifaddr. + * Note that we should decrement the refcnt at least once for all *BSD. + */ IFAFREE(&oia->ia_ifa); + + splx(s); +} + +void +in6_purgeif(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ifa, *nifa; + + for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa) + { + nifa = TAILQ_NEXT(ifa, ifa_list); + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + in6_purgeaddr(ifa); + } + + in6_ifdetach(ifp); } /* @@ -1107,7 +1424,6 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) } } - ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); @@ -1159,7 +1475,17 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) continue; if (!cmp) break; + bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); +#ifndef SCOPEDROUTING + /* + * XXX: this is adhoc, but is necessary to allow + * a user to specify fe80::/64 (not /10) for a + * link-local address. + */ + if (IN6_IS_ADDR_LINKLOCAL(&candidate)) + candidate.s6_addr16[1] = 0; +#endif candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[2] &= mask.s6_addr32[2]; @@ -1172,17 +1498,38 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { +#ifndef SCOPEDROUTING + struct sockaddr_in6 *s6; +#endif + /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); - +#ifndef SCOPEDROUTING /* XXX see above */ + s6 = (struct sockaddr_in6 *)&iflr->addr; + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { + s6->sin6_addr.s6_addr16[1] = 0; + s6->sin6_scope_id = + in6_addr2scopeid(ifp, &s6->sin6_addr); + } +#endif if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); +#ifndef SCOPEDROUTING /* XXX see above */ + s6 = (struct sockaddr_in6 *)&iflr->dstaddr; + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) { + s6->sin6_addr.s6_addr16[1] = 0; + s6->sin6_scope_id = + in6_addr2scopeid(ifp, + &s6->sin6_addr); + } +#endif } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = - in6_mask2len(&ia->ia_prefixmask.sin6_addr); + in6_mask2len(&ia->ia_prefixmask.sin6_addr, + NULL); iflr->flags = ia->ia6_flags; /*XXX*/ @@ -1218,96 +1565,73 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p) } /* - * Delete any existing route for an interface. - */ -void -in6_ifscrub(ifp, ia) - register struct ifnet *ifp; - register struct in6_ifaddr *ia; -{ - if ((ia->ia_flags & IFA_ROUTE) == 0) - return; - if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); - else - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - - /* Remove ownaddr's loopback rtentry, if it exists. */ - in6_ifremloop(&(ia->ia_ifa)); -} - -/* * Initialize an interface's intetnet6 address * and routing table entry. */ -int -in6_ifinit(ifp, ia, sin6, scrub) +static int +in6_ifinit(ifp, ia, sin6, newhost) struct ifnet *ifp; struct in6_ifaddr *ia; struct sockaddr_in6 *sin6; - int scrub; + int newhost; { - struct sockaddr_in6 oldaddr; - int error, flags = RTF_UP; + int error = 0, plen, ifacount = 0; int s = splimp(); + struct ifaddr *ifa; - oldaddr = ia->ia_addr; - ia->ia_addr = *sin6; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ - if (ifp->if_ioctl && - (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + if (ifa->ifa_addr == NULL) + continue; /* just for safety */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + ifacount++; + } + + ia->ia_addr = *sin6; + + if (ifacount <= 1 && ifp->if_ioctl && + (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { splx(s); - ia->ia_addr = oldaddr; return(error); } + splx(s); - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - case IFT_PPP: - ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - break; - } + ia->ia_ifa.ifa_metric = ifp->if_metric; + + /* we could do in(6)_socktrim here, but just omit it at this moment. */ - splx(s); - if (scrub) { - ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; - in6_ifscrub(ifp, ia); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - } - /* xxx - * in_socktrim - */ /* - * Add route for the network. + * Special case: + * If the destination address is specified for a point-to-point + * interface, install a route to the destination as an interface + * direct route. */ - ia->ia_ifa.ifa_metric = ifp->if_metric; - if (ifp->if_flags & IFF_LOOPBACK) { - ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; - flags |= RTF_HOST; - } else if (ifp->if_flags & IFF_POINTOPOINT) { - if (ia->ia_dstaddr.sin6_family != AF_INET6) - return(0); - flags |= RTF_HOST; - } - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ + if (plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { + if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, + RTF_UP | RTF_HOST)) != 0) + return(error); ia->ia_flags |= IFA_ROUTE; - /* XXX check if the subnet route points to the same interface */ - if (error == EEXIST) - error = 0; + } + if (plen < 128) { + /* + * The RTF_CLONING flag is necessary for in6_is_ifloop_auto(). + */ + ia->ia_ifa.ifa_flags |= RTF_CLONING; + } /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ - in6_ifaddloop(&(ia->ia_ifa)); + if (newhost) { + /* set the rtrequest function to create llinfo */ + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + in6_ifaddloop(&(ia->ia_ifa)); + } return(error); } @@ -1318,8 +1642,8 @@ in6_ifinit(ifp, ia, sin6, scrub) */ struct in6_multi * in6_addmulti(maddr6, ifp, errorp) - register struct in6_addr *maddr6; - register struct ifnet *ifp; + struct in6_addr *maddr6; + struct ifnet *ifp; int *errorp; { struct in6_multi *in6m; @@ -1408,7 +1732,7 @@ in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; int ignoreflags; { - register struct ifaddr *ifa; + struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -1436,7 +1760,7 @@ in6ifa_ifpwithaddr(ifp, addr) struct ifnet *ifp; struct in6_addr *addr; { - register struct ifaddr *ifa; + struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -1458,13 +1782,13 @@ static char digits[] = "0123456789abcdef"; static int ip6round = 0; char * ip6_sprintf(addr) -register struct in6_addr *addr; + const struct in6_addr *addr; { static char ip6buf[8][48]; - register int i; - register char *cp; - register u_short *a = (u_short *)addr; - register u_char *d; + int i; + char *cp; + u_short *a = (u_short *)addr; + u_char *d; int dcolon = 0; ip6round = (ip6round + 1) & 7; @@ -1522,6 +1846,27 @@ in6_localaddr(in6) return (0); } +int +in6_is_addr_deprecated(sa6) + struct sockaddr_in6 *sa6; +{ + struct in6_ifaddr *ia; + + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, + &sa6->sin6_addr) && +#ifdef SCOPEDROUTING + ia->ia_addr.sin6_scope_id == sa6->sin6_scope_id && +#endif + (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) + return(1); /* true */ + + /* XXX: do we still have to go thru the rest of the list? */ + } + + return(0); /* false */ +} + /* * return length of part which dst and src are equal * hard coding... @@ -1602,8 +1947,8 @@ in6_prefixlen2mask(maskp, len) */ struct in6_ifaddr * in6_ifawithscope(oifp, dst) - register struct ifnet *oifp; - register struct in6_addr *dst; + struct ifnet *oifp; + struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; int blen = -1; @@ -1612,7 +1957,9 @@ in6_ifawithscope(oifp, dst) struct in6_ifaddr *ifa_best = NULL; if (oifp == NULL) { +#if 0 printf("in6_ifawithscope: output interface is not specified\n"); +#endif return(NULL); } @@ -1675,13 +2022,20 @@ in6_ifawithscope(oifp, dst) * Also, if the current address has a smaller scope * than dst, ignore it unless ifa_best also has a * smaller scope. + * Consequently, after the two if-clause below, + * the followings must be satisfied: + * (scope(src) < scope(dst) && + * scope(best) < scope(dst)) + * OR + * (scope(best) >= scope(dst) && + * scope(src) >= scope(dst)) */ if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) - goto replace; + goto replace; /* (A) */ if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) - continue; + continue; /* (B) */ /* * A deprecated address SHOULD NOT be used in new @@ -1710,7 +2064,8 @@ in6_ifawithscope(oifp, dst) /* * A non-deprecated address is always preferred * to a deprecated one regardless of scopes and - * address matching. + * address matching (Note invariants ensured by the + * conditions (A) and (B) above.) */ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && (((struct in6_ifaddr *)ifa)->ia6_flags & @@ -1718,6 +2073,37 @@ in6_ifawithscope(oifp, dst) goto replace; /* + * When we use temporary addresses described in + * RFC 3041, we prefer temporary addresses to + * public autoconf addresses. Again, note the + * invariants from (A) and (B). Also note that we + * don't have any preference between static addresses + * and autoconf addresses (despite of whether or not + * the latter is temporary or public.) + */ + if (ip6_use_tempaddr) { + struct in6_ifaddr *ifat; + + ifat = (struct in6_ifaddr *)ifa; + if ((ifa_best->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == IN6_IFF_AUTOCONF && + (ifat->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) { + goto replace; + } + if ((ifa_best->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY) && + (ifat->ia6_flags & + (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + == IN6_IFF_AUTOCONF) { + continue; + } + } + + /* * At this point, we have two cases: * 1. we are looking at a non-deprecated address, * and ifa_best is also non-deprecated. @@ -1743,64 +2129,68 @@ in6_ifawithscope(oifp, dst) * 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. + * prefered to any addresses of smaller scopes + * (this must be already done above.) + * - addresses on the outgoing I/F are preferred to + * ones on other interfaces if none of above + * tiebreaks. In the table below, the column "bI" + * means if the best_ifa is on the outgoing + * interface, and the column "sI" means if the ifa + * is on the outgoing interface. * - If there is no other reasons to choose one, - * addresses on the outgoing I/F are preferred. + * longest address match against dst is considered. * * 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 bscopecmp match bI oI | replace? + * N/A equal N/A Y N | No (1) + * N/A equal N/A N Y | Yes (2) + * N/A equal larger N/A | Yes (3) + * N/A equal !larger N/A | No (4) + * larger larger N/A N/A | No (5) + * larger smaller N/A N/A | Yes (6) + * smaller larger N/A N/A | Yes (7) + * smaller smaller N/A N/A | No (8) + * equal smaller N/A N/A | Yes (9) + * equal larger (already done at A above) */ 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) */ + if (bscopecmp == 0) { + struct ifnet *bifp = ifa_best->ia_ifp; + + if (bifp == oifp && ifp != oifp) /* (1) */ + continue; + if (bifp != oifp && ifp == oifp) /* (2) */ + goto replace; + + /* + * Both bifp and ifp are on the outgoing + * interface, or both two are on a different + * interface from the outgoing I/F. + * now we need address matching against dst + * for tiebreaking. + */ + tlen = in6_matchlen(IFA_IN6(ifa), dst); + matchcmp = tlen - blen; + if (matchcmp > 0) /* (3) */ goto replace; - continue; /* (2) */ + continue; /* (4) */ } if (dscopecmp > 0) { - if (bscopecmp > 0) /* (3) */ + if (bscopecmp > 0) /* (5) */ continue; - goto replace; /* (4) */ + goto replace; /* (6) */ } if (dscopecmp < 0) { - if (bscopecmp > 0) /* (5) */ + if (bscopecmp > 0) /* (7) */ goto replace; - continue; /* (6) */ + continue; /* (8) */ } /* 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) */ + goto replace; /* (9) */ replace: ifa_best = (struct in6_ifaddr *)ifa; @@ -1837,8 +2227,8 @@ in6_ifawithscope(oifp, dst) */ struct in6_ifaddr * in6_ifawithifp(ifp, dst) - register struct ifnet *ifp; - register struct in6_addr *dst; + struct ifnet *ifp; + struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; @@ -1943,6 +2333,43 @@ in6_if_up(ifp) } } +int +in6if_do_dad(ifp) + struct ifnet *ifp; +{ + if ((ifp->if_flags & IFF_LOOPBACK) != 0) + return(0); + + switch (ifp->if_type) { +#ifdef IFT_DUMMY + case IFT_DUMMY: +#endif + case IFT_FAITH: + /* + * These interfaces do not have the IFF_LOOPBACK flag, + * but loop packets back. We do not have to do DAD on such + * interfaces. We should even omit it, because loop-backed + * NS would confuse the DAD procedure. + */ + return(0); + default: + /* + * Our DAD routine requires the interface up and running. + * However, some interfaces can be up before the RUNNING + * status. Additionaly, users may try to assign addresses + * before the interface becomes up (or running). + * We simply skip DAD in such a case as a work around. + * XXX: we should rather mark "tentative" on such addresses, + * and do DAD after the interface becomes ready. + */ + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != + (IFF_UP|IFF_RUNNING)) + return(0); + + return(1); + } +} + /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h index 1159e5251e32..168d1df64dc0 100644 --- a/sys/netinet6/in6.h +++ b/sys/netinet6/in6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6.h,v 1.48 2000/06/26 15:55:32 itojun Exp $ */ +/* $KAME: in6.h,v 1.89 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,21 +66,19 @@ */ #ifndef __KAME_NETINET_IN_H_INCLUDED_ -#error "do not include netinet6/in6.h directly, include netinet/in.h" +#error "do not include netinet6/in6.h directly, include netinet/in.h. see RFC2553" #endif #ifndef _NETINET6_IN6_H_ #define _NETINET6_IN6_H_ -#ifndef _XOPEN_SOURCE -#include <sys/queue.h> -#endif - /* * Identification of the network protocol stack + * for *BSD-current/release: http://www.kame.net/dev/cvsweb.cgi/kame/COVERAGE + * has the table of implementation/integration differences. */ #define __KAME__ -#define __KAME_VERSION "20000701/FreeBSD-current" +#define __KAME_VERSION "20010528/FreeBSD" /* * Local port number conventions: @@ -148,7 +146,7 @@ struct sockaddr_in6 { 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 */ - u_int32_t sin6_scope_id; /* intface scope id */ + u_int32_t sin6_scope_id; /* scope zone index */ }; /* @@ -167,6 +165,8 @@ struct sockaddr_in6 { #endif #ifdef _KERNEL +extern const struct sockaddr_in6 sa6_any; + extern const struct in6_addr in6mask0; extern const struct in6_addr in6mask32; extern const struct in6_addr in6mask64; @@ -238,41 +238,49 @@ extern const struct in6_addr in6addr_linklocal_allrouters; (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #endif +#ifdef _KERNEL /* non standard */ +/* see if two addresses are equal in a scope-conscious manner. */ +#define SA6_ARE_ADDR_EQUAL(a, b) \ + (((a)->sin6_scope_id == 0 || (b)->sin6_scope_id == 0 || \ + ((a)->sin6_scope_id == (b)->sin6_scope_id)) && \ + (bcmp(&(a)->sin6_addr, &(b)->sin6_addr, sizeof(struct in6_addr)) == 0)) +#endif + /* * Unspecified */ #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) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) == 0)) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) == 0)) /* * Loopback */ #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) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) == ntohl(1))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(1))) /* * IPv4 compatible */ #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) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) != 0) && \ - (*(u_int32_t *)(&(a)->s6_addr[12]) != ntohl(1))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) != 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[12]) != ntohl(1))) /* * Mapped */ #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))) + ((*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) /* * KAME Scope Values @@ -350,13 +358,27 @@ extern const struct in6_addr in6addr_linklocal_allrouters; #endif /* - * KAME Scope + * Wildcard Socket */ +#if 0 /*pre-RFC2553*/ +#define IN6_IS_ADDR_ANY(a) IN6_IS_ADDR_UNSPECIFIED(a) +#endif + #ifdef _KERNEL /*nonstandard*/ +/* + * KAME Scope + */ #define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) -#endif + +#define IFA6_IS_DEPRECATED(a) \ + ((a)->ia6_lifetime.ia6t_preferred != 0 && \ + (a)->ia6_lifetime.ia6t_preferred < time_second) +#define IFA6_IS_INVALID(a) \ + ((a)->ia6_lifetime.ia6t_expire != 0 && \ + (a)->ia6_lifetime.ia6t_expire < time_second) +#endif /* _KERNEL */ /* * IP6 route structure @@ -389,26 +411,34 @@ struct route_in6 { #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 */ +/* RFC2292 options */ +#define IPV6_PKTINFO 19 /* bool; send/recv 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 */ +#define IPV6_V6ONLY 27 /* bool; only bind INET6 at wildcard bind */ +#ifndef _KERNEL +#define IPV6_BINDV6ONLY IPV6_V6ONLY +#endif -/* for IPsec */ +#if 1 /*IPSEC*/ #define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ +#endif #define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ -/* for IPV6FIREWALL */ +#if 1 /*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 */ +#endif + /* to define items, should talk with KAME guys first, for *BSD compatibility */ #define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ @@ -528,39 +558,44 @@ struct in6_pktinfo { #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 */ +#if 0 /*obsolete*/ #define IPV6CTL_MAPPED_ADDR 23 -#ifdef notdef /*__NetBSD__ - reserved, don't delete*/ -#define IPV6CTL_BINDV6ONLY 24 #endif +#define IPV6CTL_V6ONLY 24 #define IPV6CTL_RTEXPIRE 25 /* cloned route expiration time */ #define IPV6CTL_RTMINEXPIRE 26 /* min value for expiration time */ #define IPV6CTL_RTMAXCACHE 27 /* trigger level for dynamic expire */ + +#define IPV6CTL_USETEMPADDR 32 /* use temporary addresses (RFC3041) */ +#define IPV6CTL_TEMPPLTIME 33 /* preferred lifetime for tmpaddrs */ +#define IPV6CTL_TEMPVLTIME 34 /* valid lifetime for tmpaddrs */ +#define IPV6CTL_AUTO_LINKLOCAL 35 /* automatic link-local addr assign */ +#define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ + /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ -#define IPV6CTL_MAXID 28 +#define IPV6CTL_MAXID 37 + #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_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; +struct cmsghdr; 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 *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); -extern void in6_if_up __P((struct ifnet *)); -struct sockaddr; +extern void in6_if_up __P((struct ifnet *)); +struct sockaddr; void in6_sin6_2_sin __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); @@ -569,33 +604,51 @@ 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 struct cmsghdr; -extern int inet6_option_space __P((int)); -extern int inet6_option_init __P((void *, struct cmsghdr **, int)); -extern int inet6_option_append __P((struct cmsghdr *, const u_int8_t *, - int, int)); -extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int)); -extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **)); -extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, - int)); - -extern size_t inet6_rthdr_space __P((int, int)); -extern struct cmsghdr *inet6_rthdr_init __P((void *, int)); -extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *, - u_int)); -extern int inet6_rthdr_lasthop __P((struct cmsghdr *, u_int)); -extern int inet6_rthdr_segments __P((const struct cmsghdr *)); -extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); -extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); -extern int inet6_rthdr_reverse __P((const struct cmsghdr *, - struct cmsghdr *)); +extern int inet6_option_space __P((int)); +extern int inet6_option_init __P((void *, struct cmsghdr **, int)); +extern int inet6_option_append __P((struct cmsghdr *, const u_int8_t *, + int, int)); +extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int)); +extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **)); +extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, int)); + +extern size_t inet6_rthdr_space __P((int, int)); +extern struct cmsghdr *inet6_rthdr_init __P((void *, int)); +extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *, + unsigned int)); +extern int inet6_rthdr_lasthop __P((struct cmsghdr *, unsigned int)); +#if 0 /* not implemented yet */ +extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *)); +#endif +extern int inet6_rthdr_segments __P((const struct cmsghdr *)); +extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); +extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); + +extern int inet6_opt_init __P((void *, size_t)); +extern int inet6_opt_append __P((void *, size_t, int, u_int8_t, + size_t, u_int8_t, void **)); +extern int inet6_opt_finish __P((void *, size_t, int)); +extern int inet6_opt_set_val __P((void *, size_t, void *, int)); + +extern int inet6_opt_next __P((void *, size_t, int, u_int8_t *, + size_t *, void **)); +extern int inet6_opt_find __P((void *, size_t, int, u_int8_t, + size_t *, void **)); +extern int inet6_opt_get_val __P((void *, size_t, void *, int)); +extern size_t inet6_rth_space __P((int, int)); +extern void *inet6_rth_init __P((void *, int, int, int)); +extern int inet6_rth_add __P((void *, const struct in6_addr *)); +extern int inet6_rth_reverse __P((const void *, void *)); +extern int inet6_rth_segments __P((const void *)); +extern struct in6_addr *inet6_rth_getaddr __P((const void *, int)); __END_DECLS #endif /* !_NETINET6_IN6_H_ */ diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c index db564cf571d9..fbd95cbbaaa0 100644 --- a/sys/netinet6/in6_cksum.c +++ b/sys/netinet6/in6_cksum.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_cksum.c,v 1.9 2000/09/09 15:33:31 itojun Exp $ */ +/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -92,13 +92,13 @@ int in6_cksum(m, nxt, off, len) - register struct mbuf *m; + struct mbuf *m; u_int8_t nxt; u_int32_t off, len; { - register u_int16_t *w; - register int sum = 0; - register int mlen = 0; + u_int16_t *w; + int sum = 0; + int mlen = 0; int byte_swapped = 0; #if 0 int srcifid = 0, dstifid = 0; diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index c8c1450f02fe..57bec0ef1893 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ +/* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -30,10 +30,6 @@ * SUCH DAMAGE. */ -/* - * in6_gif.c - */ - #include "opt_inet.h" #include "opt_inet6.h" @@ -43,6 +39,10 @@ #include <sys/sockio.h> #include <sys/mbuf.h> #include <sys/errno.h> +#include <sys/queue.h> +#include <sys/syslog.h> + +#include <sys/malloc.h> #include <net/if.h> #include <net/route.h> @@ -148,34 +148,19 @@ in6_gif_output(ifp, family, m, rt) ip6->ip6_nxt = proto; ip6->ip6_hlim = ip6_gif_hlim; ip6->ip6_src = sin6_src->sin6_addr; - if (ifp->if_flags & IFF_LINK0) { - /* multi-destination mode */ - 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); - return ENETUNREACH; - } - } else { - /* bidirectional configured tunnel mode */ - if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) - ip6->ip6_dst = sin6_dst->sin6_addr; - else { - m_freem(m); - return ENETUNREACH; - } + /* bidirectional configured tunnel mode */ + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else { + m_freem(m); + return ENETUNREACH; } - if (ifp->if_flags & IFF_LINK1) { - otos = 0; + if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); - ip6->ip6_flow |= htonl((u_int32_t)otos << 20); - } + else + ip_ecn_ingress(ECN_NOCARE, &otos, &itos); + ip6->ip6_flow &= ~ntohl(0xff00000); + ip6->ip6_flow |= htonl((u_int32_t)otos << 20); if (dst->sin6_family != sin6_dst->sin6_family || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { @@ -262,6 +247,8 @@ int in6_gif_input(mp, offp, proto) ip = mtod(m, struct ip *); if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); + else + ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos); break; } #endif /* INET */ @@ -278,6 +265,8 @@ int in6_gif_input(mp, offp, proto) ip6 = mtod(m, struct ip6_hdr *); if (gifp->if_flags & IFF_LINK1) ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); + else + ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow); break; } #endif @@ -321,17 +310,14 @@ gif_encapcheck6(m, off, proto, arg) 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) { + if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && + (m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { struct sockaddr_in6 sin6; struct rtentry *rt; @@ -341,15 +327,18 @@ gif_encapcheck6(m, off, proto, arg) 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); + if (!rt || rt->rt_ifp != m->m_pkthdr.rcvif) { +#if 0 + log(LOG_WARNING, "%s: packet from %s dropped " + "due to ingress filter\n", if_name(&sc->gif_if), + ip6_sprintf(&sin6.sin6_addr)); +#endif + if (rt) + rtfree(rt); return 0; } rtfree(rt); } - /* prioritize: IFF_LINK0 mode is less preferred */ - return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; + return 128 * 2; } diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index c839d014a00d..516826d76b38 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_ifattach.c,v 1.67 2000/10/01 10:51:54 itojun Exp $ */ +/* $KAME: in6_ifattach.c,v 1.118 2001/05/24 07:44:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -36,6 +36,7 @@ #include <sys/socket.h> #include <sys/sockio.h> #include <sys/kernel.h> +#include <sys/syslog.h> #include <sys/md5.h> #include <net/if.h> @@ -49,6 +50,7 @@ #include <netinet/ip6.h> #include <netinet6/ip6_var.h> +#include <netinet6/in6_var.h> #include <netinet6/in6_ifattach.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> @@ -62,13 +64,20 @@ size_t in6_ifstatmax = 0; size_t icmp6_ifstatmax = 0; unsigned long in6_maxmtu = 0; +#ifdef IP6_AUTO_LINKLOCAL +int ip6_auto_linklocal = IP6_AUTO_LINKLOCAL; +#else +int ip6_auto_linklocal = 1; /* enable by default */ +#endif + +struct callout in6_tmpaddrtimer_ch; + static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); +static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *)); 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 @@ -122,6 +131,90 @@ get_rand_ifid(ifp, in6) return 0; } +static int +generate_tmp_ifid(seed0, seed1, ret) + u_int8_t *seed0, *ret; + const u_int8_t *seed1; +{ + MD5_CTX ctxt; + u_int8_t seed[16], digest[16], nullbuf[8]; + u_int32_t val32; + struct timeval tv; + + /* If there's no hisotry, start with a random seed. */ + bzero(nullbuf, sizeof(nullbuf)); + if (bcmp(nullbuf, seed0, sizeof(nullbuf)) == 0) { + int i; + + for (i = 0; i < 2; i++) { + microtime(&tv); + val32 = random() ^ tv.tv_usec; + bcopy(&val32, seed + sizeof(val32) * i, sizeof(val32)); + } + } else + bcopy(seed0, seed, 8); + + /* copy the right-most 64-bits of the given address */ + /* XXX assumption on the size of IFID */ + bcopy(seed1, &seed[8], 8); + + if (0) { /* for debugging purposes only */ + int i; + + printf("generate_tmp_ifid: new randomized ID from: "); + for (i = 0; i < 16; i++) + printf("%02x", seed[i]); + printf(" "); + } + + /* generate 16 bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, seed, sizeof(seed)); + MD5Final(digest, &ctxt); + + /* + * RFC 3041 3.2.1. (3) + * Take the left-most 64-bits of the MD5 digest and set bit 6 (the + * left-most bit is numbered 0) to zero. + */ + bcopy(digest, ret, 8); + ret[0] &= ~EUI64_UBIT; + + /* + * XXX: we'd like to ensure that the generated value is not zero + * for simplicity. If the caclculated digest happens to be zero, + * use a random non-zero value as the last resort. + */ + if (bcmp(nullbuf, ret, sizeof(nullbuf)) == 0) { + log(LOG_INFO, + "generate_tmp_ifid: computed MD5 value is zero.\n"); + + microtime(&tv); + val32 = random() ^ tv.tv_usec; + val32 = 1 + (val32 % (0xffffffff - 1)); + } + + /* + * RFC 3041 3.2.1. (4) + * Take the rightmost 64-bits of the MD5 digest and save them in + * stable storage as the history value to be used in the next + * iteration of the algorithm. + */ + bcopy(&digest[8], seed0, 8); + + if (0) { /* for debugging purposes only */ + int i; + + printf("to: "); + for (i = 0; i < 16; i++) + printf("%02x", digest[i]); + printf("\n"); + } + + return 0; +} + /* * Get interface identifier for the specified interface. * XXX assumes single sockaddr_dl (AF_LINK address) per an interface @@ -165,7 +258,14 @@ found: case IFT_ETHER: case IFT_FDDI: case IFT_ATM: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif /* IEEE802/EUI64 cases - what others? */ + /* IEEE1394 uses 16byte length address starting with EUI64 */ + if (addrlen > 8) + addrlen = 8; /* look at IEEE802/EUI64 only */ if (addrlen != 8 && addrlen != 6) @@ -217,7 +317,7 @@ found: case IFT_STF: #endif /* - * mech-06 says: "SHOULD use IPv4 address as ifid source". + * RFC2893 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. @@ -262,19 +362,15 @@ get_ifid(ifp0, altifp, in6) /* 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 + nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n", + if_name(ifp0))); goto success; } /* 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 + nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n", + if_name(ifp0), if_name(altifp))); goto success; } @@ -291,21 +387,18 @@ get_ifid(ifp0, altifp, in6) * globally unique */ if (IFID_UNIVERSAL(in6)) { - -#ifdef ND6_DEBUG - printf("%s: borrow interface identifier from %s\n", - if_name(ifp0), if_name(ifp)); -#endif + nd6log((LOG_DEBUG, + "%s: borrow interface identifier from %s\n", + if_name(ifp0), if_name(ifp))); 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 + nd6log((LOG_DEBUG, + "%s: interface identifier generated by random number\n", + if_name(ifp0))); goto success; } @@ -313,223 +406,152 @@ get_ifid(ifp0, altifp, in6) return -1; success: -#ifdef ND6_DEBUG - printf("%s: ifid: " + nd6log((LOG_INFO, "%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 + in6->s6_addr[14], in6->s6_addr[15])); return 0; } -/* - * configure IPv6 interface address. XXX code duplicated with in.c - */ static int -in6_ifattach_addaddr(ifp, ia) +in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; - struct in6_ifaddr *ia; + struct ifnet *altifp; /* secondary EUI64 source */ { - 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); - } + struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + struct nd_prefix pr0; + int i, error; /* - * link the interface address to global list + * configure link-local address. */ - 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++; + bzero(&ifra, sizeof(ifra)); /* - * Also link into the IPv6 address chain beginning with in6_ifaddr. - * kazu opposed it, but itojun & jinmei wanted. + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - 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++; + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); - /* - * 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; - default: - printf("%s: SIOCSIFADDR error %d\n", if_name(ifp), - error); - break; + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); +#ifdef SCOPEDROUTING + ifra.ifra_addr.sin6_addr.s6_addr16[1] = 0 +#else + ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ +#endif + ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0; + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0; + ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1); + } else { + if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) { + nd6log((LOG_ERR, + "%s: no ifid available\n", if_name(ifp))); + return -1; } - - /* 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; } +#ifdef SCOPEDROUTING + ifra.ifra_addr.sin6_scope_id = + in6_addr2scopeid(ifp, &ifra.ifra_addr.sin6_addr); +#endif - /* 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: + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_prefixmask.sin6_addr = in6mask64; +#ifdef SCOPEDROUTING + /* take into accound the sin6_scope_id field for routing */ + ifra.ifra_prefixmask.sin6_scope_id = 0xffffffff; #endif - rtflag = 0; - break; - default: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - break; - } - } + /* link-local addresses should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - /* 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; + /* + * Do not let in6_update_ifa() do DAD, since we need a random delay + * before sending an NS at the first time the inteface becomes up. + * Instead, in6_if_up() will start DAD with a proper random delay. + */ + ifra.ifra_flags |= IN6_IFF_NODAD; - if ((rtflag & RTF_CLONING) != 0 && - (ifp->if_flags & IFF_MULTICAST) != 0) { + /* + * Now call in6_update_ifa() to do a bunch of procedures to configure + * a link-local address. We can set NULL to the 3rd argument, because + * we know there's no other link-local address on the interface. + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { /* - * join solicited multicast address + * XXX: When the interface does not support IPv6, this call + * would fail in the SIOCSIFADDR ioctl. I believe the + * notification is rather confusing in this case, so just + * supress it. (jinmei@kame.net 20010130) */ - 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 */ - } + if (error != EAFNOSUPPORT) + log(LOG_NOTICE, "in6_ifattach_linklocal: failed to " + "configure a link-local address on %s " + "(errno=%d)\n", + if_name(ifp), error); + return(-1); } - return 0; -} - -static int -in6_ifattach_linklocal(ifp, altifp) - struct ifnet *ifp; - struct ifnet *altifp; /*secondary EUI64 source*/ -{ - struct in6_ifaddr *ia; - /* - * configure link-local address + * Adjust ia6_flags so that in6_if_up will perform DAD. + * XXX: Some P2P interfaces seem not to send packets just after + * becoming up, so we skip p2p interfaces for safety. */ - 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; - - /* 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; - } + ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ +#ifdef DIAGNOSTIC + if (!ia) { + panic("ia == NULL in in6_ifattach_linklocal"); + /*NOTREACHED*/ } -#ifdef SCOPEDROUTING - ia->ia_addr.sin6_scope_id = in6_addr2scopeid(ifp, - &ia->ia_addr.sin6_addr); #endif + if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) { + ia->ia6_flags &= ~IN6_IFF_NODAD; + ia->ia6_flags |= IN6_IFF_TENTATIVE; + } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ - return -1; + /* + * Make the link-local prefix (fe80::/64%link) as on-link. + * Since we'd like to manage prefixes separately from addresses, + * we make an ND6 prefix structure for the link-local prefix, + * and add it to the prefix list as a never-expire prefix. + * XXX: this change might affect some existing code base... + */ + bzero(&pr0, sizeof(pr0)); + pr0.ndpr_ifp = ifp; + /* this should be 64 at this moment. */ + pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL); + pr0.ndpr_mask = ifra.ifra_prefixmask.sin6_addr; + pr0.ndpr_prefix = ifra.ifra_addr; + /* apply the mask for safety. (nd6_prelist_add will apply it again) */ + for (i = 0; i < 4; i++) { + pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= + in6mask64.s6_addr32[i]; + } + /* + * Initialize parameters. The link-local prefix must always be + * on-link, and its lifetimes never expire. + */ + pr0.ndpr_raf_onlink = 1; + pr0.ndpr_raf_auto = 1; /* probably meaningless */ + pr0.ndpr_vltime = ND6_INFINITE_LIFETIME; + pr0.ndpr_pltime = ND6_INFINITE_LIFETIME; + /* + * Since there is no other link-local addresses, nd6_prefix_lookup() + * probably returns NULL. However, we cannot always expect the result. + * For example, if we first remove the (only) existing link-local + * address, and then reconfigure another one, the prefix is still + * valid with referring to the old link-local address. + */ + if (nd6_prefix_lookup(&pr0) == NULL) { + if ((error = nd6_prelist_add(&pr0, NULL, NULL)) != 0) + return(error); } return 0; @@ -539,47 +561,52 @@ static int in6_ifattach_loopback(ifp) struct ifnet *ifp; /* must be IFT_LOOP */ { - struct in6_ifaddr *ia; + struct in6_aliasreq ifra; + int error; + + bzero(&ifra, sizeof(ifra)); /* - * configure link-local address + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. */ - 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; + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + ifra.ifra_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. + * address. Follows IPv4 practice - see in_ifinit(). */ - 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; + ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_dstaddr.sin6_family = AF_INET6; + ifra.ifra_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; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_addr = in6addr_loopback; - ia->ia_ifa.ifa_metric = ifp->if_metric; + /* the loopback address should NEVER expire. */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; - if (in6_ifattach_addaddr(ifp, ia) != 0) { - /* ia will be freed on failure */ - return -1; + /* we don't need to perfrom DAD on loopback interfaces. */ + ifra.ifra_flags |= IN6_IFF_NODAD; + + /* skip registration to the prefix list. XXX should be temporary. */ + ifra.ifra_flags |= IN6_IFF_NOPFX; + + /* + * We can set NULL to the 3rd arg. See comments in + * in6_ifattach_linklocal(). + */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + log(LOG_ERR, "in6_ifattach_loopback: failed to configure " + "the loopback address on %s (errno=%d)\n", + if_name(ifp), error); + return(-1); } return 0; @@ -591,17 +618,19 @@ in6_ifattach_loopback(ifp) * * when ifp == NULL, the caller is responsible for filling scopeid. */ -static int -nigroup(ifp, name, namelen, in6) +int +in6_nigroup(ifp, name, namelen, in6) struct ifnet *ifp; const char *name; int namelen; struct in6_addr *in6; { const char *p; + u_char *q; MD5_CTX ctxt; u_int8_t digest[16]; char l; + char n[64]; /* a single label must not exceed 63 chars */ if (!namelen || !name) return -1; @@ -609,16 +638,21 @@ nigroup(ifp, name, namelen, in6) p = name; while (p && *p && *p != '.' && p - name < namelen) p++; - if (p - name > 63) + if (p - name > sizeof(n) - 1) return -1; /*label too long*/ l = p - name; + strncpy(n, name, l); + n[(int)l] = '\0'; + for (q = n; *q; q++) { + if ('A' <= *q && *q <= 'Z') + *q = *q - 'A' + 'a'; + } /* 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); + MD5Update(&ctxt, n, l); MD5Final(digest, &ctxt); bzero(in6, sizeof(*in6)); @@ -644,15 +678,21 @@ in6_nigroup_attach(name, namelen) 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) + if (in6_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); + if (!in6m) { + if (!in6_addmulti(&mltaddr.sin6_addr, ifp, &error)) { + nd6log((LOG_ERR, "%s: failed to join %s " + "(errno=%d)\n", if_name(ifp), + ip6_sprintf(&mltaddr.sin6_addr), + error)); + } + } } } @@ -668,7 +708,7 @@ in6_nigroup_detach(name, namelen) 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) + if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) return; for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) @@ -691,13 +731,16 @@ in6_ifattach(ifp, altifp) 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); + + /* some of the interfaces are inherently not IPv6 capable */ + switch (ifp->if_type) { +#ifdef IFT_BRIDGE /*OpenBSD 2.8*/ + case IFT_BRIDGE: + return; +#endif + } /* * We have some arrays that should be indexed by if_index. @@ -763,134 +806,41 @@ in6_ifattach(ifp, altifp) * usually, we require multicast capability to the interface */ if ((ifp->if_flags & IFF_MULTICAST) == 0) { - printf("%s: not multicast capable, IPv6 not enabled\n", + log(LOG_INFO, "in6_ifattach: " + "%s is not multicast capable, IPv6 not enabled\n", if_name(ifp)); return; } /* - * 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 (ia == NULL) { - printf("%s: failed to add link-local address\n", - if_name(ifp)); - - /* we can't initialize multicasts without link-local */ - goto statinit; - } - } - - if (ifp->if_flags & IFF_POINTOPOINT) { - /* - * route local address to loopback - */ - bzero(&gate, sizeof(gate)); - gate.sin6_len = sizeof(struct sockaddr_in6); - gate.sin6_family = AF_INET6; - gate.sin6_addr = in6addr_loopback; - bzero(&mask, sizeof(mask)); - mask.sin6_len = sizeof(struct sockaddr_in6); - mask.sin6_family = AF_INET6; - mask.sin6_addr = in6mask64; - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&gate, - (struct sockaddr *)&mask, - RTF_UP|RTF_HOST, - (struct rtentry **)0); - } - - /* - * assign loopback address for loopback interface - * XXX multiple loopback interface case + * assign loopback address for loopback interface. + * XXX multiple loopback interface case. */ - in6 = in6addr_loopback; - if (ifp->if_flags & IFF_LOOPBACK) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + in6 = in6addr_loopback; if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { if (in6_ifattach_loopback(ifp) != 0) return; } } -#ifdef DIAGNOSTIC - if (!ia) { - panic("ia == NULL in in6_ifattach"); - /*NOTREACHED*/ - } -#endif - /* - * join multicast + * assign a link-local address, if there's none. */ - 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); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; - - /* - * join link-local all-nodes address - */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - - IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); - if (in6m == NULL) { - 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); - } - - /* - * 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 node-local all-nodes address, on loopback - */ - 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); + if (ip6_auto_linklocal) { + ia = in6ifa_ifpforlinklocal(ifp, 0); + if (ia == NULL) { + if (in6_ifattach_linklocal(ifp, altifp) == 0) { + /* linklocal address assigned */ + } else { + /* failed to assign linklocal address. bark? */ } } } -statinit:; +#ifdef IFT_STF /* XXX */ +statinit: +#endif /* update dynamically. */ if (in6_maxmtu < ifp->if_mtu) @@ -913,6 +863,8 @@ statinit:; /* * NOTE: in6_ifdetach() does not support loopback if at this moment. + * We don't need this function in bsdi, because interfaces are never removed + * from the ifnet list in bsdi. */ void in6_ifdetach(ifp) @@ -938,7 +890,7 @@ in6_ifdetach(ifp) next = ifa->ifa_list.tqe_next; if (ifa->ifa_addr->sa_family != AF_INET6) continue; - in6_purgeaddr(ifa, ifp); + in6_purgeaddr(ifa); } /* undo everything done by in6_ifattach(), just in case */ @@ -979,11 +931,11 @@ in6_ifdetach(ifp) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; -#ifdef ND6_DEBUG - else - printf("%s: didn't unlink in6ifaddr from " - "list\n", if_name(ifp)); -#endif + else { + nd6log((LOG_ERR, + "%s: didn't unlink in6ifaddr from " + "list\n", if_name(ifp))); + } } IFAFREE(&oia->ia_ifa); @@ -998,7 +950,14 @@ in6_ifdetach(ifp) in6m = NULL; } - /* remove neighbor management table */ + /* + * remove neighbor management table. we call it twice just to make + * sure we nuke everything. maybe we need just one call. + * XXX: since the first call did not release addresses, some prefixes + * might remain. We should call nd6_purge() again to release the + * prefixes after removing all addresses above. + * (Or can we just delay calling nd6_purge until at this point?) + */ nd6_purge(ifp); /* remove route to link-local allnodes multicast (ff02::1) */ @@ -1014,3 +973,60 @@ in6_ifdetach(ifp) rtfree(rt); } } + +void +in6_get_tmpifid(ifp, retbuf, baseid, generate) + struct ifnet *ifp; + u_int8_t *retbuf; + const u_int8_t *baseid; + int generate; +{ + u_int8_t nullbuf[8]; + struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; + + bzero(nullbuf, sizeof(nullbuf)); + if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) == 0) { + /* we've never created a random ID. Create a new one. */ + generate = 1; + } + + if (generate) { + bcopy(baseid, ndi->randomseed1, sizeof(ndi->randomseed1)); + + /* generate_tmp_ifid will update seedn and buf */ + (void)generate_tmp_ifid(ndi->randomseed0, ndi->randomseed1, + ndi->randomid); + } + bcopy(ndi->randomid, retbuf, 8); +} + +void +in6_tmpaddrtimer(ignored_arg) + void *ignored_arg; +{ + int i; + struct nd_ifinfo *ndi; + u_int8_t nullbuf[8]; + int s = splnet(); + + callout_reset(&in6_tmpaddrtimer_ch, + (ip6_temp_preferred_lifetime - ip6_desync_factor - + ip6_temp_regen_advance) * hz, + in6_tmpaddrtimer, NULL); + + bzero(nullbuf, sizeof(nullbuf)); + for (i = 1; i < if_index + 1; i++) { + ndi = &nd_ifinfo[i]; + if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { + /* + * We've been generating a random ID on this interface. + * Create a new one. + */ + (void)generate_tmp_ifid(ndi->randomseed0, + ndi->randomseed1, + ndi->randomid); + } + } + + splx(s); +} diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h index 4f82e4153085..fa4434f48a44 100644 --- a/sys/netinet6/in6_ifattach.h +++ b/sys/netinet6/in6_ifattach.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_ifattach.h,v 1.10 2000/05/27 02:57:05 itojun Exp $ */ +/* $KAME: in6_ifattach.h,v 1.14 2001/02/08 12:48:39 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -38,6 +38,9 @@ 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 *)); +void in6_get_tmpifid __P((struct ifnet *, u_int8_t *, const u_int8_t *, int)); +void in6_tmpaddrtimer __P((void *)); +int in6_nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_IFATTACH_H_ */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 67409bed1c2b..9175a65431fa 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_pcb.c,v 1.8 2000/06/09 00:37:02 itojun Exp $ */ +/* $KAME: in6_pcb.c,v 1.31 2001/05/21 05:45:10 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -66,6 +66,8 @@ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -99,12 +101,19 @@ #include <netinet6/in6_pcb.h> #include "faith.h" +#if defined(NFAITH) && NFAITH > 0 +#include <net/if_faith.h> +#endif #ifdef IPSEC #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> #endif /* IPSEC */ @@ -165,11 +174,12 @@ in6_pcbbind(inp, nam, p) /* * XXX: bind to an anycast address might accidentally * cause sending a packet with anycast source address. + * We should allow to bind to a deprecated address, since + * the application dare to use it. */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { return(EADDRNOTAVAIL); } } @@ -193,7 +203,7 @@ in6_pcbbind(inp, nam, p) (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); - if (ip6_mapped_addr_on != 0 && + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -215,7 +225,7 @@ in6_pcbbind(inp, nam, p) lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return(EADDRINUSE); - if (ip6_mapped_addr_on != 0 && + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; @@ -247,7 +257,6 @@ in6_pcbbind(inp, nam, p) return (EAGAIN); } } - inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/ return(0); } @@ -270,7 +279,6 @@ in6_pcbladdr(inp, nam, plocal_addr6) struct in6_addr **plocal_addr6; { register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; - struct in6_pktinfo *pi; struct ifnet *ifp = NULL; int error = 0; @@ -361,15 +369,11 @@ in6_pcbconnect(inp, nam, p) } inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; - /* - * xxx kazu flowlabel is necessary for connect? - * but if this line is missing, the garbage value remains. - */ - inp->in6p_flowinfo = sin6->sin6_flowinfo; - if ((inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) == 0 && - ip6_auto_flowlabel != 0) + /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; + if (inp->in6p_flags & IN6P_AUTOFLOWLABEL) inp->in6p_flowinfo |= - (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); + (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); return (0); @@ -521,11 +525,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) } if (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0) { + struct sockaddr_in6 *dst6; + /* 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; + dst6 = (struct sockaddr_in6 *)&ro->ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_addr = *dst; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1(&((struct route *)ro) ->ro_dst, 0, 0UL); @@ -584,6 +591,8 @@ in6_pcbdisconnect(inp) { bzero((caddr_t)&inp->in6p_faddr, sizeof(inp->in6p_faddr)); inp->inp_fport = 0; + /* clear flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; in_pcbrehash(inp); if (inp->inp_socket->so_state & SS_NOFDREF) in6_pcbdetach(inp); @@ -604,20 +613,13 @@ in6_pcbdetach(inp) in_pcbremlists(inp); sotoinpcb(so) = 0; sofree(so); + if (inp->in6p_options) m_freem(inp->in6p_options); - if (inp->in6p_outputopts) { - if (inp->in6p_outputopts->ip6po_rthdr && - inp->in6p_outputopts->ip6po_route.ro_rt) - RTFREE(inp->in6p_outputopts->ip6po_route.ro_rt); - if (inp->in6p_outputopts->ip6po_m) - (void)m_free(inp->in6p_outputopts->ip6po_m); - free(inp->in6p_outputopts, M_IP6OPT); - } + ip6_freepcbopts(inp->in6p_outputopts); + ip6_freemoptions(inp->in6p_moptions); if (inp->in6p_route.ro_rt) rtfree(inp->in6p_route.ro_rt); - ip6_freemoptions(inp->in6p_moptions); - /* Check and free IPv4 related resources in case of mapped addr */ if (inp->inp_options) (void)m_free(inp->inp_options); @@ -761,27 +763,33 @@ in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) * Must be called at splnet. */ void -in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) +in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, notify) struct inpcbhead *head; - struct sockaddr *dst; + struct sockaddr *dst, *src; u_int fport_arg, lport_arg; - struct in6_addr *laddr6; int cmd; void (*notify) __P((struct inpcb *, int)); { struct inpcb *inp, *ninp; - struct in6_addr faddr6; + struct sockaddr_in6 sa6_src, *sa6_dst; u_short fport = fport_arg, lport = lport_arg; + u_int32_t flowinfo; int errno, s; - int do_rtchange = (notify == in6_rtchange); if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return; - faddr6 = ((struct sockaddr_in6 *)dst)->sin6_addr; - if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) + + sa6_dst = (struct sockaddr_in6 *)dst; + if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr)) return; /* + * note that src can be NULL when we get notify by local fragmentation. + */ + sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src; + flowinfo = sa6_src.sin6_flowinfo; + + /* * Redirects go to all references to the destination, * and use in6_rtchange to invalidate the route cache. * Dead host indications: also use in6_rtchange to invalidate @@ -792,44 +800,45 @@ in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; - bzero((caddr_t)laddr6, sizeof(*laddr6)); + bzero((caddr_t)&sa6_src.sin6_addr, sizeof(sa6_src.sin6_addr)); - do_rtchange = 1; + if (cmd != PRC_HOSTDEAD) + notify = in6_rtchange; } errno = inet6ctlerrmap[cmd]; s = splnet(); for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) { ninp = LIST_NEXT(inp, inp_list); - if ((inp->inp_vflag & INP_IPV6) == NULL) + if ((inp->inp_vflag & INP_IPV6) == 0) 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)) + /* + * Detect if we should notify the error. If no source and + * destination ports are specifed, but non-zero flowinfo and + * local address match, notify the error. This is the case + * when the error is delivered with an encrypted buffer + * by ESP. Otherwise, just compare addresses and ports + * as usual. + */ + if (lport == 0 && fport == 0 && flowinfo && + inp->inp_socket != NULL && + flowinfo == (inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) && + IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &sa6_src.sin6_addr)) + goto do_notify; + else if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, + &sa6_dst->sin6_addr) || + inp->inp_socket == 0 || + (lport && inp->inp_lport != lport) || + (!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) && + !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, + &sa6_src.sin6_addr)) || + (fport && inp->inp_fport != fport)) continue; + do_notify: if (notify) - (*notify)(inp, errno); + (*notify)(inp, errno); } splx(s); } @@ -990,6 +999,13 @@ in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) struct inpcbhead *head; register struct inpcb *inp; u_short fport = fport_arg, lport = lport_arg; + int faith; + +#if defined(NFAITH) && NFAITH > 0 + faith = faithprefix(laddr); +#else + faith = 0; +#endif /* * First look for an exact match. @@ -1020,11 +1036,8 @@ in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) continue; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && inp->inp_lport == lport) { -#if defined(NFAITH) && NFAITH > 0 - if (ifp && ifp->if_type == IFT_FAITH && - (inp->inp_flags & INP_FAITH) == 0) + if (faith && (inp->inp_flags & INP_FAITH) == 0) continue; -#endif if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) return (inp); diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index 2ea310728a17..15cd033ff0a2 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_pcb.h,v 1.5 2000/07/03 06:19:53 itojun Exp $ */ +/* $KAME: in6_pcb.h,v 1.13 2001/02/06 09:16:53 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -90,7 +90,7 @@ struct inpcb * struct in6_addr *, u_int, struct in6_addr *, u_int, int, struct ifnet *)); void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, - u_int, struct in6_addr *, u_int, int, + u_int, struct sockaddr *, u_int, int, void (*)(struct inpcb *, int))); void in6_rtchange __P((struct inpcb *, int)); int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); @@ -105,11 +105,6 @@ struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, 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 1eaea506bf98..e3325f9325cd 100644 --- a/sys/netinet6/in6_prefix.c +++ b/sys/netinet6/in6_prefix.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_prefix.c,v 1.30 2000/06/12 14:53:17 jinmei Exp $ */ +/* $KAME: in6_prefix.c,v 1.47 2001/03/25 08:41:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -88,6 +88,8 @@ static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid"); struct rr_prhead rr_prefix; +struct callout in6_rr_timer_ch; + #include <net/net_osdep.h> static void add_each_addr __P((struct socket *so, struct rr_prefix *rpp, @@ -399,7 +401,7 @@ assign_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; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); #if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */ ia->ia6_ifpr = rp2ifpr(rpp); #endif @@ -465,7 +467,7 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) if (ifpr == NULL) { struct rr_prefix rp; struct socket so; - int pplen = (plen == 128) ? 64 : plen; + int pplen = (plen == 128) ? 64 : plen; /* XXX hardcoded 64 is bad */ /* allocate a prefix for ia, with default properties */ @@ -514,11 +516,11 @@ in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) if (rap != NULL) { if (rap->ra_addr == NULL) { rap->ra_addr = ia; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); } 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\n", + " already has 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))); @@ -599,6 +601,14 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen); /* don't care ifra_flags for now */ + /* + * XXX: if we did this with finite lifetime values, the lifetimes would + * decrese in time and never incremented. + * we should need more clarifications on the prefix mechanism... + */ + ifra.ifra_lifetime.ia6t_vltime = rpp->rp_vltime; + ifra.ifra_lifetime.ia6t_pltime = rpp->rp_pltime; + ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr); if (ia6 != NULL) { if (ia6->ia6_ifpr == NULL) { @@ -606,7 +616,7 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (rap->ra_addr) IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); ia6->ia6_ifpr = rp2ifpr(rpp); return; } @@ -614,7 +624,7 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) if (rap->ra_addr) IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; - rap->ra_addr->ia_ifa.ifa_refcnt++; + IFAREF(&rap->ra_addr->ia_ifa); return; } /* @@ -627,24 +637,26 @@ add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) * Or, completely duplicated prefixes? * 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\n", + log(LOG_ERR, + "in6_prefix.c: add_each_addr: addition of an addr %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)); + in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL)); return; } /* propagate ANYCAST flag if it is set for ancestor addr */ 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); - if (error != 0) + 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\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, error); return; + } /* * link beween this addr and the prefix will be done @@ -957,8 +969,10 @@ delete_each_prefix(struct rr_prefix *rpp, u_char origin) s = splnet(); rap = LIST_FIRST(&rpp->rp_addrhead); - if (rap == NULL) + if (rap == NULL) { + splx(s); break; + } LIST_REMOVE(rap, ra_entry); splx(s); if (rap->ra_addr == NULL) { @@ -967,7 +981,7 @@ delete_each_prefix(struct rr_prefix *rpp, u_char origin) } rap->ra_addr->ia6_ifpr = NULL; - in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp); + in6_purgeaddr(&rap->ra_addr->ia_ifa); IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } @@ -1166,7 +1180,8 @@ in6_rr_timer(void *ignored_arg) int s; struct rr_prefix *rpp; - timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz); + callout_reset(&in6_rr_timer_ch, ip6_rr_prune * hz, + in6_rr_timer, NULL); s = splnet(); /* expire */ diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h index 2ce18db551b3..3ae6a63640a4 100644 --- a/sys/netinet6/in6_prefix.h +++ b/sys/netinet6/in6_prefix.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_prefix.h,v 1.6 2000/03/25 07:23:45 sumikawa Exp $ */ +/* $KAME: in6_prefix.h,v 1.10 2001/02/08 16:30:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. @@ -30,6 +30,8 @@ * SUCH DAMAGE. */ +#include <sys/callout.h> + struct rr_prefix { struct ifprefix rp_ifpr; LIST_ENTRY(rr_prefix) rp_entry; @@ -85,4 +87,5 @@ LIST_HEAD(rr_prhead, rr_prefix); extern struct rr_prhead rr_prefix; void in6_rr_timer __P((void *)); +extern struct callout in6_rr_timer_ch; 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 b221f8afd83b..e73e4bb70ea3 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_proto.c,v 1.64 2000/06/20 16:20:27 itojun Exp $ */ +/* $KAME: in6_proto.c,v 1.91 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -99,25 +99,31 @@ #include <netinet/udp.h> #include <netinet/udp_var.h> #include <netinet6/tcp6_var.h> - +#include <netinet6/raw_ip6.h> #include <netinet6/udp6_var.h> - #include <netinet6/pim6_var.h> - #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> #ifdef IPSEC #include <netinet6/ipsec.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 <netinet6/ipcomp.h> +#ifdef INET6 #include <netinet6/ipcomp6.h> +#endif #endif /*IPSEC*/ #include <netinet6/ip6protosw.h> @@ -136,6 +142,9 @@ extern struct domain inet6domain; static struct pr_usrreqs nousrreqs; +#define PR_LISTEN 0 +#define PR_ABRTACPTDIS 0 + struct ip6protosw inet6sw[] = { { 0, &inet6domain, IPPROTO_IPV6, 0, 0, 0, 0, 0, @@ -143,30 +152,30 @@ struct ip6protosw inet6sw[] = { ip6_init, 0, frag6_slowtimo, frag6_drain, &nousrreqs, }, -{ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC | PR_ADDR, +{ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR, udp6_input, 0, udp6_ctlinput, ip6_ctloutput, 0, 0, 0, 0, 0, &udp6_usrreqs, }, -{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED | PR_WANTRCVD, +{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD|PR_LISTEN, tcp6_input, 0, tcp6_ctlinput, tcp_ctloutput, 0, -#ifdef INET /* don't call timeout routines twice */ - tcp_init, 0, 0, tcp_drain, +#ifdef INET /* don't call initialization and timeout routines twice */ + 0, 0, 0, tcp_drain, #else tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain, #endif &tcp6_usrreqs, }, -{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR, +{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR, rip6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs }, -{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR, - icmp6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + icmp6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, icmp6_init, icmp6_fasttimo, 0, 0, &rip6_usrreqs @@ -191,15 +200,17 @@ struct ip6protosw inet6sw[] = { }, #ifdef IPSEC { SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, - ah6_input, 0, 0, 0, + ah6_input, 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, + esp6_input, 0, + esp6_ctlinput, + 0, + 0, 0, 0, 0, 0, &nousrreqs, }, @@ -212,34 +223,30 @@ struct ip6protosw inet6sw[] = { }, #endif /* IPSEC */ #ifdef INET -{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, +{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR|PR_LASTHDR, encap6_input, rip6_output, 0, rip6_ctloutput, 0, - 0, 0, 0, 0, + encap_init, 0, 0, 0, &rip6_usrreqs }, #endif /*INET*/ -{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - encap6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + encap6_input, rip6_output, 0, rip6_ctloutput, 0, -#ifndef INET encap_init, 0, 0, 0, -#else - 0, 0, 0, 0, -#endif &rip6_usrreqs }, -{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR, - pim6_input, rip6_output, 0, rip6_ctloutput, +{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + pim6_input, rip6_output, 0, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs }, /* raw wildcard */ -{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR, +{ 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 }, }; @@ -300,7 +307,7 @@ int ip6_gif_hlim = 0; int ip6_use_deprecated = 1; /* allow deprecated addr (RFC2462 5.5.4) */ int ip6_rr_prune = 5; /* router renumbering prefix * walk list every 5 sec. */ -int ip6_mapped_addr_on = 1; +int ip6_v6only = 0; u_int32_t ip6_id = 0UL; int ip6_keepfaith = 0; @@ -328,84 +335,8 @@ u_long rip6_recvspace = RIPV6RCVQ; /* ICMPV6 parameters */ int icmp6_rediraccept = 1; /* accept and process redirects */ int icmp6_redirtimeout = 10 * 60; /* 10 minutes */ -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*/ +int icmp6_nodeinfo = 3; /* enable/disable NI response */ /* UDP on IP6 parameters */ int udp6_sendspace = 9216; /* really max datagram size */ @@ -429,76 +360,44 @@ SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); /* net.inet6.ip6 */ static int -sysctl_ip6_forwarding(SYSCTL_HANDLER_ARGS) +sysctl_ip6_temppltime(SYSCTL_HANDLER_ARGS) { int error = 0; - int old_ip6_forwarding; - int changed; + int old; error = SYSCTL_OUT(req, arg1, sizeof(int)); if (error || !req->newptr) return (error); - old_ip6_forwarding = ip6_forwarding; + old = ip6_temp_preferred_lifetime; error = SYSCTL_IN(req, arg1, sizeof(int)); - if (error != 0) - return (error); - 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 = 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 */ - while(!LIST_EMPTY(&rr_prefix)) - delete_each_prefix(LIST_FIRST(&rr_prefix), - PR_ORIG_KERNEL); + if (ip6_temp_preferred_lifetime < + ip6_desync_factor + ip6_temp_regen_advance) { + ip6_temp_preferred_lifetime = old; + return(EINVAL); } - - return (error); + return(error); } static int -sysctl_icmp6_ratelimit (SYSCTL_HANDLER_ARGS) +sysctl_ip6_tempvltime(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) + int error = 0; + int old; + + error = SYSCTL_OUT(req, arg1, sizeof(int)); + if (error || !req->newptr) 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); + old = ip6_temp_valid_lifetime; + error = SYSCTL_IN(req, arg1, sizeof(int)); + if (ip6_temp_valid_lifetime < ip6_temp_preferred_lifetime) { + ip6_temp_preferred_lifetime = old; + return(EINVAL); + } + return(error); } -SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding, - CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding, - "I", ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_FORWARDING, + forwarding, CTLFLAG_RW, &ip6_forwarding, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS, redirect, CTLFLAG_RW, &ip6_sendredirects, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM, @@ -527,8 +426,20 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEPRECATED, use_deprecated, CTLFLAG_RW, &ip6_use_deprecated, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE, rr_prune, CTLFLAG_RW, &ip6_rr_prune, 0, ""); -SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR, - mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USETEMPADDR, + use_tempaddr, CTLFLAG_RW, &ip6_use_tempaddr, 0, ""); +SYSCTL_OID(_net_inet6_ip6, IPV6CTL_TEMPPLTIME, temppltime, + CTLTYPE_INT|CTLFLAG_RW, &ip6_temp_preferred_lifetime, 0, + sysctl_ip6_temppltime, "I", ""); +SYSCTL_OID(_net_inet6_ip6, IPV6CTL_TEMPVLTIME, tempvltime, + CTLTYPE_INT|CTLFLAG_RW, &ip6_temp_valid_lifetime, 0, + sysctl_ip6_tempvltime, "I", ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_V6ONLY, + v6only, CTLFLAG_RW, &ip6_v6only, 0, ""); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_LINKLOCAL, + auto_linklocal, CTLFLAG_RW, &ip6_auto_linklocal, 0, ""); +SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_RIP6STATS, rip6stats, CTLFLAG_RD, + &rip6stat, rip6stat, ""); /* net.inet6.icmp6 */ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT, @@ -537,9 +448,6 @@ 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_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, @@ -556,3 +464,5 @@ 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, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DEBUG, + nd6_debug, CTLFLAG_RW, &nd6_debug, 0, ""); diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c index 14f72d2ff39f..a56cfe250796 100644 --- a/sys/netinet6/in6_rmx.c +++ b/sys/netinet6/in6_rmx.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_rmx.c,v 1.7 2000/04/06 08:30:43 sumikawa Exp $ */ +/* $KAME: in6_rmx.c,v 1.10 2001/05/24 05:44:58 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -146,23 +146,6 @@ in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, } } - /* - * We also specify a send and receive pipe size for every - * route added, to help TCP a bit. TCP doesn't actually - * want a true pipe size, which would be prohibitive in memory - * costs and is hard to compute anyway; it simply uses these - * values to size its buffers. So, we fill them in with the - * same values that TCP would have used anyway, and allow the - * installing program or the link layer to override these values - * as it sees fit. This will hopefully allow TCP more - * opportunities to save its ssthresh value. - */ - if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE)) - rt->rt_rmx.rmx_sendpipe = tcp_sendspace; - - if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE)) - rt->rt_rmx.rmx_recvpipe = tcp_recvspace; - if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) && rt->rt_ifp) rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 765a6928b4d9..7bf28d2c626f 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_src.c,v 1.27 2000/06/21 08:07:13 itojun Exp $ */ +/* $KAME: in6_src.c,v 1.37 2001/03/29 05:34:31 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -70,6 +70,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -97,9 +98,9 @@ #include <net/net_osdep.h> /* - * Return an IPv6 address, which is the most appropriate for given + * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. - * If necessary, this function lookups the routing table and return + * If necessary, this function lookups the routing table and returns * an entry to the caller for later use. */ struct in6_addr * @@ -241,12 +242,15 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) } if (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0) { + struct sockaddr_in6 *sa6; + /* 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; + sa6 = (struct sockaddr_in6 *)&ro->ro_dst; + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(struct sockaddr_in6); + sa6->sin6_addr = *dst; + sa6->sin6_scope_id = dstsock->sin6_scope_id; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1(&((struct route *)ro) ->ro_dst, 0, 0UL); @@ -529,15 +533,8 @@ in6_recoverscope(sin6, in6, ifp) /* 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; } @@ -545,3 +542,15 @@ in6_recoverscope(sin6, in6, ifp) return 0; } + +/* + * just clear the embedded scope identifer. + * XXX: currently used for bsdi4 only as a supplement function. + */ +void +in6_clearscope(addr) + struct in6_addr *addr; +{ + if (IN6_IS_SCOPE_LINKLOCAL(addr)) + addr->s6_addr16[1] = 0; +} diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index f2f644e8a5fb..bb5abc972568 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: in6_var.h,v 1.33 2000/05/17 05:07:26 jinmei Exp $ */ +/* $KAME: in6_var.h,v 1.56 2001/03/29 05:34:31 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -102,8 +102,12 @@ struct in6_ifaddr { struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ int ia6_flags; - struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ + struct in6_addrlifetime ia6_lifetime; struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ + + struct nd_prefix *ia6_ndpr; /* back pointer to the ND prefix + * (for autoconfigured addresses only) + */ }; /* @@ -380,7 +384,10 @@ struct in6_rrenumreq { #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) +#ifdef _KERNEL +#define OSIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ondireq) +#endif +#define SIOCGIFINFO_IN6 _IOWR('i', 108, 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) @@ -419,6 +426,14 @@ struct in6_rrenumreq { #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_NODAD 0x20 /* don't perform DAD on this address + * (used only at first SIOC* call) + */ +#define IN6_IFF_AUTOCONF 0x40 /* autoconfigurable address. */ +#define IN6_IFF_TEMPORARY 0x80 /* temporary (anonymous) address. */ +#define IN6_IFF_NOPFX 0x8000 /* skip kernel prefix management. + * XXX: this should be temporary. + */ /* do not input/output */ #define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) @@ -516,7 +531,7 @@ struct in6_multistep { /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ do { \ - register struct ifmultiaddr *ifma; \ + struct ifmultiaddr *ifma; \ TAILQ_FOREACH(ifma, &(ifp)->if_multiaddrs, ifma_link) { \ if (ifma->ifma_addr->sa_family == AF_INET6 \ && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \ @@ -549,18 +564,19 @@ do { \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) -int in6_ifinit __P((struct ifnet *, - struct in6_ifaddr *, struct sockaddr_in6 *, int)); 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 int in6_mask2len __P((struct in6_addr *, u_char *)); 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 *)); +int in6_update_ifa __P((struct ifnet *, struct in6_aliasreq *, + struct in6_ifaddr *)); +void in6_purgeaddr __P((struct ifaddr *)); +int in6if_do_dad __P((struct ifnet *)); +void in6_purgeif __P((struct ifnet *)); void in6_savemkludge __P((struct in6_ifaddr *)); void in6_setmaxmtu __P((void)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); @@ -568,7 +584,7 @@ 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 *)); +char *ip6_sprintf __P((const struct in6_addr *)); 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, @@ -579,6 +595,14 @@ int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, 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 *)); + +int in6_is_addr_deprecated __P((struct sockaddr_in6 *)); +struct inpcb; +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 *)); +void in6_clearscope __P((struct in6_addr *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ diff --git a/sys/netinet6/ip6_ecn.h b/sys/netinet6/ip6_ecn.h index e8dd11f71107..4107cf09af25 100644 --- a/sys/netinet6/ip6_ecn.h +++ b/sys/netinet6/ip6_ecn.h @@ -36,6 +36,6 @@ */ #ifdef _KERNEL -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 *)); +extern void ip6_ecn_ingress __P((int, u_int32_t *, const u_int32_t *)); +extern void ip6_ecn_egress __P((int, const u_int32_t *, u_int32_t *)); #endif diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 2664ccbf746d..444cd2c653a0 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_forward.c,v 1.43 2000/07/16 07:50:49 itojun Exp $ */ +/* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,12 +37,14 @@ #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/kernel.h> #include <sys/syslog.h> #include <net/if.h> @@ -50,15 +52,22 @@ #include <netinet/in.h> #include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> #include <netinet/ip_var.h> +#include <netinet6/in6_var.h> #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet/icmp6.h> #include <netinet6/nd6.h> +#include <netinet/in_pcb.h> + #ifdef IPSEC #include <netinet6/ipsec.h> +#ifdef INET6 #include <netinet6/ipsec6.h> +#endif #include <netkey/key.h> #endif /* IPSEC */ @@ -87,8 +96,8 @@ ip6_forward(m, srcrt) int srcrt; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct sockaddr_in6 *dst; - register struct rtentry *rt; + struct sockaddr_in6 *dst; + struct rtentry *rt; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ @@ -111,8 +120,15 @@ ip6_forward(m, srcrt) } #endif /*IPSEC*/ + /* + * Do not forward packets to multicast destination (should be handled + * by ip6_mforward(). + * Do not forward packets with unspecified source. It was discussed + * in July 2000, on ipngwg mailing list. + */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || - IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { @@ -150,7 +166,8 @@ ip6_forward(m, srcrt) #ifdef IPSEC /* get a security policy for this packet */ - sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); + sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING, + &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; @@ -238,10 +255,6 @@ ip6_forward(m, srcrt) error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; -#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) { @@ -275,7 +288,7 @@ ip6_forward(m, srcrt) skip_ipsec: #endif /* IPSEC */ - dst = &ip6_forward_rt.ro_dst; + dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst @@ -293,7 +306,7 @@ ip6_forward(m, srcrt) if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; - /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); @@ -315,7 +328,7 @@ 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) */ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); @@ -410,8 +423,25 @@ ip6_forward(m, srcrt) * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && - (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) + (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { + if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) { + /* + * If the incoming interface is equal to the outgoing + * one, and the link attached to the interface is + * point-to-point, then it will be highly probable + * that a routing loop occurs. Thus, we immediately + * drop the packet and send an ICMPv6 error message. + * + * type/code is based on suggestion by Rich Draves. + * not sure if it is the best pick. + */ + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0); + m_freem(m); + return; + } type = ND_REDIRECT; + } /* * Check with the firewall... @@ -432,8 +462,8 @@ ip6_forward(m, srcrt) * 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. + * link identifiers, we can do this stuff after making a copy for + * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* @@ -459,34 +489,21 @@ ip6_forward(m, srcrt) 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; + /* we can just use rcvif in forwarding. */ + origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; -#ifndef FAKE_LOOPBACK_IF - if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #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++; diff --git a/sys/netinet6/ip6_fw.c b/sys/netinet6/ip6_fw.c index ae1c0f1251a6..f0245cff005b 100644 --- a/sys/netinet6/ip6_fw.c +++ b/sys/netinet6/ip6_fw.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_fw.c,v 1.15 2000/07/02 14:17:37 itojun Exp $ */ +/* $KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $ */ /* * Copyright (c) 1993 Daniel Boulet @@ -87,7 +87,7 @@ LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain; SYSCTL_DECL(_net_inet6_ip6); SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, enable, CTLFLAG_RW, - &ip6_fw_enable, 0, "Enable ip6fw"); + &ip6_fw_enable, 0, "Enable ip6fw"); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW, &fw6_debug, 0, ""); 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, ""); @@ -479,7 +479,7 @@ ip6_fw_chk(struct ip6_hdr **pip6, } #endif /* IP6FW_DIVERT_RESTART */ for (; chain; chain = LIST_NEXT(chain, chain)) { - register struct ip6_fw *const f = chain->rule; + struct ip6_fw *const f = chain->rule; if (oif) { /* Check direction outbound */ @@ -758,7 +758,8 @@ got_match: flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); - m_freem(*m); + tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), + *m, ack, seq, flags); *m = NULL; break; } @@ -1064,7 +1065,7 @@ ip6_fw_ctl(int stage, struct mbuf **mm) } } for (; fcp; fcp = fcp->chain.le_next) { - memcpy(m->m_data, fcp->rule, sizeof *(fcp->rule)); + bcopy(fcp->rule, m->m_data, sizeof *(fcp->rule)); m->m_len = sizeof *(fcp->rule); m->m_next = m_get(M_TRYWAIT, MT_DATA); /* XXX */ if (!m->m_next) { @@ -1204,7 +1205,7 @@ static int ip6fw_modevent(module_t mod, int type, void *unused) { int s; - + switch (type) { case MOD_LOAD: s = splnet(); @@ -1225,7 +1226,7 @@ ip6fw_modevent(module_t mod, int type, void *unused) free(fcp->rule, M_IP6FW); free(fcp, M_IP6FW); } - + splx(s); printf("IPv6 firewall unloaded\n"); return 0; diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h index bcd1a037cf93..2298804ab48f 100644 --- a/sys/netinet6/ip6_fw.h +++ b/sys/netinet6/ip6_fw.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_fw.h,v 1.3 2000/04/06 08:30:44 sumikawa Exp $ */ +/* $KAME: ip6_fw.h,v 1.7 2001/01/24 01:25:33 itojun Exp $ */ /* * Copyright (c) 1993 Daniel Boulet diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index 474534724bf9..4b10d8e9f4b0 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_input.c,v 1.95 2000/07/02 07:49:37 jinmei Exp $ */ +/* $KAME: ip6_input.c,v 1.194 2001/05/27 13:28:35 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -73,6 +73,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/protosw.h> @@ -108,6 +109,13 @@ #include <netinet6/nd6.h> #include <netinet6/in6_prefix.h> +#ifdef IPSEC +#include <netinet6/ipsec.h> +#ifdef INET6 +#include <netinet6/ipsec6.h> +#endif +#endif + #include <netinet6/ip6_fw.h> #include <netinet6/ip6protosw.h> @@ -124,11 +132,16 @@ u_char ip6_protox[IPPROTO_MAX]; static int ip6qmaxlen = IFQ_MAXLEN; struct in6_ifaddr *in6_ifaddr; +extern struct callout in6_tmpaddrtimer_ch; + int ip6_forward_srcrt; /* XXX */ int ip6_sourcecheck; /* XXX */ int ip6_sourcecheck_interval; /* XXX */ const int int6intrq_present = 1; +int ip6_ours_check_algorithm; + + /* firewall hooks */ ip6_fw_chk_t *ip6_fw_chk_ptr; ip6_fw_ctl_t *ip6_fw_ctl_ptr; @@ -137,12 +150,14 @@ int ip6_fw_enable = 1; struct ip6stat ip6stat; static void ip6_init2 __P((void *)); +static struct mbuf *ip6_setdstifaddr __P((struct mbuf *, struct in6_ifaddr *)); 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 + /* * IP6 initialization: fill in IP6 protocol switch table. * All protocols not implemented in kernel go to raw IP6 protocol handler. @@ -150,10 +165,14 @@ static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); void ip6_init() { - register struct ip6protosw *pr; - register int i; + struct ip6protosw *pr; + int i; struct timeval tv; +#ifdef DIAGNOSTIC + if (sizeof(struct protosw) != sizeof(struct ip6protosw)) + panic("sizeof(protosw) != sizeof(ip6protosw)"); +#endif pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip6_init"); @@ -175,6 +194,8 @@ ip6_init() */ microtime(&tv); ip6_flow_seq = random() ^ tv.tv_usec; + microtime(&tv); + ip6_desync_factor = (random() ^ tv.tv_usec) % MAX_TEMP_DESYNC_FACTOR; } static void @@ -189,9 +210,18 @@ ip6_init2(dummy) in6_ifattach(&loif[0], NULL); /* nd6_timer_init */ - timeout(nd6_timer, (caddr_t)0, hz); + callout_init(&nd6_timer_ch, 0); + callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL); + /* router renumbering prefix list maintenance */ - timeout(in6_rr_timer, (caddr_t)0, hz); + callout_init(&in6_rr_timer_ch, 0); + callout_reset(&in6_rr_timer_ch, hz, in6_rr_timer, NULL); + + /* timer for regeneranation of temporary addresses randomize ID */ + callout_reset(&in6_tmpaddrtimer_ch, + (ip6_temp_preferred_lifetime - ip6_desync_factor - + ip6_temp_regen_advance) * hz, + in6_tmpaddrtimer, NULL); } /* cheat */ @@ -247,6 +277,11 @@ ip6_input(m) #endif /* + * make sure we don't have onion peering information into m_aux. + */ + ip6_delaux(m); + + /* * mbuf statistics by kazu */ if (m->m_flags & M_EXT) { @@ -255,15 +290,17 @@ ip6_input(m) else ip6stat.ip6s_mext1++; } else { +#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0])) if (m->m_next) { if (m->m_flags & M_LOOP) { ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ - } else if (m->m_pkthdr.rcvif->if_index <= 31) + } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else ip6stat.ip6s_m2m[0]++; } else ip6stat.ip6s_m1++; +#undef M2MMAX } in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); @@ -360,20 +397,42 @@ ip6_input(m) } /* - * Scope check + * Check against address spoofing/corruption. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { + /* + * XXX: "badscope" is not very suitable for a multicast source. + */ + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } + if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || + IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) && + (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { ip6stat.ip6s_badscope++; 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. + * The following check is not documented in specs. A 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. + * + * This check chokes if we are in an SIIT cloud. As none of BSDs + * support IPv4-less kernel compilation, we cannot support SIIT + * environment at all. So, it makes more sense for us to reject any + * malicious packets for non-SIIT environment, than try to do a + * partical support for SIIT environment. */ + 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; + } #if 0 /* * Reject packets with IPv4 compatible addresses (auto tunnel). @@ -389,105 +448,52 @@ ip6_input(m) 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) { - struct in6_ifaddr *ia6; - - if ((ia6 = in6ifa_ifpwithaddr(m->m_pkthdr.rcvif, - &ip6->ip6_dst)) != NULL) { - ia6->ia_ifa.if_ipackets++; - ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - } else { - /* - * The packet is looped back, but we do not - * have the destination address for some - * reason. - * XXX: should we return an icmp6 error? - */ - goto bad; - } - ours = 1; - deliverifp = m->m_pkthdr.rcvif; - goto hbhcheck; - } else { + + /* drop packets if interface ID portion is already filled */ + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) && + ip6->ip6_src.s6_addr16[1]) { + ip6stat.ip6s_badscope++; + goto bad; + } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) && + ip6->ip6_dst.s6_addr16[1]) { ip6stat.ip6s_badscope++; - in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } } -#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); - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] - = htons(m->m_pkthdr.rcvif->if_index); - } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] + = htons(m->m_pkthdr.rcvif->if_index); +#if 0 /* this case seems to be unnecessary. (jinmei, 20010401) */ /* - * 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. + * We use rt->rt_ifp to determine if the address is ours or not. + * If rt_ifp is lo0, the address is ours. + * The problem here is, rt->rt_ifp for fe80::%lo0/64 is set to lo0, + * so any address under fe80::%lo0/64 will be mistakenly considered + * local. The special case is supplied to handle the case properly + * by actually looking at interface addresses + * (using in6ifa_ifpwithaddr). */ - if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { - struct in6_ifaddr *ia6; -#ifndef FAKE_LOOPBACK_IF - int deliverifid; - - /* - * Get the "real" delivered interface, which should be - * embedded in the second 16 bits of the destination - * address. We can probably trust the value, but we - * add validation for the value just for safety. - */ - deliverifid = ntohs(ip6->ip6_dst.s6_addr16[1]); - if (deliverifid > 0 && deliverifid <= if_index) { - deliverifp = ifindex2ifnet[deliverifid]; - - /* - * XXX: fake the rcvif to the real interface. - * Since m_pkthdr.rcvif should be lo0 (or a - * variant), it would confuse scope handling - * code later. - */ - m->m_pkthdr.rcvif = deliverifp; - } - else { - /* - * Last resort; just use rcvif. - * XXX: the packet would be discarded by the - * succeeding check. - */ - deliverifp = m->m_pkthdr.rcvif; - } -#else - deliverifp = m->m_pkthdr.rcvif; -#endif - if ((ia6 = in6ifa_ifpwithaddr(deliverifp, - &ip6->ip6_dst)) != NULL) { - ia6->ia_ifa.if_ipackets++; - ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - } else { - /* - * We do not have the link-local address - * specified as the destination. - * XXX: should we return an icmp6 error? - */ - goto bad; - } - ours = 1; - goto hbhcheck; + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0 && + IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + if (!in6ifa_ifpwithaddr(m->m_pkthdr.rcvif, &ip6->ip6_dst)) { + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0); + /* m is already freed */ + return; } + + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; } +#endif /* * Multicast check @@ -516,12 +522,21 @@ ip6_input(m) /* * Unicast check */ + switch (ip6_ours_check_algorithm) { + default: + /* + * XXX: I intentionally broke our indentation rule here, + * since this switch-case is just for measurement and + * therefore should soon be removed. + */ 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)) + &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr)) ip6stat.ip6s_forward_cachehit++; else { + struct sockaddr_in6 *dst6; + if (ip6_forward_rt.ro_rt) { /* route is down or destination is different */ ip6stat.ip6s_forward_cachemiss++; @@ -530,9 +545,10 @@ ip6_input(m) } 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; + dst6 = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; #ifdef SCOPEDROUTING ip6_forward_rt.ro_dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_dst); @@ -551,10 +567,27 @@ ip6_input(m) * route to the loopback interface for the destination of the packet. * But we think it's even useful in some situations, e.g. when using * a special daemon which wants to intercept the packet. + * + * XXX: some OSes automatically make a cloned route for the destination + * of an outgoing packet. If the outgoing interface of the packet + * is a loopback one, the kernel would consider the packet to be + * accepted, even if we have no such address assinged on the interface. + * We check the cloned flag of the route entry to reject such cases, + * assuming that route entries for our own addresses are not made by + * cloning (it should be true because in6_addloop explicitly installs + * the host route). However, we might have to do an explicit check + * while it would be less efficient. Or, should we rather install a + * reject route for such a case? */ if (ip6_forward_rt.ro_rt && (ip6_forward_rt.ro_rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && +#ifdef RTF_WASCLONED + !(ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) && +#endif +#ifdef RTF_CLONED + !(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) && +#endif #if 0 /* * The check below is redundant since the comparison of @@ -562,13 +595,17 @@ ip6_input(m) * already done through looking up the routing table. */ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && + &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; - if (ia6->ia6_flags & IN6_IFF_ANYCAST) - m->m_flags |= M_ANYCAST6; + + /* + * record address information into m_aux. + */ + (void)ip6_setdstifaddr(m, ia6); + /* * packets to a tentative, duplicated, or somehow invalid * address must not be accepted. @@ -577,22 +614,21 @@ ip6_input(m) /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ - /* Count the packet in the ip address stats */ ia6->ia_ifa.if_ipackets++; ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; - goto hbhcheck; } else { /* address is not ready, so discard the packet. */ - log(LOG_INFO, - "ip6_input: packet to an unready address %s->%s", + nd6log((LOG_INFO, + "ip6_input: packet to an unready address %s->%s\n", ip6_sprintf(&ip6->ip6_src), - ip6_sprintf(&ip6->ip6_dst)); + ip6_sprintf(&ip6->ip6_dst))); goto bad; } } + } /* XXX indentation (see above) */ /* * FAITH(Firewall Aided Internet Translator) @@ -621,6 +657,27 @@ ip6_input(m) hbhcheck: /* + * record address information into m_aux, if we don't have one yet. + * note that we are unable to record it, if the address is not listed + * as our interface address (e.g. multicast addresses, addresses + * within FAITH prefixes and such). + */ + if (deliverifp && !ip6_getdstifaddr(m)) { + struct in6_ifaddr *ia6; + + ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); + if (ia6) { + if (!ip6_setdstifaddr(m, ia6)) { + /* + * XXX maybe we should drop the packet here, + * as we could not provide enough information + * to the upper layers. + */ + } + } + } + + /* * Process Hop-by-Hop options header if it's contained. * m may be modified in ip6_hopopts_input(). * If a JumboPayload option is included, plen will also be modified. @@ -749,6 +806,7 @@ ip6_input(m) ip6stat.ip6s_delivered++; in6_ifstat_inc(deliverifp, ifs6_in_deliver); nest = 0; + while (nxt != IPPROTO_DONE) { if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { ip6stat.ip6s_toomanyhdr++; @@ -765,6 +823,35 @@ ip6_input(m) goto bad; } +#if 0 + /* + * do we need to do it for every header? yeah, other + * functions can play with it (like re-allocate and copy). + */ + mhist = ip6_addaux(m); + if (mhist && M_TRAILINGSPACE(mhist) >= sizeof(nxt)) { + hist = mtod(mhist, caddr_t) + mhist->m_len; + bcopy(&nxt, hist, sizeof(nxt)); + mhist->m_len += sizeof(nxt); + } else { + ip6stat.ip6s_toomanyhdr++; + goto bad; + } +#endif + +#ifdef IPSEC + /* + * enforce IPsec policy checking if we are seeing last header. + * note that we do not visit this with protocols with pcb layer + * code - like udp/tcp/raw ip. + */ + if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto bad; + } +#endif + nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; @@ -773,6 +860,36 @@ ip6_input(m) } /* + * set/grab in6_ifaddr correspond to IPv6 destination address. + * XXX backward compatibility wrapper + */ +static struct mbuf * +ip6_setdstifaddr(m, ia6) + struct mbuf *m; + struct in6_ifaddr *ia6; +{ + struct mbuf *n; + + n = ip6_addaux(m); + if (n) + mtod(n, struct ip6aux *)->ip6a_dstia6 = ia6; + return n; /* NULL if failed to set */ +} + +struct in6_ifaddr * +ip6_getdstifaddr(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = ip6_findaux(m); + if (n) + return mtod(n, struct ip6aux *)->ip6a_dstia6; + else + return NULL; +} + +/* * Hop-by-Hop options header processing. If a valid jumbo payload option is * included, the real payload length will be stored in plenp. */ @@ -783,7 +900,7 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) struct mbuf **mp; int *offp; { - register struct mbuf *m = *mp; + struct mbuf *m = *mp; int off = *offp, hbhlen; struct ip6_hbh *hbh; u_int8_t *opt; @@ -829,6 +946,10 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp) * This function is separate from ip6_hopopts_input() in order to * handle a case where the sending node itself process its hop-by-hop * options header. In such a case, the function is called from ip6_output(). + * + * The function assumes that hbh header is located right after the IPv6 header + * (RFC2460 p7), opthead is pointer into data content in m, and opthead to + * opthead + hbhlen is located in continuous memory region. */ int ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) @@ -843,58 +964,62 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) u_int8_t *opt = opthead; u_int16_t rtalert_val; u_int32_t jumboplen; + const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { - switch(*opt) { - case IP6OPT_PAD1: - optlen = 1; - break; - case IP6OPT_PADN: - if (hbhlen < IP6OPT_MINLEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - optlen = *(opt + 1) + 2; - break; - case IP6OPT_RTALERT: - /* XXX may need check for alignment */ - if (hbhlen < IP6OPT_RTALERT_LEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of router alert opt is inconsitent(%d)", - *(opt + 1)); - optlen = IP6OPT_RTALERT_LEN; - bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); - *rtalertp = ntohs(rtalert_val); - break; - case IP6OPT_JUMBO: + switch (*opt) { + case IP6OPT_PAD1: + optlen = 1; + break; + case IP6OPT_PADN: + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = *(opt + 1) + 2; + break; + case IP6OPT_RTALERT: + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_RTALERT_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return(-1); + } + optlen = IP6OPT_RTALERT_LEN; + bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); + *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)); + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { + /* XXX stat */ + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + erroff + opt + 1 - opthead); + return(-1); + } optlen = IP6OPT_JUMBO_LEN; /* * IPv6 packets that have non 0 payload length - * must not contain a jumbo paylod option. + * must not contain a jumbo payload 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); + erroff + opt - opthead); return(-1); } @@ -918,9 +1043,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); + erroff + opt + 2 - opthead); return(-1); } #endif @@ -932,26 +1055,23 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); + erroff + opt + 2 - opthead); return(-1); } *plenp = jumboplen; break; - default: /* unknown option */ - if (hbhlen < IP6OPT_MINLEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if ((optlen = ip6_unknown_opt(opt, m, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead)) == -1) - return(-1); - optlen += 2; - break; + default: /* unknown option */ + if (hbhlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + optlen = ip6_unknown_opt(opt, m, + erroff + opt - opthead); + if (optlen == -1) + return(-1); + optlen += 2; + break; } } @@ -976,26 +1096,26 @@ ip6_unknown_opt(optp, m, off) { struct ip6_hdr *ip6; - switch(IP6OPT_TYPE(*optp)) { - case IP6OPT_TYPE_SKIP: /* ignore the option */ - return((int)*(optp + 1)); - case IP6OPT_TYPE_DISCARD: /* silently discard */ - m_freem(m); - return(-1); - case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); - return(-1); - case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ - ip6stat.ip6s_badoptions++; - ip6 = mtod(m, struct ip6_hdr *); - if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || - (m->m_flags & (M_BCAST|M_MCAST))) - m_freem(m); - else - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_OPTION, off); - return(-1); + switch (IP6OPT_TYPE(*optp)) { + case IP6OPT_TYPE_SKIP: /* ignore the option */ + return((int)*(optp + 1)); + case IP6OPT_TYPE_DISCARD: /* silently discard */ + m_freem(m); + return(-1); + case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); + return(-1); + case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ + ip6stat.ip6s_badoptions++; + ip6 = mtod(m, struct ip6_hdr *); + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + (m->m_flags & (M_BCAST|M_MCAST))) + m_freem(m); + else + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_OPTION, off); + return(-1); } m_freem(m); /* XXX: NOTREACHED */ @@ -1004,50 +1124,44 @@ ip6_unknown_opt(optp, m, off) /* * Create the "control" list for this pcb. + * The function will not modify mbuf chain at all. * + * with KAME mbuf chain restriction: * 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 in6pcb *in6p; - register struct mbuf **mp; - register struct ip6_hdr *ip6; - register struct mbuf *m; + struct inpcb *in6p; + struct mbuf **mp; + struct ip6_hdr *ip6; + struct mbuf *m; { struct proc *p = curproc; /* XXX */ - int privileged; + int privileged = 0; + int rthdr_exist = 0; + - privileged = 0; if (p && !suser(p)) - privileged++; + privileged++; - if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { +#ifdef SO_TIMESTAMP + if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) { struct timeval tv; microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), - SCM_TIMESTAMP, SOL_SOCKET); - if (*mp) + SCM_TIMESTAMP, SOL_SOCKET); + 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) { + if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { struct in6_pktinfo pi6; bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) @@ -1061,14 +1175,14 @@ ip6_savecontrol(in6p, mp, ip6, m) if (*mp) mp = &(*mp)->m_next; } - if (in6p->in6p_flags & IN6P_HOPLIMIT) { + + if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) { int hlim = ip6->ip6_hlim & 0xff; *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } - /* IN6P_NEXTHOP - for outgoing packet only */ /* * IPV6_HOPOPTS socket option. We require super-user privilege @@ -1076,7 +1190,7 @@ ip6_savecontrol(in6p, mp, ip6, m) * be some hop-by-hop options which can be returned to normal user. * See RFC 2292 section 6. */ - if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { + if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0 && privileged) { /* * Check if a hop-by-hop options header is contatined in the * received packet, and if so, store the options as ancillary @@ -1087,22 +1201,25 @@ 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; - int hbhlen; + int hbhlen = 0; +#ifdef PULLDOWN_TEST + struct mbuf *ext; +#endif #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) { + ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), + ip6->ip6_nxt); + if (ext == NULL) { ip6stat.ip6s_tooshort++; return; } + hbh = mtod(ext, struct ip6_hbh *); hbhlen = (hbh->ip6h_len + 1) << 3; - IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, - sizeof(struct ip6_hdr), hbhlen); - if (hbh == NULL) { + if (hbhlen != ext->m_len) { + m_freem(ext); ip6stat.ip6s_tooshort++; return; } @@ -1112,19 +1229,53 @@ ip6_savecontrol(in6p, mp, ip6, m) * XXX: We copy whole the header even if a jumbo * payload option is included, which option is to * be removed before returning in the RFC 2292. - * But it's too painful operation... + * Note: this constraint is removed in 2292bis. */ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, IPV6_HOPOPTS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; +#ifdef PULLDOWN_TEST + m_freem(ext); +#endif } } /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ - if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { + if ((in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) { + int proto, off, nxt; + + /* + * go through the header chain to see if a routing header is + * contained in the packet. We need this information to store + * destination options headers (if any) properly. + * XXX: performance issue. We should record this info when + * processing extension headers in incoming routine. + * (todo) use m_aux? + */ + proto = IPPROTO_IPV6; + off = 0; + nxt = -1; + while (1) { + int newoff; + + newoff = ip6_nexthdr(m, off, proto, &nxt); + if (newoff < 0) + break; + if (newoff < off) /* invalid, check for safety */ + break; + if ((proto = nxt) == IPPROTO_ROUTING) { + rthdr_exist = 1; + break; + } + off = newoff; + } + } + + if ((in6p->in6p_flags & + (IN6P_RTHDR | IN6P_DSTOPTS | IN6P_RTHDRDSTOPTS)) != 0) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);; + int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); /* * Search for destination options headers or routing @@ -1133,95 +1284,172 @@ ip6_savecontrol(in6p, mp, ip6, m) * Note that the order of the headers remains in * the chain of ancillary data. */ - while(1) { /* is explicit loop prevention necessary? */ - struct ip6_ext *ip6e; + while (1) { /* is explicit loop prevention necessary? */ + struct ip6_ext *ip6e = NULL; int elen; +#ifdef PULLDOWN_TEST + struct mbuf *ext = NULL; +#endif + + /* + * if it is not an extension header, don't try to + * pull it from the chain. + */ + switch (nxt) { + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + default: + goto loopend; + } #ifndef PULLDOWN_TEST + if (off + sizeof(*ip6e) > m->m_len) + goto loopend; 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; + if (off + elen > m->m_len) + goto loopend; #else - IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, - sizeof(struct ip6_ext)); - if (ip6e == NULL) { + ext = ip6_pullexthdr(m, off, nxt); + if (ext == NULL) { ip6stat.ip6s_tooshort++; return; } + ip6e = mtod(ext, struct ip6_ext *); 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) { + if (elen != ext->m_len) { + m_freem(ext); ip6stat.ip6s_tooshort++; return; } #endif - switch(nxt) { - case IPPROTO_DSTOPTS: - if (!in6p->in6p_flags & IN6P_DSTOPTS) - break; - - /* - * We also require super-user privilege for - * the option. - * See the comments on IN6_HOPOPTS. - */ - if (!privileged) - break; - - *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_DSTOPTS, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - break; - - case IPPROTO_ROUTING: - if (!in6p->in6p_flags & IN6P_RTHDR) - break; - - *mp = sbcreatecontrol((caddr_t)ip6e, elen, - IPV6_RTHDR, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - break; - - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ICMPV6: - default: - /* - * stop search if we encounter an upper - * layer protocol headers. - */ - goto loopend; - - case IPPROTO_HOPOPTS: - case IPPROTO_AH: /* is it possible? */ - break; + switch (nxt) { + case IPPROTO_DSTOPTS: + if ((in6p->in6p_flags & IN6P_DSTOPTS) == 0) + break; + + /* + * We also require super-user privilege for + * the option. + * See the comments on IN6_HOPOPTS. + */ + if (!privileged) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, elen, + IPV6_DSTOPTS, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + case IPPROTO_ROUTING: + if (!in6p->in6p_flags & IN6P_RTHDR) + break; + + *mp = sbcreatecontrol((caddr_t)ip6e, elen, + IPV6_RTHDR, + IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + + default: + /* + * other cases have been filtered in the above. + * none will visit this case. here we supply + * the code just in case (nxt overwritten or + * other cases). + */ +#ifdef PULLDOWN_TEST + m_freem(ext); +#endif + goto loopend; + } /* proceed with the next header. */ off += elen; nxt = ip6e->ip6e_nxt; + ip6e = NULL; +#ifdef PULLDOWN_TEST + m_freem(ext); + ext = NULL; +#endif } loopend: + ; } - if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { - /* to be done */ + +} + +#ifdef PULLDOWN_TEST +/* + * pull single extension header from mbuf chain. returns single mbuf that + * contains the result, or NULL on error. + */ +static struct mbuf * +ip6_pullexthdr(m, off, nxt) + struct mbuf *m; + size_t off; + int nxt; +{ + struct ip6_ext ip6e; + size_t elen; + struct mbuf *n; + +#ifdef DIAGNOSTIC + switch (nxt) { + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + case IPPROTO_AH: /* is it possible? */ + break; + default: + printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); + } +#endif + + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + if (nxt == IPPROTO_AH) + elen = (ip6e.ip6e_len + 2) << 2; + else + elen = (ip6e.ip6e_len + 1) << 3; + + MGET(n, M_DONTWAIT, MT_DATA); + if (n && elen >= MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } } - if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) { - /* to be done */ + if (!n) + return NULL; + + n->m_len = 0; + if (elen >= M_TRAILINGSPACE(n)) { + m_free(n); + return NULL; } - /* IN6P_RTHDR - to be done */ + m_copydata(m, off, elen, mtod(n, caddr_t)); + n->m_len = elen; + return n; } +#endif /* * Get pointer to the previous header followed by the header @@ -1253,7 +1481,7 @@ ip6_get_prevhdr(m, off) while (len < off) { ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); - switch(nxt) { + switch (nxt) { case IPPROTO_FRAGMENT: len += sizeof(struct ip6_frag); break; @@ -1382,6 +1610,55 @@ ip6_lasthdr(m, off, proto, nxtp) } } +struct mbuf * +ip6_addaux(m) + struct mbuf *m; +{ + struct mbuf *n; + +#ifdef DIAGNOSTIC + if (sizeof(struct ip6aux) > MHLEN) + panic("assumption failed on sizeof(ip6aux)"); +#endif + n = m_aux_find(m, AF_INET6, -1); + if (n) { + if (n->m_len < sizeof(struct ip6aux)) { + printf("conflicting use of ip6aux"); + return NULL; + } + } else { + n = m_aux_add(m, AF_INET6, -1); + n->m_len = sizeof(struct ip6aux); + bzero(mtod(n, caddr_t), n->m_len); + } + return n; +} + +struct mbuf * +ip6_findaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET6, -1); + if (n && n->m_len < sizeof(struct ip6aux)) { + printf("conflicting use of ip6aux"); + n = NULL; + } + return n; +} + +void +ip6_delaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET6, -1); + if (n) + m_aux_delete(m, n); +} + /* * System control for IP6 */ diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c index 82b0d4b60da1..2be8796f6754 100644 --- a/sys/netinet6/ip6_mroute.c +++ b/sys/netinet6/ip6_mroute.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_mroute.c,v 1.33 2000/10/19 02:23:43 jinmei Exp $ */ +/* $KAME: ip6_mroute.c,v 1.46 2001/04/04 05:17:30 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -50,6 +50,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/callout.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -143,7 +144,6 @@ static mifi_t nummifs = 0; static mifi_t reg_mif_num = (mifi_t)-1; static struct pim6stat pim6stat; -static struct callout_handle expire_upcalls_ch; /* * one-back cache used by ipip_input to locate a tunnel's mif @@ -165,7 +165,7 @@ static int pim6; */ #define MF6CFIND(o, g, rt) do { \ - register struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ + struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ rt = NULL; \ mrt6stat.mrt6s_mfc_lookups++; \ while (_rt) { \ @@ -187,7 +187,7 @@ static int pim6; * Borrowed from Van Jacobson's scheduling code */ #define TV_DELTA(a, b, delta) do { \ - register int xxs; \ + int xxs; \ \ delta = (a).tv_usec - (b).tv_usec; \ if ((xxs = (a).tv_sec - (b).tv_sec)) { \ @@ -221,6 +221,8 @@ static int del_m6if __P((mifi_t *)); static int add_m6fc __P((struct mf6cctl *)); static int del_m6fc __P((struct mf6cctl *)); +static struct callout expire_upcalls_ch; + /* * Handle MRT setsockopt commands to modify the multicast routing tables. */ @@ -241,33 +243,33 @@ ip6_mrouter_set(so, sopt) return (error); switch (sopt->sopt_name) { - case MRT6_INIT: + case MRT6_INIT: #ifdef MRT6_OINIT - case MRT6_OINIT: + case MRT6_OINIT: #endif - error = ip6_mrouter_init(so, m, sopt->sopt_name); - break; - case MRT6_DONE: - error = ip6_mrouter_done(); - break; - case MRT6_ADD_MIF: - error = add_m6if(mtod(m, struct mif6ctl *)); - break; - case MRT6_DEL_MIF: - error = del_m6if(mtod(m, mifi_t *)); - break; - case MRT6_ADD_MFC: - error = add_m6fc(mtod(m, struct mf6cctl *)); - break; - case MRT6_DEL_MFC: - error = del_m6fc(mtod(m, struct mf6cctl *)); - break; - case MRT6_PIM: - error = set_pim6(mtod(m, int *)); - break; - default: - error = EOPNOTSUPP; - break; + error = ip6_mrouter_init(so, m, sopt->sopt_name); + break; + case MRT6_DONE: + error = ip6_mrouter_done(); + break; + case MRT6_ADD_MIF: + error = add_m6if(mtod(m, struct mif6ctl *)); + break; + case MRT6_DEL_MIF: + error = del_m6if(mtod(m, mifi_t *)); + break; + case MRT6_ADD_MFC: + error = add_m6fc(mtod(m, struct mf6cctl *)); + break; + case MRT6_DEL_MFC: + error = del_m6fc(mtod(m, struct mf6cctl *)); + break; + case MRT6_PIM: + error = set_pim6(mtod(m, int *)); + break; + default: + error = EOPNOTSUPP; + break; } (void)m_freem(m); @@ -302,20 +304,20 @@ mrt6_ioctl(cmd, data) int cmd; caddr_t data; { - int error = 0; - - switch (cmd) { - case SIOCGETSGCNT_IN6: - return(get_sg_cnt((struct sioc_sg_req6 *)data)); - break; /* for safety */ - case SIOCGETMIFCNT_IN6: - return(get_mif6_cnt((struct sioc_mif_req6 *)data)); - break; /* for safety */ - default: - return (EINVAL); - break; - } - return error; + int error = 0; + + switch (cmd) { + case SIOCGETSGCNT_IN6: + return(get_sg_cnt((struct sioc_sg_req6 *)data)); + break; /* for safety */ + case SIOCGETMIFCNT_IN6: + return(get_mif6_cnt((struct sioc_mif_req6 *)data)); + break; /* for safety */ + default: + return (EINVAL); + break; + } + return error; } /* @@ -323,9 +325,9 @@ mrt6_ioctl(cmd, data) */ static int get_sg_cnt(req) - register struct sioc_sg_req6 *req; + struct sioc_sg_req6 *req; { - register struct mf6c *rt; + struct mf6c *rt; int s; s = splnet(); @@ -349,9 +351,9 @@ get_sg_cnt(req) */ static int get_mif6_cnt(req) - register struct sioc_mif_req6 *req; + struct sioc_mif_req6 *req; { - register mifi_t mifi = req->mifi; + mifi_t mifi = req->mifi; if (mifi >= nummifs) return EINVAL; @@ -415,8 +417,8 @@ ip6_mrouter_init(so, m, cmd) pim6 = 0;/* used for stubbing out/in pim stuff */ - expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, + expire_upcalls, NULL); #ifdef MRT6DEBUG if (mrt6debug) @@ -478,7 +480,7 @@ ip6_mrouter_done() pim6 = 0; /* used to stub out/in pim specific code */ - untimeout(expire_upcalls, (caddr_t)NULL, expire_upcalls_ch); + callout_stop(&expire_upcalls_ch); /* * Free all multicast forwarding cache entries. @@ -528,9 +530,9 @@ static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; */ static int add_m6if(mifcp) - register struct mif6ctl *mifcp; + struct mif6ctl *mifcp; { - register struct mif6 *mifp; + struct mif6 *mifp; struct ifnet *ifp; int error, s; #ifdef notyet @@ -605,8 +607,8 @@ static int del_m6if(mifip) mifi_t *mifip; { - register struct mif6 *mifp = mif6table + *mifip; - register mifi_t mifi; + struct mif6 *mifp = mif6table + *mifip; + mifi_t mifi; struct ifnet *ifp; int s; @@ -659,7 +661,7 @@ add_m6fc(mfccp) struct mf6c *rt; u_long hash; struct rtdetq *rte; - register u_short nstl; + u_short nstl; int s; MF6CFIND(mfccp->mf6cc_origin.sin6_addr, @@ -809,11 +811,11 @@ add_m6fc(mfccp) */ static void collate(t) - register struct timeval *t; + struct timeval *t; { - register u_long d; - register struct timeval tp; - register u_long delta; + u_long d; + struct timeval tp; + u_long delta; GET_TIME(tp); @@ -912,13 +914,13 @@ socket_send(s, mm, src) int ip6_mforward(ip6, ifp, m) - register struct ip6_hdr *ip6; + struct ip6_hdr *ip6; struct ifnet *ifp; struct mbuf *m; { - register struct mf6c *rt; - register struct mif6 *mifp; - register struct mbuf *mm; + struct mf6c *rt; + struct mif6 *mifp; + struct mbuf *mm; int s; mifi_t mifi; @@ -977,10 +979,10 @@ ip6_mforward(ip6, ifp, m) * send message to routing daemon */ - register struct mbuf *mb0; - register struct rtdetq *rte; - register u_long hash; -/* register int i, npkts;*/ + struct mbuf *mb0; + struct rtdetq *rte; + u_long hash; +/* int i, npkts;*/ #ifdef UPCALL_TIMING struct timeval tp; @@ -1144,7 +1146,7 @@ ip6_mforward(ip6, ifp, m) } else { /* determine if q has overflowed */ struct rtdetq **p; - register int npkts = 0; + int npkts = 0; for (p = &rt->mf6c_stall; *p != NULL; p = &(*p)->next) if (++npkts > MAX_UPQ6) { @@ -1227,8 +1229,8 @@ expire_upcalls(unused) } } splx(s); - expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, + expire_upcalls, NULL); } /* @@ -1236,14 +1238,14 @@ expire_upcalls(unused) */ static int ip6_mdq(m, ifp, rt) - register struct mbuf *m; - register struct ifnet *ifp; - register struct mf6c *rt; + struct mbuf *m; + struct ifnet *ifp; + struct mf6c *rt; { - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register mifi_t mifi, iif; - register struct mif6 *mifp; - register int plen = m->m_pkthdr.len; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + mifi_t mifi, iif; + struct mif6 *mifp; + int plen = m->m_pkthdr.len; /* * Macro to send packet on mif. Since RSVP packets don't get counted on @@ -1290,7 +1292,7 @@ ip6_mdq(m, ifp, rt) static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; - register struct mbuf *mm; + struct mbuf *mm; struct mrt6msg *im; #ifdef MRT6_OINIT struct omrt6msg *oim; @@ -1319,6 +1321,7 @@ ip6_mdq(m, ifp, rt) case MRT6_INIT: im = mtod(mm, struct mrt6msg *); im->im6_msgtype = MRT6MSG_WRONGMIF; + im->im6_mbz = 0; break; default: m_freem(mm); @@ -1408,12 +1411,13 @@ phyint_send(ip6, mifp, m) struct mif6 *mifp; struct mbuf *m; { - register struct mbuf *mb_copy; + struct mbuf *mb_copy; struct ifnet *ifp = mifp->m6_ifp; int error = 0; - int s = splnet(); - static struct route_in6 ro6; + int s = splnet(); /* needs to protect static "ro" below. */ + static struct route_in6 ro; struct in6_multi *in6m; + struct sockaddr_in6 *dst6; /* * Make a new reference to the packet; make sure that @@ -1424,8 +1428,10 @@ phyint_send(ip6, mifp, m) if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < sizeof(struct ip6_hdr))) mb_copy = m_pullup(mb_copy, sizeof(struct ip6_hdr)); - if (mb_copy == NULL) + if (mb_copy == NULL) { + splx(s); return; + } /* set MCAST flag to the outgoing packet */ mb_copy->m_flags |= M_MCAST; @@ -1443,7 +1449,7 @@ phyint_send(ip6, mifp, m) /* XXX: ip6_output will override ip6->ip6_hlim */ im6o.im6o_multicast_hlim = ip6->ip6_hlim; im6o.im6o_multicast_loop = 1; - error = ip6_output(mb_copy, NULL, &ro6, + error = ip6_output(mb_copy, NULL, &ro, IPV6_FORWARDING, &im6o, NULL); #ifdef MRT6DEBUG @@ -1459,63 +1465,62 @@ phyint_send(ip6, mifp, m) * If we belong to the destination multicast group * on the outgoing interface, loop back a copy. */ + dst6 = (struct sockaddr_in6 *)&ro.ro_dst; IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); if (in6m != NULL) { - ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro6.ro_dst.sin6_family = AF_INET6; - ro6.ro_dst.sin6_addr = ip6->ip6_dst; - ip6_mloopback(ifp, m, &ro6.ro_dst); + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; + ip6_mloopback(ifp, m, (struct sockaddr_in6 *)&ro.ro_dst); } /* * Put the packet into the sending queue of the outgoing interface * if it would fit in the MTU of the interface. */ if (mb_copy->m_pkthdr.len < ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) { - ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro6.ro_dst.sin6_family = AF_INET6; - ro6.ro_dst.sin6_addr = ip6->ip6_dst; + dst6->sin6_len = sizeof(struct sockaddr_in6); + dst6->sin6_family = AF_INET6; + dst6->sin6_addr = ip6->ip6_dst; /* * We just call if_output instead of nd6_output here, since * we need no ND for a multicast forwarded packet...right? */ error = (*ifp->if_output)(ifp, mb_copy, - (struct sockaddr *)&ro6.ro_dst, - NULL); + (struct sockaddr *)&ro.ro_dst, NULL); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, "phyint_send on mif %d err %d\n", mifp - mif6table, error); #endif - } - else { + } else { #ifdef MULTICAST_PMTUD icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - return; #else #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, - "phyint_send: packet too big on %s%u o %s g %s" + "phyint_send: packet too big on %s o %s g %s" " size %d(discarded)\n", - ifp->if_name, ifp->if_unit, + if_name(ifp), ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), mb_copy->m_pkthdr.len); #endif /* MRT6DEBUG */ m_freem(mb_copy); /* simply discard the packet */ - return; #endif } + + splx(s); } static int register_send(ip6, mif, m) - register struct ip6_hdr *ip6; + struct ip6_hdr *ip6; struct mif6 *mif; - register struct mbuf *m; + struct mbuf *m; { - register struct mbuf *mm; - register int i, len = m->m_pkthdr.len; + struct mbuf *mm; + int i, len = m->m_pkthdr.len; static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; struct mrt6msg *im6; @@ -1530,6 +1535,7 @@ register_send(ip6, mif, m) MGETHDR(mm, M_DONTWAIT, MT_HEADER); if (mm == NULL) return ENOBUFS; + mm->m_pkthdr.rcvif = NULL; mm->m_data += max_linkhdr; mm->m_len = sizeof(struct ip6_hdr); @@ -1568,8 +1574,8 @@ register_send(ip6, mif, m) log(LOG_WARNING, "register_send: ip_mrouter socket queue full\n"); #endif - ++mrt6stat.mrt6s_upq_sockfull; - return ENOBUFS; + ++mrt6stat.mrt6s_upq_sockfull; + return ENOBUFS; } return 0; } @@ -1586,21 +1592,21 @@ pim6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { - register struct pim *pim; /* pointer to a pim struct */ - register struct ip6_hdr *ip6; - register int pimlen; + struct pim *pim; /* pointer to a pim struct */ + struct ip6_hdr *ip6; + int pimlen; struct mbuf *m = *mp; - int minlen; + int minlen; int off = *offp; ++pim6stat.pim6s_rcv_total; - ip6 = mtod(m, struct ip6_hdr *); - pimlen = m->m_pkthdr.len - *offp; + ip6 = mtod(m, struct ip6_hdr *); + pimlen = m->m_pkthdr.len - *offp; - /* - * Validate lengths - */ + /* + * Validate lengths + */ if (pimlen < PIM_MINLEN) { ++pim6stat.pim6s_rcv_tooshort; #ifdef MRT6DEBUG @@ -1736,6 +1742,18 @@ pim6_input(mp, offp, proto) ip6_sprintf(&eip6->ip6_dst), ntohs(eip6->ip6_plen)); #endif + + /* verify the version number of the inner packet */ + if ((eip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + ++pim6stat.pim6s_rcv_badregisters; +#ifdef MRT6DEBUG + log(LOG_DEBUG, "pim6_input: invalid IP version (%d) " + "of the inner packet\n", + (eip6->ip6_vfc & IPV6_VERSION)); +#endif + m_freem(m); + return(IPPROTO_NONE); + } /* verify the inner packet is destined to a mcast group */ if (!IN6_IS_ADDR_MULTICAST(&eip6->ip6_dst)) { @@ -1781,7 +1799,7 @@ 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 34ec5388940f..7871150b6047 100644 --- a/sys/netinet6/ip6_mroute.h +++ b/sys/netinet6/ip6_mroute.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_mroute.h,v 1.10 2000/05/19 02:38:53 itojun Exp $ */ +/* $KAME: ip6_mroute.h,v 1.17 2001/02/10 02:05:52 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -79,7 +79,7 @@ typedef u_short mifi_t; /* type of a mif index */ #define IF_SETSIZE 256 #endif -typedef long if_mask; +typedef u_int32_t if_mask; #define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */ #ifndef howmany @@ -87,7 +87,7 @@ typedef long if_mask; #endif typedef struct if_set { - fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; + if_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; } if_set; #define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 4176bc47667f..928f3d3ffbf3 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_output.c,v 1.115 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ip6_output.c,v 1.180 2001/05/21 05:37:50 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -89,6 +89,7 @@ #include <netinet/in.h> #include <netinet/in_var.h> +#include <netinet6/in6_var.h> #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <netinet6/ip6_var.h> @@ -103,10 +104,10 @@ #include <netkey/key.h> #endif /* IPSEC */ -#include <net/net_osdep.h> - #include <netinet6/ip6_fw.h> +#include <net/net_osdep.h> + #include <netinet6/ip6protosw.h> static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); @@ -138,6 +139,22 @@ extern u_char ip6_protox[IPPROTO_MAX]; * This function may modify ver and hlim only. * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. + * + * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and + * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, + * which is rt_rmx.rmx_mtu. + * + * If MIP6 is active it will have to add a Home Address option to DH1 if + * the mobile node is roaming or a Routing Header type 0 if there exist + * a Binding Cache entry for the destination node or a BU option to DH2 + * if the mobile node initiates communication and no BUL entry exist. + * The only way to do this is to allocate new memory, copy the user data + * to the new buffer and then add the Home Address option, BU option and + * routing header type 0 respectively. MIP6 will set two flags in "struct + * pktopts" to restore the original contents once ip6_output is completed. + * To make this work, make sure that function exit is made through label + * alldone. + * */ int ip6_output(m0, opt, ro, flags, im6o, ifpp) @@ -175,7 +192,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) /* for AH processing. stupid to have "socket" variable in IP layer... */ so = ipsec_getsocket(m); - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ @@ -419,13 +436,13 @@ skip_ipsec2:; struct ip6_rthdr0 *rh0; finaldst = ip6->ip6_dst; - switch(rh->ip6r_type) { + switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)rh; ip6->ip6_dst = rh0->ip6r0_addr[0]; bcopy((caddr_t)&rh0->ip6r0_addr[1], - (caddr_t)&rh0->ip6r0_addr[0], - sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) + (caddr_t)&rh0->ip6r0_addr[0], + sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) ); rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; break; @@ -745,7 +762,7 @@ skip_ipsec2:; u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; - if (mtu > ifmtu) { + if (mtu > ifmtu || mtu == 0) { /* * The MTU on the route is larger than the MTU on * the interface! This shouldn't happen, unless the @@ -753,6 +770,9 @@ skip_ipsec2:; * interface was brought up. Change the MTU in the * route to match the interface MTU (as long as the * field isn't locked). + * + * if MTU on the route is 0, we need to fix the MTU. + * this case happens with path MTU discovery timeouts. */ mtu = ifmtu; if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) @@ -762,6 +782,12 @@ skip_ipsec2:; mtu = nd_ifinfo[ifp->if_index].linkmtu; } + /* + * advanced API (IPV6_USE_MIN_MTU) overrides mtu setting + */ + if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU) + mtu = IPV6_MMTU; + /* Fake scoped addresses */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { /* @@ -776,34 +802,44 @@ skip_ipsec2:; * 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 + * larger scopes than link will be supported in the near * future. */ + origifp = NULL; 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 + /* + * XXX: origifp can be NULL even in those two cases above. + * For example, if we remove the (only) link-local address + * from the loopback interface, and try to send a link-local + * address without link-id information. Then the source + * address is ::1, and the destination address is the + * link-local address with its s6_addr16[1] being zero. + * What is worse, if the packet goes to the loopback interface + * by a default rejected route, the null pointer would be + * passed to looutput, and the kernel would hang. + * The following last resort would prevent such disaster. + */ + if (origifp == NULL) origifp = ifp; } else origifp = ifp; -#ifndef FAKE_LOOPBACK_IF - if ((ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #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; - } /* * Check with the firewall... */ - if (ip6_fw_enable && ip6_fw_chk_ptr) { + if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; m->m_pkthdr.rcvif = NULL; /*XXX*/ /* If ipfw says divert, we have to just drop packet */ @@ -823,11 +859,14 @@ skip_ipsec2:; * (RFC 2460, section 4.) */ if (exthdrs.ip6e_hbh) { - struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, - struct ip6_hbh *); + struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); u_int32_t dummy1; /* XXX unused */ u_int32_t dummy2; /* XXX unused */ +#ifdef DIAGNOSTIC + if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) + panic("ip6e_hbh is not continuous"); +#endif /* * XXX: if we have to send an ICMPv6 error to the sender, * we need the M_LOOP flag since icmp6_error() expects @@ -889,12 +928,15 @@ skip_ipsec2:; #endif ) { - /* Record statistics for this interface address. */ - if (ia && !(flags & IPV6_FORWARDING)) { - ia->ia_ifa.if_opackets++; - ia->ia_ifa.if_obytes += m->m_pkthdr.len; - } - + /* Record statistics for this interface address. */ + if (ia && !(flags & IPV6_FORWARDING)) { + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; + } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; } else if (mtu < IPV6_MMTU) { @@ -923,6 +965,7 @@ skip_ipsec2:; hlen = unfragpartlen; if (mtu > IPV6_MAXPACKET) mtu = IPV6_MAXPACKET; + len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; @@ -962,6 +1005,7 @@ skip_ipsec2:; ip6stat.ip6s_odropped++; goto sendorfree; } + m->m_pkthdr.rcvif = NULL; m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; @@ -1011,12 +1055,15 @@ sendorfree: m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { - /* Record statistics for this interface address. */ - if (ia) { - ia->ia_ifa.if_opackets++; - ia->ia_ifa.if_obytes += m->m_pkthdr.len; - } - + /* Record statistics for this interface address. */ + if (ia) { + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; + } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); } else m_freem(m); @@ -1090,6 +1137,7 @@ ip6_insert_jumboopt(exthdrs, plen) { struct mbuf *mopt; u_char *optbuf; + u_int32_t v; #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ @@ -1112,18 +1160,42 @@ ip6_insert_jumboopt(exthdrs, plen) mopt = exthdrs->ip6e_hbh; if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { - caddr_t oldoptp = mtod(mopt, caddr_t); + /* + * XXX assumption: + * - exthdrs->ip6e_hbh is not referenced from places + * other than exthdrs. + * - exthdrs->ip6e_hbh is not an mbuf chain. + */ int oldoptlen = mopt->m_len; + struct mbuf *n; - if (mopt->m_flags & M_EXT) - return(ENOBUFS); /* XXX */ - MCLGET(mopt, M_DONTWAIT); - if ((mopt->m_flags & M_EXT) == 0) + /* + * XXX: give up if the whole (new) hbh header does + * not fit even in an mbuf cluster. + */ + if (oldoptlen + JUMBOOPTLEN > MCLBYTES) return(ENOBUFS); - bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); - optbuf = mtod(mopt, caddr_t) + oldoptlen; - mopt->m_len = oldoptlen + JUMBOOPTLEN; + /* + * As a consequence, we must always prepare a cluster + * at this point. + */ + MGET(n, M_DONTWAIT, MT_DATA); + if (n) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } + } + if (!n) + return(ENOBUFS); + n->m_len = oldoptlen + JUMBOOPTLEN; + bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t), + oldoptlen); + optbuf = mtod(n, caddr_t) + oldoptlen; + m_freem(mopt); + mopt = exthdrs->ip6e_hbh = n; } else { optbuf = mtod(mopt, u_char *) + mopt->m_len; mopt->m_len += JUMBOOPTLEN; @@ -1142,7 +1214,8 @@ ip6_insert_jumboopt(exthdrs, plen) /* fill in the option. */ optbuf[2] = IP6OPT_JUMBO; optbuf[3] = 4; - *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); + v = (u_int32_t)htonl(plen + JUMBOOPTLEN); + bcopy(&v, &optbuf[4], sizeof(u_int32_t)); /* finally, adjust the packet header length */ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; @@ -1206,7 +1279,7 @@ ip6_ctloutput(so, sopt) struct sockopt *sopt; { int privileged; - register struct inpcb *in6p = sotoinpcb(so); + struct inpcb *in6p = sotoinpcb(so); int error, optval; int level, op, optname; int optlen; @@ -1227,93 +1300,131 @@ ip6_ctloutput(so, sopt) if (level == IPPROTO_IPV6) { switch (op) { + case SOPT_SET: switch (optname) { case IPV6_PKTOPTIONS: - { + { struct mbuf *m; error = soopt_getm(sopt, &m); /* XXX */ - if (error != 0) + if (error != NULL) break; error = soopt_mcopyin(sopt, m); /* XXX */ - if (error != 0) - break; - return (ip6_pcbopts(&in6p->in6p_outputopts, - m, so, sopt)); - } - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (!privileged) { - error = EPERM; + if (error != NULL) break; - } - /* fall through */ + error = ip6_pcbopts(&in6p->in6p_outputopts, + m, so, sopt); + m_freem(m); /* XXX */ + break; + } + + /* + * Use of some Hop-by-Hop options or some + * Destination options, might require special + * privilege. That is, normal applications + * (without special privilege) might be forbidden + * from setting certain options in outgoing packets, + * and might never see certain options in received + * packets. [RFC 2292 Section 6] + * KAME specific note: + * KAME prevents non-privileged users from sending or + * receiving ANY hbh/dst options in order to avoid + * overhead of parsing options in the kernel. + */ case IPV6_UNICAST_HOPS: - case IPV6_PKTINFO: - case IPV6_HOPLIMIT: - case IPV6_RTHDR: case IPV6_CHECKSUM: case IPV6_FAITH: - case IPV6_BINDV6ONLY: - if (optlen != sizeof(int)) + + case IPV6_V6ONLY: + if (optlen != sizeof(int)) { error = EINVAL; - else { - error = sooptcopyin(sopt, &optval, - sizeof optval, sizeof optval); - if (error) - break; - switch (optname) { - - case IPV6_UNICAST_HOPS: - if (optval < -1 || optval >= 256) - error = EINVAL; - else { - /* -1 = kernel default */ - in6p->in6p_hops = optval; - if ((in6p->in6p_vflag & - INP_IPV4) != 0) - in6p->inp_ip_ttl = optval; - } - break; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + + case IPV6_UNICAST_HOPS: + if (optval < -1 || optval >= 256) + error = EINVAL; + else { + /* -1 = kernel default */ + in6p->in6p_hops = optval; + + if ((in6p->in6p_vflag & + INP_IPV4) != 0) + in6p->inp_ip_ttl = optval; + } + break; #define OPTSET(bit) \ +do { \ if (optval) \ - in6p->in6p_flags |= bit; \ + in6p->in6p_flags |= (bit); \ else \ - in6p->in6p_flags &= ~bit; - - case IPV6_PKTINFO: - OPTSET(IN6P_PKTINFO); - break; - - case IPV6_HOPLIMIT: - OPTSET(IN6P_HOPLIMIT); - break; - - case IPV6_HOPOPTS: - OPTSET(IN6P_HOPOPTS); - break; - - case IPV6_DSTOPTS: - OPTSET(IN6P_DSTOPTS); - break; + in6p->in6p_flags &= ~(bit); \ +} while (0) +#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) - case IPV6_RTHDR: - OPTSET(IN6P_RTHDR); - break; + case IPV6_CHECKSUM: + in6p->in6p_cksum = optval; + break; - case IPV6_CHECKSUM: - in6p->in6p_cksum = optval; - break; + case IPV6_FAITH: + OPTSET(IN6P_FAITH); + break; - case IPV6_FAITH: - OPTSET(IN6P_FAITH); - break; + case IPV6_V6ONLY: + /* + * XXX: BINDV6ONLY should be integrated + * into V6ONLY. + */ + OPTSET(IN6P_BINDV6ONLY); + OPTSET(IN6P_IPV6_V6ONLY); + break; + } + break; - case IPV6_BINDV6ONLY: - OPTSET(IN6P_BINDV6ONLY); - break; - } + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + case IPV6_RTHDR: + /* RFC 2292 */ + if (optlen != sizeof(int)) { + error = EINVAL; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + case IPV6_PKTINFO: + OPTSET(IN6P_PKTINFO); + break; + case IPV6_HOPLIMIT: + OPTSET(IN6P_HOPLIMIT); + break; + case IPV6_HOPOPTS: + /* + * Check super-user privilege. + * See comments for IPV6_RECVHOPOPTS. + */ + if (!privileged) + return(EPERM); + OPTSET(IN6P_HOPOPTS); + break; + case IPV6_DSTOPTS: + if (!privileged) + return(EPERM); + OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ + break; + case IPV6_RTHDR: + OPTSET(IN6P_RTHDR); + break; } break; #undef OPTSET @@ -1393,7 +1504,7 @@ ip6_ctloutput(so, sopt) m_freem(m); } break; -#endif /* IPSEC */ +#endif /* KAME IPSEC */ case IPV6_FW_ADD: case IPV6_FW_DEL: @@ -1405,11 +1516,9 @@ ip6_ctloutput(so, sopt) if (ip6_fw_ctl_ptr == NULL) return EINVAL; - if ((error = soopt_getm(sopt, &m)) - != 0) /* XXX */ + if (error = soopt_getm(sopt, &m)) /* XXX */ break; - if ((error = soopt_mcopyin(sopt, m)) - != 0) /* XXX */ + if (error = soopt_mcopyin(sopt, m)) /* XXX */ break; error = (*ip6_fw_ctl_ptr)(optname, mp); m = *mp; @@ -1433,20 +1542,11 @@ ip6_ctloutput(so, sopt) sopt->sopt_valsize = 0; break; - case IPV6_HOPOPTS: - case IPV6_DSTOPTS: - if (!privileged) { - error = EPERM; - break; - } - /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_PKTINFO: - case IPV6_HOPLIMIT: - case IPV6_RTHDR: case IPV6_CHECKSUM: + case IPV6_FAITH: - case IPV6_BINDV6ONLY: + case IPV6_V6ONLY: case IPV6_PORTRANGE: switch (optname) { @@ -1454,28 +1554,6 @@ ip6_ctloutput(so, sopt) optval = in6p->in6p_hops; break; -#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) - - case IPV6_PKTINFO: - optval = OPTBIT(IN6P_PKTINFO); - break; - - case IPV6_HOPLIMIT: - optval = OPTBIT(IN6P_HOPLIMIT); - break; - - case IPV6_HOPOPTS: - optval = OPTBIT(IN6P_HOPOPTS); - break; - - case IPV6_DSTOPTS: - optval = OPTBIT(IN6P_DSTOPTS); - break; - - case IPV6_RTHDR: - optval = OPTBIT(IN6P_RTHDR); - break; - case IPV6_CHECKSUM: optval = in6p->in6p_cksum; break; @@ -1484,14 +1562,14 @@ ip6_ctloutput(so, sopt) optval = OPTBIT(IN6P_FAITH); break; - case IPV6_BINDV6ONLY: + case IPV6_V6ONLY: + /* XXX: see the setopt case. */ optval = OPTBIT(IN6P_BINDV6ONLY); break; case IPV6_PORTRANGE: { int flags; - flags = in6p->in6p_flags; if (flags & IN6P_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; @@ -1506,6 +1584,40 @@ ip6_ctloutput(so, sopt) sizeof optval); break; + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_HOPOPTS: + case IPV6_RTHDR: + case IPV6_DSTOPTS: + if (optname == IPV6_HOPOPTS || + optname == IPV6_DSTOPTS || + !privileged) + return(EPERM); + switch (optname) { + case IPV6_PKTINFO: + optval = OPTBIT(IN6P_PKTINFO); + break; + case IPV6_HOPLIMIT: + optval = OPTBIT(IN6P_HOPLIMIT); + break; + case IPV6_HOPOPTS: + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_HOPOPTS); + break; + case IPV6_RTHDR: + optval = OPTBIT(IN6P_RTHDR); + break; + case IPV6_DSTOPTS: + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); + break; + } + error = sooptcopyout(sopt, &optval, + sizeof optval); + break; + case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: @@ -1543,10 +1655,11 @@ ip6_ctloutput(so, sopt) error = ipsec6_get_policy(in6p, req, len, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /*XXX*/ - m_freem(m); + if (error == 0 && m) + m_freem(m); break; } -#endif /* IPSEC */ +#endif /* KAME IPSEC */ case IPV6_FW_GET: { @@ -1555,6 +1668,8 @@ ip6_ctloutput(so, sopt) if (ip6_fw_ctl_ptr == NULL) { + if (m) + (void)m_free(m); return EINVAL; } error = (*ip6_fw_ctl_ptr)(optname, mp); @@ -1578,29 +1693,33 @@ ip6_ctloutput(so, sopt) } /* - * Set up IP6 options in pcb for insertion in output packets. - * Store in mbuf with pointer in pcbopt, adding pseudo-option - * with destination address if source routed. + * Set up IP6 options in pcb for insertion in output packets or + * specifying behavior of outgoing packets. */ static int ip6_pcbopts(pktopt, m, so, sopt) struct ip6_pktopts **pktopt; - register struct mbuf *m; + struct mbuf *m; struct socket *so; struct sockopt *sopt; { - register struct ip6_pktopts *opt = *pktopt; + struct ip6_pktopts *opt = *pktopt; int error = 0; struct proc *p = sopt->sopt_p; int priv = 0; /* turn off any old options. */ if (opt) { - if (opt->ip6po_m) - (void)m_free(opt->ip6po_m); +#ifdef DIAGNOSTIC + if (opt->ip6po_pktinfo || opt->ip6po_nexthop || + opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || + opt->ip6po_rhinfo.ip6po_rhi_rthdr) + printf("ip6_pcbopts: all specified options are cleared.\n"); +#endif + ip6_clearpktopts(opt, 1, -1); } else opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); - *pktopt = 0; + *pktopt = NULL; if (!m || m->m_len == 0) { /* @@ -1608,16 +1727,14 @@ ip6_pcbopts(pktopt, m, so, sopt) */ if (opt) free(opt, M_IP6OPT); - if (m) - (void)m_free(m); return(0); } /* set options specified by user. */ if (p && !suser(p)) priv = 1; - if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { - (void)m_free(m); + if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) { + ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */ return(error); } *pktopt = opt; @@ -1625,6 +1742,140 @@ ip6_pcbopts(pktopt, m, so, sopt) } /* + * initialize ip6_pktopts. beware that there are non-zero default values in + * the struct. + */ +void +init_ip6pktopts(opt) + struct ip6_pktopts *opt; +{ + + bzero(opt, sizeof(*opt)); + opt->ip6po_hlim = -1; /* -1 means default hop limit */ +} + +void +ip6_clearpktopts(pktopt, needfree, optname) + struct ip6_pktopts *pktopt; + int needfree, optname; +{ + if (pktopt == NULL) + return; + + if (optname == -1) { + if (needfree && pktopt->ip6po_pktinfo) + free(pktopt->ip6po_pktinfo, M_IP6OPT); + pktopt->ip6po_pktinfo = NULL; + } + if (optname == -1) + pktopt->ip6po_hlim = -1; + if (optname == -1) { + if (needfree && pktopt->ip6po_nexthop) + free(pktopt->ip6po_nexthop, M_IP6OPT); + pktopt->ip6po_nexthop = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_hbh) + free(pktopt->ip6po_hbh, M_IP6OPT); + pktopt->ip6po_hbh = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_dest1) + free(pktopt->ip6po_dest1, M_IP6OPT); + pktopt->ip6po_dest1 = NULL; + } + if (optname == -1) { + if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) + free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); + pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; + if (pktopt->ip6po_route.ro_rt) { + RTFREE(pktopt->ip6po_route.ro_rt); + pktopt->ip6po_route.ro_rt = NULL; + } + } + if (optname == -1) { + if (needfree && pktopt->ip6po_dest2) + free(pktopt->ip6po_dest2, M_IP6OPT); + pktopt->ip6po_dest2 = NULL; + } +} + +#define PKTOPT_EXTHDRCPY(type) \ +do {\ + if (src->type) {\ + int hlen =\ + (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ + dst->type = malloc(hlen, M_IP6OPT, canwait);\ + if (dst->type == NULL && canwait == M_NOWAIT)\ + goto bad;\ + bcopy(src->type, dst->type, hlen);\ + }\ +} while (0) + +struct ip6_pktopts * +ip6_copypktopts(src, canwait) + struct ip6_pktopts *src; + int canwait; +{ + struct ip6_pktopts *dst; + + if (src == NULL) { + printf("ip6_clearpktopts: invalid argument\n"); + return(NULL); + } + + dst = malloc(sizeof(*dst), M_IP6OPT, canwait); + if (dst == NULL && canwait == M_NOWAIT) + goto bad; + bzero(dst, sizeof(*dst)); + + dst->ip6po_hlim = src->ip6po_hlim; + if (src->ip6po_pktinfo) { + dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), + M_IP6OPT, canwait); + if (dst->ip6po_pktinfo == NULL && canwait == M_NOWAIT) + goto bad; + *dst->ip6po_pktinfo = *src->ip6po_pktinfo; + } + if (src->ip6po_nexthop) { + dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len, + M_IP6OPT, canwait); + if (dst->ip6po_nexthop == NULL && canwait == M_NOWAIT) + goto bad; + bcopy(src->ip6po_nexthop, dst->ip6po_nexthop, + src->ip6po_nexthop->sa_len); + } + PKTOPT_EXTHDRCPY(ip6po_hbh); + PKTOPT_EXTHDRCPY(ip6po_dest1); + PKTOPT_EXTHDRCPY(ip6po_dest2); + PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */ + return(dst); + + bad: + printf("ip6_copypktopts: copy failed"); + if (dst->ip6po_pktinfo) free(dst->ip6po_pktinfo, M_IP6OPT); + if (dst->ip6po_nexthop) free(dst->ip6po_nexthop, M_IP6OPT); + if (dst->ip6po_hbh) free(dst->ip6po_hbh, M_IP6OPT); + if (dst->ip6po_dest1) free(dst->ip6po_dest1, M_IP6OPT); + if (dst->ip6po_dest2) free(dst->ip6po_dest2, M_IP6OPT); + if (dst->ip6po_rthdr) free(dst->ip6po_rthdr, M_IP6OPT); + return(NULL); +} +#undef PKTOPT_EXTHDRCPY + +void +ip6_freepcbopts(pktopt) + struct ip6_pktopts *pktopt; +{ + if (pktopt == NULL) + return; + + ip6_clearpktopts(pktopt, 1, -1); + + free(pktopt, M_IP6OPT); +} + +/* * Set the IP6 multicast options in response to user setsockopt(). */ static int @@ -1670,7 +1921,7 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - ifindex = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex)); if (ifindex < 0 || if_index < ifindex) { error = ENXIO; /* XXX EINVAL? */ break; @@ -1693,7 +1944,7 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - optval = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &optval, sizeof(optval)); if (optval < -1 || optval >= 256) error = EINVAL; else if (optval == -1) @@ -1708,8 +1959,12 @@ ip6_setmoptions(optname, im6op, m) * Set the loopback flag for outgoing multicast packets. * Must be zero or one. */ - if (m == NULL || m->m_len != sizeof(u_int) || - (loop = *(mtod(m, u_int *))) > 1) { + if (m == NULL || m->m_len != sizeof(u_int)) { + error = EINVAL; + break; + } + bcopy(mtod(m, u_int *), &loop, sizeof(loop)); + if (loop > 1) { error = EINVAL; break; } @@ -1915,8 +2170,8 @@ ip6_setmoptions(optname, im6op, m) static int ip6_getmoptions(optname, im6o, mp) int optname; - register struct ip6_moptions *im6o; - register struct mbuf **mp; + struct ip6_moptions *im6o; + struct mbuf **mp; { u_int *hlim, *loop, *ifindex; @@ -1961,7 +2216,7 @@ ip6_getmoptions(optname, im6o, mp) */ void ip6_freemoptions(im6o) - register struct ip6_moptions *im6o; + struct ip6_moptions *im6o; { struct in6_multi_mship *imm; @@ -1981,18 +2236,17 @@ ip6_freemoptions(im6o) * Set IPv6 outgoing packet options based on advanced API. */ int -ip6_setpktoptions(control, opt, priv) +ip6_setpktoptions(control, opt, priv, needcopy) struct mbuf *control; struct ip6_pktopts *opt; - int priv; + int priv, needcopy; { - register struct cmsghdr *cm = 0; + struct cmsghdr *cm = 0; if (control == 0 || opt == 0) return(EINVAL); - bzero(opt, sizeof(*opt)); - opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ + init_ip6pktopts(opt); /* * XXX: Currently, we assume all the optional information is stored @@ -2001,21 +2255,31 @@ 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)) { + for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), + control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { cm = mtod(control, struct cmsghdr *); if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) return(EINVAL); if (cm->cmsg_level != IPPROTO_IPV6) continue; - switch(cm->cmsg_type) { + /* + * XXX should check if RFC2292 API is mixed with 2292bis API + */ + switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) return(EINVAL); - opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); + if (needcopy) { + /* XXX: Is it really WAITOK? */ + opt->ip6po_pktinfo = + malloc(sizeof(struct in6_pktinfo), + M_IP6OPT, M_WAITOK); + bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo, + sizeof(struct in6_pktinfo)); + } else + opt->ip6po_pktinfo = + (struct in6_pktinfo *)CMSG_DATA(cm); if (opt->ip6po_pktinfo->ipi6_ifindex && IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr)) opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] = @@ -2026,8 +2290,13 @@ ip6_setpktoptions(control, opt, priv) return(ENXIO); } + /* + * Check if the requested source address is indeed a + * unicast address assigned to the node, and can be + * used as the packet's source address. + */ if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { - struct ifaddr *ia; + struct in6_ifaddr *ia6; struct sockaddr_in6 sin6; bzero(&sin6, sizeof(sin6)); @@ -2035,19 +2304,10 @@ ip6_setpktoptions(control, opt, priv) sin6.sin6_family = AF_INET6; sin6.sin6_addr = opt->ip6po_pktinfo->ipi6_addr; - ia = ifa_ifwithaddr(sin6tosa(&sin6)); - if (ia == NULL || - (opt->ip6po_pktinfo->ipi6_ifindex && - (ia->ifa_ifp->if_index != - opt->ip6po_pktinfo->ipi6_ifindex))) { - return(EADDRNOTAVAIL); - } - /* - * Check if the requested source address is - * indeed a unicast address assigned to the - * node. - */ - if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) + ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6)); + if (ia6 == NULL || + (ia6->ia6_flags & (IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY)) != 0) return(EADDRNOTAVAIL); } break; @@ -2064,66 +2324,120 @@ ip6_setpktoptions(control, opt, priv) case IPV6_NEXTHOP: if (!priv) return(EPERM); + if (cm->cmsg_len < sizeof(u_char) || + /* check if cmsg_len is large enough for sa_len */ cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) return(EINVAL); - opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); - + if (needcopy) { + opt->ip6po_nexthop = + malloc(*CMSG_DATA(cm), + M_IP6OPT, M_WAITOK); + bcopy(CMSG_DATA(cm), + opt->ip6po_nexthop, + *CMSG_DATA(cm)); + } else + opt->ip6po_nexthop = + (struct sockaddr *)CMSG_DATA(cm); break; case IPV6_HOPOPTS: + { + struct ip6_hbh *hbh; + int hbhlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) return(EINVAL); - opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3)) + hbh = (struct ip6_hbh *)CMSG_DATA(cm); + hbhlen = (hbh->ip6h_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(hbhlen)) return(EINVAL); + + if (needcopy) { + opt->ip6po_hbh = + malloc(hbhlen, M_IP6OPT, M_WAITOK); + bcopy(hbh, opt->ip6po_hbh, hbhlen); + } else + opt->ip6po_hbh = hbh; break; + } case IPV6_DSTOPTS: + { + struct ip6_dest *dest, **newdest; + int destlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) return(EINVAL); + dest = (struct ip6_dest *)CMSG_DATA(cm); + destlen = (dest->ip6d_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(destlen)) + return(EINVAL); - /* - * If there is no routing header yet, the destination - * options header should be put on the 1st part. - * Otherwise, the header should be on the 2nd part. - * (See RFC 2460, section 4.1) + /* + * The old advacned API is ambiguous on this + * point. Our approach is to determine the + * position based according to the existence + * of a routing header. Note, however, that + * this depends on the order of the extension + * headers in the ancillary data; the 1st part + * of the destination options header must + * appear before the routing header in the + * ancillary data, too. + * RFC2292bis solved the ambiguity by + * introducing separate cmsg types. */ - if (opt->ip6po_rthdr == NULL) { - opt->ip6po_dest1 = - (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1) - << 3)) - return(EINVAL); - } else { - opt->ip6po_dest2 = - (struct ip6_dest *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) - << 3)) - return(EINVAL); - } + if (opt->ip6po_rthdr == NULL) + newdest = &opt->ip6po_dest1; + else + newdest = &opt->ip6po_dest2; + + if (needcopy) { + *newdest = malloc(destlen, M_IP6OPT, M_WAITOK); + bcopy(dest, *newdest, destlen); + } else + *newdest = dest; + break; + } case IPV6_RTHDR: + { + struct ip6_rthdr *rth; + int rthlen; + if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) return(EINVAL); - opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm); - if (cm->cmsg_len != - CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3)) + rth = (struct ip6_rthdr *)CMSG_DATA(cm); + rthlen = (rth->ip6r_len + 1) << 3; + if (cm->cmsg_len != CMSG_LEN(rthlen)) return(EINVAL); - switch(opt->ip6po_rthdr->ip6r_type) { + + switch (rth->ip6r_type) { case IPV6_RTHDR_TYPE_0: - if (opt->ip6po_rthdr->ip6r_segleft == 0) + /* must contain one addr */ + if (rth->ip6r_len == 0) + return(EINVAL); + /* length must be even */ + if (rth->ip6r_len % 2) + return(EINVAL); + if (rth->ip6r_len / 2 != rth->ip6r_segleft) return(EINVAL); break; default: - return(EINVAL); + return(EINVAL); /* not supported */ } + + if (needcopy) { + opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, + M_WAITOK); + bcopy(rth, opt->ip6po_rthdr, rthlen); + } else + opt->ip6po_rthdr = rth; + break; + } default: return(ENOPROTOOPT); @@ -2142,8 +2456,8 @@ ip6_setpktoptions(control, opt, priv) void ip6_mloopback(ifp, m, dst) struct ifnet *ifp; - register struct mbuf *m; - register struct sockaddr_in6 *dst; + struct mbuf *m; + struct sockaddr_in6 *dst; { struct mbuf *copym; struct ip6_hdr *ip6; @@ -2171,18 +2485,15 @@ ip6_mloopback(ifp, m, dst) } #endif -#ifndef FAKE_LOOPBACK_IF - if ((ifp->if_flags & IFF_LOOPBACK) == 0) -#else - if (1) + ip6 = mtod(copym, struct ip6_hdr *); +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); #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); } @@ -2236,10 +2547,11 @@ ip6_optlen(in6p) (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) len += elen(in6p->in6p_outputopts->ip6po_hbh); - len += elen(in6p->in6p_outputopts->ip6po_dest1); + if (in6p->in6p_outputopts->ip6po_rthdr) + /* dest1 is valid with rthdr only */ + len += elen(in6p->in6p_outputopts->ip6po_dest1); len += elen(in6p->in6p_outputopts->ip6po_rthdr); len += elen(in6p->in6p_outputopts->ip6po_dest2); return len; #undef elen } - diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 234b2e9b13d0..dea9c0705d94 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ +/* $KAME: ip6_var.h,v 1.62 2001/05/03 14:51:48 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -129,15 +129,29 @@ struct ip6po_rhinfo { struct ip6_pktopts { struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */ - int ip6po_hlim; /* Hoplimit for outgoing packets */ - struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */ - struct sockaddr *ip6po_nexthop; /* Next-hop address */ + int ip6po_hlim; /* Hoplimit for outgoing packets */ + + /* Outgoing IF/address information */ + struct in6_pktinfo *ip6po_pktinfo; + + struct sockaddr *ip6po_nexthop; /* Next-hop address */ + struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */ - struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */ - struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */ - struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */ + + /* Destination options header (before a routing header) */ + struct ip6_dest *ip6po_dest1; + + /* Routing header related info. */ + struct ip6po_rhinfo ip6po_rhinfo; + + /* Destination options header (after a routing header) */ + struct ip6_dest *ip6po_dest2; }; +/* + * Control options for incoming packets + */ + struct ip6stat { u_quad_t ip6s_total; /* total packets received */ u_quad_t ip6s_tooshort; /* packet too short */ @@ -200,6 +214,37 @@ struct ip6stat { }; #ifdef _KERNEL +/* + * IPv6 onion peeling state. + * it will be initialized when we come into ip6_input(). + * XXX do not make it a kitchen sink! + */ +struct ip6aux { + u_int32_t ip6a_flags; +#define IP6A_SWAP 0x01 /* swapped home/care-of on packet */ +#define IP6A_HASEEN 0x02 /* HA was present */ +#define IP6A_BRUID 0x04 /* BR Unique Identifier was present */ +#define IP6A_RTALERTSEEN 0x08 /* rtalert present */ + + /* ip6.ip6_src */ + struct in6_addr ip6a_careof; /* care-of address of the peer */ + struct in6_addr ip6a_home; /* home address of the peer */ + u_int16_t ip6a_bruid; /* BR unique identifier */ + + /* ip6.ip6_dst */ + struct in6_ifaddr *ip6a_dstia6; /* my ifaddr that matches ip6_dst */ + + /* rtalert */ + u_int16_t ip6a_rtalert; /* rtalert option value */ + + /* + * decapsulation history will be here. + * with IPsec it may not be accurate. + */ +}; +#endif + +#ifdef _KERNEL /* flags passed to ip6_output as last parameter */ #define IPV6_DADOUTPUT 0x01 /* DAD */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ @@ -215,7 +260,8 @@ extern int ip6_gif_hlim; /* Hop limit for gif encap packet */ extern int ip6_use_deprecated; /* allow deprecated addr as source */ extern int ip6_rr_prune; /* router renumbering prefix * walk list every 5 sec. */ -extern int ip6_mapped_addr_on; +#define ip6_mapped_addr_on (!ip6_v6only) +extern int ip6_v6only; extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ @@ -231,6 +277,14 @@ extern int ip6_dad_count; /* DupAddrDetectionTransmits */ extern u_int32_t ip6_flow_seq; extern int ip6_auto_flowlabel; +extern int ip6_auto_linklocal; + +extern int ip6_anonportmin; /* minimum ephemeral port */ +extern int ip6_anonportmax; /* maximum ephemeral port */ +extern int ip6_lowportmin; /* minimum reserved port */ +extern int ip6_lowportmax; /* maximum reserved port */ + +extern int ip6_use_tempaddr; /* whether to use temporary addresses. */ extern struct pr_usrreqs rip6_usrreqs; struct sockopt; @@ -239,29 +293,41 @@ struct inpcb; int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt)); +struct in6_ifaddr; void ip6_init __P((void)); void ip6intr __P((void)); void ip6_input __P((struct mbuf *)); +struct in6_ifaddr *ip6_getdstifaddr __P((struct mbuf *)); +void ip6_freepcbopts __P((struct ip6_pktopts *)); 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 *)); + +struct mbuf *ip6_addaux __P((struct mbuf *)); +struct mbuf *ip6_findaux __P((struct mbuf *)); +void ip6_delaux __P((struct mbuf *)); + 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 *)); void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *, - struct mbuf *)); + struct mbuf *)); +void ip6_notify_pmtu __P((struct inpcb *, struct sockaddr_in6 *, + u_int32_t *)); int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ip6_forward __P((struct mbuf *, int)); void ip6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *)); int ip6_output __P((struct mbuf *, struct ip6_pktopts *, - struct route_in6 *, int, + struct route_in6 *, + int, 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 init_ip6pktopts __P((struct ip6_pktopts *)); +int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int, 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 *)); diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h index bd897931dcd3..c2f38fc57637 100644 --- a/sys/netinet6/ip6protosw.h +++ b/sys/netinet6/ip6protosw.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ip6protosw.h,v 1.11 2000/10/03 09:59:35 jinmei Exp $ */ +/* $KAME: ip6protosw.h,v 1.22 2001/02/08 18:02:08 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -87,16 +87,38 @@ struct socket; struct domain; struct proc; struct ip6_hdr; +struct icmp6_hdr; +struct in6_addr; struct pr_usrreqs; /* * argument type for the last arg of pr_ctlinput(). * should be consulted only with AF_INET6 family. + * + * IPv6 ICMP IPv6 [exthdrs] finalhdr paylaod + * ^ ^ ^ ^ + * | | ip6c_ip6 ip6c_off + * | ip6c_icmp6 + * ip6c_m + * + * ip6c_finaldst usually points to ip6c_ip6->ip6_dst. if the original + * (internal) packet carries a routing header, it may point the final + * dstination address in the routing header. + * + * ip6c_src: ip6c_ip6->ip6_src + scope info + flowlabel in ip6c_ip6 + * (beware of flowlabel, if you try to compare it against others) + * ip6c_dst: ip6c_finaldst + scope info */ struct ip6ctlparam { struct mbuf *ip6c_m; /* start of mbuf chain */ + struct icmp6_hdr *ip6c_icmp6; /* icmp6 header of target packet */ struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ int ip6c_off; /* offset of the target proto header */ + struct sockaddr_in6 *ip6c_src; /* srcaddr w/ additional info */ + struct sockaddr_in6 *ip6c_dst; /* (final) dstaddr w/ additional info */ + struct in6_addr *ip6c_finaldst; /* final destination address */ + void *ip6c_cmdarg; /* control command dependent data */ + u_int8_t ip6c_nxt; /* final next header field */ }; struct ip6protosw { diff --git a/sys/netinet6/ipcomp.h b/sys/netinet6/ipcomp.h index dea5ea8e7097..08d62da2b7b4 100644 --- a/sys/netinet6/ipcomp.h +++ b/sys/netinet6/ipcomp.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ +/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -37,6 +37,10 @@ #ifndef _NETINET6_IPCOMP_H_ #define _NETINET6_IPCOMP_H_ +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif + struct ipcomp { u_int8_t comp_nxt; /* Next Header */ u_int8_t comp_flags; /* reserved, must be zero */ @@ -59,7 +63,7 @@ struct ipcomp_algorithm { }; struct ipsecrequest; -extern struct ipcomp_algorithm ipcomp_algorithms[]; +extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int)); extern void ipcomp4_input __P((struct mbuf *, ...)); extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *)); #endif /*KERNEL*/ diff --git a/sys/netinet6/ipcomp6.h b/sys/netinet6/ipcomp6.h index 553c8df7c4f4..4a1046af9074 100644 --- a/sys/netinet6/ipcomp6.h +++ b/sys/netinet6/ipcomp6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ +/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. diff --git a/sys/netinet6/ipcomp_core.c b/sys/netinet6/ipcomp_core.c index 1eee2538b968..ec031f68e721 100644 --- a/sys/netinet6/ipcomp_core.c +++ b/sys/netinet6/ipcomp_core.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_core.c,v 1.12 2000/05/05 11:01:01 sumikawa Exp $ */ +/* $KAME: ipcomp_core.c,v 1.24 2000/10/23 04:24:22 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -85,13 +85,20 @@ 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 }, +static const struct ipcomp_algorithm ipcomp_algorithms[] = { { deflate_compress, deflate_decompress, 90 }, - { NULL, NULL, 90 }, }; +const struct ipcomp_algorithm * +ipcomp_algorithm_lookup(idx) + int idx; +{ + + if (idx == SADB_X_CALG_DEFLATE) + return &ipcomp_algorithms[0]; + return NULL; +} + static void * deflate_alloc(aux, items, siz) void *aux; @@ -99,7 +106,7 @@ deflate_alloc(aux, items, siz) u_int siz; { void *ptr; - MALLOC(ptr, void *, items * siz, M_TEMP, M_NOWAIT); + ptr = malloc(items * siz, M_TEMP, M_NOWAIT); return ptr; } @@ -108,7 +115,7 @@ deflate_free(aux, ptr) void *aux; void *ptr; { - FREE(ptr, M_TEMP); + free(ptr, M_TEMP); } static int @@ -120,12 +127,47 @@ deflate_common(m, md, lenp, mode) { struct mbuf *mprev; struct mbuf *p; - struct mbuf *n, *n0 = NULL, **np; + struct mbuf *n = NULL, *n0 = NULL, **np; z_stream zs; int error = 0; int zerror; size_t offset; - int firsttime, final, flush; + +#define MOREBLOCK() \ +do { \ + /* 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; \ + n = NULL; \ + } \ + \ + /* 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; \ +} while (0) for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; @@ -148,113 +190,107 @@ deflate_common(m, md, lenp, mode) 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); - } + while (p && p->m_len == 0) { + p = p->m_next; + } + /* input stream and output stream are available */ + while (p && zs.avail_in == 0) { /* 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; + while (p && p->m_len == 0) { + p = p->m_next; } } /* 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; - } + MOREBLOCK(); + } - /* 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); + zerror = mode ? inflate(&zs, Z_NO_FLUSH) + : deflate(&zs, Z_NO_FLUSH); + + if (zerror == Z_STREAM_END) + ; /*once more.*/ + else if (zerror == Z_OK) { + /* inflate: Z_OK can indicate the end of decode */ + if (mode && !p && zs.avail_out != 0) + goto terminate; + else + ; /*once more.*/ + } else { + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_NO_FLUSH): %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_NO_FLUSH): unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); } + mode ? inflateEnd(&zs) : deflateEnd(&zs); + error = EINVAL; + goto fail; + } + } - zs.next_out = mtod(n, u_int8_t *); - zs.avail_out = n->m_len; + if (zerror == Z_STREAM_END) + goto terminate; + + /* termination */ + while (1) { + /* get output buffer */ + if (zs.next_out == NULL || zs.avail_out == 0) { + MOREBLOCK(); } - 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) + zerror = mode ? inflate(&zs, Z_FINISH) + : deflate(&zs, Z_FINISH); + + if (zerror == Z_STREAM_END) break; + else if (zerror == Z_OK) + ; /*once more.*/ else { - ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", - mode ? "de" : "", mode ? "in" : "de", - zs.msg ? zs.msg : "unknown error")); + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_FINISH): %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflate(Z_FINISH): unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); + } + mode ? inflateEnd(&zs) : deflateEnd(&zs); error = EINVAL; goto fail; } } + +terminate: 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")); + if (zs.msg) { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflateEnd: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg)); + } else { + ipseclog((LOG_ERR, "ipcomp_%scompress: " + "%sflateEnd: unknown error (%d)\n", + mode ? "de" : "", mode ? "in" : "de", + zerror)); + } error = EINVAL; goto fail; } @@ -264,6 +300,7 @@ deflate_common(m, md, lenp, mode) offset = zs.total_out; *np = n; np = &n->m_next; + n = NULL; } /* switch the mbuf to the new one */ @@ -276,9 +313,12 @@ deflate_common(m, md, lenp, mode) fail: if (m) m_freem(m); + if (n) + m_freem(n); if (n0) m_freem(n0); return error; +#undef MOREBLOCK } static int diff --git a/sys/netinet6/ipcomp_input.c b/sys/netinet6/ipcomp_input.c index da0184c82c7a..0a6e2f5d57f1 100644 --- a/sys/netinet6/ipcomp_input.c +++ b/sys/netinet6/ipcomp_input.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_input.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ipcomp_input.c,v 1.25 2001/03/01 09:12:09 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -95,9 +95,10 @@ ipcomp4_input(m, va_alist) va_dcl #endif { + struct mbuf *md; struct ip *ip; struct ipcomp *ipcomp; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ u_int16_t nxt; size_t hlen; @@ -112,42 +113,23 @@ ipcomp4_input(m, va_alist) proto = va_arg(ap, int); va_end(ap); - if (off + sizeof(struct ipcomp) > MHLEN) { - /*XXX the restriction should be relaxed*/ + if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) { ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " - "(header too long)\n")); + "(packet too short)\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; - } + md = m_pulldown(m, off, sizeof(*ipcomp), NULL); + if (!m) { + m = NULL; /*already freed*/ + ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " + "(pulldown failure)\n")); + ipsecstat.in_inval++; + goto fail; + } + ipcomp = mtod(md, struct ipcomp *); 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; @@ -167,10 +149,7 @@ ipcomp4_input(m, va_alist) /* other parameters to look at? */ } } - if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) - algo = &ipcomp_algorithms[cpi]; - else - algo = NULL; + algo = ipcomp_algorithm_lookup(cpi); if (!algo) { ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n", cpi)); @@ -180,7 +159,8 @@ ipcomp4_input(m, va_alist) /* chop ipcomp header */ ipcomp = NULL; - m->m_len -= sizeof(struct ipcomp); + md->m_data += sizeof(struct ipcomp); + md->m_len -= sizeof(struct ipcomp); m->m_pkthdr.len -= sizeof(struct ipcomp); #ifdef IPLEN_FLIPPED ip->ip_len -= sizeof(struct ipcomp); @@ -237,13 +217,22 @@ ipcomp4_input(m, va_alist) if (sav) { key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { + ipsecstat.in_nomem++; + goto fail; + } key_freesav(sav); sav = NULL; } - if (nxt != IPPROTO_DONE) + if (nxt != IPPROTO_DONE) { + if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto fail; + } (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); - else + } else m_freem(m); m = NULL; @@ -268,66 +257,30 @@ ipcomp6_input(mp, offp, proto) struct mbuf *m, *md; int off; struct ip6_hdr *ip6; - struct mbuf *ipcompm; struct ipcomp *ipcomp; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ u_int16_t nxt; int error; size_t newlen; struct secasvar *sav = NULL; + char *prvnxtp; 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++; + md = m_pulldown(m, off, sizeof(*ipcomp), NULL); + if (!m) { + m = NULL; /*already freed*/ + ipseclog((LOG_DEBUG, "IPv6 IPComp input: assumption failed " + "(pulldown failure)\n")); + ipsec6stat.in_inval++; goto fail; } + ipcomp = mtod(md, struct ipcomp *); 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) { @@ -340,10 +293,7 @@ ipcomp6_input(mp, offp, proto) /* other parameters to look at? */ } } - if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) - algo = &ipcomp_algorithms[cpi]; - else - algo = NULL; + algo = ipcomp_algorithm_lookup(cpi); if (!algo) { ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; " "dropping the packet for simplicity\n", cpi)); @@ -351,7 +301,13 @@ ipcomp6_input(mp, offp, proto) goto fail; } - newlen = m->m_pkthdr.len - off - sizeof(struct ipcomp); + /* chop ipcomp header */ + ipcomp = NULL; + md->m_data += sizeof(struct ipcomp); + md->m_len -= sizeof(struct ipcomp); + m->m_pkthdr.len -= sizeof(struct ipcomp); + + newlen = m->m_pkthdr.len - off; error = (*algo->decompress)(m, md, &newlen); if (error != 0) { if (error == EINVAL) @@ -362,7 +318,7 @@ ipcomp6_input(mp, offp, proto) goto fail; } ipsec6stat.in_comphist[cpi]++; - m->m_pkthdr.len = off + sizeof(struct ipcomp) + newlen; + m->m_pkthdr.len = off + newlen; /* * returning decompressed packet onto icmp is meaningless. @@ -370,25 +326,21 @@ ipcomp6_input(mp, offp, proto) */ m->m_flags |= M_DECRYPTED; - { - char *prvnxtp; - - /* chop IPComp header */ + /* update next header field */ 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)); - } + /* + * no need to adjust payload length, as all the IPv6 protocols + * look at m->m_pkthdr.len + */ if (sav) { key_sa_recordxfer(sav, m); + if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) { + ipsec6stat.in_nomem++; + goto fail; + } key_freesav(sav); sav = NULL; } diff --git a/sys/netinet6/ipcomp_output.c b/sys/netinet6/ipcomp_output.c index 265700ee68af..b82840a640a5 100644 --- a/sys/netinet6/ipcomp_output.c +++ b/sys/netinet6/ipcomp_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipcomp_output.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ +/* $KAME: ipcomp_output.c,v 1.23 2001/01/23 08:59:37 itojun Exp $ */ /* * Copyright (C) 1999 WIDE Project. @@ -87,7 +87,7 @@ static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *, /* * 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 failure, free the given mbuf and return non-zero. * * on invocation: * m nexthdrp md @@ -112,25 +112,29 @@ ipcomp_output(m, nexthdrp, md, isr, af) { struct mbuf *n; struct mbuf *md0; + struct mbuf *mcopy; struct mbuf *mprev; struct ipcomp *ipcomp; struct secasvar *sav = isr->sav; - struct ipcomp_algorithm *algo; + const struct ipcomp_algorithm *algo; u_int16_t cpi; /* host order */ size_t plen0, plen; /*payload length to be compressed*/ size_t compoff; int afnumber; int error = 0; + struct ipsecstat *stat; switch (af) { #ifdef INET case AF_INET: afnumber = 4; + stat = &ipsecstat; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; + stat = &ipsec6stat; break; #endif default: @@ -139,9 +143,9 @@ ipcomp_output(m, nexthdrp, md, isr, af) } /* grab parameters */ - if ((ntohl(sav->spi) & ~0xffff) != 0 || sav->alg_enc >= IPCOMP_MAX - || ipcomp_algorithms[sav->alg_enc].compress == NULL) { - ipsecstat.out_inval++; + algo = ipcomp_algorithm_lookup(sav->alg_enc); + if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) { + stat->out_inval++; m_freem(m); return EINVAL; } @@ -149,7 +153,6 @@ ipcomp_output(m, nexthdrp, md, isr, af) cpi = sav->alg_enc; else cpi = ntohl(sav->spi) & 0xffff; - algo = &ipcomp_algorithms[sav->alg_enc]; /*XXX*/ /* compute original payload length */ plen = 0; @@ -161,11 +164,21 @@ ipcomp_output(m, nexthdrp, md, isr, af) return 0; /* - * keep the original data packet, so that we can backout - * our changes when compression is not necessary. + * retain the original packet for two purposes: + * (1) we need to backout our changes when compression is not necessary. + * (2) byte lifetime computation should use the original packet. + * see RFC2401 page 23. + * compromise two m_copym(). we will be going through every byte of + * the payload during compression process anyways. */ + mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT); + if (mcopy == NULL) { + error = ENOBUFS; + return 0; + } md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); if (md0 == NULL) { + m_freem(mcopy); error = ENOBUFS; return 0; } @@ -177,26 +190,17 @@ ipcomp_output(m, nexthdrp, md, isr, af) 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 - } + stat->out_inval++; m_freem(m); m_freem(md0); + m_freem(mcopy); return EINVAL; } mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); m_freem(md0); + m_freem(mcopy); error = ENOBUFS; goto fail; } @@ -207,33 +211,12 @@ ipcomp_output(m, nexthdrp, md, isr, af) 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 - } + m_freem(mcopy); + stat->out_inval++; 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 - } + stat->out_comphist[sav->alg_enc]++; md = mprev->m_next; /* @@ -242,13 +225,17 @@ ipcomp_output(m, nexthdrp, md, isr, af) */ if (plen0 < plen) { m_freem(md); + m_freem(mcopy); mprev->m_next = md0; return 0; } - /* no need to backout change beyond here */ + /* + * no need to backout change beyond here. + */ m_freem(md0); md0 = NULL; + m->m_pkthdr.len -= plen0; m->m_pkthdr.len += plen; @@ -341,47 +328,14 @@ ipcomp_output(m, nexthdrp, md, isr, af) 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 - } + stat->out_inval++; } -#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); + stat->out_success++; + + /* compute byte lifetime against original packet */ + key_sa_recordxfer(sav, mcopy); + m_freem(mcopy); + return 0; fail: diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c index b8a24471a94f..3cb835da4e83 100644 --- a/sys/netinet6/ipsec.c +++ b/sys/netinet6/ipsec.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.c,v 1.66 2000/06/15 04:08:54 itojun Exp $ */ +/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -67,9 +67,11 @@ #ifdef INET6 #include <netinet6/ip6_ecn.h> #endif +#include <netinet/tcp.h> +#include <netinet/udp.h> -#ifdef INET6 #include <netinet/ip6.h> +#ifdef INET6 #include <netinet6/ip6_var.h> #endif #include <netinet/in_pcb.h> @@ -97,25 +99,12 @@ #endif #include <netkey/key.h> #include <netkey/keydb.h> -#ifdef IPSEC_DEBUG #include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #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 @@ -132,11 +121,14 @@ int ip4_ah_trans_deflev = IPSEC_LEVEL_USE; 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) */ +int ip4_esp_randpad = -1; +#ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet_ipsec); #ifdef INET6 SYSCTL_DECL(_net_inet6_ipsec6); #endif +#endif /* net.inet.ipsec */ SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, @@ -161,6 +153,8 @@ 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, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD, + esp_randpad, CTLFLAG_RW, &ip4_esp_randpad, 0, ""); #ifdef INET6 struct ipsecstat ipsec6stat; @@ -170,6 +164,7 @@ int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; int ip6_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip6_def_policy; int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ +int ip6_esp_randpad = -1; /* net.inet6.ipsec6 */ SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, @@ -188,16 +183,22 @@ 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, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD, + esp_randpad, CTLFLAG_RW, &ip6_esp_randpad, 0, ""); #endif /* INET6 */ static int ipsec_setspidx_mbuf - __P((struct secpolicyindex *, u_int, u_int, struct mbuf *)); -static void ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); -static void ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); + __P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int)); +static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); +#ifdef INET6 +static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); +#endif +static int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); +static void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); +static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #ifdef INET6 -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 *)); +static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); +static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #endif static struct inpcbpolicy *ipsec_newpcbpolicy __P((void)); static void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); @@ -208,14 +209,21 @@ 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 *)); static size_t ipsec_hdrsiz __P((struct secpolicy *)); +#ifdef INET static struct mbuf *ipsec4_splithdr __P((struct mbuf *)); +#endif #ifdef INET6 static struct mbuf *ipsec6_splithdr __P((struct mbuf *)); #endif +#ifdef INET static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); +#endif #ifdef INET6 static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); #endif +static struct mbuf *ipsec_addaux __P((struct mbuf *)); +static struct mbuf *ipsec_findaux __P((struct mbuf *)); +static void ipsec_optaux __P((struct mbuf *, struct mbuf *)); /* * For OUTBOUND packet having a socket. Searching SPD for packet, @@ -247,19 +255,29 @@ ipsec4_getpolicybysock(m, dir, so, error) 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; + *error = ipsec4_setspidx_inpcb(m, sotoinpcb(so)); break; #ifdef INET6 case AF_INET6: /* set spidx in pcb */ - ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); - pcbsp = sotoin6pcb(so)->in6p_sp; + *error = ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); break; #endif default: panic("ipsec4_getpolicybysock: unsupported address family\n"); } + if (*error) + return NULL; + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: + pcbsp = sotoinpcb(so)->inp_sp; + break; +#ifdef INET6 + case AF_INET6: + pcbsp = sotoin6pcb(so)->in6p_sp; + break; +#endif + } /* sanity check */ if (pcbsp == NULL) @@ -404,7 +422,8 @@ ipsec4_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m, + (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; @@ -460,6 +479,11 @@ ipsec6_getpolicybysock(m, dir, so, error) if (m == NULL || so == NULL || error == NULL) panic("ipsec6_getpolicybysock: NULL pointer was passed.\n"); +#ifdef DIAGNOSTIC + if (so->so_proto->pr_domain->dom_family != AF_INET6) + panic("ipsec6_getpolicybysock: socket domain != inet6\n"); +#endif + /* set spidx in pcb */ ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); @@ -615,7 +639,8 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m, + (flag & IP_FORWARDING) ? 0 : 1); if (*error != 0) return NULL; @@ -656,302 +681,286 @@ ipsec6_getpolicybyaddr(m, dir, flag, error) * other: failure, and set errno. */ int -ipsec_setspidx_mbuf(spidx, dir, family, m) +ipsec_setspidx_mbuf(spidx, dir, family, m, needport) struct secpolicyindex *spidx; u_int dir, family; struct mbuf *m; + int needport; { + int error; /* sanity check */ if (spidx == NULL || m == NULL) panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n"); - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: begin\n"); kdebug_mbuf(m)); - - /* initialize */ bzero(spidx, sizeof(*spidx)); + error = ipsec_setspidx(m, spidx, needport); + if (error) + goto bad; spidx->dir = dir; - { - /* sanity check for packet length. */ - struct mbuf *n; - int tlen; - - tlen = 0; - for (n = m; n; n = n->m_next) - tlen += n->m_len; - if (m->m_pkthdr.len != tlen) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "total of m_len(%d) != pkthdr.len(%d), " - "ignored.\n", - tlen, m->m_pkthdr.len)); - goto bad; - } - } + return 0; - switch (family) { - case AF_INET: - { - struct ip *ip; - struct ip ipbuf; - struct sockaddr_in *sin; + bad: + /* XXX initialize */ + bzero(spidx, sizeof(*spidx)); + return EINVAL; +} - /* sanity check 1 for minimum ip header length */ - if (m->m_pkthdr.len < sizeof(struct ip)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "pkthdr.len(%d) < sizeof(struct ip), " - "ignored.\n", - m->m_pkthdr.len)); - goto bad; - } +static int +ipsec4_setspidx_inpcb(m, pcb) + struct mbuf *m; + struct inpcb *pcb; +{ + struct secpolicyindex *spidx; + int error; - /* - * get IPv4 header packet. usually the mbuf is contiguous - * and we need no copies. - */ - if (m->m_len >= sizeof(*ip)) - ip = mtod(m, struct ip *); - else { - m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); - ip = &ipbuf; - } + /* sanity check */ + if (pcb == NULL) + panic("ipsec4_setspidx_inpcb: no PCB found.\n"); + if (pcb->inp_sp == NULL) + panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); + if (pcb->inp_sp->sp_out == NULL || pcb->inp_sp->sp_in == NULL) + panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); - /* XXX some more checks on IPv4 header. */ + bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - 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; + spidx = &pcb->inp_sp->sp_in->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_INBOUND; - 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 = &pcb->inp_sp->sp_out->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->prefs = spidx->prefd = sizeof(struct in_addr) << 3; + return 0; - spidx->ul_proto = ip->ip_p; - break; - } +bad: + bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); + return error; +} #ifdef INET6 - case AF_INET6: - { - struct ip6_hdr *ip6; - struct ip6_hdr ip6buf; - struct sockaddr_in6 *sin6; +static int +ipsec6_setspidx_in6pcb(m, pcb) + struct mbuf *m; + struct in6pcb *pcb; +{ + struct secpolicyindex *spidx; + int error; - /* sanity check 1 for minimum ip header length */ - if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "pkthdr.len(%d) < sizeof(struct ip6_hdr), " - "ignored.\n", - m->m_pkthdr.len)); - goto bad; - } + /* sanity check */ + if (pcb == NULL) + panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); + if (pcb->in6p_sp == NULL) + panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); + if (pcb->in6p_sp->sp_out == NULL || pcb->in6p_sp->sp_in == NULL) + panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); - /* - * get IPv6 header packet. usually the mbuf is contiguous - * and we need no copies. - */ - if (m->m_len >= sizeof(*ip6)) - ip6 = mtod(m, struct ip6_hdr *); - else { - m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6 = &ip6buf; - } + bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); - /* some more checks on IPv4 header. */ - if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "wrong ip version on packet " - "(expected IPv6), ignored.\n")); - goto bad; - } + spidx = &pcb->in6p_sp->sp_in->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_INBOUND; - 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]); - } + spidx = &pcb->in6p_sp->sp_out->spidx; + error = ipsec_setspidx(m, spidx, 1); + if (error) + goto bad; + spidx->dir = IPSEC_DIR_OUTBOUND; - 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]); - } + return 0; - spidx->prefs = spidx->prefd = sizeof(struct in6_addr) << 3; +bad: + bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); + bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); + return error; +} +#endif - ipsec6_get_ulp(m, spidx); - break; - } -#endif /* INET6 */ - default: - panic("ipsec_secsecidx: no supported family passed.\n"); - } +/* + * configure security policy index (src/dst/proto/sport/dport) + * by looking at the content of mbuf. + * the caller is responsible for error recovery (like clearing up spidx). + */ +static int +ipsec_setspidx(m, spidx, needport) + struct mbuf *m; + struct secpolicyindex *spidx; + int needport; +{ + struct ip *ip = NULL; + struct ip ipbuf; + u_int v; + struct mbuf *n; + int len; + int error; - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: end\n"); - kdebug_secpolicyindex(spidx)); + if (m == NULL) + panic("ipsec_setspidx: m == 0 passed.\n"); - return 0; + /* + * validate m->m_pkthdr.len. we see incorrect length if we + * mistakenly call this function with inconsistent mbuf chain + * (like 4.4BSD tcp/udp processing). XXX should we panic here? + */ + len = 0; + for (n = m; n; n = n->m_next) + len += n->m_len; + if (m->m_pkthdr.len != len) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "total of m_len(%d) != pkthdr.len(%d), " + "ignored.\n", + len, m->m_pkthdr.len)); + return EINVAL; + } - bad: - /* XXX initialize */ - bzero(spidx, sizeof(*spidx)); - return EINVAL; -} + if (m->m_pkthdr.len < sizeof(struct ip)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "pkthdr.len(%d) < sizeof(struct ip), ignored.\n", + m->m_pkthdr.len)); + return EINVAL; + } + if (m->m_len >= sizeof(*ip)) + ip = mtod(m, struct ip *); + else { + m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); + ip = &ipbuf; + } +#ifdef _IP_VHL + v = _IP_VHL_V(ip->ip_vhl); +#else + v = ip->ip_v; +#endif + switch (v) { + case 4: + error = ipsec4_setspidx_ipaddr(m, spidx); + if (error) + return error; + ipsec4_get_ulp(m, spidx, needport); + return 0; #ifdef INET6 -/* - * Get upper layer protocol number and port number if there. - * Assumed all extension headers are in single mbuf. - */ -#include <netinet/tcp.h> -#include <netinet/udp.h> + case 6: + if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "pkthdr.len(%d) < sizeof(struct ip6_hdr), " + "ignored.\n", m->m_pkthdr.len)); + return EINVAL; + } + error = ipsec6_setspidx_ipaddr(m, spidx); + if (error) + return error; + ipsec6_get_ulp(m, spidx, needport); + return 0; +#endif + default: + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec_setspidx: " + "unknown IP version %u, ignored.\n", v)); + return EINVAL; + } +} + static void -ipsec6_get_ulp(m, spidx) +ipsec4_get_ulp(m, spidx, needport) struct mbuf *m; struct secpolicyindex *spidx; + int needport; { - int off, nxt; + struct ip ip; + struct ip6_ext ip6e; + u_int8_t nxt; + int off; + struct tcphdr th; + struct udphdr uh; /* sanity check */ if (m == NULL) - panic("ipsec6_get_ulp: NULL pointer was passed.\n"); - - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); + panic("ipsec4_get_ulp: NULL pointer was passed.\n"); + if (m->m_pkthdr.len < sizeof(ip)) + panic("ipsec4_get_ulp: too short\n"); /* 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; + ((struct sockaddr_in *)&spidx->src)->sin_port = IPSEC_PORT_ANY; + ((struct sockaddr_in *)&spidx->dst)->sin_port = IPSEC_PORT_ANY; - nxt = -1; - off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); - if (off < 0 || m->m_pkthdr.len < off) + m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); + /* ip_input() flips it into host endian XXX need more checking */ + if (ip.ip_off & (IP_MF | IP_OFFMASK)) return; - switch (nxt) { - case IPPROTO_TCP: - spidx->ul_proto = nxt; - if (off + sizeof(struct tcphdr) <= m->m_pkthdr.len) { - struct tcphdr th; + nxt = ip.ip_p; +#ifdef _IP_VHL + off = _IP_VHL_HL(ip->ip_vhl) << 2; +#else + off = ip.ip_hl << 2; +#endif + while (off < m->m_pkthdr.len) { + switch (nxt) { + case IPPROTO_TCP: + spidx->ul_proto = nxt; + if (!needport) + return; + if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) + return; m_copydata(m, off, sizeof(th), (caddr_t)&th); - ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + ((struct sockaddr_in *)&spidx->src)->sin_port = th.th_sport; - ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + ((struct sockaddr_in *)&spidx->dst)->sin_port = th.th_dport; - } - break; - case IPPROTO_UDP: - spidx->ul_proto = nxt; - if (off + sizeof(struct udphdr) <= m->m_pkthdr.len) { - struct udphdr uh; + return; + case IPPROTO_UDP: + spidx->ul_proto = nxt; + if (!needport) + return; + if (off + sizeof(struct udphdr) > m->m_pkthdr.len) + return; m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + ((struct sockaddr_in *)&spidx->src)->sin_port = uh.uh_sport; - ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + ((struct sockaddr_in *)&spidx->dst)->sin_port = uh.uh_dport; + return; + case IPPROTO_AH: + if (m->m_pkthdr.len > off + sizeof(ip6e)) + return; + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + off += (ip6e.ip6e_len + 2) << 2; + nxt = ip6e.ip6e_nxt; + break; + case IPPROTO_ICMP: + default: + /* XXX intermediate headers??? */ + spidx->ul_proto = nxt; + return; } - break; - case IPPROTO_ICMPV6: - spidx->ul_proto = nxt; - break; - default: - break; } } -#endif - -static void -ipsec4_setspidx_inpcb(m, pcb) - struct mbuf *m; - struct inpcb *pcb; -{ - struct secpolicyindex *spidx; - struct sockaddr_in *sin1, *sin2; - - /* sanity check */ - if (pcb == NULL) - panic("ipsec4_setspidx_inpcb: no PCB found.\n"); - if (pcb->inp_sp == NULL) - panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); - if (pcb->inp_sp->sp_out ==NULL || pcb->inp_sp->sp_in == NULL) - panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); - - bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); - - spidx = &pcb->inp_sp->sp_in->spidx; - spidx->dir = IPSEC_DIR_INBOUND; - 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; - 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; - 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; - sin1->sin_port = pcb->inp_lport; - sin2->sin_port = pcb->inp_fport; - ipsec4_setspidx_ipaddr(m, spidx); - - return; -} -static void +/* assumes that m is sane */ +static int ipsec4_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; { struct ip *ip = NULL; struct ip ipbuf; - - /* sanity check 1 for minimum ip header length */ - if (m == NULL) - panic("ipsec4_setspidx_ipaddr: m == 0 passed.\n"); - - if (m->m_pkthdr.len < sizeof(struct ip)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec4_setspidx_ipaddr: " - "pkthdr.len(%d) < sizeof(struct ip), " - "ignored.\n", - m->m_pkthdr.len)); - return; - } + struct sockaddr_in *sin; if (m->m_len >= sizeof(*ip)) ip = mtod(m, struct ip *); @@ -960,64 +969,81 @@ ipsec4_setspidx_ipaddr(m, spidx) ip = &ipbuf; } - 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)); + sin = (struct sockaddr_in *)&spidx->src; + bzero(sin, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_src, &sin->sin_addr, sizeof(ip->ip_src)); + spidx->prefs = sizeof(struct in_addr) << 3; - return; + sin = (struct sockaddr_in *)&spidx->dst; + bzero(sin, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)); + spidx->prefd = sizeof(struct in_addr) << 3; + return 0; } #ifdef INET6 static void -ipsec6_setspidx_in6pcb(m, pcb) +ipsec6_get_ulp(m, spidx, needport) struct mbuf *m; - struct in6pcb *pcb; -{ struct secpolicyindex *spidx; - struct sockaddr_in6 *sin1, *sin2; + int needport; +{ + int off, nxt; + struct tcphdr th; + struct udphdr uh; /* sanity check */ - if (pcb == NULL) - panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); - if (pcb->in6p_sp == NULL) - panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); - if (pcb->in6p_sp->sp_out ==NULL || pcb->in6p_sp->sp_in == NULL) - panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); + if (m == NULL) + panic("ipsec6_get_ulp: NULL pointer was passed.\n"); - bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); - bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); + KEYDEBUG(KEYDEBUG_IPSEC_DUMP, + printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); - spidx = &pcb->in6p_sp->sp_in->spidx; - spidx->dir = IPSEC_DIR_INBOUND; - 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; - sin1->sin6_port = pcb->in6p_fport; - sin2->sin6_port = pcb->in6p_lport; - ipsec6_setspidx_ipaddr(m, spidx); + /* 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; - spidx = &pcb->in6p_sp->sp_out->spidx; - spidx->dir = IPSEC_DIR_OUTBOUND; - 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; - sin1->sin6_port = pcb->in6p_lport; - sin2->sin6_port = pcb->in6p_fport; - ipsec6_setspidx_ipaddr(m, spidx); + nxt = -1; + off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + if (off < 0 || m->m_pkthdr.len < off) + return; - return; + switch (nxt) { + case IPPROTO_TCP: + spidx->ul_proto = nxt; + if (!needport) + break; + if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) + break; + 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 (!needport) + break; + if (off + sizeof(struct udphdr) > m->m_pkthdr.len) + break; + 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; + break; + case IPPROTO_ICMPV6: + default: + /* XXX intermediate headers??? */ + spidx->ul_proto = nxt; + break; + } } -static void +/* assumes that m is sane */ +static int ipsec6_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; @@ -1026,19 +1052,6 @@ ipsec6_setspidx_ipaddr(m, spidx) struct ip6_hdr ip6buf; struct sockaddr_in6 *sin6; - /* sanity check 1 for minimum ip header length */ - if (m == NULL) - panic("ipsec6_setspidx_in6pcb: m == 0 passed.\n"); - - if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec6_setspidx_ipaddr: " - "pkthdr.len(%d) < sizeof(struct ip6_hdr), " - "ignored.\n", - m->m_pkthdr.len)); - return; - } - if (m->m_len >= sizeof(*ip6)) ip6 = mtod(m, struct ip6_hdr *); else { @@ -1046,29 +1059,29 @@ ipsec6_setspidx_ipaddr(m, spidx) ip6 = &ip6buf; } - if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { - KEYDEBUG(KEYDEBUG_IPSEC_DUMP, - printf("ipsec_setspidx_mbuf: " - "wrong ip version on packet " - "(expected IPv6), ignored.\n")); - return; - } - sin6 = (struct sockaddr_in6 *)&spidx->src; + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); 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]); } + spidx->prefs = sizeof(struct in6_addr) << 3; sin6 = (struct sockaddr_in6 *)&spidx->dst; + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); 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]); } + spidx->prefd = sizeof(struct in6_addr) << 3; - return; + return 0; } #endif @@ -1628,6 +1641,8 @@ ipsec_in_reject(sp, m) need_conf = 0; need_icv = 0; + /* XXX should compare policy against ipsec header history */ + for (isr = sp->req; isr != NULL; isr = isr->next) { /* get current level */ @@ -1653,7 +1668,8 @@ ipsec_in_reject(sp, m) case IPPROTO_IPCOMP: /* * we don't really care, as IPcomp document says that - * we shouldn't compress small packets + * we shouldn't compress small packets, IPComp policy + * should always be treated as being in "use" level. */ break; } @@ -1717,12 +1733,10 @@ ipsec4_in_reject(m, inp) { if (inp == NULL) return ipsec4_in_reject_so(m, NULL); - else { - if (inp->inp_socket) - return ipsec4_in_reject_so(m, inp->inp_socket); - else - panic("ipsec4_in_reject: invalid inpcb/socket"); - } + if (inp->inp_socket) + return ipsec4_in_reject_so(m, inp->inp_socket); + else + panic("ipsec4_in_reject: invalid inpcb/socket"); } #ifdef INET6 @@ -1771,12 +1785,10 @@ ipsec6_in_reject(m, in6p) { if (in6p == NULL) return ipsec6_in_reject_so(m, NULL); - else { - if (in6p->in6p_socket) - return ipsec6_in_reject_so(m, in6p->in6p_socket); - else - panic("ipsec6_in_reject: invalid in6p/socket"); - } + if (in6p->in6p_socket) + return ipsec6_in_reject_so(m, in6p->in6p_socket); + else + panic("ipsec6_in_reject: invalid in6p/socket"); } #endif @@ -2054,6 +2066,7 @@ ipsec4_encapsulate(m, sav) &ip->ip_src, sizeof(ip->ip_src)); bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); + ip->ip_ttl = IPDEFTTL; /* XXX Should ip_src be updated later ? */ @@ -2133,6 +2146,7 @@ ipsec6_encapsulate(m, sav) &ip6->ip6_src, sizeof(ip6->ip6_src)); bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); + ip6->ip6_hlim = IPV6_DEFHLIM; /* XXX Should ip6_src be updated later ? */ @@ -2345,11 +2359,11 @@ ipsec4_logpacketstr(ip, spi) snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); while (p && *p) p++; - snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d", + snprintf(p, sizeof(buf) - (p - buf), "src=%u.%u.%u.%u", s[0], s[1], s[2], s[3]); while (p && *p) p++; - snprintf(p, sizeof(buf) - (p - buf), " dst=%d.%d.%d.%d", + snprintf(p, sizeof(buf) - (p - buf), " dst=%u.%u.%u.%u", d[0], d[1], d[2], d[3]); while (p && *p) p++; @@ -2454,6 +2468,7 @@ ipsec_dumpmbuf(m) printf("---\n"); } +#ifdef INET /* * IPsec output logic for IPv4. */ @@ -2500,6 +2515,8 @@ ipsec4_output(state, sp, flags) /* make SA index for search proper SA */ ip = mtod(state->m, struct ip *); bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; sin = (struct sockaddr_in *)&saidx.src; if (sin->sin_len == 0) { sin->sin_len = sizeof(*sin); @@ -2595,7 +2612,7 @@ ipsec4_output(state, sp, flags) && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(state->ro->ro_rt); - bzero((caddr_t)state->ro, sizeof (*state->ro)); + state->ro->ro_rt = NULL; } if (state->ro->ro_rt == 0) { dst4->sin_family = AF_INET; @@ -2672,6 +2689,7 @@ bad: state->m = NULL; return error; } +#endif #ifdef INET6 /* @@ -2720,6 +2738,8 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) /* make SA index for search proper SA */ ip6 = mtod(state->m, struct ip6_hdr *); bcopy(&isr->saidx, &saidx, sizeof(saidx)); + saidx.mode = isr->saidx.mode; + saidx.reqid = isr->saidx.reqid; sin6 = (struct sockaddr_in6 *)&saidx.src; if (sin6->sin6_len == 0) { sin6->sin6_len = sizeof(*sin6); @@ -2757,6 +2777,18 @@ ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) */ ipsec6stat.out_nosa++; error = ENOENT; + + /* + * Notify the fact that the packet is discarded + * to ourselves. I believe this is better than + * just silently discarding. (jinmei@kame.net) + * XXX: should we restrict the error to TCP packets? + * XXX: should we directly notify sockets via + * pfctlinputs? + */ + icmp6_error(state->m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADMIN, 0); + state->m = NULL; /* icmp6_error freed the mbuf */ goto bad; } @@ -2951,7 +2983,7 @@ ipsec6_output_tunnel(state, sp, flags) && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { RTFREE(state->ro->ro_rt); - bzero((caddr_t)state->ro, sizeof (*state->ro)); + state->ro->ro_rt = NULL; } if (state->ro->ro_rt == 0) { bzero(dst6, sizeof(*dst6)); @@ -3030,6 +3062,7 @@ bad: } #endif /*INET6*/ +#ifdef INET /* * Chop IP header and option off from the payload. */ @@ -3071,6 +3104,7 @@ ipsec4_splithdr(m) } return m; } +#endif #ifdef INET6 static struct mbuf * @@ -3111,38 +3145,89 @@ ipsec6_splithdr(m) /* validate inbound IPsec tunnel packet. */ int -ipsec4_tunnel_validate(ip, nxt0, sav) - struct ip *ip; +ipsec4_tunnel_validate(m, off, nxt0, sav) + struct mbuf *m; /* no pullup permitted, m->m_len >= ip */ + int off; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in *sin; + struct sockaddr_in osrc, odst, isrc, idst; int hlen; + struct secpolicy *sp; + struct ip *oip; +#ifdef DIAGNOSTIC + if (m->m_len < sizeof(struct ip)) + panic("too short mbuf on ipsec4_tunnel_validate"); +#endif if (nxt != IPPROTO_IPV4) return 0; + if (m->m_pkthdr.len < off + sizeof(struct ip)) + return 0; + /* do not decapsulate if the SA is for transport mode only */ + if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT) + return 0; + + oip = mtod(m, struct ip *); #ifdef _IP_VHL - hlen = _IP_VHL_HL(ip->ip_vhl) << 2; + hlen = _IP_VHL_HL(oip->ip_vhl) << 2; #else - hlen = ip->ip_hl << 2; + hlen = oip->ip_hl << 2; #endif if (hlen != sizeof(struct ip)) return 0; - 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) - return 0; - break; -#ifdef INET6 - case AF_INET6: - /* should be supported, but at this moment we don't. */ - /*FALLTHROUGH*/ -#endif - default: + + /* AF_INET6 should be supported, but at this moment we don't. */ + sin = (struct sockaddr_in *)&sav->sah->saidx.dst; + if (sin->sin_family != AF_INET) return 0; - } + if (bcmp(&oip->ip_dst, &sin->sin_addr, sizeof(oip->ip_dst)) != 0) + return 0; + + /* XXX slow */ + bzero(&osrc, sizeof(osrc)); + bzero(&odst, sizeof(odst)); + bzero(&isrc, sizeof(isrc)); + bzero(&idst, sizeof(idst)); + osrc.sin_family = odst.sin_family = isrc.sin_family = idst.sin_family = + AF_INET; + osrc.sin_len = odst.sin_len = isrc.sin_len = idst.sin_len = + sizeof(struct sockaddr_in); + osrc.sin_addr = oip->ip_src; + odst.sin_addr = oip->ip_dst; + m_copydata(m, off + offsetof(struct ip, ip_src), sizeof(isrc.sin_addr), + (caddr_t)&isrc.sin_addr); + m_copydata(m, off + offsetof(struct ip, ip_dst), sizeof(idst.sin_addr), + (caddr_t)&idst.sin_addr); + + /* + * RFC2401 5.2.1 (b): (assume that we are using tunnel mode) + * - if the inner destination is multicast address, there can be + * multiple permissible inner source address. implementation + * may want to skip verification of inner source address against + * SPD selector. + * - if the inner protocol is ICMP, the packet may be an error report + * from routers on the other side of the VPN cloud (R in the + * following diagram). in this case, we cannot verify inner source + * address against SPD selector. + * me -- gw === gw -- R -- you + * + * we consider the first bullet to be users responsibility on SPD entry + * configuration (if you need to encrypt multicast traffic, set + * the source range of SPD selector to 0.0.0.0/0, or have explicit + * address ranges for possible senders). + * the second bullet is not taken care of (yet). + * + * therefore, we do not do anything special about inner source. + */ + + sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, + (struct sockaddr *)&isrc, (struct sockaddr *)&idst); + if (!sp) + return 0; + key_freesp(sp); return 1; } @@ -3150,28 +3235,64 @@ ipsec4_tunnel_validate(ip, nxt0, sav) #ifdef INET6 /* validate inbound IPsec tunnel packet. */ int -ipsec6_tunnel_validate(ip6, nxt0, sav) - struct ip6_hdr *ip6; +ipsec6_tunnel_validate(m, off, nxt0, sav) + struct mbuf *m; /* no pullup permitted, m->m_len >= ip */ + int off; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in6 *sin6; + struct sockaddr_in6 osrc, odst, isrc, idst; + struct secpolicy *sp; + struct ip6_hdr *oip6; +#ifdef DIAGNOSTIC + if (m->m_len < sizeof(struct ip6_hdr)) + panic("too short mbuf on ipsec6_tunnel_validate"); +#endif if (nxt != IPPROTO_IPV6) return 0; - 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)) - return 0; - break; - case AF_INET: - /* should be supported, but at this moment we don't. */ - /*FALLTHROUGH*/ - default: + if (m->m_pkthdr.len < off + sizeof(struct ip6_hdr)) + return 0; + /* do not decapsulate if the SA is for transport mode only */ + if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT) + return 0; + + oip6 = mtod(m, struct ip6_hdr *); + /* AF_INET should be supported, but at this moment we don't. */ + sin6 = (struct sockaddr_in6 *)&sav->sah->saidx.dst; + if (sin6->sin6_family != AF_INET6) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&oip6->ip6_dst, &sin6->sin6_addr)) return 0; - } + + /* XXX slow */ + bzero(&osrc, sizeof(osrc)); + bzero(&odst, sizeof(odst)); + bzero(&isrc, sizeof(isrc)); + bzero(&idst, sizeof(idst)); + osrc.sin6_family = odst.sin6_family = isrc.sin6_family = + idst.sin6_family = AF_INET6; + osrc.sin6_len = odst.sin6_len = isrc.sin6_len = idst.sin6_len = + sizeof(struct sockaddr_in6); + osrc.sin6_addr = oip6->ip6_src; + odst.sin6_addr = oip6->ip6_dst; + m_copydata(m, off + offsetof(struct ip6_hdr, ip6_src), + sizeof(isrc.sin6_addr), (caddr_t)&isrc.sin6_addr); + m_copydata(m, off + offsetof(struct ip6_hdr, ip6_dst), + sizeof(idst.sin6_addr), (caddr_t)&idst.sin6_addr); + + /* + * regarding to inner source address validation, see a long comment + * in ipsec4_tunnel_validate. + */ + + sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst, + (struct sockaddr *)&isrc, (struct sockaddr *)&idst); + if (!sp) + return 0; + key_freesp(sp); return 1; } @@ -3235,7 +3356,7 @@ ipsec_copypkt(m) */ remain = n->m_len; copied = 0; - while(1) { + while (1) { int len; struct mbuf *mn; @@ -3265,6 +3386,7 @@ ipsec_copypkt(m) MGETHDR(mn, M_DONTWAIT, MT_HEADER); if (mn == NULL) goto fail; + mn->m_pkthdr.rcvif = NULL; mm->m_next = mn; mm = mn; } @@ -3288,27 +3410,78 @@ ipsec_copypkt(m) return(NULL); } +static struct mbuf * +ipsec_addaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (!n) + n = m_aux_add(m, AF_INET, IPPROTO_ESP); + if (!n) + return n; /* ENOBUFS */ + n->m_len = sizeof(struct socket *); + bzero(mtod(n, void *), n->m_len); + return n; +} + +static struct mbuf * +ipsec_findaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); +#ifdef DIAGNOSTIC + if (n && n->m_len < sizeof(struct socket *)) + panic("invalid ipsec m_aux"); +#endif + return n; +} + void +ipsec_delaux(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (n) + m_aux_delete(m, n); +} + +/* if the aux buffer is unnecessary, nuke it. */ +static void +ipsec_optaux(m, n) + struct mbuf *m; + struct mbuf *n; +{ + + if (!n) + return; + if (n->m_len == sizeof(struct socket *) && !*mtod(n, struct socket **)) + ipsec_delaux(m); +} + +int 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); - } + /* if so == NULL, don't insist on getting the aux mbuf */ + if (so) { + n = ipsec_addaux(m); + if (!n) + return ENOBUFS; + } else + n = ipsec_findaux(m); + if (n && n->m_len >= sizeof(struct socket *)) + *mtod(n, struct socket **) = so; + ipsec_optaux(m, n); + return 0; } struct socket * @@ -3317,9 +3490,66 @@ ipsec_getsocket(m) { struct mbuf *n; - n = m_aux_find(m, AF_INET, IPPROTO_ESP); + n = ipsec_findaux(m); if (n && n->m_len >= sizeof(struct socket *)) return *mtod(n, struct socket **); else return NULL; } + +int +ipsec_addhist(m, proto, spi) + struct mbuf *m; + int proto; + u_int32_t spi; +{ + struct mbuf *n; + struct ipsec_history *p; + + n = ipsec_addaux(m); + if (!n) + return ENOBUFS; + if (M_TRAILINGSPACE(n) < sizeof(*p)) + return ENOSPC; /*XXX*/ + p = (struct ipsec_history *)(mtod(n, caddr_t) + n->m_len); + n->m_len += sizeof(*p); + bzero(p, sizeof(*p)); + p->ih_proto = proto; + p->ih_spi = spi; + return 0; +} + +struct ipsec_history * +ipsec_gethist(m, lenp) + struct mbuf *m; + int *lenp; +{ + struct mbuf *n; + int l; + + n = ipsec_findaux(m); + if (!n) + return NULL; + l = n->m_len; + if (sizeof(struct socket *) > l) + return NULL; + if ((l - sizeof(struct socket *)) % sizeof(struct ipsec_history)) + return NULL; + /* XXX does it make more sense to divide by sizeof(ipsec_history)? */ + if (lenp) + *lenp = l - sizeof(struct socket *); + return (struct ipsec_history *) + (mtod(n, caddr_t) + sizeof(struct socket *)); +} + +void +ipsec_clearhist(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = ipsec_findaux(m); + if ((n) && n->m_len > sizeof(struct socket *)) + n->m_len = sizeof(struct socket *); + ipsec_optaux(m, n); +} diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h index 80be10c0312c..33cef87a1736 100644 --- a/sys/netinet6/ipsec.h +++ b/sys/netinet6/ipsec.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ +/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,6 +37,11 @@ #ifndef _NETINET6_IPSEC_H_ #define _NETINET6_IPSEC_H_ +#if defined(_KERNEL) && !defined(_LKM) && !defined(KLD_MODULE) +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + #include <net/pfkeyv2.h> #include <netkey/keydb.h> @@ -79,6 +84,18 @@ struct secpolicy { struct ipsecrequest *req; /* pointer to the ipsec request tree, */ /* if policy == IPSEC else this value == NULL.*/ + + /* + * lifetime handler. + * the policy can be used without limitiation if both lifetime and + * validtime are zero. + * "lifetime" is passed by sadb_lifetime.sadb_lifetime_addtime. + * "validtime" is passed by sadb_lifetime.sadb_lifetime_usetime. + */ + long created; /* time created the policy */ + long lastused; /* updated every when kernel sends a packet */ + long lifetime; /* duration of the lifetime of this policy */ + long validtime; /* duration this policy is valid without use */ }; /* Request for IPsec */ @@ -107,7 +124,7 @@ struct secspacq { struct secpolicyindex spidx; - u_int32_t tick; /* for lifetime */ + long created; /* for lifetime */ int count; /* for lifetime */ /* XXX: here is mbuf place holder to be sent ? */ }; @@ -137,9 +154,9 @@ struct secspacq { /* Policy level */ /* - * IPSEC, ENTRUST and BYPASS are allowd for setsockopt() in PCB, - * DISCARD, IPSEC and NONE are allowd for setkey() in SPD. - * DISCARD and NONE are allowd for system default. + * IPSEC, ENTRUST and BYPASS are allowed for setsockopt() in PCB, + * DISCARD, IPSEC and NONE are allowed for setkey() in SPD. + * DISCARD and NONE are allowed for system default. */ #define IPSEC_POLICY_DISCARD 0 /* discarding packet */ #define IPSEC_POLICY_NONE 1 /* through IPsec engine */ @@ -216,7 +233,8 @@ struct ipsecstat { #define IPSECCTL_DFBIT 10 #define IPSECCTL_ECN 11 #define IPSECCTL_DEBUG 12 -#define IPSECCTL_MAXID 13 +#define IPSECCTL_ESP_RANDPAD 13 +#define IPSECCTL_MAXID 14 #define IPSECCTL_NAMES { \ { 0, 0 }, \ @@ -232,6 +250,7 @@ struct ipsecstat { { "dfbit", CTLTYPE_INT }, \ { "ecn", CTLTYPE_INT }, \ { "debug", CTLTYPE_INT }, \ + { "esp_randpad", CTLTYPE_INT }, \ } #define IPSEC6CTL_NAMES { \ @@ -248,6 +267,7 @@ struct ipsecstat { { 0, 0 }, \ { "ecn", CTLTYPE_INT }, \ { "debug", CTLTYPE_INT }, \ + { "esp_randpad", CTLTYPE_INT }, \ } #ifdef _KERNEL @@ -257,6 +277,11 @@ struct ipsec_output_state { struct sockaddr *dst; }; +struct ipsec_history { + int ih_proto; + u_int32_t ih_spi; +}; + extern int ipsec_debug; extern struct ipsecstat ipsecstat; @@ -269,6 +294,7 @@ extern int ip4_ah_cleartos; extern int ip4_ah_offsetmask; extern int ip4_ipsec_dfbit; extern int ip4_ipsec_ecn; +extern int ip4_esp_randpad; #define ipseclog(x) do { if (ipsec_debug) log x; } while (0) @@ -307,10 +333,15 @@ extern void ipsec_dumpmbuf __P((struct mbuf *)); 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 int ipsec4_tunnel_validate __P((struct mbuf *, int, u_int, + struct secasvar *)); extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); -extern void ipsec_setsocket __P((struct mbuf *, struct socket *)); +extern void ipsec_delaux __P((struct mbuf *)); +extern int ipsec_setsocket __P((struct mbuf *, struct socket *)); extern struct socket *ipsec_getsocket __P((struct mbuf *)); +extern int ipsec_addhist __P((struct mbuf *, int, u_int32_t)); +extern struct ipsec_history *ipsec_gethist __P((struct mbuf *, int *)); +extern void ipsec_clearhist __P((struct mbuf *)); #endif /*_KERNEL*/ #ifndef _KERNEL @@ -318,7 +349,7 @@ 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 char *ipsec_strerror __P((void)); +extern const char *ipsec_strerror __P((void)); #endif /*!_KERNEL*/ #endif /*_NETINET6_IPSEC_H_*/ diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h index 383c12581995..e9b8a2c9d3b7 100644 --- a/sys/netinet6/ipsec6.h +++ b/sys/netinet6/ipsec6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ +/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -48,6 +48,7 @@ extern int ip6_esp_net_deflev; extern int ip6_ah_trans_deflev; extern int ip6_ah_net_deflev; extern int ip6_ipsec_ecn; +extern int ip6_esp_randpad; extern struct secpolicy *ipsec6_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); @@ -64,6 +65,8 @@ 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 tcp6cb; + extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); struct ip6_hdr; @@ -73,7 +76,7 @@ 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, +extern int ipsec6_tunnel_validate __P((struct mbuf *, int, u_int, struct secasvar *)); #endif /*_KERNEL*/ diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 473985648458..27b8480ec9b8 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: mld6.c,v 1.19 2000/05/05 11:01:03 sumikawa Exp $ */ +/* $KAME: mld6.c,v 1.27 2001/04/04 05:17:30 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -129,9 +129,8 @@ mld6_init() hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t)); + init_ip6pktopts(&ip6_opts); ip6_opts.ip6po_hbh = hbh; - /* We will specify the hoplimit by a multicast option. */ - ip6_opts.ip6po_hlim = -1; } void @@ -192,31 +191,34 @@ mld6_input(m, off) struct ifmultiaddr *ifma; int timer; /* timer value in the MLD query header */ +#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 + /* source address validation */ + ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { log(LOG_ERR, - "mld6_input: src %s is not link-local\n", - ip6_sprintf(&ip6->ip6_src)); + "mld6_input: src %s is not link-local (grp=%s)\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&mldh->mld6_addr)); /* * 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. + * XXX: do we have to allow :: as source? */ 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. * @@ -234,7 +236,7 @@ mld6_input(m, off) break; if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && - !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) + !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) break; /* print error or log stat? */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = @@ -343,7 +345,7 @@ mld6_input(m, off) void mld6_fasttimeo() { - register struct in6_multi *in6m; + struct in6_multi *in6m; struct in6_multistep step; int s; @@ -408,6 +410,7 @@ mld6_sendpkt(in6m, type, dst) } mh->m_next = md; + mh->m_pkthdr.rcvif = NULL; 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)); @@ -455,16 +458,16 @@ mld6_sendpkt(in6m, type, dst) ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); - switch(type) { - case MLD6_LISTENER_QUERY: - icmp6_ifstat_inc(outif, ifs6_out_mldquery); - break; - case MLD6_LISTENER_REPORT: - icmp6_ifstat_inc(outif, ifs6_out_mldreport); - break; - case MLD6_LISTENER_DONE: - icmp6_ifstat_inc(outif, ifs6_out_mlddone); - break; + switch (type) { + case MLD6_LISTENER_QUERY: + icmp6_ifstat_inc(outif, ifs6_out_mldquery); + break; + case MLD6_LISTENER_REPORT: + icmp6_ifstat_inc(outif, ifs6_out_mldreport); + break; + case MLD6_LISTENER_DONE: + icmp6_ifstat_inc(outif, ifs6_out_mlddone); + break; } } } diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 833e90262bc9..270550bf4903 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6.c,v 1.68 2000/07/02 14:48:02 itojun Exp $ */ +/* $KAME: nd6.c,v 1.144 2001/05/24 07:44:00 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -43,6 +43,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/callout.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -53,6 +54,7 @@ #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> +#include <sys/sysctl.h> #include <net/if.h> #include <net/if_dl.h> @@ -84,12 +86,19 @@ 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_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */ /* 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 */ +#ifdef ND6_DEBUG +int nd6_debug = 1; +#else +int nd6_debug = 0; +#endif + /* for debugging? */ static int nd6_inuse, nd6_allocated; @@ -103,6 +112,11 @@ int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; static struct sockaddr_in6 all1_sa; static void nd6_slowtimo __P((void *)); +static int regen_tmpaddr __P((struct in6_ifaddr *)); + +struct callout nd6_slowtimo_ch; +struct callout nd6_timer_ch; +extern struct callout in6_tmpaddrtimer_ch; void nd6_init() @@ -126,7 +140,8 @@ nd6_init() nd6_init_done = 1; /* start timer */ - timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); + callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, + nd6_slowtimo, NULL); } void @@ -192,22 +207,30 @@ nd6_setmtu(ifp) u_long oldmaxmtu = ndi->maxmtu; u_long oldlinkmtu = ndi->linkmtu; - switch(ifp->if_type) { - case IFT_ARCNET: /* XXX MTU handling needs more work */ - ndi->maxmtu = MIN(60480, ifp->if_mtu); - break; - case IFT_ETHER: - ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); - break; - case IFT_FDDI: - ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); - break; - case IFT_ATM: - ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); - break; - default: - ndi->maxmtu = ifp->if_mtu; - break; + switch (ifp->if_type) { + case IFT_ARCNET: /* XXX MTU handling needs more work */ + ndi->maxmtu = MIN(60480, ifp->if_mtu); + break; + case IFT_ETHER: + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; + case IFT_FDDI: + ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); + break; + case IFT_ATM: + ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); + break; + case IFT_IEEE1394: /* XXX should be IEEE1394MTU(1500) */ + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: /* XXX should be IEEE80211MTU(1500) */ + ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); + break; +#endif + default: + ndi->maxmtu = ifp->if_mtu; + break; } if (oldmaxmtu != ndi->maxmtu) { @@ -329,6 +352,7 @@ nd6_options(ndopts) * Message validation requires that all included * options have a length that is greater than zero. */ + icmp6stat.icp6s_nd_badopt++; bzero(ndopts, sizeof(*ndopts)); return -1; } @@ -342,8 +366,9 @@ nd6_options(ndopts) case ND_OPT_MTU: case ND_OPT_REDIRECTED_HEADER: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { - printf("duplicated ND6 option found " - "(type=%d)\n", nd_opt->nd_opt_type); + nd6log((LOG_INFO, + "duplicated ND6 option found (type=%d)\n", + nd_opt->nd_opt_type)); /* XXX bark? */ } else { ndopts->nd_opt_array[nd_opt->nd_opt_type] @@ -363,16 +388,16 @@ nd6_options(ndopts) * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ - log(LOG_DEBUG, + nd6log((LOG_DEBUG, "nd6_options: unsupported option %d - " - "option ignored\n", nd_opt->nd_opt_type); + "option ignored\n", nd_opt->nd_opt_type)); } skip1: i++; if (i > nd6_maxndopt) { icmp6stat.icp6s_nd_toomanyopt++; - printf("too many loop in nd opt\n"); + nd6log((LOG_INFO, "too many loop in nd opt\n")); break; } @@ -391,18 +416,21 @@ nd6_timer(ignored_arg) void *ignored_arg; { int s; - register struct llinfo_nd6 *ln; - register struct nd_defrouter *dr; - register struct nd_prefix *pr; + struct llinfo_nd6 *ln; + struct nd_defrouter *dr; + struct nd_prefix *pr; + struct ifnet *ifp; + struct in6_ifaddr *ia6, *nia6; + struct in6_addrlifetime *lt6; s = splnet(); - timeout(nd6_timer, (caddr_t)0, nd6_prune * hz); + callout_reset(&nd6_timer_ch, nd6_prune * hz, + nd6_timer, NULL); ln = llinfo_nd6.ln_next; /* XXX BSD/OS separates this code -- itojun */ while (ln && ln != &llinfo_nd6) { struct rtentry *rt; - struct ifnet *ifp; struct sockaddr_in6 *dst; struct llinfo_nd6 *next = ln->ln_next; /* XXX: used for the DELAY case only: */ @@ -458,17 +486,22 @@ nd6_timer(ignored_arg) ICMP6_DST_UNREACH_ADDR, 0); ln->ln_hold = NULL; } - nd6_free(rt); + next = nd6_free(rt); } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) + if (ln->ln_expire) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } break; - /* - * ND6_LLINFO_STALE state requires nothing for timer - * routine. - */ + + case ND6_LLINFO_STALE: + /* Garbage Collection(RFC 2461 5.3) */ + if (ln->ln_expire) + next = nd6_free(rt); + break; + case ND6_LLINFO_DELAY: if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { /* We need NUD */ @@ -479,8 +512,10 @@ nd6_timer(ignored_arg) nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); - } else + } else { ln->ln_state = ND6_LLINFO_STALE; /* XXX */ + ln->ln_expire = time_second + nd6_gctimer; + } break; case ND6_LLINFO_PROBE: if (ln->ln_asked < nd6_umaxtries) { @@ -490,17 +525,14 @@ nd6_timer(ignored_arg) nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); } else { - nd6_free(rt); + next = nd6_free(rt); } break; - case ND6_LLINFO_WAITDELETE: - nd6_free(rt); - break; } ln = next; } - /* expire */ + /* expire default router list */ dr = TAILQ_FIRST(&nd_defrouter); while (dr) { if (dr->expire && dr->expire < time_second) { @@ -512,28 +544,82 @@ nd6_timer(ignored_arg) dr = TAILQ_NEXT(dr, dr_entry); } } - pr = nd_prefix.lh_first; - while (pr) { - struct in6_ifaddr *ia6; - struct in6_addrlifetime *lt6; - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - - if (ia6) { - /* check address lifetime */ - lt6 = &ia6->ia6_lifetime; - if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second) - ia6->ia6_flags |= IN6_IFF_DEPRECATED; - if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) { - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ + /* + * expire interface addresses. + * in the past the loop was inside prefix expiry processing. + * However, from a stricter speci-confrmance standpoint, we should + * rather separate address lifetimes and prefix lifetimes. + */ + addrloop: + for (ia6 = in6_ifaddr; ia6; ia6 = nia6) { + nia6 = ia6->ia_next; + /* check address lifetime */ + lt6 = &ia6->ia6_lifetime; + if (IFA6_IS_INVALID(ia6)) { + int regen = 0; + + /* + * If the expiring address is temporary, try + * regenerating a new one. This would be useful when + * we suspended a laptop PC, then turned on after a + * period that could invalidate all temporary + * addresses. Although we may have to restart the + * loop (see below), it must be after purging the + * address. Otherwise, we'd see an infinite loop of + * regeneration. + */ + if (ip6_use_tempaddr && + (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { + if (regen_tmpaddr(ia6) == 0) + regen = 1; } + + in6_purgeaddr(&ia6->ia_ifa); + + if (regen) + goto addrloop; /* XXX: see below */ + } else if (IFA6_IS_DEPRECATED(ia6)) { + int oldflags = ia6->ia6_flags; + + ia6->ia6_flags |= IN6_IFF_DEPRECATED; + + /* + * If a temporary address has just become deprecated, + * regenerate a new one if possible. + */ + if (ip6_use_tempaddr && + (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + (oldflags & IN6_IFF_DEPRECATED) == 0) { + + if (regen_tmpaddr(ia6) == 0) { + /* + * A new temporary address is + * generated. + * XXX: this means the address chain + * has changed while we are still in + * the loop. Although the change + * would not cause disaster (because + * it's not an addition, but a + * deletion,) we'd rather restart the + * loop just for safety. Or does this + * significantly reduce performance?? + */ + goto addrloop; + } + } + } else if (IFA6_IS_DEPRECATED(ia6)) { + /* + * A new RA might have made a deprecated address + * preferred. + */ + ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; } + } + /* expire prefix list */ + pr = nd_prefix.lh_first; + while (pr) { /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for @@ -543,15 +629,17 @@ nd6_timer(ignored_arg) * can use the old prefix information to validate the * next prefix information to come. See prelist_update() * for actual validation. + * + * I don't think such an offset is necessary. + * (jinmei@kame.net, 20010130). */ - if (pr->ndpr_expire - && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { + if (pr->ndpr_expire && pr->ndpr_expire < time_second) { struct nd_prefix *t; t = pr->ndpr_next; /* * address expiration and prefix expiration are - * separate. NEVER perform in6_ifdel here. + * separate. NEVER perform in6_purgeaddr here. */ prelist_remove(pr); @@ -562,6 +650,70 @@ nd6_timer(ignored_arg) splx(s); } +static int +regen_tmpaddr(ia6) + struct in6_ifaddr *ia6; /* deprecated/invalidated temporary address */ +{ + struct ifaddr *ifa; + struct ifnet *ifp; + struct in6_ifaddr *public_ifa6 = NULL; + + ifp = ia6->ia_ifa.ifa_ifp; + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) + { + struct in6_ifaddr *it6; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + it6 = (struct in6_ifaddr *)ifa; + + /* ignore no autoconf addresses. */ + if ((it6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + /* ignore autoconf addresses with different prefixes. */ + if (it6->ia6_ndpr == NULL || it6->ia6_ndpr != ia6->ia6_ndpr) + continue; + + /* + * Now we are looking at an autoconf address with the same + * prefix as ours. If the address is temporary and is still + * preferred, do not create another one. It would be rare, but + * could happen, for example, when we resume a laptop PC after + * a long period. + */ + if ((it6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && + !IFA6_IS_DEPRECATED(it6)) { + public_ifa6 = NULL; + break; + } + + /* + * This is a public autoconf address that has the same prefix + * as ours. If it is preferred, keep it. We can't break the + * loop here, because there may be a still-preferred temporary + * address with the prefix. + */ + if (!IFA6_IS_DEPRECATED(it6)) + public_ifa6 = it6; + } + + if (public_ifa6 != NULL) { + int e; + + if ((e = in6_tmpifadd(public_ifa6, 0)) != 0) { + log(LOG_NOTICE, "regen_tmpaddr: failed to create a new" + " tmp addr,errno=%d\n", e); + return(-1); + } + return(0); + } + + return(-1); +} + /* * Nuke neighbor cache/prefix/default router management table, right before * ifp goes away. @@ -594,8 +746,14 @@ nd6_purge(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); + /* + * Previously, pr->ndpr_addr is removed as well, + * but I strongly believe we don't have to do it. + * nd6_purge() is only called from in6_ifdetach(), + * which removes all the associated interface addresses + * by itself. + * (jinmei@kame.net 20010129) + */ prelist_remove(pr); } } @@ -626,30 +784,7 @@ nd6_purge(ifp) 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); - } + nln = nd6_free(rt); } ln = nln; } @@ -757,7 +892,7 @@ nd6_is_addr_neighbor(addr, ifp) struct sockaddr_in6 *addr; struct ifnet *ifp; { - register struct ifaddr *ifa; + struct ifaddr *ifa; int i; #define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) @@ -807,26 +942,25 @@ nd6_is_addr_neighbor(addr, ifp) /* * Free an nd6 llinfo entry. */ -void +struct llinfo_nd6 * nd6_free(rt) struct rtentry *rt; { - struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; - struct sockaddr_dl *sdl; + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; struct nd_defrouter *dr; /* - * Clear all destination cache entries for the neighbor. - * XXX: is it better to restrict this to hosts? + * we used to have pfctlinput(PRC_HOSTDEAD) here. + * even though it is not harmful, it was not really necessary. */ - 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, rt->rt_ifp); + if (ln->ln_router || dr) { /* * rt6_flush must be called whether or not the neighbor @@ -852,6 +986,14 @@ nd6_free(rt) */ ln->ln_state = ND6_LLINFO_INCOMPLETE; + /* + * Since defrouter_select() does not affect the + * on-link determination and MIP6 needs the check + * before the default router selection, we perform + * the check now. + */ + pfxlist_onlink_check(); + if (dr == TAILQ_FIRST(&nd_defrouter)) { /* * It is used as the current default router, @@ -865,22 +1007,27 @@ nd6_free(rt) defrouter_select(); } - pfxlist_onlink_check(); } splx(s); } - if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && - 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; - } + /* + * Before deleting the entry, remember the next entry as the + * return value. We need this because pfxlist_onlink_check() above + * might have freed other entries (particularly the old next entry) as + * a side effect (XXX). + */ + next = ln->ln_next; + /* + * Detach the route from the routing tree and the list of neighbor + * caches, and disable the route entry not to be used in already + * cached routes. + */ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), 0, (struct rtentry **)0); + + return(next); } /* @@ -935,103 +1082,6 @@ nd6_nud_hint(rt, dst6, force) 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: - m_freem(m); - 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); - } - } - /* Do not free mbuf chain here as it is queued in llinfo_nd6 */ - return(0); -} -#endif /* OLDIP6OUTPUT */ - void nd6_rtrequest(req, rt, sa) int req; @@ -1047,6 +1097,17 @@ nd6_rtrequest(req, rt, sa) if (rt->rt_flags & RTF_GATEWAY) return; + if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) { + /* + * This is probably an interface direct route for a link + * which does not need neighbor caches (e.g. fe80::%lo0/64). + * We do not need special treatment below for such a route. + * Moreover, the RTF_LLINFO flag which would be set below + * would annoy the ndp(8) command. + */ + return; + } + switch (req) { case RTM_ADD: /* @@ -1072,7 +1133,7 @@ nd6_rtrequest(req, rt, sa) ln->ln_expire = time_second; #if 1 if (ln && ln->ln_expire == 0) { - /* cludge for desktops */ + /* kludge for desktops */ #if 0 printf("nd6_request: time.tv_sec is zero; " "treat it as 1\n"); @@ -1093,10 +1154,10 @@ nd6_rtrequest(req, rt, sa) * (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 + * note that the mechanism needs 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. + * a new protocol, or a new kludge. + * - from RFC2461 6.2.4, host MUST NOT send an 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) @@ -1112,7 +1173,7 @@ nd6_rtrequest(req, rt, sa) #endif /* FALLTHROUGH */ case RTM_RESOLVE: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { /* * Address resolution isn't necessary for a point to * point link, so we can skip this test for a p2p link. @@ -1120,7 +1181,8 @@ nd6_rtrequest(req, rt, sa) if (gate->sa_family != AF_LINK || gate->sa_len < sizeof(null_sdl)) { log(LOG_DEBUG, - "nd6_rtrequest: bad gateway value\n"); + "nd6_rtrequest: bad gateway value: %s\n", + if_name(ifp)); break; } SDL(gate)->sdl_type = ifp->if_type; @@ -1192,7 +1254,7 @@ nd6_rtrequest(req, rt, sa) */ if (ifa != rt->rt_ifa) { IFAFREE(rt->rt_ifa); - ifa->ifa_refcnt++; + IFAREF(ifa); rt->rt_ifa = ifa; } } @@ -1213,10 +1275,11 @@ nd6_rtrequest(req, rt, sa) 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); + if (!in6_addmulti(&llsol, ifp, &error)) { + nd6log((LOG_ERR, "%s: failed to join " + "%s (errno=%d)\n", if_name(ifp), + ip6_sprintf(&llsol), error)); + } } } break; @@ -1253,65 +1316,6 @@ nd6_rtrequest(req, rt, sa) } } -void -nd6_p2p_rtrequest(req, rt, sa) - int req; - struct rtentry *rt; - struct sockaddr *sa; /* xxx unused */ -{ - struct sockaddr *gate = rt->rt_gateway; - static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; - struct ifnet *ifp = rt->rt_ifp; - struct ifaddr *ifa; - - if (rt->rt_flags & RTF_GATEWAY) - return; - - switch (req) { - case RTM_ADD: - /* - * There is no backward compatibility :) - * - * if ((rt->rt_flags & RTF_HOST) == 0 && - * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) - * rt->rt_flags |= RTF_CLONING; - */ - if (rt->rt_flags & RTF_CLONING) { - /* - * Case 1: This route should come from - * a route to interface. - */ - rt_setgate(rt, rt_key(rt), - (struct sockaddr *)&null_sdl); - gate = rt->rt_gateway; - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - break; - } - /* Announce a new entry if requested. */ - 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, NULL); - /* FALLTHROUGH */ - case RTM_RESOLVE: - /* - * check if rt_key(rt) is one of my address assigned - * to the interface. - */ - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, - &SIN6(rt_key(rt))->sin6_addr); - if (ifa) { - if (nd6_useloopback) { - rt->rt_ifp = &loif[0]; /*XXX*/ - } - } - break; - } -} - int nd6_ioctl(cmd, data, ifp) u_long cmd; @@ -1331,6 +1335,9 @@ nd6_ioctl(cmd, data, ifp) switch (cmd) { case SIOCGDRLST_IN6: + /* + * obsolete API, use sysctl under net.inet6.icmp6 + */ bzero(drl, sizeof(*drl)); s = splnet(); dr = TAILQ_FIRST(&nd_defrouter); @@ -1356,6 +1363,9 @@ nd6_ioctl(cmd, data, ifp) break; case SIOCGPRLST_IN6: /* + * obsolete API, use sysctl under net.inet6.icmp6 + */ + /* * XXX meaning of fields, especialy "raflags", is very * differnet between RA prefix list and RR/static prefix list. * how about separating ioctls into two? @@ -1367,7 +1377,8 @@ nd6_ioctl(cmd, data, ifp) struct nd_pfxrouter *pfr; int j; - prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; + (void)in6_embedscope(&prl->prefix[i].prefix, + &pr->ndpr_prefix, NULL, NULL); prl->prefix[i].raflags = pr->ndpr_raf; prl->prefix[i].prefixlen = pr->ndpr_plen; prl->prefix[i].vltime = pr->ndpr_vltime; @@ -1377,7 +1388,7 @@ nd6_ioctl(cmd, data, ifp) pfr = pr->ndpr_advrtrs.lh_first; j = 0; - while(pfr) { + while (pfr) { if (j < DRLSTSIZ) { #define RTRADDR prl->prefix[i].advrtr[j] RTRADDR = pfr->router->rtaddr; @@ -1408,7 +1419,8 @@ nd6_ioctl(cmd, data, ifp) rpp = LIST_NEXT(rpp, rp_entry)) { if (i >= PRLSTSIZ) break; - prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr; + (void)in6_embedscope(&prl->prefix[i].prefix, + &pr->ndpr_prefix, NULL, NULL); prl->prefix[i].raflags = rpp->rp_raf; prl->prefix[i].prefixlen = rpp->rp_plen; prl->prefix[i].vltime = rpp->rp_vltime; @@ -1423,6 +1435,22 @@ nd6_ioctl(cmd, data, ifp) splx(s); break; + case OSIOCGIFINFO_IN6: + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } + ndi->ndi.linkmtu = nd_ifinfo[ifp->if_index].linkmtu; + ndi->ndi.maxmtu = nd_ifinfo[ifp->if_index].maxmtu; + ndi->ndi.basereachable = + nd_ifinfo[ifp->if_index].basereachable; + ndi->ndi.reachable = nd_ifinfo[ifp->if_index].reachable; + ndi->ndi.retrans = nd_ifinfo[ifp->if_index].retrans; + ndi->ndi.flags = nd_ifinfo[ifp->if_index].flags; + ndi->ndi.recalctm = nd_ifinfo[ifp->if_index].recalctm; + ndi->ndi.chlim = nd_ifinfo[ifp->if_index].chlim; + ndi->ndi.receivedra = nd_ifinfo[ifp->if_index].receivedra; + break; case SIOCGIFINFO_IN6: if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { error = EINVAL; @@ -1456,9 +1484,24 @@ nd6_ioctl(cmd, data, ifp) s = splnet(); for (pr = nd_prefix.lh_first; pr; pr = next) { + struct in6_ifaddr *ia, *ia_next; + next = pr->ndpr_next; - if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; /* XXX */ + + /* do we really have to remove addresses as well? */ + for (ia = in6_ifaddr; ia; ia = ia_next) { + /* ia might be removed. keep the next ptr. */ + ia_next = ia->ia_next; + + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ia->ia6_ndpr == pr) + in6_purgeaddr(&ia->ia_ifa); + } prelist_remove(pr); } splx(s); @@ -1577,14 +1620,18 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) rt = nd6_lookup(from, 1, ifp); is_newentry = 1; - } else + } else { + /* do nothing if static ndp is set */ + if (rt->rt_flags & RTF_STATIC) + return NULL; is_newentry = 0; + } if (!rt) return NULL; if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { fail: - nd6_free(rt); + (void)nd6_free(rt); return NULL; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; @@ -1647,12 +1694,15 @@ fail: ln->ln_state = newstate; if (ln->ln_state == ND6_LLINFO_STALE) { - rt->rt_flags &= ~RTF_REJECT; + /* + * XXX: since nd6_output() below will cause + * state tansition to DELAY and reset the timer, + * we must set the timer now, although it is actually + * meaningless. + */ + ln->ln_expire = time_second + nd6_gctimer; + if (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. @@ -1660,8 +1710,7 @@ fail: nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); -#endif - ln->ln_hold = 0; + ln->ln_hold = NULL; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* probe right away */ @@ -1742,10 +1791,11 @@ nd6_slowtimo(ignored_arg) void *ignored_arg; { int s = splnet(); - register int i; - register struct nd_ifinfo *nd6if; + int i; + struct nd_ifinfo *nd6if; - timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); + callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, + nd6_slowtimo, NULL); for (i = 1; i < if_index + 1; i++) { if (!nd_ifinfo || i >= nd_ifinfo_indexlim) continue; @@ -1768,14 +1818,14 @@ nd6_slowtimo(ignored_arg) #define senderr(e) { error = (e); goto bad;} int nd6_output(ifp, origifp, m0, dst, rt0) - register struct ifnet *ifp; + 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 mbuf *m = m0; + struct rtentry *rt = rt0; struct sockaddr_in6 *gw6 = NULL; struct llinfo_nd6 *ln = NULL; int error = 0; @@ -1783,22 +1833,8 @@ nd6_output(ifp, origifp, m0, dst, rt0) if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) goto sendpkt; - /* - * XXX: we currently do not make neighbor cache on any interface - * 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: + if (nd6_need_cache(ifp) == 0) goto sendpkt; - } /* * next hop determination. This routine is derived from ether_outpout. @@ -1824,7 +1860,7 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* * 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 + * of view, regardless the value of the * nd_ifinfo.flags. * The second condition is a bit tricky: we skip * if the gateway is our own address, which is @@ -1832,9 +1868,6 @@ nd6_output(ifp, origifp, m0, dst, rt0) */ 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. @@ -1855,8 +1888,6 @@ nd6_output(ifp, origifp, m0, dst, rt0) senderr(EHOSTUNREACH); } } - if (rt->rt_flags & RTF_REJECT) - senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } /* @@ -1894,8 +1925,10 @@ nd6_output(ifp, origifp, m0, dst, rt0) /* 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_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } /* * The first time we send a packet to a neighbor whose entry is @@ -1926,14 +1959,12 @@ nd6_output(ifp, origifp, m0, dst, rt0) * 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) + if (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++; @@ -1946,12 +1977,10 @@ nd6_output(ifp, origifp, m0, dst, rt0) sendpkt: -#ifdef FAKE_LOOPBACK_IF - if (ifp->if_flags & IFF_LOOPBACK) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, rt)); } -#endif return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); bad: @@ -1962,6 +1991,32 @@ nd6_output(ifp, origifp, m0, dst, rt0) #undef senderr int +nd6_need_cache(ifp) + struct ifnet *ifp; +{ + /* + * XXX: we currently do not make neighbor cache on any interface + * other than ARCnet, Ethernet, FDDI and GIF. + * + * RFC2893 says: + * - unidirectional tunnels needs no ND + */ + switch (ifp->if_type) { + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + case IFT_GIF: /* XXX need more cases? */ + return(1); + default: + return(0); + } +} + +int nd6_storelladdr(ifp, rt, m, dst, desten) struct ifnet *ifp; struct rtentry *rt; @@ -1969,16 +2024,23 @@ nd6_storelladdr(ifp, rt, m, dst, desten) struct sockaddr *dst; u_char *desten; { + int i; struct sockaddr_dl *sdl; if (m->m_flags & M_MCAST) { switch (ifp->if_type) { case IFT_ETHER: - case IFT_FDDI: + case IFT_FDDI: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, desten); return(1); - break; + case IFT_IEEE1394: + for (i = 0; i < ifp->if_addrlen; i++) + desten[i] = ~0; + return(1); case IFT_ARCNET: *desten = 0; return(1); @@ -1989,12 +2051,12 @@ nd6_storelladdr(ifp, rt, m, dst, desten) } if (rt == NULL) { - /* This could happen if we could not allocate memory */ + /* this could happen, if we could not allocate memory */ m_freem(m); return(0); } if (rt->rt_gateway->sa_family != AF_LINK) { - printf("nd6_storelladdr: something odd happened\n"); + printf("nd6_storelladdr: something odd happens\n"); m_freem(m); return(0); } @@ -2009,3 +2071,129 @@ nd6_storelladdr(ifp, rt, m, dst, desten) bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } + +static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS); +static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS); +#ifdef SYSCTL_DECL +SYSCTL_DECL(_net_inet6_icmp6); +#endif +SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, + CTLFLAG_RD, nd6_sysctl_drlist, ""); +SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, + CTLFLAG_RD, nd6_sysctl_prlist, ""); + +static int +nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) +{ + int error; + char buf[1024]; + struct in6_defrouter *d, *de; + struct nd_defrouter *dr; + + if (req->newptr) + return EPERM; + error = 0; + + for (dr = TAILQ_FIRST(&nd_defrouter); + dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + d = (struct in6_defrouter *)buf; + de = (struct in6_defrouter *)(buf + sizeof(buf)); + + if (d + 1 <= de) { + bzero(d, sizeof(*d)); + d->rtaddr.sin6_family = AF_INET6; + d->rtaddr.sin6_len = sizeof(d->rtaddr); + if (in6_recoverscope(&d->rtaddr, &dr->rtaddr, + dr->ifp) != 0) + log(LOG_ERR, + "scope error in " + "default router list (%s)\n", + ip6_sprintf(&dr->rtaddr)); + d->flags = dr->flags; + d->rtlifetime = dr->rtlifetime; + d->expire = dr->expire; + d->if_index = dr->ifp->if_index; + } else + panic("buffer too short"); + + error = SYSCTL_OUT(req, buf, sizeof(*d)); + if (error) + break; + } + return error; +} + +static int +nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS) +{ + int error; + char buf[1024]; + struct in6_prefix *p, *pe; + struct nd_prefix *pr; + + if (req->newptr) + return EPERM; + error = 0; + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + u_short advrtrs; + size_t advance; + struct sockaddr_in6 *sin6, *s6; + struct nd_pfxrouter *pfr; + + p = (struct in6_prefix *)buf; + pe = (struct in6_prefix *)(buf + sizeof(buf)); + + if (p + 1 <= pe) { + bzero(p, sizeof(*p)); + sin6 = (struct sockaddr_in6 *)(p + 1); + + p->prefix = pr->ndpr_prefix; + if (in6_recoverscope(&p->prefix, + &p->prefix.sin6_addr, pr->ndpr_ifp) != 0) + log(LOG_ERR, + "scope error in prefix list (%s)\n", + ip6_sprintf(&p->prefix.sin6_addr)); + p->raflags = pr->ndpr_raf; + p->prefixlen = pr->ndpr_plen; + p->vltime = pr->ndpr_vltime; + p->pltime = pr->ndpr_pltime; + p->if_index = pr->ndpr_ifp->if_index; + p->expire = pr->ndpr_expire; + p->refcnt = pr->ndpr_refcnt; + p->flags = pr->ndpr_stateflags; + p->origin = PR_ORIG_RA; + advrtrs = 0; + for (pfr = pr->ndpr_advrtrs.lh_first; + pfr; + pfr = pfr->pfr_next) { + if ((void *)&sin6[advrtrs + 1] > + (void *)pe) { + advrtrs++; + continue; + } + s6 = &sin6[advrtrs]; + bzero(s6, sizeof(*s6)); + s6->sin6_family = AF_INET6; + s6->sin6_len = sizeof(*sin6); + if (in6_recoverscope(s6, + &pfr->router->rtaddr, + pfr->router->ifp) != 0) + log(LOG_ERR, + "scope error in " + "prefix list (%s)\n", + ip6_sprintf(&pfr->router->rtaddr)); + advrtrs++; + } + p->advrtrs = advrtrs; + } else + panic("buffer too short"); + + advance = sizeof(*p) + sizeof(*sin6) * advrtrs; + error = SYSCTL_OUT(req, buf, advance); + if (error) + break; + } + return error; +} diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 26f58b50a991..73bbcd5aef0e 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6.h,v 1.23 2000/06/04 12:54:57 itojun Exp $ */ +/* $KAME: nd6.h,v 1.55 2001/04/27 15:09:49 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -39,6 +39,7 @@ #endif #include <sys/queue.h> +#include <sys/callout.h> struct llinfo_nd6 { struct llinfo_nd6 *ln_next; @@ -53,7 +54,14 @@ struct llinfo_nd6 { }; #define ND6_LLINFO_NOSTATE -2 -#define ND6_LLINFO_WAITDELETE -1 +/* + * We don't need the WAITDELETE state any more, but we keep the definition + * in a comment line instead of removing it. This is necessary to avoid + * unintentionally reusing the value for another purpose, which might + * affect backward compatibility with old applications. + * (20000711 jinmei@kame.net) + */ +/* #define ND6_LLINFO_WAITDELETE -1 */ #define ND6_LLINFO_INCOMPLETE 0 #define ND6_LLINFO_REACHABLE 1 #define ND6_LLINFO_STALE 2 @@ -72,6 +80,10 @@ struct nd_ifinfo { int recalctm; /* BaseReacable re-calculation timer */ u_int8_t chlim; /* CurHopLimit */ u_int8_t receivedra; + /* the followings are for privacy extension for addrconf */ + u_int8_t randomseed0[8]; /* upper 64 bits of MD5 digest */ + u_int8_t randomseed1[8]; /* lower 64 bits (usually the EUI64 IFID) */ + u_int8_t randomid[8]; /* current random ID */ }; #define ND6_IFF_PERFORMNUD 0x1 @@ -98,6 +110,14 @@ struct in6_drlist { } defrouter[DRLSTSIZ]; }; +struct in6_defrouter { + struct sockaddr_in6 rtaddr; + u_char flags; + u_short rtlifetime; + u_long expire; + u_short if_index; +} __attribute__((__packed__)); + struct in6_prlist { char ifname[IFNAMSIZ]; struct { @@ -114,6 +134,38 @@ struct in6_prlist { } prefix[PRLSTSIZ]; }; +struct in6_prefix { + struct sockaddr_in6 prefix; + struct prf_ra raflags; + u_char prefixlen; + u_char origin; + u_long vltime; + u_long pltime; + u_long expire; + u_int32_t flags; + int refcnt; + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ + /* struct sockaddr_in6 advrtr[] */ +} __attribute__((__packed__)); + +#ifdef _KERNEL +struct in6_ondireq { + char ifname[IFNAMSIZ]; + struct { + 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; + } ndi; +}; +#endif + struct in6_ndireq { char ifname[IFNAMSIZ]; struct nd_ifinfo ndi; @@ -124,6 +176,9 @@ struct in6_ndifreq { u_long ifindex; }; +/* Prefix status */ +#define NDPRF_ONLINK 0x1 +#define NDPRF_DETACHED 0x2 /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ @@ -139,6 +194,10 @@ struct in6_ndifreq { #define RETRANS_TIMER 1000 /* msec */ #define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ #define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define DEF_TEMP_VALID_LIFETIME 604800 /* 1 week */ +#define DEF_TEMP_PREFERRED_LIFETIME 86400 /* 1 day */ +#define TEMPADDR_REGEN_ADVANCE 5 /* sec */ +#define MAX_TEMP_DESYNC_FACTOR 600 /* 10 min */ #define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) @@ -167,13 +226,11 @@ struct nd_prefix { time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */ struct prf_ra ndpr_flags; + u_int32_t ndpr_stateflags; /* actual state 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; - } ndpr_stateflags; + int ndpr_refcnt; /* reference couter from addresses */ }; #define ndpr_next ndpr_entry.le_next @@ -182,9 +239,6 @@ struct nd_prefix { #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 - /* * We keep expired prefix for certain amount of time, for validation purposes. * 1800s = MaxRtrAdvInterval @@ -235,13 +289,23 @@ extern int nd6_umaxtries; extern int nd6_mmaxtries; extern int nd6_useloopback; extern int nd6_maxnudhint; +extern int nd6_gctimer; 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_debug; + +#define nd6log(x) do { if (nd6_debug) log x; } while (0) + +extern struct callout nd6_timer_ch; /* nd6_rtr.c */ extern int nd6_defifindex; +extern int ip6_desync_factor; /* seconds */ +extern u_int32_t ip6_temp_preferred_lifetime; /* seconds */ +extern u_int32_t ip6_temp_valid_lifetime; /* seconds */ +extern int ip6_temp_regen_advance; /* seconds */ union nd_opts { struct nd_opt_hdr *nd_opt_array[9]; /*max = home agent info*/ @@ -285,30 +349,30 @@ struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); void nd6_setmtu __P((struct ifnet *)); void nd6_timer __P((void *)); void nd6_purge __P((struct ifnet *)); -void nd6_free __P((struct rtentry *)); +struct llinfo_nd6 *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 ifnet *, struct mbuf *, struct sockaddr_in6 *, struct rtentry *)); int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *)); +int nd6_need_cache __P((struct ifnet *)); /* 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, struct sockaddr *)); +void nd6_na_output __P((struct ifnet *, const struct in6_addr *, + const 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)); +void nd6_ns_output __P((struct ifnet *, const struct in6_addr *, + const 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_stop __P((struct ifaddr *)); void nd6_dad_duplicated __P((struct ifaddr *)); /* nd6_rtr.c */ @@ -321,14 +385,19 @@ 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 *)); + struct mbuf *)); +int nd6_prelist_add __P((struct nd_prefix *, struct nd_defrouter *, + struct nd_prefix **)); +int nd6_prefix_onlink __P((struct nd_prefix *)); +int nd6_prefix_offlink __P((struct nd_prefix *)); 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 *)); +struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefix *)); int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); void rt6_flush __P((struct in6_addr *, struct ifnet *)); int nd6_setdefaultiface __P((int)); +int in6_tmpifadd __P((const struct in6_ifaddr *, int)); #endif /* _KERNEL */ diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index d3fa831fa695..7527d432ba83 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6_nbr.c,v 1.37 2000/06/04 12:46:13 itojun Exp $ */ +/* $KAME: nd6_nbr.c,v 1.64 2001/05/17 03:48:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -45,6 +45,7 @@ #include <sys/errno.h> #include <sys/syslog.h> #include <sys/queue.h> +#include <sys/callout.h> #include <net/if.h> #include <net/if_types.h> @@ -72,6 +73,8 @@ struct dadq; static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static void nd6_dad_starttimer __P((struct dadq *, int)); +static void nd6_dad_stoptimer __P((struct dadq *)); 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 *)); @@ -106,10 +109,25 @@ nd6_ns_input(m, off, icmp6len) union nd_opts ndopts; struct sockaddr_dl *proxydl = NULL; +#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 + ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ + taddr6 = nd_ns->nd_ns_target; + if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { @@ -121,26 +139,14 @@ nd6_ns_input(m, off, icmp6len) && daddr6.s6_addr8[12] == 0xff) { ; /*good*/ } else { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(wrong ip6 dst)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(wrong ip6 dst)\n")); goto bad; } } -#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"); + nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } @@ -150,8 +156,10 @@ nd6_ns_input(m, off, icmp6len) icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { - log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n"); - goto bad; + nd6log((LOG_INFO, + "nd6_ns_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ + goto freeit; } if (ndopts.nd_opts_src_lladdr) { @@ -160,8 +168,8 @@ nd6_ns_input(m, off, icmp6len) } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { - log(LOG_INFO, "nd6_ns_input: bad DAD packet " - "(link-layer address option)\n"); + nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " + "(link-layer address option)\n")); goto bad; } @@ -223,7 +231,7 @@ nd6_ns_input(m, off, icmp6len) } if (!ifa) { /* - * We've got a NS packet, and we don't have that adddress + * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ @@ -236,10 +244,11 @@ nd6_ns_input(m, off, icmp6len) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { @@ -306,9 +315,10 @@ nd6_ns_input(m, off, icmp6len) 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)); + nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); + nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); + nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); + icmp6stat.icp6s_badns++; m_freem(m); } @@ -324,7 +334,7 @@ nd6_ns_input(m, off, icmp6len) void nd6_ns_output(ifp, daddr6, taddr6, ln, dad) struct ifnet *ifp; - struct in6_addr *daddr6, *taddr6; + const struct in6_addr *daddr6, *taddr6; struct llinfo_nd6 *ln; /* for source address determination */ int dad; /* duplicated address detection */ { @@ -362,6 +372,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -495,7 +506,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad) #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); if (outif) { @@ -541,9 +552,11 @@ nd6_na_input(m, off, icmp6len) union nd_opts ndopts; if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } #ifndef PULLDOWN_TEST @@ -566,22 +579,24 @@ nd6_na_input(m, off, icmp6len) taddr6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_MULTICAST(&taddr6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "nd6_na_input: invalid target address %s\n", - ip6_sprintf(&taddr6)); - goto freeit; + ip6_sprintf(&taddr6))); + goto bad; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { - log(LOG_ERR, - "nd6_na_input: a solicited adv is multicasted\n"); - goto freeit; + nd6log((LOG_ERR, + "nd6_na_input: a solicited adv is multicasted\n")); + goto bad; } 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"); + nd6log((LOG_INFO, + "nd6_na_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -616,10 +631,11 @@ nd6_na_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " "(if %d, NA packet %d)\n", - ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } /* @@ -649,10 +665,19 @@ nd6_na_input(m, off, icmp6len) ln->ln_byhint = 0; if (ln->ln_expire) ln->ln_expire = time_second + - nd_ifinfo[rt->rt_ifp->if_index].reachable; - } else + nd_ifinfo[rt->rt_ifp->if_index].reachable; + } else { ln->ln_state = ND6_LLINFO_STALE; - ln->ln_router = is_router; + ln->ln_expire = time_second + nd6_gctimer; + } + if ((ln->ln_router = is_router) != 0) { + /* + * This means a router's state has changed from + * non-reachable to probably reachable, and might + * affect the status of associated prefixes.. + */ + pfxlist_onlink_check(); + } } else { int llchange; @@ -695,8 +720,10 @@ nd6_na_input(m, off, icmp6len) * If state is REACHABLE, make it STALE. * no other updates should be done. */ - if (ln->ln_state == ND6_LLINFO_REACHABLE) + if (ln->ln_state == ND6_LLINFO_REACHABLE) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ @@ -719,11 +746,13 @@ nd6_na_input(m, off, icmp6len) ln->ln_byhint = 0; if (ln->ln_expire) { ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].reachable; + nd_ifinfo[ifp->if_index].reachable; } } else { - if (lladdr && llchange) + if (lladdr && llchange) { ln->ln_state = ND6_LLINFO_STALE; + ln->ln_expire = time_second + nd6_gctimer; + } } } @@ -759,21 +788,22 @@ nd6_na_input(m, off, icmp6len) rt->rt_flags &= ~RTF_REJECT; ln->ln_asked = 0; if (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); + return; + + bad: + icmp6stat.icp6s_badna++; + m_freem(m); } /* @@ -788,7 +818,7 @@ nd6_na_input(m, off, icmp6len) void nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ifnet *ifp; - struct in6_addr *daddr6, *taddr6; + const struct in6_addr *daddr6, *taddr6; u_long flags; int tlladdr; /* 1 if include target link-layer address */ struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ @@ -824,6 +854,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) } if (m == NULL) return; + m->m_pkthdr.rcvif = NULL; if (IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; @@ -917,7 +948,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) #ifdef IPSEC /* Don't lookup socket */ - ipsec_setsocket(m, NULL); + (void)ipsec_setsocket(m, NULL); #endif ip6_output(m, NULL, NULL, 0, &im6o, &outif); if (outif) { @@ -935,6 +966,10 @@ nd6_ifptomac(ifp) case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_IEEE1394: +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif return ((caddr_t)(ifp + 1)); break; default: @@ -951,10 +986,11 @@ struct dadq { int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; - struct callout_handle dad_timer; + struct callout dad_timer_ch; }; static struct dadq_head dadq; +static int dad_init = 0; static struct dadq * nd6_dad_find(ifa) @@ -969,6 +1005,24 @@ nd6_dad_find(ifa) return NULL; } +static void +nd6_dad_starttimer(dp, ticks) + struct dadq *dp; + int ticks; +{ + + callout_reset(&dp->dad_timer_ch, ticks, + (void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa); +} + +static void +nd6_dad_stoptimer(dp) + struct dadq *dp; +{ + + callout_stop(&dp->dad_timer_ch); +} + /* * Start Duplicated Address Detection (DAD) for specified interface address. */ @@ -979,7 +1033,6 @@ nd6_dad_start(ifa, tick) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; - static int dad_init = 0; if (!dad_init) { TAILQ_INIT(&dadq); @@ -1026,12 +1079,11 @@ nd6_dad_start(ifa, tick) return; } bzero(dp, sizeof(*dp)); + callout_init(&dp->dad_timer_ch, 0); TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); -#ifdef ND6_DEBUG - log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + ip6_sprintf(&ia->ia_addr.sin6_addr))); /* * Send NS packet for DAD, ip6_dad_count times. @@ -1040,15 +1092,14 @@ nd6_dad_start(ifa, tick) * (re)initialization. */ dp->dad_ifa = ifa; - ifa->ifa_refcnt++; /*just for safety*/ + IFAREF(ifa); /*just for safety*/ dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; if (!tick) { 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); + nd6_dad_starttimer(dp, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { int ntick; @@ -1057,12 +1108,35 @@ nd6_dad_start(ifa, tick) else ntick = *tick + random() % (hz / 2); *tick = ntick; - dp->dad_timer = - timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, - ntick); + nd6_dad_starttimer(dp, ntick); } } +/* + * terminate DAD unconditionally. used for address removals. + */ +void +nd6_dad_stop(ifa) + struct ifaddr *ifa; +{ + struct dadq *dp; + + if (!dad_init) + return; + dp = nd6_dad_find(ifa); + if (!dp) { + /* DAD wasn't started yet */ + return; + } + + nd6_dad_stoptimer(dp); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + IFAFREE(ifa); +} + static void nd6_dad_timer(ifa) struct ifaddr *ifa; @@ -1100,8 +1174,8 @@ nd6_dad_timer(ifa) /* 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)); + nd6log((LOG_INFO, "%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); @@ -1116,9 +1190,8 @@ nd6_dad_timer(ifa) * We have more NS to go. Send NS packet for DAD. */ 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); + nd6_dad_starttimer(dp, + nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { /* * We have transmitted sufficient number of DAD packets. @@ -1177,12 +1250,10 @@ nd6_dad_timer(ifa) */ ia->ia6_flags &= ~IN6_IFF_TENTATIVE; -#ifdef ND6_DEBUG - log(LOG_INFO, + nd6log((LOG_DEBUG, "%s: DAD complete for %s - no duplicates found\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr)); -#endif + ip6_sprintf(&ia->ia_addr.sin6_addr))); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); @@ -1208,18 +1279,16 @@ nd6_dad_duplicated(ifa) return; } - log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " - "%d NA\n", if_name(ifa->ifa_ifp), - ip6_sprintf(&ia->ia_addr.sin6_addr), - dp->dad_ns_icount, dp->dad_na_icount); + log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " + "NS in/out=%d/%d, NA in=%d\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), + dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; /* We are done with DAD, with duplicated address found. (failure) */ - untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa - , dp->dad_timer - ); + nd6_dad_stoptimer(dp); log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); @@ -1264,7 +1333,7 @@ nd6_dad_ns_input(ifa) { struct in6_ifaddr *ia; struct ifnet *ifp; - struct in6_addr *taddr6; + const struct in6_addr *taddr6; struct dadq *dp; int duplicate; @@ -1277,17 +1346,12 @@ nd6_dad_ns_input(ifa) duplicate = 0; dp = nd6_dad_find(ifa); - /* - * If it is from myself, ignore this. - */ - if (ifp && (ifp->if_flags & IFF_LOOPBACK)) - return; - /* Quickhack - completely ignore DAD NS packets */ if (dad_ignore_ns) { - log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for " + nd6log((LOG_INFO, + "nd6_dad_ns_input: ignoring DAD NS packet for " "address %s(%s)\n", ip6_sprintf(taddr6), - if_name(ifa->ifa_ifp)); + if_name(ifa->ifa_ifp))); return; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 258a59c144df..715ccf09785f 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: nd6_rtr.c,v 1.47 2000/08/08 08:58:42 jinmei Exp $ */ +/* $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -40,8 +40,10 @@ #include <sys/socket.h> #include <sys/sockio.h> #include <sys/time.h> +#include <sys/kernel.h> #include <sys/errno.h> #include <sys/syslog.h> +#include <sys/queue.h> #include <net/if.h> #include <net/if_types.h> @@ -51,6 +53,7 @@ #include <netinet/in.h> #include <netinet6/in6_var.h> +#include <netinet6/in6_ifattach.h> #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> @@ -62,34 +65,39 @@ #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 in6_ifaddr *in6_ifadd __P((struct nd_prefix *, + struct in6_addr *)); static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, - struct nd_defrouter *)); + 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 *)); + __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 void nd6_rtmsg __P((int, struct rtentry *)); static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, - struct in6_addrlifetime *lt6, - int update_vltime)); + struct in6_addrlifetime *lt6)); static int rt6_deleteroute __P((struct radix_node *, void *)); extern int nd6_recalc_reachtm_interval; -struct ifnet *nd6_defifp; +static struct ifnet *nd6_defifp; int nd6_defifindex; +int ip6_use_tempaddr = 0; + +int ip6_desync_factor; +u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME; +u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME; +/* + * shorter lifetimes for debugging purposes. +int ip6_temp_preferred_lifetime = 800; +static int ip6_temp_valid_lifetime = 1800; +*/ +int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; + /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program @@ -125,9 +133,11 @@ nd6_rs_input(m, off, icmp6len) /* Sanity checks */ if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } /* @@ -151,7 +161,9 @@ nd6_rs_input(m, off, icmp6len) 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"); + nd6log((LOG_INFO, + "nd6_rs_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -161,16 +173,22 @@ nd6_rs_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_rs_input: lladdrlen mismatch for %s " "(if %d, RS packet %d)\n", - ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); freeit: m_freem(m); + return; + + bad: + icmp6stat.icp6s_badrs++; + m_freem(m); } /* @@ -203,16 +221,18 @@ nd6_ra_input(m, off, icmp6len) goto freeit; if (ip6->ip6_hlim != 255) { - log(LOG_ERR, - "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); - goto freeit; + nd6log((LOG_ERR, + "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n", + ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); + goto bad; } if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { - log(LOG_ERR, + nd6log((LOG_ERR, "nd6_ra_input: src %s is not link-local\n", - ip6_sprintf(&saddr6)); - goto freeit; + ip6_sprintf(&saddr6))); + goto bad; } #ifndef PULLDOWN_TEST @@ -229,7 +249,9 @@ nd6_ra_input(m, off, icmp6len) 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"); + nd6log((LOG_INFO, + "nd6_ra_input: invalid ND option, ignored\n")); + /* nd6_options have incremented stats */ goto freeit; } @@ -267,7 +289,7 @@ nd6_ra_input(m, off, icmp6len) */ if (ndopts.nd_opts_pi) { struct nd_opt_hdr *pt; - struct nd_opt_prefix_info *pi; + struct nd_opt_prefix_info *pi = NULL; struct nd_prefix pr; for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; @@ -279,34 +301,38 @@ nd6_ra_input(m, off, icmp6len) pi = (struct nd_opt_prefix_info *)pt; if (pi->nd_opt_pi_len != 4) { - log(LOG_INFO, "nd6_ra_input: invalid option " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_len); + nd6log((LOG_INFO, + "nd6_ra_input: invalid option " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_len)); continue; } if (128 < pi->nd_opt_pi_prefix_len) { - log(LOG_INFO, "nd6_ra_input: invalid prefix " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_prefix_len); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefix " + "len %d for prefix information option, " + "ignored\n", pi->nd_opt_pi_prefix_len)); continue; } if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { - log(LOG_INFO, "nd6_ra_input: invalid prefix " - "%s, ignored\n", - ip6_sprintf(&pi->nd_opt_pi_prefix)); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefix " + "%s, ignored\n", + ip6_sprintf(&pi->nd_opt_pi_prefix))); continue; } /* aggregatable unicast address, rfc2374 */ if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 && pi->nd_opt_pi_prefix_len != 64) { - log(LOG_INFO, "nd6_ra_input: invalid prefixlen " - "%d for rfc2374 prefix %s, ignored\n", - pi->nd_opt_pi_prefix_len, - ip6_sprintf(&pi->nd_opt_pi_prefix)); + nd6log((LOG_INFO, + "nd6_ra_input: invalid prefixlen " + "%d for rfc2374 prefix %s, ignored\n", + pi->nd_opt_pi_prefix_len, + ip6_sprintf(&pi->nd_opt_pi_prefix))); continue; } @@ -340,9 +366,9 @@ nd6_ra_input(m, off, icmp6len) /* lower bound */ if (mtu < IPV6_MMTU) { - log(LOG_INFO, "nd6_ra_input: bogus mtu option " + nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option " "mtu=%d sent from %s, ignoring\n", - mtu, ip6_sprintf(&ip6->ip6_src)); + mtu, ip6_sprintf(&ip6->ip6_src))); goto skip; } @@ -355,17 +381,17 @@ nd6_ra_input(m, off, icmp6len) if (change) /* in6_maxmtu may change */ in6_setmaxmtu(); } else { - log(LOG_INFO, "nd6_ra_input: bogus mtu " + nd6log((LOG_INFO, "nd6_ra_input: bogus mtu " "mtu=%d sent from %s; " "exceeds maxmtu %d, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src), - ndi->maxmtu); + ndi->maxmtu)); } } else { - log(LOG_INFO, "nd6_ra_input: mtu option " + nd6log((LOG_INFO, "nd6_ra_input: mtu option " "mtu=%d sent from %s; maxmtu unknown, " "ignoring\n", - mtu, ip6_sprintf(&ip6->ip6_src)); + mtu, ip6_sprintf(&ip6->ip6_src))); } } @@ -384,10 +410,11 @@ nd6_ra_input(m, off, icmp6len) } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { - log(LOG_INFO, + nd6log((LOG_INFO, "nd6_ra_input: lladdrlen mismatch for %s " "(if %d, RA packet %d)\n", - ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); + ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); + goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); @@ -400,7 +427,12 @@ nd6_ra_input(m, off, icmp6len) pfxlist_onlink_check(); } -freeit: + freeit: + m_freem(m); + return; + + bad: + icmp6stat.icp6s_badra++; m_freem(m); } @@ -408,10 +440,9 @@ freeit: * 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) +nd6_rtmsg(cmd, rt) int cmd; struct rtentry *rt; { @@ -421,10 +452,12 @@ defrouter_msg(cmd, rt) info.rti_info[RTAX_DST] = rt_key(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); + info.rti_info[RTAX_IFP] = + (struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist); + info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; rt_missmsg(cmd, &info, rt->rt_flags, 0); } -#endif void defrouter_addreq(new) @@ -448,9 +481,7 @@ defrouter_addreq(new) (struct sockaddr *)&gate, (struct sockaddr *)&mask, RTF_GATEWAY, &newrt); if (newrt) { -#ifdef ND6_USE_RTSOCK - defrouter_msg(RTM_ADD, newrt); /* tell user process */ -#endif + nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ newrt->rt_refcnt--; } splx(s); @@ -478,31 +509,27 @@ defrouter_addifreq(ifp) * 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? */ + nd6log((LOG_ERR, /* better error? */ "defrouter_addifreq: failed to find an ifaddr " "to install a route to interface %s\n", - if_name(ifp)); + 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, + error = rtrequest(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr, + (struct sockaddr *)&mask, flags, &newrt); + if (error != 0) { + nd6log((LOG_ERR, "defrouter_addifreq: failed to install a route to " "interface %s (errno = %d)\n", - if_name(ifp), error); + 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 + nd6_rtmsg(RTM_ADD, newrt); newrt->rt_refcnt--; } } @@ -546,9 +573,7 @@ defrouter_delreq(dr, dofree) (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt); if (oldrt) { -#ifdef ND6_USE_RTSOCK - defrouter_msg(RTM_DELETE, oldrt); -#endif + nd6_rtmsg(RTM_DELETE, oldrt); if (oldrt->rt_refcnt <= 0) { /* * XXX: borrowed from the RTM_DELETE case of @@ -669,15 +694,17 @@ defrouter_select() /* * Install a route to the default interface * as default route. + * XXX: we enable this for host only, because + * this may override a default route installed + * a user process (e.g. routing daemon) in a + * router case. */ defrouter_addifreq(nd6_defifp); - } -#ifdef ND6_DEBUG - else /* noisy log? */ - log(LOG_INFO, "defrouter_select: " + } else { + nd6log((LOG_INFO, "defrouter_select: " "there's no default router and no default" - " interface\n"); -#endif + " interface\n")); + } } } @@ -775,8 +802,8 @@ pfxrtr_del(pfr) free(pfr, M_IP6NDP); } -static struct nd_prefix * -prefix_lookup(pr) +struct nd_prefix * +nd6_prefix_lookup(pr) struct nd_prefix *pr; { struct nd_prefix *search; @@ -795,12 +822,12 @@ prefix_lookup(pr) return(search); } -static int -prelist_add(pr, dr) - struct nd_prefix *pr; +int +nd6_prelist_add(pr, dr, newp) + struct nd_prefix *pr, **newp; struct nd_defrouter *dr; { - struct nd_prefix *new; + struct nd_prefix *new = NULL; int i, s; new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); @@ -808,9 +835,10 @@ prelist_add(pr, dr) return ENOMEM; bzero(new, sizeof(*new)); *new = *pr; + if (newp != NULL) + *newp = new; /* initilization */ - new->ndpr_statef_onlink = pr->ndpr_statef_onlink; LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ @@ -818,13 +846,24 @@ prelist_add(pr, dr) new->ndpr_prefix.sin6_addr.s6_addr32[i] &= new->ndpr_mask.s6_addr32[i]; - /* xxx ND_OPT_PI_FLAG_ONLINK processing */ - s = splnet(); /* link ndpr_entry to nd_prefix list */ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); splx(s); + /* ND_OPT_PI_FLAG_ONLINK processing */ + if (new->ndpr_raf_onlink) { + int e; + + if ((e = nd6_prefix_onlink(new)) != 0) { + nd6log((LOG_ERR, "nd6_prelist_add: failed to make " + "the prefix %s/%d on-link on %s (errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* proceed anyway. XXX: is it correct? */ + } + } + if (dr) { pfxrtr_add(new, dr); } @@ -837,12 +876,35 @@ prelist_remove(pr) struct nd_prefix *pr; { struct nd_pfxrouter *pfr, *next; - int s; + int e, s; + + /* make sure to invalidate the prefix until it is really freed. */ + pr->ndpr_vltime = 0; + pr->ndpr_pltime = 0; +#if 0 + /* + * Though these flags are now meaningless, we'd rather keep the value + * not to confuse users when executing "ndp -p". + */ + pr->ndpr_raf_onlink = 0; + pr->ndpr_raf_auto = 0; +#endif + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && + (e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink " + "on %s, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* what should we do? */ + } + + if (pr->ndpr_refcnt > 0) + return; /* notice here? */ s = splnet(); + /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); - splx(s); /* free list of routers that adversed the prefix */ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { @@ -850,30 +912,28 @@ prelist_remove(pr) free(pfr, M_IP6NDP); } + splx(s); + free(pr, M_IP6NDP); pfxlist_onlink_check(); } -/* - * NOTE: We set address lifetime to keep - * address lifetime <= prefix lifetime - * invariant. This is to simplify on-link determination code. - * If onlink determination is udated, this routine may have to be updated too. - */ int prelist_update(new, dr, m) struct nd_prefix *new; struct nd_defrouter *dr; /* may be NULL */ struct mbuf *m; { - struct in6_ifaddr *ia6 = NULL; + struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; + struct ifaddr *ifa; + struct ifnet *ifp = new->ndpr_ifp; struct nd_prefix *pr; int s = splnet(); int error = 0; + int newprefix = 0; int auth; - struct in6_addrlifetime *lt6; - u_char onlink; /* Mobile IPv6 */ + struct in6_addrlifetime lt6_tmp; auth = 0; if (m) { @@ -887,170 +947,259 @@ prelist_update(new, dr, m) #endif } - if ((pr = prefix_lookup(new)) != NULL) { - if (pr->ndpr_ifp != new->ndpr_ifp) { - error = EADDRNOTAVAIL; - goto end; - } - - /* update prefix information */ - pr->ndpr_flags = new->ndpr_flags; - pr->ndpr_vltime = new->ndpr_vltime; - pr->ndpr_pltime = new->ndpr_pltime; - pr->ndpr_preferred = new->ndpr_preferred; - pr->ndpr_expire = new->ndpr_expire; + if ((pr = nd6_prefix_lookup(new)) != NULL) { /* - * RFC 2462 5.5.3 (d) or (e) - * We got a prefix which we have seen in the past. + * nd6_prefix_lookup() ensures that pr and new have the same + * prefix on a same interface. */ - if (!new->ndpr_raf_auto) - goto noautoconf1; - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + /* + * Update prefix information. Note that the on-link (L) bit + * and the autonomous (A) bit should NOT be changed from 1 + * to 0. + */ + if (new->ndpr_raf_onlink == 1) + pr->ndpr_raf_onlink = 1; + if (new->ndpr_raf_auto == 1) + pr->ndpr_raf_auto = 1; + if (new->ndpr_raf_onlink) { + pr->ndpr_vltime = new->ndpr_vltime; + pr->ndpr_pltime = new->ndpr_pltime; + pr->ndpr_preferred = new->ndpr_preferred; + pr->ndpr_expire = new->ndpr_expire; + } - if (ia6 == NULL) { - /* - * Special case: - * (1) We have seen the prefix advertised before, but - * we have never performed autoconfig for this prefix. - * This is because Autonomous bit was 0 previously, or - * autoconfig failed due to some other reasons. - * (2) We have seen the prefix advertised before and - * we have performed autoconfig in the past, but - * we seem to have no interface address right now. - * This is because the interface address have expired. - * - * This prefix is fresh, with respect to autoconfig - * process. - * - * Add an address based on RFC 2462 5.5.3 (d). - */ - ia6 = in6_ifadd(pr->ndpr_ifp, - &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, - new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; - log(LOG_ERR, "prelist_update: failed to add a " - "new address\n"); - goto noautoconf1; + if (new->ndpr_raf_onlink && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { + int e; + + if ((e = nd6_prefix_onlink(pr)) != 0) { + nd6log((LOG_ERR, + "prelist_update: failed to make " + "the prefix %s/%d on-link on %s " + "(errno=%d)\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); + /* proceed anyway. XXX: is it correct? */ } + } - lt6 = &ia6->ia6_lifetime; + if (dr && pfxrtr_lookup(pr, dr) == NULL) + pfxrtr_add(pr, dr); + } else { + struct nd_prefix *newpr = NULL; - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); - } else { -#define TWOHOUR (120*60) - /* - * We have seen the prefix before, and we have added - * interface address in the past. We still have - * the interface address assigned. - * - * update address lifetime based on RFC 2462 - * 5.5.3 (e). - */ - int update = 0; - - lt6 = &ia6->ia6_lifetime; - -#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++; - } + newprefix = 1; - /* 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; - update++; - } else if (auth) { - lt6->ia6t_vltime = new->ndpr_vltime; - update++; - } + if (new->ndpr_vltime == 0) + goto end; + if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) + goto end; - /* jim bound rule is not imposed for pref lifetime */ - lt6->ia6t_pltime = new->ndpr_pltime; -#endif - in6_init_address_ltimes(new, lt6, update); + bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + + error = nd6_prelist_add(new, dr, &newpr); + if (error != 0 || newpr == NULL) { + nd6log((LOG_NOTICE, "prelist_update: " + "nd6_prelist_add failed for %s/%d on %s " + "errno=%d, returnpr=%p\n", + ip6_sprintf(&new->ndpr_prefix.sin6_addr), + new->ndpr_plen, if_name(new->ndpr_ifp), + error, newpr)); + goto end; /* we should just give up in this case. */ } - noautoconf1: + /* + * XXX: from the ND point of view, we can ignore a prefix + * with the on-link bit being zero. However, we need a + * prefix structure for references from autoconfigured + * addresses. Thus, we explicitly make suret that the prefix + * itself expires now. + */ + if (newpr->ndpr_raf_onlink == 0) { + newpr->ndpr_vltime = 0; + newpr->ndpr_pltime = 0; + in6_init_prefix_ltimes(newpr); + } -#if 0 - /* address lifetime expire processing, RFC 2462 5.5.4. */ - if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { - struct in6_ifaddr *ia6; + pr = newpr; + } - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; - } -#endif + /* + * Address autoconfiguration based on Section 5.5.3 of RFC 2462. + * Note that pr must be non NULL at this point. + */ - onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */ + /* 5.5.3 (a). Ignore the prefix without the A bit set. */ + if (!new->ndpr_raf_auto) + goto afteraddrconf; - if (dr && pfxrtr_lookup(pr, dr) == NULL) - pfxrtr_add(pr, dr); + /* + * 5.5.3 (b). the link-local prefix should have been ignored in + * nd6_ra_input. + */ - } else { - int error_tmp; + /* + * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. + * This should have been done in nd6_ra_input. + */ - if (new->ndpr_vltime == 0) goto end; + /* + * 5.5.3 (d). If the prefix advertised does not match the prefix of an + * address already in the list, and the Valid Lifetime is not 0, + * form an address. Note that even a manually configured address + * should reject autoconfiguration of a new address. + */ + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + struct in6_ifaddr *ifa6; + int ifa_plen; + u_int32_t storedlifetime; - bzero(&new->ndpr_addr, sizeof(struct in6_addr)); + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + + /* + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen != new->ndpr_plen || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &new->ndpr_prefix.sin6_addr, + ifa_plen)) + continue; + + if (ia6_match == NULL) /* remember the first one */ + ia6_match = ifa6; + + if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; /* - * RFC 2462 5.5.3 (d) - * We got a fresh prefix. Perform some sanity checks - * and add an interface address by appending interface ID - * to the advertised prefix. + * An already autoconfigured address matched. Now that we + * are sure there is at least one matched address, we can + * proceed to 5.5.3. (e): update the lifetimes according to the + * "two hours" rule and the privacy extension. */ - if (!new->ndpr_raf_auto) - goto noautoconf2; - - ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, - &new->ndpr_addr, new->ndpr_plen); - if (!ia6) { - error = EADDRNOTAVAIL; - log(LOG_ERR, "prelist_update: " - "failed to add a new address\n"); - goto noautoconf2; +#define TWOHOUR (120*60) + lt6_tmp = ifa6->ia6_lifetime; + + storedlifetime = IFA6_IS_INVALID(ifa6) ? 0 : + (lt6_tmp.ia6t_expire - time_second); + + if (TWOHOUR < new->ndpr_vltime || + storedlifetime < new->ndpr_vltime) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } else if (storedlifetime <= TWOHOUR +#if 0 + /* + * This condition is logically redundant, so we just + * omit it. + * See IPng 6712, 6717, and 6721. + */ + && new->ndpr_vltime <= storedlifetime +#endif + ) { + if (auth) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } + } else { + /* + * new->ndpr_vltime <= TWOHOUR && + * TWOHOUR < storedlifetime + */ + lt6_tmp.ia6t_vltime = TWOHOUR; } - /* set onlink bit if an interface route is configured */ - new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; - lt6 = &ia6->ia6_lifetime; + /* The 2 hour rule is not imposed for preferred lifetime. */ + lt6_tmp.ia6t_pltime = new->ndpr_pltime; + + in6_init_address_ltimes(pr, <6_tmp); + + /* + * When adjusting the lifetimes of an existing temporary + * address, only lower the lifetimes. + * RFC 3041 3.3. (1). + * XXX: how should we modify ia6t_[pv]ltime? + */ + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { + if (lt6_tmp.ia6t_expire == 0 || /* no expire */ + lt6_tmp.ia6t_expire > + ifa6->ia6_lifetime.ia6t_expire) { + lt6_tmp.ia6t_expire = + ifa6->ia6_lifetime.ia6t_expire; + } + if (lt6_tmp.ia6t_preferred == 0 || /* no expire */ + lt6_tmp.ia6t_preferred > + ifa6->ia6_lifetime.ia6t_preferred) { + lt6_tmp.ia6t_preferred = + ifa6->ia6_lifetime.ia6t_preferred; + } + } + + ifa6->ia6_lifetime = lt6_tmp; + } + if (ia6_match == NULL && new->ndpr_vltime) { + /* + * No address matched and the valid lifetime is non-zero. + * Create a new address. + */ + if ((ia6 = in6_ifadd(new, NULL)) != NULL) { + /* + * note that we should use pr (not new) for reference. + */ + pr->ndpr_refcnt++; + ia6->ia6_ndpr = pr; + +#if 0 + /* XXXYYY Don't do this, according to Jinmei. */ + pr->ndpr_addr = new->ndpr_addr; +#endif - /* address lifetime <= prefix lifetime */ - lt6->ia6t_vltime = new->ndpr_vltime; - lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6, 1); + /* + * RFC 3041 3.3 (2). + * When a new public address is created as described + * in RFC2462, also create a new temporary address. + * + * RFC 3041 3.5. + * When an interface connects to a new link, a new + * randomized interface identifier should be generated + * immediately together with a new set of temporary + * addresses. Thus, we specifiy 1 as the 2nd arg of + * in6_tmpifadd(). + */ + if (ip6_use_tempaddr) { + int e; + if ((e = in6_tmpifadd(ia6, 1)) != 0) { + nd6log((LOG_NOTICE, "prelist_update: " + "failed to create a temporary " + "address, errno=%d\n", + e)); + } + } - noautoconf2: - error_tmp = prelist_add(new, dr); - error = error_tmp ? error_tmp : error; + /* + * A newly added address might affect the status + * of other addresses, so we check and update it. + * XXX: what if address duplication happens? + */ + pfxlist_onlink_check(); + } else { + /* just set an error. do not bark here. */ + error = EADDRNOTAVAIL; /* XXX: might be unused. */ + } } + afteraddrconf: + end: splx(s); return error; @@ -1061,7 +1210,7 @@ prelist_update(new, dr, m) * detect if a given prefix has a (probably) reachable advertising router. * XXX: lengthy function name... */ -struct nd_pfxrouter * +static struct nd_pfxrouter * find_pfxlist_reachable_router(pr) struct nd_prefix *pr; { @@ -1084,14 +1233,14 @@ find_pfxlist_reachable_router(pr) /* * Check if each prefix in the prefix list has at least one available router - * that advertised the prefix (A router is "available" if its neighbor cache - * entry has reachable or probably reachable). + * that advertised the prefix (a router is "available" if its neighbor cache + * entry is 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 available router, we still regards - * all the prefixes as on-link. This is because we can't tell if all the + * 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 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. */ @@ -1099,55 +1248,275 @@ void pfxlist_onlink_check() { struct nd_prefix *pr; + struct in6_ifaddr *ifa; /* * 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)) + if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) break; } if (pr) { /* * 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. + * Detach prefixes which have no reachable advertising + * router, and attach other prefixes. */ 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); - } + /* XXX: a link-local prefix should never be detached */ + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + /* + * we aren't interested in prefixes without the L bit + * set. + */ + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + find_pfxlist_reachable_router(pr) == NULL) + pr->ndpr_stateflags |= NDPRF_DETACHED; + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + find_pfxlist_reachable_router(pr) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; } + } else { + /* there is no prefix that has a reachable router */ 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); + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) + pr->ndpr_stateflags &= ~NDPRF_DETACHED; + } + } + + /* + * Remove each interface route associated with a (just) detached + * prefix, and reinstall the interface route for a (just) attached + * prefix. Note that all attempt of reinstallation does not + * necessarily success, when a same prefix is shared among multiple + * interfaces. Such cases will be handled in nd6_prefix_onlink, + * so we don't have to care about them. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + int e; + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + continue; + + if (pr->ndpr_raf_onlink == 0) + continue; + + if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + if ((e = nd6_prefix_offlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && + (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && + pr->ndpr_raf_onlink) { + if ((e = nd6_prefix_onlink(pr)) != 0) { + nd6log((LOG_ERR, + "pfxlist_onlink_check: failed to " + "make %s/%d offlink, errno=%d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, e)); + } + } + } + + /* + * Changes on the prefix status might affect address status as well. + * Make sure that all addresses derived from an attached prefix are + * attached, and that all addresses derived from a detached prefix are + * detached. Note, however, that a manually configured address should + * always be attached. + * The precise detection logic is same as the one for prefixes. + */ + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ifa->ia6_ndpr == NULL) { + /* + * This can happen when we first configure the address + * (i.e. the address exists, but the prefix does not). + * XXX: complicated relationships... + */ + continue; + } + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + break; + } + if (ifa) { + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + if (ifa->ia6_ndpr == NULL) /* XXX: see above. */ + continue; + + if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + else + ifa->ia6_flags |= IN6_IFF_DETACHED; } } 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); + for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) { + if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) + continue; + + ifa->ia6_flags &= ~IN6_IFF_DETACHED; + } } } -static void -nd6_detach_prefix(pr) +int +nd6_prefix_onlink(pr) struct nd_prefix *pr; { - struct in6_ifaddr *ia6; - struct sockaddr_in6 sa6, mask6; + struct ifaddr *ifa; + struct ifnet *ifp = pr->ndpr_ifp; + struct sockaddr_in6 mask6; + struct nd_prefix *opr; + u_long rtflags; + int error = 0; + struct rtentry *rt = NULL; + + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_onlink: %s/%d is already on-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); + return(EEXIST)); + } /* - * Delete the interface route associated with the prefix. + * Add the interface route associated with the prefix. Before + * installing the route, check if there's the same prefix on another + * interface, and the prefix has already installed the interface route. + * Although such a configuration is expected to be rare, we explicitly + * allow it. */ + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; + + if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, + pr->ndpr_plen)) + return(0); + } + + /* + * We prefer link-local addresses as the associated interface address. + */ + /* search for a link-local addr */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY| + IN6_IFF_ANYCAST); + if (ifa == NULL) { + /* XXX: freebsd does not have ifa_ifwithaf */ + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + if (ifa->ifa_addr->sa_family == AF_INET6) + break; + } + /* should we care about ia6_flags? */ + } + if (ifa == NULL) { + /* + * This can still happen, when, for example, we receive an RA + * containing a prefix with the L bit set and the A bit clear, + * after removing all IPv6 addresses on the receiving + * interface. This should, of course, be rare though. + */ + nd6log((LOG_NOTICE, + "nd6_prefix_onlink: failed to find any ifaddr" + " to add route for a prefix(%s/%d) on %s\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(ifp))); + return(0); + } + + /* + * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. + * ifa->ifa_rtrequest = nd6_rtrequest; + */ + bzero(&mask6, sizeof(mask6)); + mask6.sin6_len = sizeof(mask6); + mask6.sin6_addr = pr->ndpr_mask; + rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP; + if (nd6_need_cache(ifp)) { + /* explicitly set in case ifa_flags does not set the flag. */ + rtflags |= RTF_CLONING; + } else { + /* + * explicitly clear the cloning bit in case ifa_flags sets it. + */ + rtflags &= ~RTF_CLONING; + } + error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, + ifa->ifa_addr, (struct sockaddr *)&mask6, + rtflags, &rt); + if (error == 0) { + if (rt != NULL) /* this should be non NULL, though */ + nd6_rtmsg(RTM_ADD, rt); + pr->ndpr_stateflags |= NDPRF_ONLINK; + } + else { + nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" + " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " + "errno = %d\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(ifp), + ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), + ip6_sprintf(&mask6.sin6_addr), rtflags, error)); + } + + if (rt != NULL) + rt->rt_refcnt--; + + return(error); +} + +int +nd6_prefix_offlink(pr) + struct nd_prefix *pr; +{ + int error = 0; + struct ifnet *ifp = pr->ndpr_ifp; + struct nd_prefix *opr; + struct sockaddr_in6 sa6, mask6; + struct rtentry *rt = NULL; + + /* sanity check */ + if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { + nd6log((LOG_ERR, + "nd6_prefix_offlink: %s/%d is already off-link\n", + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen)); + return(EEXIST); + } + bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); @@ -1157,103 +1526,113 @@ nd6_detach_prefix(pr) mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(sa6); bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); - { - int e; + error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, + (struct sockaddr *)&mask6, 0, &rt); + if (error == 0) { + pr->ndpr_stateflags &= ~NDPRF_ONLINK; - e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, - (struct sockaddr *)&mask6, 0, NULL); - if (e) { - log(LOG_ERR, - "nd6_detach_prefix: failed to delete route: " - "%s/%d (errno = %d)\n", - ip6_sprintf(&sa6.sin6_addr), - pr->ndpr_plen, - e); - } - } + /* report the route deletion to the routing socket. */ + if (rt != NULL) + nd6_rtmsg(RTM_DELETE, rt); - /* - * Mark the address derived from the prefix detached so that - * it won't be used as a source address for a new connection. - */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) - ia6->ia6_flags |= IN6_IFF_DETACHED; -} + /* + * There might be the same prefix on another interface, + * the prefix which could not be on-link just because we have + * the interface route (see comments in nd6_prefix_onlink). + * If there's one, try to make the prefix on-link on the + * interface. + */ + for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + if (opr == pr) + continue; -static void -nd6_attach_prefix(pr) - struct nd_prefix *pr; -{ - struct ifaddr *ifa; - struct in6_ifaddr *ia6; + if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) + continue; - /* - * Add the interface route associated with the prefix(if necessary) - * Should we consider if the L bit is set in pr->ndpr_flags? - */ - ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, - pr->ndpr_ifp); - if (ifa == NULL) { - log(LOG_ERR, - "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); + /* + * KAME specific: detached prefixes should not be + * on-link. + */ + if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) + continue; + + if (opr->ndpr_plen == pr->ndpr_plen && + in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, + &opr->ndpr_prefix.sin6_addr, + pr->ndpr_plen)) { + int e; + + if ((e = nd6_prefix_onlink(opr)) != 0) { + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to " + "recover a prefix %s/%d from %s " + "to %s (errno = %d)\n", + ip6_sprintf(&opr->ndpr_prefix.sin6_addr), + opr->ndpr_plen, if_name(ifp), + if_name(opr->ndpr_ifp), e)); + } + } + } } else { - int e; - struct sockaddr_in6 mask6; - - bzero(&mask6, sizeof(mask6)); - mask6.sin6_family = AF_INET6; - mask6.sin6_len = sizeof(mask6); - mask6.sin6_addr = pr->ndpr_mask; - e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, - ifa->ifa_addr, (struct sockaddr *)&mask6, - ifa->ifa_flags, NULL); - if (e == 0) - pr->ndpr_statef_onlink = 1; - else { - log(LOG_ERR, - "nd6_attach_prefix: failed to add route for" - " a prefix(%s/%d), errno = %d\n", - ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e); - } + /* XXX: can we still set the NDPRF_ONLINK flag? */ + nd6log((LOG_ERR, + "nd6_prefix_offlink: failed to delete route: " + "%s/%d on %s (errno = %d)\n", + ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, if_name(ifp), + error)); } - /* - * Now the address derived from the prefix can be used as a source - * for a new connection, so clear the detached flag. - */ - if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) - ia6 = NULL; - else - ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); - if (ia6) { - ia6->ia6_flags &= ~IN6_IFF_DETACHED; - if (pr->ndpr_statef_onlink) - ia6->ia_flags |= IFA_ROUTE; + if (rt != NULL) { + if (rt->rt_refcnt <= 0) { + /* XXX: we should free the entry ourselves. */ + rt->rt_refcnt++; + rtfree(rt); + } } + + return(error); } static struct in6_ifaddr * -in6_ifadd(ifp, in6, addr, prefixlen) - struct ifnet *ifp; - struct in6_addr *in6; - struct in6_addr *addr; - int prefixlen; /* prefix len of the new prefix in "in6" */ +in6_ifadd(pr, ifid) + struct nd_prefix *pr; + struct in6_addr *ifid; /* Mobile IPv6 addition */ { + struct ifnet *ifp = pr->ndpr_ifp; struct ifaddr *ifa; - struct in6_ifaddr *ia, *ib, *oia; - int s, error; + struct in6_aliasreq ifra; + struct in6_ifaddr *ia, *ib; + int error, plen0; struct in6_addr mask; + int prefixlen = pr->ndpr_plen; in6_len2mask(&mask, prefixlen); - /* find link-local address (will be interface ID) */ + /* + * find a link-local address (will be interface ID). + * Is it really mandatory? Theoretically, a global or a site-local + * address can be configured without a link-local address, if we + * have a unique interface identifier... + * + * it is not mandatory to have a link-local address, we can generate + * interface identifier on the fly. we do this because: + * (1) it should be the easiest way to find interface identifier. + * (2) RFC2462 5.4 suggesting the use of the same interface identifier + * for multiple addresses on a single interface, and possible shortcut + * of DAD. we omitted DAD for this reason in the past. + * (3) a user can prevent autoconfiguration of global address + * by removing link-local address by hand (this is partly because we + * don't have other way to control the use of IPv6 on a interface. + * this has been our design choice - cf. NRL's "ifconfig auto"). + * (4) it is easier to manage when an interface has addresses + * with the same interface identifier, than to have multiple addresses + * with different interface identifiers. + * + * Mobile IPv6 addition: allow for caller to specify a wished interface + * ID. This is to not break connections when moving addresses between + * interfaces. + */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; @@ -1269,204 +1648,199 @@ in6_ifadd(ifp, in6, addr, prefixlen) #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" - "(prefix=%d ifid=%d)\n", if_name(ifp), - prefixlen, - 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)); + plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); + if (prefixlen != plen0) { + nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s " + "(prefix=%d ifid=%d)\n", + if_name(ifp), prefixlen, 128 - plen0)); return NULL; } /* make ifaddr */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT); - if (ia == NULL) { - printf("ENOBUFS in in6_ifadd %d\n", __LINE__); - return NULL; - } - 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; - - /* link to in6_ifaddr */ - if ((oia = in6_ifaddr) != NULL) { - for( ; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } 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 */ - 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); - ia->ia_addr.sin6_family = AF_INET6; + bzero(&ifra, sizeof(ifra)); + /* + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. + */ + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); /* prefix */ - bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); - ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; - ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; - ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; - ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - /* interface ID */ - ia->ia_addr.sin6_addr.s6_addr32[0] - |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); - ia->ia_addr.sin6_addr.s6_addr32[1] - |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); - ia->ia_addr.sin6_addr.s6_addr32[2] - |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); - ia->ia_addr.sin6_addr.s6_addr32[3] - |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); - - /* new prefix */ - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - bcopy(&mask, &ia->ia_prefixmask.sin6_addr, - sizeof(ia->ia_prefixmask.sin6_addr)); - - /* same routine */ - ia->ia_ifa.ifa_rtrequest = - (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - ia->ia_ifa.ifa_metric = ifp->if_metric; - - /* add interface route */ - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { - log(LOG_NOTICE, "in6_ifadd: failed to add an interface route " - "for %s/%d on %s, errno = %d\n", - ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, - if_name(ifp), error); - } else - ia->ia_flags |= IFA_ROUTE; + bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, + sizeof(ifra.ifra_addr.sin6_addr)); + ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; + ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; + ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; + ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - *addr = ia->ia_addr.sin6_addr; + /* interface ID */ + if (ifid == NULL || IN6_IS_ADDR_UNSPECIFIED(ifid)) + ifid = &ib->ia_addr.sin6_addr; + ifra.ifra_addr.sin6_addr.s6_addr32[0] + |= (ifid->s6_addr32[0] & ~mask.s6_addr32[0]); + ifra.ifra_addr.sin6_addr.s6_addr32[1] + |= (ifid->s6_addr32[1] & ~mask.s6_addr32[1]); + ifra.ifra_addr.sin6_addr.s6_addr32[2] + |= (ifid->s6_addr32[2] & ~mask.s6_addr32[2]); + ifra.ifra_addr.sin6_addr.s6_addr32[3] + |= (ifid->s6_addr32[3] & ~mask.s6_addr32[3]); + + /* new prefix mask. */ + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, + sizeof(ifra.ifra_prefixmask.sin6_addr)); - if (ifp->if_flags & IFF_MULTICAST) { - int error; /* not used */ - struct in6_addr sol6; + /* + * lifetime. + * XXX: in6_init_address_ltimes would override these values later. + * We should reconsider this logic. + */ + ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; + ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; - /* join solicited node multicast address */ - bzero(&sol6, sizeof(sol6)); - sol6.s6_addr16[0] = htons(0xff02); - sol6.s6_addr16[1] = htons(ifp->if_index); - sol6.s6_addr32[1] = 0; - sol6.s6_addr32[2] = htonl(1); - sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - sol6.s6_addr8[12] = 0xff; - (void)in6_addmulti(&sol6, ifp, &error); - } + /* XXX: scope zone ID? */ - ia->ia6_flags |= IN6_IFF_TENTATIVE; + ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ + /* + * temporarily set the nopfx flag to avoid conflict. + * XXX: we should reconsider the entire mechanism about prefix + * manipulation. + */ + ifra.ifra_flags |= IN6_IFF_NOPFX; /* - * To make the interface up. Only AF_INET6 in ia is used... + * keep the new address, regardless of the result of in6_update_ifa. + * XXX: this address is now meaningless. + * We should reconsider its role. */ - s = splimp(); - if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) { - splx(s); - return NULL; + pr->ndpr_addr = ifra.ifra_addr.sin6_addr; + + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + nd6log((LOG_ERR, + "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp), + error)); + return(NULL); /* ifaddr must not have been allocated. */ } - splx(s); - /* Perform DAD, if needed. */ - nd6_dad_start((struct ifaddr *)ia, NULL); + ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - return ia; + return(ia); /* this must NOT be NULL. */ } int -in6_ifdel(ifp, in6) - struct ifnet *ifp; - struct in6_addr *in6; +in6_tmpifadd(ia0, forcegen) + const struct in6_ifaddr *ia0; /* corresponding public address */ { - struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; - struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; + struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; + struct in6_ifaddr *newia; + struct in6_aliasreq ifra; + int i, error; + int trylimit = 3; /* XXX: adhoc value */ + u_int32_t randid[2]; + time_t vltime0, pltime0; + + bzero(&ifra, sizeof(ifra)); + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr = ia0->ia_addr; + /* copy prefix mask */ + ifra.ifra_prefixmask = ia0->ia_prefixmask; + /* clear the old IFID */ + for (i = 0; i < 4; i++) { + ifra.ifra_addr.sin6_addr.s6_addr32[i] + &= ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; + } - if (!ifp) - return -1; + again: + in6_get_tmpifid(ifp, (u_int8_t *)randid, + (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], + forcegen); + ifra.ifra_addr.sin6_addr.s6_addr32[2] + |= (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2])); + ifra.ifra_addr.sin6_addr.s6_addr32[3] + |= (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3])); - ia = in6ifa_ifpwithaddr(ifp, in6); - if (!ia) - return -1; + /* + * If by chance the new temporary address is the same as an address + * already assigned to the interface, generate a new randomized + * interface identifier and repeat this step. + * RFC 3041 3.3 (4). + */ + if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) { + if (trylimit-- == 0) { + nd6log((LOG_NOTICE, "in6_tmpifadd: failed to find " + "a unique random IFID\n")); + return(EEXIST); + } + forcegen = 1; + goto again; + } - 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); - } - - if (ia->ia_flags & IFA_ROUTE) { - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; - } - - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - IFAFREE(&ia->ia_ifa); - - /* lladdr is never deleted */ - 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 - return -1; + /* + * The Valid Lifetime is the lower of the Valid Lifetime of the + * public address or TEMP_VALID_LIFETIME. + * The Preferred Lifetime is the lower of the Preferred Lifetime + * of the public address or TEMP_PREFERRED_LIFETIME - + * DESYNC_FACTOR. + */ + if (ia0->ia6_lifetime.ia6t_expire != 0) { + vltime0 = IFA6_IS_INVALID(ia0) ? 0 : + (ia0->ia6_lifetime.ia6t_expire - time_second); + if (vltime0 > ip6_temp_valid_lifetime) + vltime0 = ip6_temp_valid_lifetime; + } else + vltime0 = ip6_temp_valid_lifetime; + if (ia0->ia6_lifetime.ia6t_preferred != 0) { + pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : + (ia0->ia6_lifetime.ia6t_preferred - time_second); + if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){ + pltime0 = ip6_temp_preferred_lifetime - + ip6_desync_factor; + } + } else + pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor; + ifra.ifra_lifetime.ia6t_vltime = vltime0; + ifra.ifra_lifetime.ia6t_pltime = pltime0; + + /* + * A temporary address is created only if this calculated Preferred + * Lifetime is greater than REGEN_ADVANCE time units. + */ + if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance) + return(0); + + /* XXX: scope zone ID? */ + + ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); + + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) + return(error); + + newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); + if (newia == NULL) { /* XXX: can it happen? */ + nd6log((LOG_ERR, + "in6_tmpifadd: ifa update succeeded, but we got " + "no ifaddr\n")); + return(EINVAL); /* XXX */ } + newia->ia6_ndpr = ia0->ia6_ndpr; + newia->ia6_ndpr->ndpr_refcnt++; - IFAFREE((&oia->ia_ifa)); -/* xxx - rtrequest(RTM_DELETE, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)0 - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP|RTF_CLONING, - (struct rtentry **)0); -*/ - return 0; -} + return(0); +} int in6_init_prefix_ltimes(struct nd_prefix *ndpr) { - - /* check if preferred lifetime > valid lifetime */ + /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { - log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" + nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" "(%d) is greater than valid lifetime(%d)\n", - (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); + (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime)); return (EINVAL); } if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) @@ -1482,24 +1856,15 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr) } static void -in6_init_address_ltimes(struct nd_prefix *new, - struct in6_addrlifetime *lt6, - int update_vltime) +in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) { - /* 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_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; } /* init ia6t_preferred */ @@ -1509,10 +1874,6 @@ in6_init_address_ltimes(struct nd_prefix *new, lt6->ia6t_preferred = time_second; lt6->ia6t_preferred += lt6->ia6t_pltime; } - /* Ensure addr lifetime <= prefix lifetime. */ - if (new->ndpr_preferred && lt6->ia6t_preferred - && new->ndpr_preferred < lt6->ia6t_preferred) - lt6->ia6t_preferred = new->ndpr_preferred; } /* @@ -1522,8 +1883,8 @@ in6_init_address_ltimes(struct nd_prefix *new, */ void rt6_flush(gateway, ifp) - struct in6_addr *gateway; - struct ifnet *ifp; + struct in6_addr *gateway; + struct ifnet *ifp; { struct radix_node_head *rnh = rt_tables[AF_INET6]; int s = splnet(); @@ -1556,6 +1917,14 @@ rt6_deleteroute(rn, arg) return(0); /* + * Do not delete a static route. + * XXX: this seems to be a bit ad-hoc. Should we consider the + * 'cloned' bit instead? + */ + if ((rt->rt_flags & RTF_STATIC) != 0) + return(0); + + /* * We delete only host route. This means, in particular, we don't * delete default route. */ diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 42f38fd5d62f..eda8bfa5eb73 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -65,6 +65,7 @@ */ #include "opt_ipsec.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/malloc.h> @@ -94,6 +95,7 @@ #ifdef ENABLE_DEFAULT_SCOPE #include <netinet6/scope6_var.h> #endif +#include <netinet6/raw_ip6.h> #ifdef IPSEC #include <netinet6/ipsec.h> @@ -103,6 +105,9 @@ #include <machine/stdarg.h> #include "faith.h" +#if defined(NFAITH) && 0 < NFAITH +#include <net/if_faith.h> +#endif #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) @@ -116,6 +121,8 @@ extern struct inpcbinfo ripcbinfo; extern u_long rip_sendspace; extern u_long rip_recvspace; +struct rip6stat rip6stat; + /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with @@ -130,18 +137,19 @@ rip6_input(mp, offp, proto) register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register struct inpcb *in6p; struct inpcb *last = 0; - struct mbuf *opts = 0; + struct mbuf *opts = NULL; struct sockaddr_in6 rip6src; + rip6stat.rip6s_ipackets++; + #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif) { - if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } + if (faithprefix(&ip6->ip6_dst)) { + /* XXX send icmp6 host/port unreach? */ + m_freem(m); + return IPPROTO_DONE; } #endif + init_sin6(&rip6src, m); /* general init */ LIST_FOREACH(in6p, &ripcb, inp_list) { @@ -156,14 +164,27 @@ rip6_input(mp, offp, proto) if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; - if (in6p->in6p_cksum != -1 - && in6_cksum(m, ip6->ip6_nxt, *offp, - m->m_pkthdr.len - *offp)) { - /* XXX bark something */ - continue; + if (in6p->in6p_cksum != -1) { + rip6stat.rip6s_isum++; + if (in6_cksum(m, ip6->ip6_nxt, *offp, + m->m_pkthdr.len - *offp)) { + rip6stat.rip6s_badsum++; + continue; + } } if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); + +#ifdef IPSEC + /* + * Check AH/ESP integrity. + */ + if (n && ipsec6_in_reject_so(n, last->inp_socket)) { + m_freem(n); + ipsec6stat.in_polvio++; + /* do not inject data into pcb */ + } else +#endif /*IPSEC*/ if (n) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) @@ -173,10 +194,10 @@ rip6_input(mp, offp, proto) if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts) == 0) { - /* should notify about lost packet */ m_freem(n); if (opts) m_freem(opts); + rip6stat.rip6s_fullsock++; } else sorwakeup(last->in6p_socket); opts = NULL; @@ -184,6 +205,17 @@ rip6_input(mp, offp, proto) } last = in6p; } +#ifdef IPSEC + /* + * Check AH/ESP integrity. + */ + if (last && ipsec6_in_reject_so(m, last->inp_socket)) { + m_freem(m); + ipsec6stat.in_polvio++; + ip6stat.ip6s_delivered--; + /* do not inject data into pcb */ + } else +#endif /*IPSEC*/ if (last) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) @@ -195,9 +227,13 @@ rip6_input(mp, offp, proto) m_freem(m); if (opts) m_freem(opts); + rip6stat.rip6s_fullsock++; } else sorwakeup(last->in6p_socket); } else { + rip6stat.rip6s_nosock++; + if (m->m_flags & M_MCAST) + rip6stat.rip6s_nosockmcast++; if (proto == IPPROTO_NONE) m_freem(m); else { @@ -217,10 +253,11 @@ rip6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off = 0; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; void (*notify) __P((struct inpcb *, int)) = in6_rtchange; if (sa->sa_family != AF_INET6 || @@ -238,37 +275,19 @@ rip6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* 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); + (void) in6_pcbnotify(&ripcb, sa, 0, (struct sockaddr *)sa6_src, + 0, cmd, notify); } /* @@ -311,7 +330,7 @@ rip6_output(m, va_alist) priv = 1; dst = &dstsock->sin6_addr; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) goto bad; optp = &opt; } else @@ -431,7 +450,10 @@ rip6_output(m, va_alist) } #ifdef IPSEC - ipsec_setsocket(m, so); + if (ipsec_setsocket(m, so) != 0) { + error = ENOBUFS; + goto bad; + } #endif /*IPSEC*/ error = ip6_output(m, optp, &in6p->in6p_route, 0, @@ -440,7 +462,8 @@ rip6_output(m, va_alist) if (oifp) icmp6_ifoutstat_inc(oifp, type, code); icmp6stat.icp6s_outhist[type]++; - } + } else + rip6stat.rip6s_opackets++; goto freectl; @@ -451,8 +474,11 @@ rip6_output(m, va_alist) freectl: if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) RTFREE(optp->ip6po_route.ro_rt); - if (control) + if (control) { + if (optp == &opt) + ip6_clearpktopts(optp, 0, -1); m_freem(control); + } return(error); } diff --git a/sys/netinet6/raw_ip6.h b/sys/netinet6/raw_ip6.h new file mode 100644 index 000000000000..cfb390b4cc1d --- /dev/null +++ b/sys/netinet6/raw_ip6.h @@ -0,0 +1,54 @@ +/* $FreeBSD$ */ +/* $KAME: raw_ip6.h,v 1.2 2001/05/27 13:28:35 itojun Exp $ */ + +/* + * Copyright (C) 2001 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_RAW_IP6_H_ +#define _NETINET6_RAW_IP6_H_ + +/* + * ICMPv6 stat is counted separately. see netinet/icmp6.h + */ +struct rip6stat { + u_quad_t rip6s_ipackets; /* total input packets */ + u_quad_t rip6s_isum; /* input checksum computations */ + u_quad_t rip6s_badsum; /* of above, checksum error */ + u_quad_t rip6s_nosock; /* no matching socket */ + u_quad_t rip6s_nosockmcast; /* of above, arrived as multicast */ + u_quad_t rip6s_fullsock; /* not delivered, input socket full */ + + u_quad_t rip6s_opackets; /* total output packets */ +}; + +#ifdef _KERNEL +extern struct rip6stat rip6stat; +#endif + +#endif diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c index 3cd95a10a959..807c6ad36590 100644 --- a/sys/netinet6/route6.c +++ b/sys/netinet6/route6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: route6.c,v 1.15 2000/06/23 16:18:20 itojun Exp $ */ +/* $KAME: route6.c,v 1.24 2001/03/14 03:07:05 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -37,6 +37,7 @@ #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/systm.h> +#include <sys/queue.h> #include <net/if.h> @@ -55,10 +56,22 @@ route6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; /* proto is unused */ { - register struct ip6_hdr *ip6; - register struct mbuf *m = *mp; - register struct ip6_rthdr *rh; + struct ip6_hdr *ip6; + struct mbuf *m = *mp; + struct ip6_rthdr *rh; int off = *offp, rhlen; + struct mbuf *n; + + n = ip6_findaux(m); + if (n) { + struct ip6aux *ip6a = mtod(n, struct ip6aux *); + /* XXX reject home-address option before rthdr */ + if (ip6a->ip6a_flags & IP6A_SWAP) { + ip6stat.ip6s_badoptions++; + m_freem(m); + return IPPROTO_DONE; + } + } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); @@ -119,6 +132,9 @@ route6_input(mp, offp, proto) /* * Type0 routing header processing + * + * RFC2292 backward compatibility warning: no support for strict/loose bitmap, + * as it was dropped between RFC1883 and RFC2460. */ static int ip6_rthdr0(m, ip6, rh0) @@ -176,7 +192,7 @@ ip6_rthdr0(m, ip6, rh0) 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)) { + IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { ip6stat.ip6s_badoptions++; m_freem(m); return(-1); diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c index 43a6fb722afa..09bba0c4a4b0 100644 --- a/sys/netinet6/scope6.c +++ b/sys/netinet6/scope6.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei Exp $ */ +/* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. @@ -35,6 +35,7 @@ #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/systm.h> +#include <sys/queue.h> #include <net/route.h> #include <net/if.h> diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index ee050190c2f7..fd4d6f3f1cd6 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: udp6_output.c,v 1.14 2000/06/13 10:31:23 itojun Exp $ */ +/* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -69,6 +69,7 @@ #include "opt_inet.h" #include <sys/param.h> +#include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -120,22 +121,22 @@ int udp6_output(in6p, m, addr6, control, p) - register struct in6pcb *in6p; - register struct mbuf *m; + struct in6pcb *in6p; + struct mbuf *m; struct mbuf *control; struct sockaddr *addr6; struct proc *p; { - register u_int32_t ulen = m->m_pkthdr.len; + 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; + 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 af = AF_INET6, hlen = sizeof(struct ip6_hdr); int flags; struct sockaddr_in6 tmp; @@ -143,7 +144,7 @@ udp6_output(in6p, m, addr6, control, p) if (p && !suser(p)) priv = 1; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) goto release; in6p->in6p_outputopts = &opt; } @@ -164,6 +165,7 @@ udp6_output(in6p, m, addr6, control, p) } if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + /* how about ::ffff:0.0.0.0 case? */ error = EISCONN; goto release; } @@ -175,6 +177,24 @@ udp6_output(in6p, m, addr6, control, p) faddr = &sin6->sin6_addr; fport = sin6->sin6_port; /* allow 0 port */ + if (IN6_IS_ADDR_V4MAPPED(faddr)) { + if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { + /* + * I believe we should explicitly discard the + * packet when mapped addresses are disabled, + * rather than send the packet as an IPv6 one. + * If we chose the latter approach, the packet + * might be sent out on the wire based on the + * default route, the situation which we'd + * probably want to avoid. + * (20010421 jinmei@kame.net) + */ + error = EINVAL; + goto release; + } else + af = AF_INET; + } + /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { error = EINVAL; @@ -187,7 +207,7 @@ udp6_output(in6p, m, addr6, control, p) &in6p->in6p_route, &in6p->in6p_laddr, &error); } else - laddr = &in6p->in6p_laddr; /*XXX*/ + laddr = &in6p->in6p_laddr; /* XXX */ if (laddr == NULL) { if (error == 0) error = EADDRNOTAVAIL; @@ -201,18 +221,29 @@ udp6_output(in6p, m, addr6, control, p) error = ENOTCONN; goto release; } + if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_faddr)) { + if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { + /* + * XXX: this case would happen when the + * application sets the V6ONLY flag after + * connecting the foreign address. + * Such applications should be fixed, + * so we bark here. + */ + log(LOG_INFO, "udp6_output: IPV6_V6ONLY " + "option was set for a connected socket\n"); + error = EINVAL; + goto release; + } else + af = AF_INET; + } 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; + if (af == AF_INET) hlen = sizeof(struct ip); - } /* * Calculate data length and get a mbuf @@ -261,10 +292,13 @@ udp6_output(in6p, m, addr6, control, p) udp6stat.udp6s_opackets++; #ifdef IPSEC - ipsec_setsocket(m, in6p->in6p_socket); + if (ipsec_setsocket(m, in6p->in6p_socket) != 0) { + error = ENOBUFS; + goto release; + } #endif /*IPSEC*/ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - flags, in6p->in6p_moptions, NULL); + flags, in6p->in6p_moptions, NULL); break; case AF_INET: error = EAFNOSUPPORT; @@ -277,6 +311,7 @@ release: releaseopt: if (control) { + ip6_clearpktopts(in6p->in6p_outputopts, 0, -1); in6p->in6p_outputopts = stickyopt; m_freem(control); } diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index ca9ce2fe25a8..bb5a38a466ad 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $KAME: udp6_usrreq.c,v 1.17 2000/10/13 17:46:21 itojun Exp $ */ +/* $KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -107,6 +107,9 @@ #endif /*IPSEC*/ #include "faith.h" +#if defined(NFAITH) && NFAITH > 0 +#include <net/if_faith.h> +#endif /* * UDP protocol inplementation. @@ -149,25 +152,25 @@ udp6_input(mp, offp, proto) register struct ip6_hdr *ip6; register struct udphdr *uh; register struct inpcb *in6p; - struct mbuf *opts = 0; + struct mbuf *opts = NULL; int off = *offp; int plen, ulen; struct sockaddr_in6 udp_in6; + IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + #if defined(NFAITH) && 0 < NFAITH - if (m->m_pkthdr.rcvif) { - if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } + if (faithprefix(&ip6->ip6_dst)) { + /* XXX send icmp6 host/port unreach? */ + m_freem(m); + return IPPROTO_DONE; } #endif - udpstat.udps_ipackets++; - IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); + udpstat.udps_ipackets++; - ip6 = mtod(m, struct ip6_hdr *); plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); uh = (struct udphdr *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); @@ -274,6 +277,7 @@ udp6_input(mp, offp, proto) || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, n); + m_adj(n, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, @@ -284,7 +288,7 @@ udp6_input(mp, offp, proto) udpstat.udps_fullsock++; } else sorwakeup(last->in6p_socket); - opts = 0; + opts = NULL; } } last = in6p; @@ -401,13 +405,17 @@ udp6_ctlinput(cmd, sa, d) struct sockaddr *sa; void *d; { - register struct udphdr *uhp; struct udphdr uh; - struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off = 0; + struct ip6ctlparam *ip6cp = NULL; + const struct sockaddr_in6 *sa6_src = NULL; void (*notify) __P((struct inpcb *, int)) = udp_notify; + struct udp_portonly { + u_int16_t uh_sport; + u_int16_t uh_dport; + } *uhp; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) @@ -424,51 +432,35 @@ udp6_ctlinput(cmd, sa, d) /* if the parameter is from icmp6, decode it. */ if (d != NULL) { - struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + sa6_src = ip6cp->ip6c_src; } else { m = NULL; ip6 = NULL; + sa6_src = &sa6_any; } - /* 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); /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(uh)) + if (m->m_pkthdr.len < off + sizeof(*uhp)) return; - if (m->m_len < off + sizeof(uh)) { - /* - * this should be rare case, - * so we compromise on this copy... - */ - m_copydata(m, off, sizeof(uh), (caddr_t)&uh); - uhp = &uh; - } else - uhp = (struct udphdr *)(mtod(m, caddr_t) + off); - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, - uhp->uh_dport, &s, - uhp->uh_sport, cmd, notify); + bzero(&uh, sizeof(uh)); + m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); + + (void) in6_pcbnotify(&udb, sa, uh.uh_dport, ip6cp->ip6c_src, + uh.uh_sport, cmd, notify); } else - (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, notify); + (void) in6_pcbnotify(&udb, sa, 0, (struct sockaddr *)&sa6_src, + 0, cmd, notify); } static int @@ -583,7 +575,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; @@ -618,7 +610,8 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) inp = sotoinpcb(so); if (inp == 0) return EINVAL; - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; @@ -644,15 +637,12 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 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; - inp->inp_vflag |= INP_IPV6; + if (ip6_mapped_addr_on) { /* should be non mapped addr */ + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; + } soisconnected(so); } return error; |