summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authorume <ume@FreeBSD.org>2001-06-11 12:39:29 +0000
committerume <ume@FreeBSD.org>2001-06-11 12:39:29 +0000
commit832f8d224926758a9ae0b23a6b45353e44fbc87a (patch)
treea79fc7ad2b97862c4a404f352f0211ad93a7b5f1 /sys/netinet6
parent2693854b01a52b0395a91322aa3edf926bddff38 (diff)
downloadFreeBSD-src-832f8d224926758a9ae0b23a6b45353e44fbc87a.zip
FreeBSD-src-832f8d224926758a9ae0b23a6b45353e44fbc87a.tar.gz
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
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/ah.h14
-rw-r--r--sys/netinet6/ah6.h6
-rw-r--r--sys/netinet6/ah_core.c547
-rw-r--r--sys/netinet6/ah_input.c204
-rw-r--r--sys/netinet6/ah_output.c42
-rw-r--r--sys/netinet6/dest6.c27
-rw-r--r--sys/netinet6/esp.h34
-rw-r--r--sys/netinet6/esp6.h4
-rw-r--r--sys/netinet6/esp_core.c1581
-rw-r--r--sys/netinet6/esp_input.c264
-rw-r--r--sys/netinet6/esp_output.c184
-rw-r--r--sys/netinet6/esp_rijndael.c116
-rw-r--r--sys/netinet6/esp_rijndael.h39
-rw-r--r--sys/netinet6/frag6.c27
-rw-r--r--sys/netinet6/icmp6.c1041
-rw-r--r--sys/netinet6/in6.c1527
-rw-r--r--sys/netinet6/in6.h183
-rw-r--r--sys/netinet6/in6_cksum.c10
-rw-r--r--sys/netinet6/in6_gif.c73
-rw-r--r--sys/netinet6/in6_ifattach.c750
-rw-r--r--sys/netinet6/in6_ifattach.h5
-rw-r--r--sys/netinet6/in6_pcb.c145
-rw-r--r--sys/netinet6/in6_pcb.h9
-rw-r--r--sys/netinet6/in6_prefix.c45
-rw-r--r--sys/netinet6/in6_prefix.h5
-rw-r--r--sys/netinet6/in6_proto.c240
-rw-r--r--sys/netinet6/in6_rmx.c19
-rw-r--r--sys/netinet6/in6_src.c37
-rw-r--r--sys/netinet6/in6_var.h44
-rw-r--r--sys/netinet6/ip6_ecn.h4
-rw-r--r--sys/netinet6/ip6_forward.c91
-rw-r--r--sys/netinet6/ip6_fw.c15
-rw-r--r--sys/netinet6/ip6_fw.h2
-rw-r--r--sys/netinet6/ip6_input.c833
-rw-r--r--sys/netinet6/ip6_mroute.c246
-rw-r--r--sys/netinet6/ip6_mroute.h6
-rw-r--r--sys/netinet6/ip6_output.c800
-rw-r--r--sys/netinet6/ip6_var.h88
-rw-r--r--sys/netinet6/ip6protosw.h24
-rw-r--r--sys/netinet6/ipcomp.h8
-rw-r--r--sys/netinet6/ipcomp6.h2
-rw-r--r--sys/netinet6/ipcomp_core.c220
-rw-r--r--sys/netinet6/ipcomp_input.c160
-rw-r--r--sys/netinet6/ipcomp_output.c126
-rw-r--r--sys/netinet6/ipsec.c1004
-rw-r--r--sys/netinet6/ipsec.h49
-rw-r--r--sys/netinet6/ipsec6.h7
-rw-r--r--sys/netinet6/mld6.c59
-rw-r--r--sys/netinet6/nd6.c832
-rw-r--r--sys/netinet6/nd6.h105
-rw-r--r--sys/netinet6/nd6_nbr.c250
-rw-r--r--sys/netinet6/nd6_rtr.c1431
-rw-r--r--sys/netinet6/raw_ip6.c108
-rw-r--r--sys/netinet6/raw_ip6.h54
-rw-r--r--sys/netinet6/route6.c26
-rw-r--r--sys/netinet6/scope6.c3
-rw-r--r--sys/netinet6/udp6_output.c67
-rw-r--r--sys/netinet6/udp6_usrreq.c90
58 files changed, 8603 insertions, 5329 deletions
diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h
index 3c7d0a6..0846466 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 4010f13..ead07bf 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 477de51..5c7afaf 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 b0489d1..070883e 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 59263cd..983ecab 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 8d3987c..8ecde86 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 95deec3..6f794a8 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 5d2f984..d774d24 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 e5f8ec5..0319255 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 4d4344b..4542f13 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 8d505ae..7770b78 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 0000000..5b0e5fa
--- /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 0000000..0c40d78
--- /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 ea4e6ca..dbf9279 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 9dca71e..4ea9a3a 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 *)&notifymtu;
+ 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(&ltime, 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 5f51cef..1fd566b 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 1159e52..168d1df 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 db564cf..fbd95cb 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 c8c1450..57bec0e 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 c839d01..516826d 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 4f82e41..fa4434f 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 67409be..9175a65 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 2ea3107..15cd033 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 1eaea50..e3325f9 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 2ce18db..3ae6a63 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 b221f8a..e73e4bb 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 14f72d2..a56cfe2 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 765a692..7bf28d2 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 f2f644e..bb5abc9 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 e8dd11f..4107cf0 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 2664ccb..444cd2c 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 ae1c0f1..f0245cf 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 bcd1a03..2298804 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 4745347..4b10d8e 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 82b0d4b..2be8796 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 34ec538..7871150 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 4176bc4..928f3d3 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 234b2e9..dea9c07 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 bd89793..c2f38fc 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 dea5ea8..08d62da 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 553c8df..4a1046a 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 1eee253..ec031f6 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 da0184c..0a6e2f5 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 265700e..b82840a 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 b8a2447..3cb835d 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 80be10c..33cef87 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 383c125..e9b8a2c 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 4739856..27b8480 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 833e902..270550b 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 26f58b5..73bbcd5 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 d3fa831..7527d43 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 258a59c..715ccf0 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, &lt6_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 42f38fd..eda8bfa 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 0000000..cfb390b
--- /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 3cd95a1..807c6ad 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 43a6fb7..09bba0c 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 ee05019..fd4d6f3 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 ca9ce2f..bb5a38a 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;
OpenPOWER on IntegriCloud