summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/README81
-rw-r--r--sys/netinet6/ah.h94
-rw-r--r--sys/netinet6/ah6.h52
-rw-r--r--sys/netinet6/ah_core.c1644
-rw-r--r--sys/netinet6/ah_input.c1036
-rw-r--r--sys/netinet6/ah_output.c588
-rw-r--r--sys/netinet6/dest6.c127
-rw-r--r--sys/netinet6/esp.h109
-rw-r--r--sys/netinet6/esp6.h48
-rw-r--r--sys/netinet6/esp_core.c1136
-rw-r--r--sys/netinet6/esp_input.c981
-rw-r--r--sys/netinet6/esp_output.c720
-rw-r--r--sys/netinet6/esp_rijndael.c117
-rw-r--r--sys/netinet6/esp_rijndael.h39
-rw-r--r--sys/netinet6/frag6.c695
-rw-r--r--sys/netinet6/icmp6.c2894
-rw-r--r--sys/netinet6/icmp6.h4
-rw-r--r--sys/netinet6/in6.c2455
-rw-r--r--sys/netinet6/in6.h671
-rw-r--r--sys/netinet6/in6_cksum.c321
-rw-r--r--sys/netinet6/in6_gif.c398
-rw-r--r--sys/netinet6/in6_gif.h45
-rw-r--r--sys/netinet6/in6_ifattach.c1050
-rw-r--r--sys/netinet6/in6_ifattach.h46
-rw-r--r--sys/netinet6/in6_pcb.c1158
-rw-r--r--sys/netinet6/in6_pcb.h116
-rw-r--r--sys/netinet6/in6_prefix.c1203
-rw-r--r--sys/netinet6/in6_prefix.h91
-rw-r--r--sys/netinet6/in6_proto.c460
-rw-r--r--sys/netinet6/in6_rmx.c487
-rw-r--r--sys/netinet6/in6_src.c558
-rw-r--r--sys/netinet6/in6_var.h607
-rw-r--r--sys/netinet6/ip6.h4
-rw-r--r--sys/netinet6/ip6_ecn.h41
-rw-r--r--sys/netinet6/ip6_forward.c595
-rw-r--r--sys/netinet6/ip6_fw.c1299
-rw-r--r--sys/netinet6/ip6_fw.h231
-rw-r--r--sys/netinet6/ip6_input.c1643
-rw-r--r--sys/netinet6/ip6_mroute.c1814
-rw-r--r--sys/netinet6/ip6_mroute.h277
-rw-r--r--sys/netinet6/ip6_output.c2614
-rw-r--r--sys/netinet6/ip6_var.h353
-rw-r--r--sys/netinet6/ip6protosw.h163
-rw-r--r--sys/netinet6/ipcomp.h71
-rw-r--r--sys/netinet6/ipcomp6.h46
-rw-r--r--sys/netinet6/ipcomp_core.c358
-rw-r--r--sys/netinet6/ipcomp_input.c349
-rw-r--r--sys/netinet6/ipcomp_output.c384
-rw-r--r--sys/netinet6/ipsec.c3504
-rw-r--r--sys/netinet6/ipsec.h353
-rw-r--r--sys/netinet6/ipsec6.h84
-rw-r--r--sys/netinet6/mld6.c474
-rw-r--r--sys/netinet6/mld6_var.h53
-rw-r--r--sys/netinet6/nd6.c2251
-rw-r--r--sys/netinet6/nd6.h404
-rw-r--r--sys/netinet6/nd6_nbr.c1396
-rw-r--r--sys/netinet6/nd6_rtr.c1982
-rw-r--r--sys/netinet6/pim6.h69
-rw-r--r--sys/netinet6/pim6_var.h70
-rw-r--r--sys/netinet6/raw_ip6.c743
-rw-r--r--sys/netinet6/raw_ip6.h54
-rw-r--r--sys/netinet6/route6.c221
-rw-r--r--sys/netinet6/scope6.c303
-rw-r--r--sys/netinet6/scope6_var.h46
-rw-r--r--sys/netinet6/tcp6_var.h88
-rw-r--r--sys/netinet6/udp6_output.c312
-rw-r--r--sys/netinet6/udp6_usrreq.c772
-rw-r--r--sys/netinet6/udp6_var.h82
68 files changed, 43534 insertions, 0 deletions
diff --git a/sys/netinet6/README b/sys/netinet6/README
new file mode 100644
index 0000000..70cec5e
--- /dev/null
+++ b/sys/netinet6/README
@@ -0,0 +1,81 @@
+a note to committers about KAME tree
+$FreeBSD$
+KAME project
+
+
+FreeBSD IPv6/IPsec tree is from KAMEproject (http://www.kame.net/).
+To synchronize KAME tree and FreeBSD better today and in the future,
+please understand the following:
+
+- DO NOT MAKE COSTMETIC CHANGES.
+ "Cosmetic changes" here includes tabify, untabify, removal of space at EOL,
+ minor KNF items, and whatever adds more output lines on "diff freebsd kame".
+ To make future synchronization easier. it is critical to preserve certain
+ statements in the code. Also, as KAME tree supports all 4 BSDs (Free, Open,
+ Net, BSD/OS) in single shared tree, it is not always possible to backport
+ FreeBSD changes into KAME tree. So again, please do not make cosmetic
+ changes. Even if you think it a right thing, that will bite KAME guys badly
+ during upgrade attempts, and prevent us from synchronizing two trees.
+ (you don't usually make cosmetic changes against third-party code, do you?)
+
+- REPORT CHANGES/BUGS TO KAME GUYS.
+ It is not always possible for KAME guys to watch all the freebsd mailing
+ list traffic, as the traffic is HUGE. So if possible, please, inform
+ kame guys of changes you made in IPv6/IPsec related portion. Contact
+ path would be snap-users@kame.net or KAME PR database on www.kame.net.
+ (or to core@kame.net if it is necessary to make it confidential)
+
+Thank you for your cooperation and have a happy IPv6 life!
+
+
+Note: KAME-origin code is in the following locations.
+The above notice applies to corresponding manpages too.
+The list may not be complete. If you see $KAME$ in the code, it is from
+KAME distribution. If you see some file that is IPv6/IPsec related, it is
+highly possible that the file is from KAME distribution.
+
+include/ifaddrs.h
+lib/libc/net
+lib/libc/net/getaddrinfo.c
+lib/libc/net/getifaddrs.c
+lib/libc/net/getnameinfo.c
+lib/libc/net/ifname.c
+lib/libc/net/ip6opt.c
+lib/libc/net/map_v4v6.c
+lib/libc/net/name6.c
+lib/libftpio
+lib/libipsec
+sbin/ip6fw
+sbin/ping6
+sbin/rtsol
+share/doc/IPv6
+share/man/man4/ip6.4
+share/man/man4/inet6.4
+sys/crypto (except sys/crypto/rc4)
+sys/kern/uipc_mbuf2.c
+sys/net/if_faith.[ch]
+sys/net/if_gif.[ch]
+sys/net/if_stf.[ch]
+sys/net/pfkeyv2.h
+sys/netinet/icmp6.h
+sys/netinet/in_gif.[ch]
+sys/netinet/ip6.h
+sys/netinet/ip_encap.[ch]
+sys/netinet6
+sys/netkey
+usr.sbin/faithd
+usr.sbin/gifconfig
+usr.sbin/ifmcstat
+usr.sbin/mld6query
+usr.sbin/ndp
+usr.sbin/pim6dd
+usr.sbin/pim6sd
+usr.sbin/prefix
+usr.sbin/rip6query
+usr.sbin/route6d
+usr.sbin/rrenumd
+usr.sbin/rtadvd
+usr.sbin/rtsold
+usr.sbin/scope6config
+usr.sbin/setkey
+usr.sbin/traceroute6
diff --git a/sys/netinet6/ah.h b/sys/netinet6/ah.h
new file mode 100644
index 0000000..1934569
--- /dev/null
+++ b/sys/netinet6/ah.h
@@ -0,0 +1,94 @@
+/* $FreeBSD$ */
+/* $KAME: ah.h,v 1.16 2001/09/04 08:43:19 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.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#ifndef _NETINET6_AH_H_
+#define _NETINET6_AH_H_
+
+#if defined(_KERNEL) && !defined(_LKM)
+#include "opt_inet.h"
+#endif
+
+struct ah {
+ u_int8_t ah_nxt; /* Next Header */
+ u_int8_t ah_len; /* Length of data, in 32bit */
+ u_int16_t ah_reserve; /* Reserved for future use */
+ u_int32_t ah_spi; /* Security parameter index */
+ /* variable size, 32bit bound*/ /* Authentication data */
+};
+
+struct newah {
+ u_int8_t ah_nxt; /* Next Header */
+ u_int8_t ah_len; /* Length of data + 1, in 32bit */
+ u_int16_t ah_reserve; /* Reserved for future use */
+ u_int32_t ah_spi; /* Security parameter index */
+ u_int32_t ah_seq; /* Sequence number field */
+ /* variable size, 32bit bound*/ /* Authentication data */
+};
+
+#ifdef _KERNEL
+struct secasvar;
+
+struct ah_algorithm_state {
+ struct secasvar *sav;
+ void* foo; /* per algorithm data - maybe */
+};
+
+struct ah_algorithm {
+ int (*sumsiz) __P((struct secasvar *));
+ int (*mature) __P((struct secasvar *));
+ int keymin; /* in bits */
+ int keymax; /* in bits */
+ const char *name;
+ int (*init) __P((struct ah_algorithm_state *, struct secasvar *));
+ void (*update) __P((struct ah_algorithm_state *, caddr_t, size_t));
+ void (*result) __P((struct ah_algorithm_state *, caddr_t));
+};
+
+#define AH_MAXSUMSIZE 16
+
+extern const struct ah_algorithm *ah_algorithm_lookup __P((int));
+
+/* cksum routines */
+extern int ah_hdrlen __P((struct secasvar *));
+
+extern size_t ah_hdrsiz __P((struct ipsecrequest *));
+extern void ah4_input __P((struct mbuf *, int));
+extern int ah4_output __P((struct mbuf *, struct ipsecrequest *));
+extern int ah4_calccksum __P((struct mbuf *, caddr_t, size_t,
+ const struct ah_algorithm *, struct secasvar *));
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_AH_H_ */
diff --git a/sys/netinet6/ah6.h b/sys/netinet6/ah6.h
new file mode 100644
index 0000000..ead07bf
--- /dev/null
+++ b/sys/netinet6/ah6.h
@@ -0,0 +1,52 @@
+/* $FreeBSD$ */
+/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 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.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#ifndef _NETINET6_AH6_H_
+#define _NETINET6_AH6_H_
+
+#ifdef _KERNEL
+struct secasvar;
+
+extern int ah6_input __P((struct mbuf **, int *, int));
+extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *,
+ struct ipsecrequest *));
+extern int ah6_calccksum __P((struct mbuf *, caddr_t, size_t,
+ 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
new file mode 100644
index 0000000..cbb6206
--- /dev/null
+++ b/sys/netinet6/ah_core.c
@@ -0,0 +1,1644 @@
+/* $FreeBSD$ */
+/* $KAME: ah_core.c,v 1.44 2001/03/12 11:24:39 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.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+/* TODO: have shared routines for hmac-* algorithms */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#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 <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <sys/md5.h>
+#include <crypto/sha1.h>
+#include <crypto/sha2/sha2.h>
+
+#include <net/net_osdep.h>
+
+#define HMACSIZE 16
+
+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 void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t));
+static void ah_none_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_keyed_md5_mature __P((struct secasvar *));
+static int ah_keyed_md5_init __P((struct ah_algorithm_state *,
+ struct secasvar *));
+static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, caddr_t,
+ size_t));
+static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_keyed_sha1_mature __P((struct secasvar *));
+static int ah_keyed_sha1_init __P((struct ah_algorithm_state *,
+ struct secasvar *));
+static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, caddr_t,
+ size_t));
+static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_md5_mature __P((struct secasvar *));
+static int ah_hmac_md5_init __P((struct ah_algorithm_state *,
+ struct secasvar *));
+static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, caddr_t,
+ size_t));
+static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_sha1_mature __P((struct secasvar *));
+static int ah_hmac_sha1_init __P((struct ah_algorithm_state *,
+ struct secasvar *));
+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 int
+ah_sumsiz_1216(sav)
+ struct secasvar *sav;
+{
+ if (!sav)
+ return -1;
+ if (sav->flags & SADB_X_EXT_OLD)
+ return 16;
+ else
+ return 12;
+}
+
+static int
+ah_sumsiz_zero(sav)
+ struct secasvar *sav;
+{
+ if (!sav)
+ return -1;
+ return 0;
+}
+
+static int
+ah_none_mature(sav)
+ struct secasvar *sav;
+{
+ if (sav->sah->saidx.proto == IPPROTO_AH) {
+ ipseclog((LOG_ERR,
+ "ah_none_mature: protocol and algorithm mismatch.\n"));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+ah_none_init(state, sav)
+ struct ah_algorithm_state *state;
+ struct secasvar *sav;
+{
+ state->foo = NULL;
+ return 0;
+}
+
+static void
+ah_none_loop(state, addr, len)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+ size_t len;
+{
+}
+
+static void
+ah_none_result(state, addr)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+{
+}
+
+static int
+ah_keyed_md5_mature(sav)
+ struct secasvar *sav;
+{
+ /* anything is okay */
+ return 0;
+}
+
+static int
+ah_keyed_md5_init(state, sav)
+ struct ah_algorithm_state *state;
+ struct secasvar *sav;
+{
+ size_t padlen;
+ size_t keybitlen;
+ u_int8_t buf[32];
+
+ if (!state)
+ panic("ah_keyed_md5_init: what?");
+
+ state->sav = sav;
+ state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
+ if (state->foo == NULL)
+ return ENOBUFS;
+
+ MD5Init((MD5_CTX *)state->foo);
+ if (state->sav) {
+ MD5Update((MD5_CTX *)state->foo,
+ (u_int8_t *)_KEYBUF(state->sav->key_auth),
+ (u_int)_KEYLEN(state->sav->key_auth));
+
+ /*
+ * Pad after the key.
+ * We cannot simply use md5_pad() since the function
+ * won't update the total length.
+ */
+ if (_KEYLEN(state->sav->key_auth) < 56)
+ padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
+ else
+ padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
+ keybitlen = _KEYLEN(state->sav->key_auth);
+ keybitlen *= 8;
+
+ buf[0] = 0x80;
+ MD5Update((MD5_CTX *)state->foo, &buf[0], 1);
+ padlen--;
+
+ bzero(buf, sizeof(buf));
+ while (sizeof(buf) < padlen) {
+ MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf));
+ padlen -= sizeof(buf);
+ }
+ if (padlen) {
+ MD5Update((MD5_CTX *)state->foo, &buf[0], padlen);
+ }
+
+ buf[0] = (keybitlen >> 0) & 0xff;
+ buf[1] = (keybitlen >> 8) & 0xff;
+ buf[2] = (keybitlen >> 16) & 0xff;
+ buf[3] = (keybitlen >> 24) & 0xff;
+ MD5Update((MD5_CTX *)state->foo, buf, 8);
+ }
+
+ return 0;
+}
+
+static void
+ah_keyed_md5_loop(state, addr, len)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+ size_t len;
+{
+ if (!state)
+ panic("ah_keyed_md5_loop: what?");
+
+ MD5Update((MD5_CTX *)state->foo, addr, len);
+}
+
+static void
+ah_keyed_md5_result(state, addr)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+{
+ u_char digest[16];
+
+ if (!state)
+ panic("ah_keyed_md5_result: what?");
+
+ if (state->sav) {
+ MD5Update((MD5_CTX *)state->foo,
+ (u_int8_t *)_KEYBUF(state->sav->key_auth),
+ (u_int)_KEYLEN(state->sav->key_auth));
+ }
+ MD5Final(&digest[0], (MD5_CTX *)state->foo);
+ free(state->foo, M_TEMP);
+ bcopy(&digest[0], (void *)addr, sizeof(digest));
+}
+
+static int
+ah_keyed_sha1_mature(sav)
+ struct secasvar *sav;
+{
+ 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_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,
+ "ah_keyed_sha1_mature: invalid key length %d.\n",
+ sav->key_auth->sadb_key_bits));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+ah_keyed_sha1_init(state, sav)
+ struct ah_algorithm_state *state;
+ struct secasvar *sav;
+{
+ SHA1_CTX *ctxt;
+ size_t padlen;
+ size_t keybitlen;
+ u_int8_t buf[32];
+
+ if (!state)
+ panic("ah_keyed_sha1_init: what?");
+
+ state->sav = sav;
+ state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT);
+ if (!state->foo)
+ return ENOBUFS;
+
+ ctxt = (SHA1_CTX *)state->foo;
+ SHA1Init(ctxt);
+
+ if (state->sav) {
+ SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
+ (u_int)_KEYLEN(state->sav->key_auth));
+
+ /*
+ * Pad after the key.
+ */
+ if (_KEYLEN(state->sav->key_auth) < 56)
+ padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
+ else
+ padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
+ keybitlen = _KEYLEN(state->sav->key_auth);
+ keybitlen *= 8;
+
+ buf[0] = 0x80;
+ SHA1Update(ctxt, &buf[0], 1);
+ padlen--;
+
+ bzero(buf, sizeof(buf));
+ while (sizeof(buf) < padlen) {
+ SHA1Update(ctxt, &buf[0], sizeof(buf));
+ padlen -= sizeof(buf);
+ }
+ if (padlen) {
+ SHA1Update(ctxt, &buf[0], padlen);
+ }
+
+ buf[0] = (keybitlen >> 0) & 0xff;
+ buf[1] = (keybitlen >> 8) & 0xff;
+ buf[2] = (keybitlen >> 16) & 0xff;
+ buf[3] = (keybitlen >> 24) & 0xff;
+ SHA1Update(ctxt, buf, 8);
+ }
+
+ return 0;
+}
+
+static void
+ah_keyed_sha1_loop(state, addr, len)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+ size_t len;
+{
+ SHA1_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_keyed_sha1_loop: what?");
+ ctxt = (SHA1_CTX *)state->foo;
+
+ SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_keyed_sha1_result(state, addr)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+{
+ u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */
+ SHA1_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_keyed_sha1_result: what?");
+ ctxt = (SHA1_CTX *)state->foo;
+
+ if (state->sav) {
+ SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
+ (u_int)_KEYLEN(state->sav->key_auth));
+ }
+ SHA1Final((caddr_t)&digest[0], ctxt);
+ bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+ free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_md5_mature(sav)
+ struct secasvar *sav;
+{
+ 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_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,
+ "ah_hmac_md5_mature: invalid key length %d.\n",
+ sav->key_auth->sadb_key_bits));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+ah_hmac_md5_init(state, sav)
+ struct ah_algorithm_state *state;
+ struct secasvar *sav;
+{
+ u_char *ipad;
+ u_char *opad;
+ u_char tk[16];
+ u_char *key;
+ size_t keylen;
+ size_t i;
+ MD5_CTX *ctxt;
+
+ if (!state)
+ panic("ah_hmac_md5_init: what?");
+
+ state->sav = sav;
+ state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
+ if (!state->foo)
+ return ENOBUFS;
+
+ ipad = (u_char *)state->foo;
+ opad = (u_char *)(ipad + 64);
+ ctxt = (MD5_CTX *)(opad + 64);
+
+ /* compress the key if necessery */
+ if (64 < _KEYLEN(state->sav->key_auth)) {
+ MD5Init(ctxt);
+ MD5Update(ctxt, _KEYBUF(state->sav->key_auth),
+ _KEYLEN(state->sav->key_auth));
+ MD5Final(&tk[0], ctxt);
+ key = &tk[0];
+ keylen = 16;
+ } 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;
+ }
+
+ MD5Init(ctxt);
+ MD5Update(ctxt, ipad, 64);
+
+ return 0;
+}
+
+static void
+ah_hmac_md5_loop(state, addr, len)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+ size_t len;
+{
+ MD5_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_hmac_md5_loop: what?");
+ ctxt = (MD5_CTX *)(((caddr_t)state->foo) + 128);
+ MD5Update(ctxt, addr, len);
+}
+
+static void
+ah_hmac_md5_result(state, addr)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+{
+ u_char digest[16];
+ u_char *ipad;
+ u_char *opad;
+ MD5_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_hmac_md5_result: what?");
+
+ ipad = (u_char *)state->foo;
+ opad = (u_char *)(ipad + 64);
+ ctxt = (MD5_CTX *)(opad + 64);
+
+ MD5Final(&digest[0], ctxt);
+
+ MD5Init(ctxt);
+ MD5Update(ctxt, opad, 64);
+ MD5Update(ctxt, &digest[0], sizeof(digest));
+ MD5Final(&digest[0], ctxt);
+
+ bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+ free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_sha1_mature(sav)
+ struct secasvar *sav;
+{
+ 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_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,
+ "ah_hmac_sha1_mature: invalid key length %d.\n",
+ sav->key_auth->sadb_key_bits));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+ah_hmac_sha1_init(state, sav)
+ struct ah_algorithm_state *state;
+ struct secasvar *sav;
+{
+ u_char *ipad;
+ u_char *opad;
+ SHA1_CTX *ctxt;
+ u_char tk[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */
+ u_char *key;
+ size_t keylen;
+ size_t i;
+
+ if (!state)
+ panic("ah_hmac_sha1_init: what?");
+
+ state->sav = sav;
+ state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX),
+ M_TEMP, M_NOWAIT);
+ if (!state->foo)
+ return ENOBUFS;
+
+ ipad = (u_char *)state->foo;
+ opad = (u_char *)(ipad + 64);
+ ctxt = (SHA1_CTX *)(opad + 64);
+
+ /* compress the key if necessery */
+ if (64 < _KEYLEN(state->sav->key_auth)) {
+ SHA1Init(ctxt);
+ SHA1Update(ctxt, _KEYBUF(state->sav->key_auth),
+ _KEYLEN(state->sav->key_auth));
+ SHA1Final(&tk[0], ctxt);
+ key = &tk[0];
+ keylen = SHA1_RESULTLEN;
+ } 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;
+ }
+
+ SHA1Init(ctxt);
+ SHA1Update(ctxt, ipad, 64);
+
+ return 0;
+}
+
+static void
+ah_hmac_sha1_loop(state, addr, len)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+ size_t len;
+{
+ SHA1_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_hmac_sha1_loop: what?");
+
+ ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128);
+ SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_hmac_sha1_result(state, addr)
+ struct ah_algorithm_state *state;
+ caddr_t addr;
+{
+ u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */
+ u_char *ipad;
+ u_char *opad;
+ SHA1_CTX *ctxt;
+
+ if (!state || !state->foo)
+ panic("ah_hmac_sha1_result: what?");
+
+ ipad = (u_char *)state->foo;
+ opad = (u_char *)(ipad + 64);
+ ctxt = (SHA1_CTX *)(opad + 64);
+
+ SHA1Final((caddr_t)&digest[0], ctxt);
+
+ SHA1Init(ctxt);
+ SHA1Update(ctxt, opad, 64);
+ SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
+ SHA1Final((caddr_t)&digest[0], ctxt);
+
+ bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+ 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);
+}
+
+/*------------------------------------------------------------*/
+
+/*
+ * go generate the checksum.
+ */
+static void
+ah_update_mbuf(m, off, len, algo, algos)
+ struct mbuf *m;
+ int off;
+ int len;
+ const struct ah_algorithm *algo;
+ struct ah_algorithm_state *algos;
+{
+ struct mbuf *n;
+ int tlen;
+
+ /* easy case first */
+ if (off + len <= m->m_len) {
+ (algo->update)(algos, mtod(m, caddr_t) + off, len);
+ return;
+ }
+
+ for (n = m; n; n = n->m_next) {
+ if (off < n->m_len)
+ break;
+
+ off -= n->m_len;
+ }
+
+ if (!n)
+ panic("ah_update_mbuf: wrong offset specified");
+
+ for (/* nothing */; n && len > 0; n = n->m_next) {
+ if (n->m_len == 0)
+ continue;
+ if (n->m_len - off < len)
+ tlen = n->m_len - off;
+ else
+ tlen = len;
+
+ (algo->update)(algos, mtod(n, caddr_t) + off, tlen);
+
+ len -= tlen;
+ off = 0;
+ }
+}
+
+#ifdef INET
+/*
+ * Go generate the checksum. This function won't modify the mbuf chain
+ * except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
+ */
+int
+ah4_calccksum(m, ahdat, len, algo, sav)
+ struct mbuf *m;
+ caddr_t ahdat;
+ size_t len;
+ const struct ah_algorithm *algo;
+ struct secasvar *sav;
+{
+ int off;
+ int hdrtype;
+ size_t advancewidth;
+ struct ah_algorithm_state algos;
+ u_char sumbuf[AH_MAXSUMSIZE];
+ int error = 0;
+ int ahseen;
+ struct mbuf *n = NULL;
+
+ if ((m->m_flags & M_PKTHDR) == 0)
+ return EINVAL;
+
+ ahseen = 0;
+ hdrtype = -1; /* dummy, it is called IPPROTO_IP */
+
+ off = 0;
+
+ error = (algo->init)(&algos, sav);
+ if (error)
+ return error;
+
+ advancewidth = 0; /* safety */
+
+again:
+ /* gory. */
+ switch (hdrtype) {
+ case -1: /* first one only */
+ {
+ /*
+ * copy ip hdr, modify to fit the AH checksum rule,
+ * then take a checksum.
+ */
+ struct ip iphdr;
+ size_t hlen;
+
+ m_copydata(m, off, sizeof(iphdr), (caddr_t)&iphdr);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(iphdr.ip_vhl) << 2;
+#else
+ hlen = iphdr.ip_hl << 2;
+#endif
+ iphdr.ip_ttl = 0;
+ iphdr.ip_sum = htons(0);
+ if (ip4_ah_cleartos)
+ iphdr.ip_tos = 0;
+ iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask);
+ (algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip));
+
+ if (hlen != sizeof(struct ip)) {
+ u_char *p;
+ int i, l, skip;
+
+ if (hlen > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && hlen > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, hlen, mtod(n, caddr_t));
+
+ /*
+ * IP options processing.
+ * See RFC2402 appendix A.
+ */
+ p = mtod(n, u_char *);
+ i = sizeof(struct ip);
+ while (i < hlen) {
+ if (i + IPOPT_OPTVAL >= hlen) {
+ ipseclog((LOG_ERR, "ah4_calccksum: "
+ "invalid IP option\n"));
+ error = EINVAL;
+ goto fail;
+ }
+ if (p[i + IPOPT_OPTVAL] == IPOPT_EOL ||
+ p[i + IPOPT_OPTVAL] == IPOPT_NOP ||
+ i + IPOPT_OLEN < hlen)
+ ;
+ else {
+ ipseclog((LOG_ERR,
+ "ah4_calccksum: invalid IP option "
+ "(type=%02x)\n",
+ p[i + IPOPT_OPTVAL]));
+ error = EINVAL;
+ goto fail;
+ }
+
+ skip = 1;
+ switch (p[i + IPOPT_OPTVAL]) {
+ case IPOPT_EOL:
+ case IPOPT_NOP:
+ l = 1;
+ skip = 0;
+ break;
+ case IPOPT_SECURITY: /* 0x82 */
+ case 0x85: /* Extended security */
+ case 0x86: /* Commercial security */
+ case 0x94: /* Router alert */
+ case 0x95: /* RFC1770 */
+ l = p[i + IPOPT_OLEN];
+ if (l < 2)
+ goto invalopt;
+ skip = 0;
+ break;
+ default:
+ l = p[i + IPOPT_OLEN];
+ if (l < 2)
+ goto invalopt;
+ skip = 1;
+ break;
+ }
+ if (l < 1 || hlen - i < l) {
+ invalopt:
+ ipseclog((LOG_ERR,
+ "ah4_calccksum: invalid IP option "
+ "(type=%02x len=%02x)\n",
+ p[i + IPOPT_OPTVAL],
+ p[i + IPOPT_OLEN]));
+ error = EINVAL;
+ goto fail;
+ }
+ if (skip)
+ bzero(p + i, l);
+ if (p[i + IPOPT_OPTVAL] == IPOPT_EOL)
+ break;
+ i += l;
+ }
+ p = mtod(n, u_char *) + sizeof(struct ip);
+ (algo->update)(&algos, p, hlen - sizeof(struct ip));
+
+ m_free(n);
+ n = NULL;
+ }
+
+ hdrtype = (iphdr.ip_p) & 0xff;
+ advancewidth = hlen;
+ break;
+ }
+
+ case IPPROTO_AH:
+ {
+ struct ah ah;
+ int siz;
+ int hdrsiz;
+ int totlen;
+
+ m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
+ hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+ ? sizeof(struct ah)
+ : sizeof(struct newah);
+ siz = (*algo->sumsiz)(sav);
+ totlen = (ah.ah_len + 2) << 2;
+
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (!ahseen) {
+ if (totlen > m->m_pkthdr.len - off ||
+ totlen > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && totlen > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, totlen, mtod(n, caddr_t));
+ n->m_len = totlen;
+ bzero(mtod(n, caddr_t) + hdrsiz, siz);
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ } else
+ ah_update_mbuf(m, off, totlen, algo, &algos);
+ ahseen++;
+
+ hdrtype = ah.ah_nxt;
+ advancewidth = totlen;
+ break;
+ }
+
+ default:
+ ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, &algos);
+ advancewidth = m->m_pkthdr.len - off;
+ break;
+ }
+
+ off += advancewidth;
+ if (off < m->m_pkthdr.len)
+ goto again;
+
+ if (len < (*algo->sumsiz)(sav)) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ (algo->result)(&algos, &sumbuf[0]);
+ bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
+
+ if (n)
+ m_free(n);
+ return error;
+
+fail:
+ if (n)
+ m_free(n);
+ return error;
+}
+#endif
+
+#ifdef INET6
+/*
+ * Go generate the checksum. This function won't modify the mbuf chain
+ * except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
+ */
+int
+ah6_calccksum(m, ahdat, len, algo, sav)
+ struct mbuf *m;
+ caddr_t ahdat;
+ size_t len;
+ const struct ah_algorithm *algo;
+ struct secasvar *sav;
+{
+ int newoff, off;
+ int proto, nxt;
+ struct mbuf *n = NULL;
+ int error;
+ int ahseen;
+ struct ah_algorithm_state algos;
+ u_char sumbuf[AH_MAXSUMSIZE];
+
+ if ((m->m_flags & M_PKTHDR) == 0)
+ return EINVAL;
+
+ error = (algo->init)(&algos, sav);
+ if (error)
+ return error;
+
+ off = 0;
+ proto = IPPROTO_IPV6;
+ nxt = -1;
+ ahseen = 0;
+
+ again:
+ newoff = ip6_nexthdr(m, off, proto, &nxt);
+ if (newoff < 0)
+ newoff = m->m_pkthdr.len;
+ else if (newoff <= off) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ switch (proto) {
+ case IPPROTO_IPV6:
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (off == 0) {
+ struct ip6_hdr ip6copy;
+
+ if (newoff - off != sizeof(struct ip6_hdr)) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ m_copydata(m, off, newoff - off, (caddr_t)&ip6copy);
+ /* RFC2402 */
+ ip6copy.ip6_flow = 0;
+ ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6copy.ip6_vfc |= IPV6_VERSION;
+ ip6copy.ip6_hlim = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
+ ip6copy.ip6_src.s6_addr16[1] = 0x0000;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
+ ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
+ (algo->update)(&algos, (caddr_t)&ip6copy,
+ sizeof(struct ip6_hdr));
+ } else {
+ newoff = m->m_pkthdr.len;
+ ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo,
+ &algos);
+ }
+ break;
+
+ case IPPROTO_AH:
+ {
+ int siz;
+ int hdrsiz;
+
+ hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+ ? sizeof(struct ah)
+ : sizeof(struct newah);
+ siz = (*algo->sumsiz)(sav);
+
+ /*
+ * special treatment is necessary for the first one, not others
+ */
+ if (!ahseen) {
+ if (newoff - off > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && newoff - off > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+ n->m_len = newoff - off;
+ bzero(mtod(n, caddr_t) + hdrsiz, siz);
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ } else
+ ah_update_mbuf(m, off, newoff - off, algo, &algos);
+ ahseen++;
+ break;
+ }
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ {
+ struct ip6_ext *ip6e;
+ int hdrlen, optlen;
+ u_int8_t *p, *optend, *optp;
+
+ if (newoff - off > MCLBYTES) {
+ error = EMSGSIZE;
+ goto fail;
+ }
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (n && newoff - off > MLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+ n->m_len = newoff - off;
+
+ ip6e = mtod(n, struct ip6_ext *);
+ hdrlen = (ip6e->ip6e_len + 1) << 3;
+ if (newoff - off < hdrlen) {
+ error = EINVAL;
+ m_free(n);
+ n = NULL;
+ goto fail;
+ }
+ p = mtod(n, u_int8_t *);
+ optend = p + hdrlen;
+
+ /*
+ * ICV calculation for the options header including all
+ * options. This part is a little tricky since there are
+ * two type of options; mutable and immutable. We try to
+ * null-out mutable ones here.
+ */
+ optp = p + 2;
+ while (optp < optend) {
+ if (optp[0] == IP6OPT_PAD1)
+ optlen = 1;
+ else {
+ if (optp + 2 > optend) {
+ error = EINVAL;
+ m_free(n);
+ n = NULL;
+ goto fail;
+ }
+ optlen = optp[1] + 2;
+
+ if (optp[0] & IP6OPT_MUTABLE)
+ bzero(optp + 2, optlen - 2);
+ }
+
+ optp += optlen;
+ }
+
+ (algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+ m_free(n);
+ n = NULL;
+ break;
+ }
+
+ case IPPROTO_ROUTING:
+ /*
+ * For an input packet, we can just calculate `as is'.
+ * For an output packet, we assume ip6_output have already
+ * made packet how it will be received at the final
+ * destination.
+ */
+ /* FALLTHROUGH */
+
+ default:
+ ah_update_mbuf(m, off, newoff - off, algo, &algos);
+ break;
+ }
+
+ if (newoff < m->m_pkthdr.len) {
+ proto = nxt;
+ off = newoff;
+ goto again;
+ }
+
+ if (len < (*algo->sumsiz)(sav)) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ (algo->result)(&algos, &sumbuf[0]);
+ bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
+
+ /* just in case */
+ if (n)
+ m_free(n);
+ return 0;
+fail:
+ /* just in case */
+ if (n)
+ m_free(n);
+ return error;
+}
+#endif
diff --git a/sys/netinet6/ah_input.c b/sys/netinet6/ah_input.c
new file mode 100644
index 0000000..d7d3030
--- /dev/null
+++ b/sys/netinet6/ah_input.c
@@ -0,0 +1,1036 @@
+/* $FreeBSD$ */
+/* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc 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.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <machine/cpu.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+#ifdef INET6
+#include <netinet6/ip6_ecn.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6protosw.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netinet6/ah.h>
+#ifdef INET6
+#include <netinet6/ah6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#ifdef IPSEC_DEBUG
+#include <netkey/key_debug.h>
+#else
+#define KEYDEBUG(lev,arg)
+#endif
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+#define IPLEN_FLIPPED
+
+#ifdef INET
+extern struct protosw inetsw[];
+
+void
+ah4_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip *ip;
+ struct ah *ah;
+ u_int32_t spi;
+ 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 proto;
+ size_t stripsiz = 0;
+
+#ifndef PULLDOWN_TEST
+ if (m->m_len < off + sizeof(struct newah)) {
+ m = m_pullup(m, off + sizeof(struct newah));
+ if (!m) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
+ "dropping the packet for simplicity\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+ }
+
+ ip = mtod(m, struct ip *);
+ proto = ip->ip_p;
+ ah = (struct ah *)(((caddr_t)ip) + off);
+#else
+ ip = mtod(m, struct ip *);
+ proto = ip->ip_p;
+ IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
+ if (ah == NULL) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
+ "dropping the packet for simplicity\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+#endif
+ nxt = ah->ah_nxt;
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ /* find the sassoc. */
+ spi = ah->ah_spi;
+
+ if ((sav = key_allocsa(AF_INET,
+ (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+ IPPROTO_AH, spi)) == 0) {
+ ipseclog((LOG_WARNING,
+ "IPv4 AH input: no key association found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_nosa++;
+ goto fail;
+ }
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah4_input called to allocate SA:%p\n", sav));
+ if (sav->state != SADB_SASTATE_MATURE
+ && sav->state != SADB_SASTATE_DYING) {
+ ipseclog((LOG_DEBUG,
+ "IPv4 AH input: non-mature/dying SA found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_badspi++;
+ goto fail;
+ }
+
+ algo = ah_algorithm_lookup(sav->alg_auth);
+ if (!algo) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: "
+ "unsupported authentication algorithm for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_badspi++;
+ goto fail;
+ }
+
+ siz = (*algo->sumsiz)(sav);
+ siz1 = ((siz + 3) & ~(4 - 1));
+
+ /*
+ * sanity checks for header, 1.
+ */
+ {
+ int sizoff;
+
+ sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+ /*
+ * Here, we do not do "siz1 == siz". This is because the way
+ * RFC240[34] section 2 is written. They do not require truncation
+ * to 96 bits.
+ * For example, Microsoft IPsec stack attaches 160 bits of
+ * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1,
+ * 32 bits of padding is attached.
+ *
+ * There are two downsides to this specification.
+ * They have no real harm, however, they leave us fuzzy feeling.
+ * - if we attach more than 96 bits of authentication data onto AH,
+ * we will never notice about possible modification by rogue
+ * intermediate nodes.
+ * Since extra bits in AH checksum is never used, this constitutes
+ * no real issue, however, it is wacky.
+ * - even if the peer attaches big authentication data, we will never
+ * notice the difference, since longer authentication data will just
+ * work.
+ *
+ * We may need some clarification in the spec.
+ */
+ if (siz1 < siz) {
+ ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
+ "(%lu, should be at least %lu): %s\n",
+ (u_long)siz1, (u_long)siz,
+ ipsec4_logpacketstr(ip, spi)));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+ if ((ah->ah_len << 2) - sizoff != siz1) {
+ ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
+ "(%d should be %lu): %s\n",
+ (ah->ah_len << 2) - sizoff, (u_long)siz1,
+ ipsec4_logpacketstr(ip, spi)));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+
+#ifndef PULLDOWN_TEST
+ if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
+ m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
+ if (!m) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+
+ ip = mtod(m, struct ip *);
+ ah = (struct ah *)(((caddr_t)ip) + off);
+ }
+#else
+ IP6_EXTHDR_GET(ah, struct ah *, m, off,
+ sizeof(struct ah) + sizoff + siz1);
+ if (ah == NULL) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+#endif
+ }
+
+ /*
+ * check for sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
+ ; /* okey */
+ else {
+ ipsecstat.in_ahreplay++;
+ ipseclog((LOG_WARNING,
+ "replay packet in IPv4 AH input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ goto fail;
+ }
+ }
+
+ /*
+ * alright, it seems sane. now we are going to check the
+ * cryptographic checksum.
+ */
+ cksum = malloc(siz1, M_TEMP, M_NOWAIT);
+ if (!cksum) {
+ ipseclog((LOG_DEBUG, "IPv4 AH input: "
+ "couldn't alloc temporary region for cksum\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+
+ /*
+ * 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);
+ 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]++;
+ /*
+ * flip them back.
+ */
+ ip->ip_len = ntohs(ip->ip_len) - hlen;
+ ip->ip_off = ntohs(ip->ip_off);
+
+ {
+ caddr_t sumpos = NULL;
+
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1826 */
+ sumpos = (caddr_t)(ah + 1);
+ } else {
+ /* RFC 2402 */
+ sumpos = (caddr_t)(((struct newah *)ah) + 1);
+ }
+
+ if (bcmp(sumpos, cksum, siz) != 0) {
+ ipseclog((LOG_WARNING,
+ "checksum mismatch in IPv4 AH input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ free(cksum, M_TEMP);
+ ipsecstat.in_ahauthfail++;
+ goto fail;
+ }
+ }
+
+ free(cksum, M_TEMP);
+
+ m->m_flags |= M_AUTHIPHDR;
+ m->m_flags |= M_AUTHIPDGM;
+
+#if 0
+ /*
+ * looks okey, but we need more sanity check.
+ * XXX should elaborate.
+ */
+ if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
+ struct ip *nip;
+ size_t sizoff;
+
+ sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+ if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
+ m = m_pullup(m, off + sizeof(struct ah)
+ + sizoff + siz1 + hlen);
+ if (!m) {
+ ipseclog((LOG_DEBUG,
+ "IPv4 AH input: can't pullup\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+ }
+
+ nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
+ if (nip->ip_src.s_addr != ip->ip_src.s_addr
+ || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+ }
+#ifdef INET6
+ else if (ah->ah_nxt == IPPROTO_IPV6) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+#endif /* INET6 */
+#endif /* 0 */
+
+ if (m->m_flags & M_AUTHIPHDR
+ && m->m_flags & M_AUTHIPDGM) {
+#if 0
+ ipseclog((LOG_DEBUG,
+ "IPv4 AH input: authentication succeess\n"));
+#endif
+ ipsecstat.in_ahauthsucc++;
+ } else {
+ ipseclog((LOG_WARNING,
+ "authentication failed in IPv4 AH input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_ahauthfail++;
+ goto fail;
+ }
+
+ /*
+ * update sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
+ ipsecstat.in_ahreplay++;
+ goto fail;
+ }
+ }
+
+ /* was it transmitted over the IPsec tunnel SA? */
+ 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
+ *
+ * XXX more sanity checks
+ * XXX relationship with gif?
+ */
+ u_int8_t tos;
+
+ tos = ip->ip_tos;
+ m_adj(m, off + stripsiz);
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (!m) {
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+ }
+ ip = mtod(m, struct ip *);
+ /* ECN consideration. */
+ ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
+ if (!key_checktunnelsanity(sav, AF_INET,
+ (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
+ ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
+ "in IPv4 AH input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+
+#if 1
+ /*
+ * Should the inner packet be considered authentic?
+ * My current answer is: NO.
+ *
+ * host1 -- gw1 === gw2 -- host2
+ * In this case, gw2 can trust the authenticity of the
+ * outer packet, but NOT inner. Packet may be altered
+ * between host1 and gw1.
+ *
+ * host1 -- gw1 === host2
+ * This case falls into the same scenario as above.
+ *
+ * host1 === host2
+ * This case is the only case when we may be able to leave
+ * M_AUTHIPHDR and M_AUTHIPDGM set.
+ * However, if host1 is wrongly configured, and allows
+ * attacker to inject some packet with src=host1 and
+ * dst=host2, you are in risk.
+ */
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+#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 (! netisr_queue(NETISR_IP, m)) {
+ ipsecstat.in_inval++;
+ m = NULL;
+ goto fail;
+ }
+ m = NULL;
+ nxt = IPPROTO_DONE;
+ } else {
+ /*
+ * strip off AH.
+ */
+
+ ip = mtod(m, struct ip *);
+#ifndef PULLDOWN_TEST
+ /*
+ * We do deep-copy since KAME requires that
+ * the packet is placed in a single external mbuf.
+ */
+ ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+#else
+ /*
+ * even in m_pulldown case, we need to strip off AH so that
+ * we can compute checksum for multiple AH correctly.
+ */
+ if (m->m_len >= stripsiz + off) {
+ ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+ } else {
+ /*
+ * this comes with no copy if the boundary is on
+ * cluster
+ */
+ struct mbuf *n;
+
+ n = m_split(m, off, M_DONTWAIT);
+ if (n == NULL) {
+ /* m is retained by m_split */
+ goto fail;
+ }
+ m_adj(n, stripsiz);
+ m_cat(m, n);
+ /* m_cat does not update m_pkthdr.len */
+ m->m_pkthdr.len += n->m_pkthdr.len;
+ }
+#endif
+
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (m == NULL) {
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+ }
+ ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+ ip->ip_len = ip->ip_len - stripsiz;
+#else
+ ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
+#endif
+ ip->ip_p = nxt;
+ /* forget about IP hdr checksum, the check has already been passed */
+
+ key_sa_recordxfer(sav, m);
+ if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
+ ipsecstat.in_nomem++;
+ goto fail;
+ }
+
+ 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);
+ } else
+ m_freem(m);
+ m = NULL;
+ }
+
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah4_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ ipsecstat.in_success++;
+ return;
+
+fail:
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah4_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ if (m)
+ m_freem(m);
+ return;
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+ah6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ int off = *offp;
+ struct ip6_hdr *ip6;
+ struct ah *ah;
+ u_int32_t spi;
+ 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);
+ ah = (struct ah *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
+ if (ah == NULL) {
+ ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
+ ipsec6stat.in_inval++;
+ return IPPROTO_DONE;
+ }
+#endif
+ ip6 = mtod(m, struct ip6_hdr *);
+ nxt = ah->ah_nxt;
+
+ /* find the sassoc. */
+ spi = ah->ah_spi;
+
+ if (ntohs(ip6->ip6_plen) == 0) {
+ ipseclog((LOG_ERR, "IPv6 AH input: "
+ "AH with IPv6 jumbogram is not supported.\n"));
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+
+ if ((sav = key_allocsa(AF_INET6,
+ (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+ IPPROTO_AH, spi)) == 0) {
+ ipseclog((LOG_WARNING,
+ "IPv6 AH input: no key association found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_nosa++;
+ goto fail;
+ }
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah6_input called to allocate SA:%p\n", sav));
+ if (sav->state != SADB_SASTATE_MATURE
+ && sav->state != SADB_SASTATE_DYING) {
+ ipseclog((LOG_DEBUG,
+ "IPv6 AH input: non-mature/dying SA found for spi %u; ",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_badspi++;
+ goto fail;
+ }
+
+ algo = ah_algorithm_lookup(sav->alg_auth);
+ if (!algo) {
+ ipseclog((LOG_DEBUG, "IPv6 AH input: "
+ "unsupported authentication algorithm for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_badspi++;
+ goto fail;
+ }
+
+ siz = (*algo->sumsiz)(sav);
+ siz1 = ((siz + 3) & ~(4 - 1));
+
+ /*
+ * sanity checks for header, 1.
+ */
+ {
+ int sizoff;
+
+ sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+ /*
+ * Here, we do not do "siz1 == siz". See ah4_input() for complete
+ * description.
+ */
+ if (siz1 < siz) {
+ ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
+ "(%lu, should be at least %lu): %s\n",
+ (u_long)siz1, (u_long)siz,
+ ipsec6_logpacketstr(ip6, spi)));
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+ if ((ah->ah_len << 2) - sizoff != siz1) {
+ ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
+ "(%d should be %lu): %s\n",
+ (ah->ah_len << 2) - sizoff, (u_long)siz1,
+ ipsec6_logpacketstr(ip6, spi)));
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE);
+#else
+ IP6_EXTHDR_GET(ah, struct ah *, m, off,
+ sizeof(struct ah) + sizoff + siz1);
+ if (ah == NULL) {
+ ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
+ ipsec6stat.in_inval++;
+ m = NULL;
+ goto fail;
+ }
+#endif
+ }
+
+ /*
+ * check for sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
+ ; /* okey */
+ else {
+ ipsec6stat.in_ahreplay++;
+ ipseclog((LOG_WARNING,
+ "replay packet in IPv6 AH input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi),
+ ipsec_logsastr(sav)));
+ goto fail;
+ }
+ }
+
+ /*
+ * alright, it seems sane. now we are going to check the
+ * cryptographic checksum.
+ */
+ cksum = malloc(siz1, M_TEMP, M_NOWAIT);
+ if (!cksum) {
+ ipseclog((LOG_DEBUG, "IPv6 AH input: "
+ "couldn't alloc temporary region for cksum\n"));
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+
+ if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
+ free(cksum, M_TEMP);
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+ ipsec6stat.in_ahhist[sav->alg_auth]++;
+
+ {
+ caddr_t sumpos = NULL;
+
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1826 */
+ sumpos = (caddr_t)(ah + 1);
+ } else {
+ /* RFC 2402 */
+ sumpos = (caddr_t)(((struct newah *)ah) + 1);
+ }
+
+ if (bcmp(sumpos, cksum, siz) != 0) {
+ ipseclog((LOG_WARNING,
+ "checksum mismatch in IPv6 AH input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ free(cksum, M_TEMP);
+ ipsec6stat.in_ahauthfail++;
+ goto fail;
+ }
+ }
+
+ free(cksum, M_TEMP);
+
+ m->m_flags |= M_AUTHIPHDR;
+ m->m_flags |= M_AUTHIPDGM;
+
+#if 0
+ /*
+ * looks okey, but we need more sanity check.
+ * XXX should elaborate.
+ */
+ if (ah->ah_nxt == IPPROTO_IPV6) {
+ struct ip6_hdr *nip6;
+ size_t sizoff;
+
+ sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
+ + sizeof(struct ip6_hdr), IPPROTO_DONE);
+
+ nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
+ if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
+ || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+ } else if (ah->ah_nxt == IPPROTO_IPIP) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ } else if (ah->ah_nxt == IPPROTO_IP) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+#endif
+
+ if (m->m_flags & M_AUTHIPHDR
+ && m->m_flags & M_AUTHIPDGM) {
+#if 0
+ ipseclog((LOG_DEBUG,
+ "IPv6 AH input: authentication succeess\n"));
+#endif
+ ipsec6stat.in_ahauthsucc++;
+ } else {
+ ipseclog((LOG_WARNING,
+ "authentication failed in IPv6 AH input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ ipsec6stat.in_ahauthfail++;
+ goto fail;
+ }
+
+ /*
+ * update sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
+ ipsec6stat.in_ahreplay++;
+ goto fail;
+ }
+ }
+
+ /* was it transmitted over the IPsec tunnel SA? */
+ 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
+ *
+ * XXX more sanity checks
+ * XXX relationship with gif?
+ */
+ u_int32_t flowinfo; /* net endian */
+
+ flowinfo = ip6->ip6_flow;
+ m_adj(m, off + stripsiz);
+ if (m->m_len < sizeof(*ip6)) {
+ /*
+ * m_pullup is prohibited in KAME IPv6 input processing
+ * but there's no other way!
+ */
+ m = m_pullup(m, sizeof(*ip6));
+ if (!m) {
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ /* ECN consideration. */
+ ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
+ if (!key_checktunnelsanity(sav, AF_INET6,
+ (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
+ ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
+ "in IPv6 AH input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi),
+ ipsec_logsastr(sav)));
+ ipsec6stat.in_inval++;
+ goto fail;
+ }
+
+#if 1
+ /*
+ * should the inner packet be considered authentic?
+ * see comment in ah4_input().
+ */
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+#endif
+
+ key_sa_recordxfer(sav, m);
+ if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
+ ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
+ ipsec6stat.in_nomem++;
+ goto fail;
+ }
+
+ if (! netisr_queue(NETISR_IPV6, m)) {
+ ipsec6stat.in_inval++;
+ m = NULL;
+ goto fail;
+ }
+ m = NULL;
+ nxt = IPPROTO_DONE;
+ } else {
+ /*
+ * strip off AH.
+ */
+ char *prvnxtp;
+
+ /*
+ * Copy the value of the next header field of AH to the
+ * next header field of the previous header.
+ * This is necessary because AH will be stripped off below.
+ */
+ prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
+ *prvnxtp = nxt;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+#ifndef PULLDOWN_TEST
+ /*
+ * We do deep-copy since KAME requires that
+ * the packet is placed in a single mbuf.
+ */
+ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+#else
+ /*
+ * even in m_pulldown case, we need to strip off AH so that
+ * we can compute checksum for multiple AH correctly.
+ */
+ if (m->m_len >= stripsiz + off) {
+ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+ } else {
+ /*
+ * this comes with no copy if the boundary is on
+ * cluster
+ */
+ struct mbuf *n;
+
+ n = m_split(m, off, M_DONTWAIT);
+ if (n == NULL) {
+ /* m is retained by m_split */
+ goto fail;
+ }
+ m_adj(n, stripsiz);
+ m_cat(m, n);
+ /* m_cat does not update m_pkthdr.len */
+ m->m_pkthdr.len += n->m_pkthdr.len;
+ }
+#endif
+ ip6 = mtod(m, struct ip6_hdr *);
+ /* XXX jumbogram */
+ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
+
+ key_sa_recordxfer(sav, m);
+ if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
+ ipsec6stat.in_nomem++;
+ goto fail;
+ }
+ }
+
+ *offp = off;
+ *mp = m;
+
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah6_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ ipsec6stat.in_success++;
+ return nxt;
+
+fail:
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ah6_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ if (m)
+ 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;
+ off = 0; /* calm gcc */
+ }
+
+ 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.
+ */
+ sa6_src = ip6cp->ip6c_src;
+ sa6_dst = (struct sockaddr_in6 *)sa;
+ 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
new file mode 100644
index 0000000..0887c41
--- /dev/null
+++ b/sys/netinet6/ah_output.c
@@ -0,0 +1,588 @@
+/* $FreeBSD$ */
+/* $KAME: ah_output.c,v 1.31 2001/07/26 06:53:15 jinmei 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.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netinet6/ah.h>
+#ifdef INET6
+#include <netinet6/ah6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#include <net/net_osdep.h>
+
+#ifdef INET
+static struct in_addr *ah4_finaldst __P((struct mbuf *));
+#endif
+
+/*
+ * compute AH header size.
+ * transport mode only. for tunnel mode, we should implement
+ * virtual interface, and control MTU/MSS by the interface MTU.
+ */
+size_t
+ah_hdrsiz(isr)
+ struct ipsecrequest *isr;
+{
+ const struct ah_algorithm *algo;
+ size_t hdrsiz;
+
+ /* sanity check */
+ if (isr == NULL)
+ panic("ah_hdrsiz: NULL was passed.");
+
+ if (isr->saidx.proto != IPPROTO_AH)
+ panic("unsupported mode passed to ah_hdrsiz");
+
+ if (isr->sav == NULL)
+ goto estimate;
+ if (isr->sav->state != SADB_SASTATE_MATURE
+ && isr->sav->state != SADB_SASTATE_DYING)
+ goto estimate;
+
+ /* we need transport mode AH. */
+ algo = ah_algorithm_lookup(isr->sav->alg_auth);
+ if (!algo)
+ goto estimate;
+
+ /*
+ * XXX
+ * right now we don't calcurate the padding size. simply
+ * treat the padding size as constant, for simplicity.
+ *
+ * XXX variable size padding support
+ */
+ hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
+ if (isr->sav->flags & SADB_X_EXT_OLD)
+ hdrsiz += sizeof(struct ah);
+ else
+ hdrsiz += sizeof(struct newah);
+
+ return hdrsiz;
+
+ estimate:
+ /* ASSUMING:
+ * sizeof(struct newah) > sizeof(struct ah).
+ * 16 = (16 + 3) & ~(4 - 1).
+ */
+ 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.
+ *
+ * assumes that the first mbuf contains IPv4 header + option only.
+ * the function does not modify m.
+ */
+int
+ah4_output(m, isr)
+ struct mbuf *m;
+ struct ipsecrequest *isr;
+{
+ struct secasvar *sav = isr->sav;
+ const struct ah_algorithm *algo;
+ u_int32_t spi;
+ u_char *ahdrpos;
+ u_char *ahsumpos = NULL;
+ size_t hlen = 0; /* IP header+option in bytes */
+ size_t plen = 0; /* AH payload size in bytes */
+ size_t ahlen = 0; /* plen + sizeof(ah) */
+ struct ip *ip;
+ struct in_addr dst;
+ struct in_addr *finaldst;
+ int error;
+
+ /* sanity checks */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+ struct ip *ip;
+
+ ip = mtod(m, struct ip *);
+ ipseclog((LOG_DEBUG, "ah4_output: internal error: "
+ "sav->replay is null: %x->%x, SPI=%u\n",
+ (u_int32_t)ntohl(ip->ip_src.s_addr),
+ (u_int32_t)ntohl(ip->ip_dst.s_addr),
+ (u_int32_t)ntohl(sav->spi)));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+
+ 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;
+
+ /*
+ * determine the size to grow.
+ */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1826 */
+ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
+ ahlen = plen + sizeof(struct ah);
+ } else {
+ /* RFC 2402 */
+ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
+ ahlen = plen + sizeof(struct newah);
+ }
+
+ /*
+ * grow the mbuf to accomodate AH.
+ */
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ if (m->m_len != hlen)
+ panic("ah4_output: assumption failed (first mbuf length)");
+ if (M_LEADINGSPACE(m->m_next) < ahlen) {
+ struct mbuf *n;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
+ __LINE__));
+ m_freem(m);
+ return ENOBUFS;
+ }
+ n->m_len = ahlen;
+ n->m_next = m->m_next;
+ m->m_next = n;
+ m->m_pkthdr.len += ahlen;
+ ahdrpos = mtod(n, u_char *);
+ } else {
+ m->m_next->m_len += ahlen;
+ m->m_next->m_data -= ahlen;
+ m->m_pkthdr.len += ahlen;
+ ahdrpos = mtod(m->m_next, u_char *);
+ }
+
+ ip = mtod(m, struct ip *); /* just to be sure */
+
+ /*
+ * initialize AH.
+ */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ struct ah *ahdr;
+
+ ahdr = (struct ah *)ahdrpos;
+ ahsumpos = (u_char *)(ahdr + 1);
+ ahdr->ah_len = plen >> 2;
+ ahdr->ah_nxt = ip->ip_p;
+ ahdr->ah_reserve = htons(0);
+ ahdr->ah_spi = spi;
+ bzero(ahdr + 1, plen);
+ } else {
+ struct newah *ahdr;
+
+ ahdr = (struct newah *)ahdrpos;
+ ahsumpos = (u_char *)(ahdr + 1);
+ ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
+ ahdr->ah_nxt = ip->ip_p;
+ ahdr->ah_reserve = htons(0);
+ ahdr->ah_spi = spi;
+ if (sav->replay->count == ~0) {
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ /* XXX Is it noisy ? */
+ ipseclog((LOG_WARNING,
+ "replay counter overflowed. %s\n",
+ ipsec_logsastr(sav)));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+ }
+ sav->replay->count++;
+ /*
+ * XXX sequence number must not be cycled, if the SA is
+ * installed by IKE daemon.
+ */
+ ahdr->ah_seq = htonl(sav->replay->count);
+ bzero(ahdr + 1, plen);
+ }
+
+ /*
+ * modify IPv4 header.
+ */
+ ip->ip_p = IPPROTO_AH;
+ if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
+ ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
+ else {
+ ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ return EMSGSIZE;
+ }
+
+ /*
+ * If there is source routing option, update destination field in
+ * the IPv4 header to the final destination.
+ * Note that we do not need to update source routing option itself
+ * (as done in IPv4 AH processing -- see ip6_output()), since
+ * source routing option is not part of the ICV computation.
+ */
+ finaldst = ah4_finaldst(m);
+ if (finaldst) {
+ dst.s_addr = ip->ip_dst.s_addr;
+ ip->ip_dst.s_addr = finaldst->s_addr;
+ }
+
+ /*
+ * calcurate the checksum, based on security association
+ * and the algorithm specified.
+ */
+ error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
+ if (error) {
+ ipseclog((LOG_ERR,
+ "error after ah4_calccksum, called from ah4_output"));
+ m_freem(m);
+ m = NULL;
+ ipsecstat.out_inval++;
+ return error;
+ }
+
+ if (finaldst) {
+ ip = mtod(m, struct ip *); /* just to make sure */
+ ip->ip_dst.s_addr = dst.s_addr;
+ }
+ ipsecstat.out_success++;
+ ipsecstat.out_ahhist[sav->alg_auth]++;
+ key_sa_recordxfer(sav, m);
+
+ return 0;
+}
+#endif
+
+/* Calculate AH length */
+int
+ah_hdrlen(sav)
+ struct secasvar *sav;
+{
+ const struct ah_algorithm *algo;
+ int plen, ahlen;
+
+ 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? */
+ ahlen = plen + sizeof(struct ah);
+ } else {
+ /* RFC 2402 */
+ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
+ ahlen = plen + sizeof(struct newah);
+ }
+
+ return(ahlen);
+}
+
+#ifdef INET6
+/*
+ * Fill in the Authentication Header and calculate checksum.
+ */
+int
+ah6_output(m, nexthdrp, md, isr)
+ struct mbuf *m;
+ u_char *nexthdrp;
+ struct mbuf *md;
+ struct ipsecrequest *isr;
+{
+ struct mbuf *mprev;
+ struct mbuf *mah;
+ struct secasvar *sav = isr->sav;
+ const struct ah_algorithm *algo;
+ u_int32_t spi;
+ u_char *ahsumpos = NULL;
+ size_t plen; /* AH payload size in bytes */
+ int error = 0;
+ int ahlen;
+ struct ip6_hdr *ip6;
+
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
+ m_freem(m);
+ return EINVAL;
+ }
+
+ ahlen = ah_hdrlen(sav);
+ if (ahlen == 0)
+ return 0;
+
+ for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+ ;
+ if (!mprev || mprev->m_next != md) {
+ ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
+ m_freem(m);
+ return EINVAL;
+ }
+
+ MGET(mah, M_DONTWAIT, MT_DATA);
+ if (!mah) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ if (ahlen > MLEN) {
+ MCLGET(mah, M_DONTWAIT);
+ if ((mah->m_flags & M_EXT) == 0) {
+ m_free(mah);
+ m_freem(m);
+ return ENOBUFS;
+ }
+ }
+ mah->m_len = ahlen;
+ mah->m_next = md;
+ mprev->m_next = mah;
+ m->m_pkthdr.len += ahlen;
+
+ /* fix plen */
+ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
+ ipseclog((LOG_ERR,
+ "ip6_output: AH with IPv6 jumbogram is not supported\n"));
+ m_freem(m);
+ return EINVAL;
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
+
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+ ipseclog((LOG_DEBUG, "ah6_output: internal error: "
+ "sav->replay is null: SPI=%u\n",
+ (u_int32_t)ntohl(sav->spi)));
+ ipsec6stat.out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+
+ 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;
+
+ /*
+ * initialize AH.
+ */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ struct ah *ahdr = mtod(mah, struct ah *);
+
+ plen = mah->m_len - sizeof(struct ah);
+ ahsumpos = (u_char *)(ahdr + 1);
+ ahdr->ah_nxt = *nexthdrp;
+ *nexthdrp = IPPROTO_AH;
+ ahdr->ah_len = plen >> 2;
+ ahdr->ah_reserve = htons(0);
+ ahdr->ah_spi = spi;
+ bzero(ahdr + 1, plen);
+ } else {
+ struct newah *ahdr = mtod(mah, struct newah *);
+
+ plen = mah->m_len - sizeof(struct newah);
+ ahsumpos = (u_char *)(ahdr + 1);
+ ahdr->ah_nxt = *nexthdrp;
+ *nexthdrp = IPPROTO_AH;
+ ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
+ ahdr->ah_reserve = htons(0);
+ ahdr->ah_spi = spi;
+ if (sav->replay->count == ~0) {
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ /* XXX Is it noisy ? */
+ ipseclog((LOG_WARNING,
+ "replay counter overflowed. %s\n",
+ ipsec_logsastr(sav)));
+ ipsec6stat.out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+ }
+ sav->replay->count++;
+ /*
+ * XXX sequence number must not be cycled, if the SA is
+ * installed by IKE daemon.
+ */
+ ahdr->ah_seq = htonl(sav->replay->count);
+ bzero(ahdr + 1, plen);
+ }
+
+ /*
+ * calcurate the checksum, based on security association
+ * and the algorithm specified.
+ */
+ error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
+ if (error) {
+ ipsec6stat.out_inval++;
+ m_freem(m);
+ } else {
+ ipsec6stat.out_success++;
+ key_sa_recordxfer(sav, m);
+ }
+ ipsec6stat.out_ahhist[sav->alg_auth]++;
+
+ return(error);
+}
+#endif
+
+#ifdef INET
+/*
+ * Find the final destination if there is loose/strict source routing option.
+ * Returns NULL if there's no source routing options.
+ * Returns NULL on errors too.
+ * Note that this function will return a pointer INTO the given parameter,
+ * struct mbuf *m.
+ * The mbuf must be pulled up toward, at least, ip option part.
+ */
+static struct in_addr *
+ah4_finaldst(m)
+ struct mbuf *m;
+{
+ struct ip *ip;
+ int optlen;
+ u_char *q;
+ int i;
+ int hlen;
+
+ if (!m)
+ panic("ah4_finaldst: m == NULL");
+ ip = mtod(m, struct ip *);
+ hlen = (ip->ip_hl << 2);
+
+ if (m->m_len < hlen) {
+ ipseclog((LOG_DEBUG,
+ "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
+ return NULL;
+ }
+
+ if (hlen == sizeof(struct ip))
+ return NULL;
+
+ optlen = hlen - sizeof(struct ip);
+ if (optlen < 0) {
+ ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
+ optlen));
+ return NULL;
+ }
+
+ q = (u_char *)(ip + 1);
+ i = 0;
+ while (i < optlen) {
+ if (i + IPOPT_OPTVAL >= optlen)
+ return NULL;
+ if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
+ q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
+ i + IPOPT_OLEN < optlen)
+ ;
+ else
+ return NULL;
+
+ switch (q[i + IPOPT_OPTVAL]) {
+ case IPOPT_EOL:
+ i = optlen; /* bye */
+ break;
+ case IPOPT_NOP:
+ i++;
+ break;
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
+ optlen - i < q[i + IPOPT_OLEN]) {
+ ipseclog((LOG_ERR,
+ "ip_finaldst: invalid IP option "
+ "(code=%02x len=%02x)\n",
+ q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
+ return NULL;
+ }
+ i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
+ return (struct in_addr *)(q + i);
+ default:
+ if (q[i + IPOPT_OLEN] < 2 ||
+ optlen - i < q[i + IPOPT_OLEN]) {
+ ipseclog((LOG_ERR,
+ "ip_finaldst: invalid IP option "
+ "(code=%02x len=%02x)\n",
+ q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
+ return NULL;
+ }
+ i += q[i + IPOPT_OLEN];
+ break;
+ }
+ }
+ return NULL;
+}
+#endif
diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c
new file mode 100644
index 0000000..ced896f
--- /dev/null
+++ b/sys/netinet6/dest6.c
@@ -0,0 +1,127 @@
+/* $FreeBSD$ */
+/* $KAME: dest6.c,v 1.34 2002/01/08 02:40:55 k-sugyou 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/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>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+/*
+ * Destination options header processing.
+ */
+int
+dest6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ 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
+ IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, sizeof(*dstopts));
+ if (dstopts == NULL)
+ return IPPROTO_DONE;
+#endif
+ dstoptlen = (dstopts->ip6d_len + 1) << 3;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, dstoptlen);
+ if (dstopts == NULL)
+ return IPPROTO_DONE;
+#endif
+ off += dstoptlen;
+ dstoptlen -= sizeof(struct ip6_dest);
+ opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest);
+
+ /* search header for all options. */
+ for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) {
+ if (*opt != IP6OPT_PAD1 &&
+ (dstoptlen < IP6OPT_MINLEN || *(opt + 1) + 2 > dstoptlen)) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+
+ switch (*opt) {
+ case IP6OPT_PAD1:
+ optlen = 1;
+ break;
+ case IP6OPT_PADN:
+ optlen = *(opt + 1) + 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);
+
+ bad:
+ m_freem(m);
+ return (IPPROTO_DONE);
+}
diff --git a/sys/netinet6/esp.h b/sys/netinet6/esp.h
new file mode 100644
index 0000000..28fe456
--- /dev/null
+++ b/sys/netinet6/esp.h
@@ -0,0 +1,109 @@
+/* $FreeBSD$ */
+/* $KAME: esp.h,v 1.19 2001/09/04 08:43:19 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.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#ifndef _NETINET6_ESP_H_
+#define _NETINET6_ESP_H_
+
+#if defined(_KERNEL) && !defined(_LKM)
+#include "opt_inet.h"
+#endif
+
+struct esp {
+ u_int32_t esp_spi; /* ESP */
+ /* variable size, 32bit bound */ /* Initialization Vector */
+ /* variable size */ /* Payload data */
+ /* variable size */ /* padding */
+ /* 8bit */ /* pad size */
+ /* 8bit */ /* next header */
+ /* 8bit */ /* next header */
+ /* variable size, 32bit bound */ /* Authentication data (new IPsec) */
+};
+
+struct newesp {
+ u_int32_t esp_spi; /* ESP */
+ u_int32_t esp_seq; /* Sequence number */
+ /* variable size */ /* (IV and) Payload data */
+ /* variable size */ /* padding */
+ /* 8bit */ /* pad size */
+ /* 8bit */ /* next header */
+ /* 8bit */ /* next header */
+ /* variable size, 32bit bound *//* Authentication data */
+};
+
+struct esptail {
+ u_int8_t esp_padlen; /* pad length */
+ u_int8_t esp_nxt; /* Next header */
+ /* variable size, 32bit bound *//* Authentication data (new IPsec)*/
+};
+
+#ifdef _KERNEL
+struct secasvar;
+
+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((const struct esp_algorithm *, struct secasvar *));
+ int (*decrypt) __P((struct mbuf *, size_t,
+ struct secasvar *, const struct esp_algorithm *, int));
+ int (*encrypt) __P((struct mbuf *, size_t, size_t,
+ 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 *));
+};
+
+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 *, int));
+extern size_t esp_hdrsiz __P((struct ipsecrequest *));
+
+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
new file mode 100644
index 0000000..d774d24
--- /dev/null
+++ b/sys/netinet6/esp6.h
@@ -0,0 +1,48 @@
+/* $FreeBSD$ */
+/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 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.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#ifndef _NETINET6_ESP6_H_
+#define _NETINET6_ESP6_H_
+
+#ifdef _KERNEL
+extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *,
+ struct ipsecrequest *));
+extern int esp6_input __P((struct mbuf **, int *, int));
+
+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
new file mode 100644
index 0000000..db09b94
--- /dev/null
+++ b/sys/netinet6/esp_core.c
@@ -0,0 +1,1136 @@
+/* $FreeBSD$ */
+/* $KAME: esp_core.c,v 1.50 2000/11/02 12:27:38 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/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netinet6/ah.h>
+#ifdef INET6
+#include <netinet6/ah6.h>
+#endif
+#include <netinet6/esp.h>
+#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 <net/net_osdep.h>
+
+static int esp_null_mature __P((struct secasvar *));
+static int esp_null_decrypt __P((struct mbuf *, size_t,
+ struct secasvar *, const struct esp_algorithm *, int));
+static int esp_null_encrypt __P((struct mbuf *, size_t, size_t,
+ struct secasvar *, const struct esp_algorithm *, int));
+static int esp_descbc_mature __P((struct secasvar *));
+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_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 },
+};
+
+const struct esp_algorithm *
+esp_algorithm_lookup(idx)
+ int idx;
+{
+
+ 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;
+{
+ 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_NOWAIT);
+ 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_mature(sav)
+ struct secasvar *sav;
+{
+
+ /* anything is okay */
+ return 0;
+}
+
+static int
+esp_null_decrypt(m, off, sav, algo, ivlen)
+ struct mbuf *m;
+ size_t off; /* offset to ESP header */
+ struct secasvar *sav;
+ const struct esp_algorithm *algo;
+ int ivlen;
+{
+
+ return 0; /* do nothing */
+}
+
+static int
+esp_null_encrypt(m, off, plen, sav, algo, ivlen)
+ struct mbuf *m;
+ size_t off; /* offset to ESP header */
+ size_t plen; /* payload length (to be encrypted) */
+ struct secasvar *sav;
+ const struct esp_algorithm *algo;
+ int ivlen;
+{
+
+ return 0; /* do nothing */
+}
+
+static int
+esp_descbc_mature(sav)
+ struct secasvar *sav;
+{
+ const struct esp_algorithm *algo;
+
+ if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) {
+ ipseclog((LOG_ERR, "esp_cbc_mature: "
+ "algorithm incompatible with 4 octets IV length\n"));
+ return 1;
+ }
+
+ if (!sav->key_enc) {
+ ipseclog((LOG_ERR, "esp_descbc_mature: no key is given.\n"));
+ return 1;
+ }
+
+ 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)));
+ return 1;
+ }
+
+ /* weak key check */
+ if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc))) {
+ ipseclog((LOG_ERR,
+ "esp_descbc_mature: weak key was passed.\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+esp_descbc_ivlen(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+
+ 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_des_schedlen(algo)
+ const struct esp_algorithm *algo;
+{
+
+ return sizeof(des_key_schedule);
+}
+
+static int
+esp_des_schedule(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+
+ if (des_key_sched((des_cblock *)_KEYBUF(sav->key_enc),
+ *(des_key_schedule *)sav->sched))
+ return EINVAL;
+ else
+ return 0;
+}
+
+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;
+{
+
+ /* 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;
+}
+
+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;
+{
+
+ /* 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
+esp_cbc_mature(sav)
+ struct secasvar *sav;
+{
+ int keylen;
+ const struct esp_algorithm *algo;
+
+ if (sav->flags & SADB_X_EXT_OLD) {
+ ipseclog((LOG_ERR,
+ "esp_cbc_mature: algorithm incompatible with esp-old\n"));
+ return 1;
+ }
+ if (sav->flags & SADB_X_EXT_DERIV) {
+ ipseclog((LOG_ERR,
+ "esp_cbc_mature: algorithm incompatible with derived\n"));
+ return 1;
+ }
+
+ 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 %s: unsupported algorithm.\n", algo->name));
+ return 1;
+ }
+
+ keylen = sav->key_enc->sadb_key_bits;
+ if (keylen < algo->keymin || algo->keymax < keylen) {
+ 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((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 %s: weak key was passed.\n",
+ algo->name));
+ return 1;
+ }
+ break;
+ 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;
+ }
+
+ return 0;
+}
+
+static int
+esp_blowfish_schedlen(algo)
+ const struct esp_algorithm *algo;
+{
+
+ return sizeof(BF_KEY);
+}
+
+static int
+esp_blowfish_schedule(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+
+ BF_set_key((BF_KEY *)sav->sched, _KEYLEN(sav->key_enc),
+ _KEYBUF(sav->key_enc));
+ return 0;
+}
+
+static int
+esp_blowfish_blockdecrypt(algo, sav, s, d)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+ u_int8_t *s;
+ u_int8_t *d;
+{
+ /* HOLY COW! BF_decrypt() 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_decrypt(t, (BF_KEY *)sav->sched);
+ t[0] = htonl(t[0]);
+ t[1] = htonl(t[1]);
+ bcopy(t, d, sizeof(t));
+ return 0;
+}
+
+static int
+esp_blowfish_blockencrypt(algo, sav, s, d)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+ u_int8_t *s;
+ u_int8_t *d;
+{
+ /* 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);
+ t[0] = htonl(t[0]);
+ t[1] = htonl(t[1]);
+ bcopy(t, d, sizeof(t));
+ return 0;
+}
+
+static int
+esp_cast128_schedlen(algo)
+ const struct esp_algorithm *algo;
+{
+
+ return sizeof(u_int32_t) * 32;
+}
+
+static int
+esp_cast128_schedule(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+
+ set_cast128_subkey((u_int32_t *)sav->sched, _KEYBUF(sav->key_enc),
+ _KEYLEN(sav->key_enc));
+ return 0;
+}
+
+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;
+{
+
+ 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;
+}
+
+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;
+{
+
+ 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;
+}
+
+static int
+esp_3des_schedlen(algo)
+ const struct esp_algorithm *algo;
+{
+
+ return sizeof(des_key_schedule) * 3;
+}
+
+static int
+esp_3des_schedule(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+ int error;
+ des_key_schedule *p;
+ int i;
+ char *k;
+
+ 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;
+ }
+ return 0;
+}
+
+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_ecb3_encrypt((des_cblock *)d, (des_cblock *)d,
+ p[0], p[1], p[2], DES_DECRYPT);
+ return 0;
+}
+
+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_ecb3_encrypt((des_cblock *)d, (des_cblock *)d,
+ p[0], p[1], p[2], DES_ENCRYPT);
+ return 0;
+}
+
+static int
+esp_common_ivlen(algo, sav)
+ const struct esp_algorithm *algo;
+ struct secasvar *sav;
+{
+
+ if (!algo)
+ panic("esp_common_ivlen: unknown algorithm");
+ return algo->ivlenval;
+}
+
+static int
+esp_cbc_decrypt(m, off, sav, algo, ivlen)
+ struct mbuf *m;
+ size_t off;
+ struct secasvar *sav;
+ const struct esp_algorithm *algo;
+ int ivlen;
+{
+ 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;
+
+ 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;
+ }
+
+ /* assumes blocklen == padbound */
+ blocklen = algo->padbound;
+
+#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
+
+ 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;
+ }
+ }
+
+ /* grab iv */
+ m_copydata(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 (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 ((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;
+ }
+
+ s = m;
+ d = d0 = dp = NULL;
+ soff = doff = sn = dn = 0;
+ ivp = sp = NULL;
+
+ /* skip bodyoff */
+ while (soff < bodyoff) {
+ if (soff + s->m_len >= bodyoff) {
+ sn = bodyoff - soff;
+ break;
+ }
+
+ soff += s->m_len;
+ s = s->m_next;
+ }
+ scut = s;
+ scutoff = sn;
+
+ /* skip over empty mbuf */
+ while (s && s->m_len == 0)
+ s = s->m_next;
+
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* decrypt */
+ (*algo->blockdecrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn);
+
+ /* xor */
+ p = ivp ? ivp : iv;
+ q = mtod(d, u_int8_t *) + dn;
+ for (i = 0; i < blocklen; i++)
+ q[i] ^= p[i];
+
+ /* next iv */
+ if (sp == sbuf) {
+ bcopy(sbuf, iv, blocklen);
+ ivp = NULL;
+ } else
+ ivp = sp;
+
+ sn += blocklen;
+ dn += blocklen;
+
+ /* find the next source block */
+ while (s && sn >= s->m_len) {
+ sn -= s->m_len;
+ soff += s->m_len;
+ s = s->m_next;
+ }
+
+ /* skip over empty mbuf */
+ while (s && s->m_len == 0)
+ s = s->m_next;
+ }
+
+ m_freem(scut->m_next);
+ scut->m_len = scutoff;
+ scut->m_next = d0;
+
+ /* just in case */
+ bzero(iv, sizeof(iv));
+ bzero(sbuf, sizeof(sbuf));
+
+ return 0;
+}
+
+static int
+esp_cbc_encrypt(m, off, plen, sav, algo, ivlen)
+ struct mbuf *m;
+ size_t off;
+ size_t plen;
+ struct secasvar *sav;
+ const struct esp_algorithm *algo;
+ int ivlen;
+{
+ 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;
+
+ 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;
+ }
+
+ /* 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;
+ }
+#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 (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 ((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;
+ }
+
+ s = m;
+ d = d0 = dp = NULL;
+ soff = doff = sn = dn = 0;
+ ivp = sp = NULL;
+
+ /* skip bodyoff */
+ while (soff < bodyoff) {
+ if (soff + s->m_len >= bodyoff) {
+ sn = bodyoff - soff;
+ break;
+ }
+
+ soff += s->m_len;
+ s = s->m_next;
+ }
+ scut = s;
+ scutoff = sn;
+
+ /* skip over empty mbuf */
+ while (s && s->m_len == 0)
+ s = s->m_next;
+
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* xor */
+ p = ivp ? ivp : iv;
+ q = sp;
+ for (i = 0; i < blocklen; i++)
+ q[i] ^= p[i];
+
+ /* encrypt */
+ (*algo->blockencrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn);
+
+ /* next iv */
+ ivp = mtod(d, u_int8_t *) + dn;
+
+ sn += blocklen;
+ dn += blocklen;
+
+ /* find the next source block */
+ while (s && sn >= s->m_len) {
+ sn -= s->m_len;
+ soff += s->m_len;
+ s = s->m_next;
+ }
+
+ /* skip over empty mbuf */
+ while (s && s->m_len == 0)
+ s = s->m_next;
+ }
+
+ 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;
+}
+
+/*------------------------------------------------------------*/
+
+/* does not free m0 on error */
+int
+esp_auth(m0, skip, length, sav, sum)
+ struct mbuf *m0;
+ size_t skip; /* offset to ESP header */
+ size_t length; /* payload length */
+ struct secasvar *sav;
+ u_char *sum;
+{
+ struct mbuf *m;
+ size_t off;
+ struct ah_algorithm_state s;
+ u_char sumbuf[AH_MAXSUMSIZE];
+ const struct ah_algorithm *algo;
+ size_t siz;
+ int error;
+
+ /* sanity checks */
+ if (m0->m_pkthdr.len < skip) {
+ ipseclog((LOG_DEBUG, "esp_auth: mbuf length < skip\n"));
+ return EINVAL;
+ }
+ if (m0->m_pkthdr.len < skip + length) {
+ ipseclog((LOG_DEBUG,
+ "esp_auth: mbuf length < skip + length\n"));
+ return EINVAL;
+ }
+ /*
+ * length of esp part (excluding authentication data) must be 4n,
+ * since nexthdr must be at offset 4n+3.
+ */
+ if (length % 4) {
+ ipseclog((LOG_ERR, "esp_auth: length is not multiple of 4\n"));
+ return EINVAL;
+ }
+ if (!sav) {
+ ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n"));
+ return EINVAL;
+ }
+ algo = ah_algorithm_lookup(sav->alg_auth);
+ if (!algo) {
+ ipseclog((LOG_ERR,
+ "esp_auth: bad ESP auth algorithm passed: %d\n",
+ sav->alg_auth));
+ return EINVAL;
+ }
+
+ m = m0;
+ off = 0;
+
+ siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1));
+ if (sizeof(sumbuf) < siz) {
+ ipseclog((LOG_DEBUG,
+ "esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n",
+ (u_long)siz));
+ return EINVAL;
+ }
+
+ /* skip the header */
+ while (skip) {
+ if (!m)
+ panic("mbuf chain?");
+ if (m->m_len <= skip) {
+ skip -= m->m_len;
+ m = m->m_next;
+ off = 0;
+ } else {
+ off = skip;
+ skip = 0;
+ }
+ }
+
+ error = (*algo->init)(&s, sav);
+ if (error)
+ return error;
+
+ while (0 < length) {
+ if (!m)
+ panic("mbuf chain?");
+
+ if (m->m_len - off < length) {
+ (*algo->update)(&s, mtod(m, u_char *) + off,
+ m->m_len - off);
+ length -= m->m_len - off;
+ m = m->m_next;
+ off = 0;
+ } else {
+ (*algo->update)(&s, mtod(m, u_char *) + off, length);
+ break;
+ }
+ }
+ (*algo->result)(&s, sumbuf);
+ bcopy(sumbuf, sum, siz); /* XXX */
+
+ return 0;
+}
diff --git a/sys/netinet6/esp_input.c b/sys/netinet6/esp_input.c
new file mode 100644
index 0000000..43f0a61
--- /dev/null
+++ b/sys/netinet6/esp_input.c
@@ -0,0 +1,981 @@
+/* $FreeBSD$ */
+/* $KAME: esp_input.c,v 1.62 2002/01/07 11:39:57 kjc 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.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <machine/cpu.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_ecn.h>
+#ifdef INET6
+#include <netinet6/ip6_ecn.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6protosw.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netinet6/ah.h>
+#ifdef INET6
+#include <netinet6/ah6.h>
+#endif
+#include <netinet6/esp.h>
+#ifdef INET6
+#include <netinet6/esp6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+#define IPLEN_FLIPPED
+
+#define ESPMAXLEN \
+ (sizeof(struct esp) < sizeof(struct newesp) \
+ ? sizeof(struct newesp) : sizeof(struct esp))
+
+#ifdef INET
+extern struct protosw inetsw[];
+
+void
+esp4_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip *ip;
+ struct esp *esp;
+ struct esptail esptail;
+ u_int32_t spi;
+ struct secasvar *sav = NULL;
+ size_t taillen;
+ u_int16_t nxt;
+ const struct esp_algorithm *algo;
+ int ivlen;
+ size_t hlen;
+ size_t esplen;
+ int proto;
+
+ /* sanity check for alignment. */
+ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) {
+ ipseclog((LOG_ERR, "IPv4 ESP input: packet alignment problem "
+ "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ if (m->m_len < off + ESPMAXLEN) {
+ m = m_pullup(m, off + ESPMAXLEN);
+ if (!m) {
+ ipseclog((LOG_DEBUG,
+ "IPv4 ESP input: can't pullup in esp4_input\n"));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+ }
+
+ ip = mtod(m, struct ip *);
+ proto = ip->ip_p;
+ esp = (struct esp *)(((u_int8_t *)ip) + off);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ /* find the sassoc. */
+ spi = esp->esp_spi;
+
+ if ((sav = key_allocsa(AF_INET,
+ (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+ IPPROTO_ESP, spi)) == 0) {
+ ipseclog((LOG_WARNING,
+ "IPv4 ESP input: no key association found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_nosa++;
+ goto bad;
+ }
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp4_input called to allocate SA:%p\n", sav));
+ if (sav->state != SADB_SASTATE_MATURE
+ && sav->state != SADB_SASTATE_DYING) {
+ ipseclog((LOG_DEBUG,
+ "IPv4 ESP input: non-mature/dying SA found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_badspi++;
+ goto bad;
+ }
+ algo = esp_algorithm_lookup(sav->alg_enc);
+ if (!algo) {
+ ipseclog((LOG_DEBUG, "IPv4 ESP input: "
+ "unsupported encryption algorithm for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsecstat.in_badspi++;
+ goto bad;
+ }
+
+ /* check if we have proper ivlen information */
+ ivlen = sav->ivlen;
+ if (ivlen < 0) {
+ ipseclog((LOG_ERR, "inproper ivlen in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay
+ && (sav->alg_auth && sav->key_auth)))
+ goto noreplaycheck;
+
+ if (sav->alg_auth == SADB_X_AALG_NULL ||
+ sav->alg_auth == SADB_AALG_NONE)
+ goto noreplaycheck;
+
+ /*
+ * check for sequence number.
+ */
+ if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav))
+ ; /* okey */
+ else {
+ ipsecstat.in_espreplay++;
+ ipseclog((LOG_WARNING,
+ "replay packet in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ goto bad;
+ }
+
+ /* check ICV */
+ {
+ u_char sum0[AH_MAXSUMSIZE];
+ u_char sum[AH_MAXSUMSIZE];
+ const struct ah_algorithm *sumalgo;
+ size_t siz;
+
+ sumalgo = ah_algorithm_lookup(sav->alg_auth);
+ if (!sumalgo)
+ goto noreplaycheck;
+ siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1));
+ if (m->m_pkthdr.len < off + ESPMAXLEN + siz) {
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+ if (AH_MAXSUMSIZE < siz) {
+ ipseclog((LOG_DEBUG,
+ "internal error: AH_MAXSUMSIZE must be larger than %lu\n",
+ (u_long)siz));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]);
+
+ if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) {
+ ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_espauthfail++;
+ goto bad;
+ }
+
+ if (bcmp(sum0, sum, siz) != 0) {
+ ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_espauthfail++;
+ goto bad;
+ }
+
+ /* strip off the authentication data */
+ m_adj(m, -siz);
+ ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+ ip->ip_len = ip->ip_len - siz;
+#else
+ ip->ip_len = htons(ntohs(ip->ip_len) - siz);
+#endif
+ m->m_flags |= M_AUTHIPDGM;
+ ipsecstat.in_espauthsucc++;
+ }
+
+ /*
+ * update sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) {
+ ipsecstat.in_espreplay++;
+ goto bad;
+ }
+ }
+
+noreplaycheck:
+
+ /* process main esp header. */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1827 */
+ esplen = sizeof(struct esp);
+ } else {
+ /* RFC 2406 */
+ if (sav->flags & SADB_X_EXT_DERIV)
+ esplen = sizeof(struct esp);
+ else
+ esplen = sizeof(struct newesp);
+ }
+
+ if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) {
+ ipseclog((LOG_WARNING,
+ "IPv4 ESP input: packet too short\n"));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ if (m->m_len < off + esplen + ivlen) {
+ m = m_pullup(m, off + esplen + ivlen);
+ if (!m) {
+ ipseclog((LOG_DEBUG,
+ "IPv4 ESP input: can't pullup in esp4_input\n"));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+ }
+
+ /*
+ * 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)) {
+ /* 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.
+ */
+ m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail),
+ (caddr_t)&esptail);
+ nxt = esptail.esp_nxt;
+ taillen = esptail.esp_padlen + sizeof(esptail);
+
+ if (m->m_pkthdr.len < taillen
+ || m->m_pkthdr.len - taillen < hlen) { /* ? */
+ ipseclog((LOG_WARNING,
+ "bad pad length in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ /* strip off the trailing pad area. */
+ m_adj(m, -taillen);
+
+#ifdef IPLEN_FLIPPED
+ ip->ip_len = ip->ip_len - taillen;
+#else
+ ip->ip_len = htons(ntohs(ip->ip_len) - taillen);
+#endif
+
+ /* was it transmitted over the IPsec tunnel SA? */
+ if (ipsec4_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) {
+ /*
+ * strip off all the headers that precedes ESP header.
+ * IP4 xx ESP IP4' payload -> IP4' payload
+ *
+ * XXX more sanity checks
+ * XXX relationship with gif?
+ */
+ u_int8_t tos;
+
+ tos = ip->ip_tos;
+ m_adj(m, off + esplen + ivlen);
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (!m) {
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+ }
+ ip = mtod(m, struct ip *);
+ /* ECN consideration. */
+ ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
+ if (!key_checktunnelsanity(sav, AF_INET,
+ (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
+ ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
+ "in IPv4 ESP input: %s %s\n",
+ ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+
+ 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 (! netisr_queue(NETISR_IP, m)) {
+ ipsecstat.in_inval++;
+ m = NULL;
+ goto bad;
+ }
+ m = NULL;
+ nxt = IPPROTO_DONE;
+ } else {
+ /*
+ * strip off ESP header and IV.
+ * even in m_pulldown case, we need to strip off ESP so that
+ * we can always compute checksum for AH correctly.
+ */
+ size_t stripsiz;
+
+ stripsiz = esplen + ivlen;
+
+ ip = mtod(m, struct ip *);
+ ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+
+ ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+ ip->ip_len = ip->ip_len - stripsiz;
+#else
+ ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
+#endif
+ ip->ip_p = nxt;
+
+ key_sa_recordxfer(sav, m);
+ if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) {
+ ipsecstat.in_nomem++;
+ goto bad;
+ }
+
+ 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);
+ } else
+ m_freem(m);
+ m = NULL;
+ }
+
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp4_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ ipsecstat.in_success++;
+ return;
+
+bad:
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp4_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ if (m)
+ m_freem(m);
+ return;
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+esp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ int off = *offp;
+ struct ip6_hdr *ip6;
+ struct esp *esp;
+ struct esptail esptail;
+ u_int32_t spi;
+ struct secasvar *sav = NULL;
+ size_t taillen;
+ u_int16_t nxt;
+ const struct esp_algorithm *algo;
+ int ivlen;
+ size_t esplen;
+
+ /* sanity check for alignment. */
+ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) {
+ ipseclog((LOG_ERR, "IPv6 ESP input: packet alignment problem "
+ "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, ESPMAXLEN, IPPROTO_DONE);
+ esp = (struct esp *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(esp, struct esp *, m, off, ESPMAXLEN);
+ if (esp == NULL) {
+ ipsec6stat.in_inval++;
+ return IPPROTO_DONE;
+ }
+#endif
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ if (ntohs(ip6->ip6_plen) == 0) {
+ ipseclog((LOG_ERR, "IPv6 ESP input: "
+ "ESP with IPv6 jumbogram is not supported.\n"));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+ /* find the sassoc. */
+ spi = esp->esp_spi;
+
+ if ((sav = key_allocsa(AF_INET6,
+ (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+ IPPROTO_ESP, spi)) == 0) {
+ ipseclog((LOG_WARNING,
+ "IPv6 ESP input: no key association found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_nosa++;
+ goto bad;
+ }
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp6_input called to allocate SA:%p\n", sav));
+ if (sav->state != SADB_SASTATE_MATURE
+ && sav->state != SADB_SASTATE_DYING) {
+ ipseclog((LOG_DEBUG,
+ "IPv6 ESP input: non-mature/dying SA found for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_badspi++;
+ goto bad;
+ }
+ algo = esp_algorithm_lookup(sav->alg_enc);
+ if (!algo) {
+ ipseclog((LOG_DEBUG, "IPv6 ESP input: "
+ "unsupported encryption algorithm for spi %u\n",
+ (u_int32_t)ntohl(spi)));
+ ipsec6stat.in_badspi++;
+ goto bad;
+ }
+
+ /* check if we have proper ivlen information */
+ ivlen = sav->ivlen;
+ if (ivlen < 0) {
+ ipseclog((LOG_ERR, "inproper ivlen in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ ipsec6stat.in_badspi++;
+ goto bad;
+ }
+
+ if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay
+ && (sav->alg_auth && sav->key_auth)))
+ goto noreplaycheck;
+
+ if (sav->alg_auth == SADB_X_AALG_NULL ||
+ sav->alg_auth == SADB_AALG_NONE)
+ goto noreplaycheck;
+
+ /*
+ * check for sequence number.
+ */
+ if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav))
+ ; /* okey */
+ else {
+ ipsec6stat.in_espreplay++;
+ ipseclog((LOG_WARNING,
+ "replay packet in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ goto bad;
+ }
+
+ /* check ICV */
+ {
+ u_char sum0[AH_MAXSUMSIZE];
+ u_char sum[AH_MAXSUMSIZE];
+ const struct ah_algorithm *sumalgo;
+ size_t siz;
+
+ sumalgo = ah_algorithm_lookup(sav->alg_auth);
+ if (!sumalgo)
+ goto noreplaycheck;
+ siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1));
+ if (m->m_pkthdr.len < off + ESPMAXLEN + siz) {
+ ipsecstat.in_inval++;
+ goto bad;
+ }
+ if (AH_MAXSUMSIZE < siz) {
+ ipseclog((LOG_DEBUG,
+ "internal error: AH_MAXSUMSIZE must be larger than %lu\n",
+ (u_long)siz));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+ m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]);
+
+ if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) {
+ ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ ipsec6stat.in_espauthfail++;
+ goto bad;
+ }
+
+ if (bcmp(sum0, sum, siz) != 0) {
+ ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ ipsec6stat.in_espauthfail++;
+ goto bad;
+ }
+
+ /* strip off the authentication data */
+ m_adj(m, -siz);
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - siz);
+
+ m->m_flags |= M_AUTHIPDGM;
+ ipsec6stat.in_espauthsucc++;
+ }
+
+ /*
+ * update sequence number.
+ */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+ if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) {
+ ipsec6stat.in_espreplay++;
+ goto bad;
+ }
+ }
+
+noreplaycheck:
+
+ /* process main esp header. */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1827 */
+ esplen = sizeof(struct esp);
+ } else {
+ /* RFC 2406 */
+ if (sav->flags & SADB_X_EXT_DERIV)
+ esplen = sizeof(struct esp);
+ else
+ esplen = sizeof(struct newesp);
+ }
+
+ if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) {
+ ipseclog((LOG_WARNING,
+ "IPv6 ESP input: packet too short\n"));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, esplen + ivlen, IPPROTO_DONE); /* XXX */
+#else
+ IP6_EXTHDR_GET(esp, struct esp *, m, off, esplen + ivlen);
+ if (esp == NULL) {
+ ipsec6stat.in_inval++;
+ m = NULL;
+ goto bad;
+ }
+#endif
+ ip6 = mtod(m, struct ip6_hdr *); /* set it again just in case */
+
+ /*
+ * 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)) {
+ /* 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;
+ }
+ ipsec6stat.in_esphist[sav->alg_enc]++;
+
+ m->m_flags |= M_DECRYPTED;
+
+ /*
+ * find the trailer of the ESP.
+ */
+ m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail),
+ (caddr_t)&esptail);
+ nxt = esptail.esp_nxt;
+ taillen = esptail.esp_padlen + sizeof(esptail);
+
+ if (m->m_pkthdr.len < taillen
+ || m->m_pkthdr.len - taillen < sizeof(struct ip6_hdr)) { /* ? */
+ ipseclog((LOG_WARNING,
+ "bad pad length in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+ /* strip off the trailing pad area. */
+ m_adj(m, -taillen);
+
+ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen);
+
+ /* was it transmitted over the IPsec tunnel SA? */
+ if (ipsec6_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) {
+ /*
+ * strip off all the headers that precedes ESP header.
+ * IP6 xx ESP IP6' payload -> IP6' payload
+ *
+ * XXX more sanity checks
+ * XXX relationship with gif?
+ */
+ u_int32_t flowinfo; /* net endian */
+ flowinfo = ip6->ip6_flow;
+ m_adj(m, off + esplen + ivlen);
+ if (m->m_len < sizeof(*ip6)) {
+#ifndef PULLDOWN_TEST
+ /*
+ * m_pullup is prohibited in KAME IPv6 input processing
+ * but there's no other way!
+ */
+#else
+ /* okay to pullup in m_pulldown style */
+#endif
+ m = m_pullup(m, sizeof(*ip6));
+ if (!m) {
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ /* ECN consideration. */
+ ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
+ if (!key_checktunnelsanity(sav, AF_INET6,
+ (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
+ ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
+ "in IPv6 ESP input: %s %s\n",
+ ipsec6_logpacketstr(ip6, spi),
+ ipsec_logsastr(sav)));
+ ipsec6stat.in_inval++;
+ goto bad;
+ }
+
+ 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 (! netisr_queue(NETISR_IPV6, m)) {
+ ipsec6stat.in_inval++;
+ m = NULL;
+ goto bad;
+ }
+ m = NULL;
+ nxt = IPPROTO_DONE;
+ } else {
+ /*
+ * strip off ESP header and IV.
+ * even in m_pulldown case, we need to strip off ESP so that
+ * we can always compute checksum for AH correctly.
+ */
+ size_t stripsiz;
+ char *prvnxtp;
+
+ /*
+ * Set the next header field of the previous header correctly.
+ */
+ prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
+ *prvnxtp = nxt;
+
+ stripsiz = esplen + ivlen;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (m->m_len >= stripsiz + off) {
+ ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+ m->m_data += stripsiz;
+ m->m_len -= stripsiz;
+ m->m_pkthdr.len -= stripsiz;
+ } else {
+ /*
+ * this comes with no copy if the boundary is on
+ * cluster
+ */
+ struct mbuf *n;
+
+ n = m_split(m, off, M_DONTWAIT);
+ if (n == NULL) {
+ /* m is retained by m_split */
+ goto bad;
+ }
+ m_adj(n, stripsiz);
+ m_cat(m, n);
+ /* m_cat does not update m_pkthdr.len */
+ m->m_pkthdr.len += n->m_pkthdr.len;
+ }
+
+#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_MOVE_PKTHDR(n, m);
+ if (n && n->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 (n->m_pkthdr.len <= maxlen) {
+ m_copydata(m, 0, n->m_pkthdr.len, mtod(n, caddr_t));
+ n->m_len = n->m_pkthdr.len;
+ n->m_next = NULL;
+ m_freem(m);
+ } else {
+ m_copydata(m, 0, maxlen, mtod(n, caddr_t));
+ n->m_len = maxlen;
+ n->m_next = m;
+ m_adj(m, maxlen);
+ }
+ 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;
+ *mp = m;
+
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp6_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ ipsec6stat.in_success++;
+ return nxt;
+
+bad:
+ if (sav) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP esp6_input call free SA:%p\n", sav));
+ key_freesav(sav);
+ }
+ if (m)
+ 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;
+ off = 0; /* calm gcc */
+ }
+
+ 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.
+ */
+ sa6_src = ip6cp->ip6c_src;
+ sa6_dst = (struct sockaddr_in6 *)sa;
+ sav = key_allocsa(AF_INET6,
+ (caddr_t)&sa6_src->sin6_addr,
+ (caddr_t)&sa6_dst->sin6_addr,
+ 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
new file mode 100644
index 0000000..0365e20
--- /dev/null
+++ b/sys/netinet6/esp_output.c
@@ -0,0 +1,720 @@
+/* $FreeBSD$ */
+/* $KAME: esp_output.c,v 1.44 2001/07/26 06:53:15 jinmei 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"
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netinet6/ah.h>
+#ifdef INET6
+#include <netinet6/ah6.h>
+#endif
+#include <netinet6/esp.h>
+#ifdef INET6
+#include <netinet6/esp6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#include <net/net_osdep.h>
+
+static int esp_output __P((struct mbuf *, u_char *, struct mbuf *,
+ struct ipsecrequest *, int));
+
+/*
+ * compute ESP header size.
+ */
+size_t
+esp_hdrsiz(isr)
+ struct ipsecrequest *isr;
+{
+ struct secasvar *sav;
+ const struct esp_algorithm *algo;
+ const struct ah_algorithm *aalgo;
+ size_t ivlen;
+ size_t authlen;
+ size_t hdrsiz;
+
+ /* sanity check */
+ if (isr == NULL)
+ panic("esp_hdrsiz: NULL was passed.");
+
+ sav = isr->sav;
+
+ if (isr->saidx.proto != IPPROTO_ESP)
+ panic("unsupported mode passed to esp_hdrsiz");
+
+ if (sav == NULL)
+ goto estimate;
+ if (sav->state != SADB_SASTATE_MATURE
+ && sav->state != SADB_SASTATE_DYING)
+ goto estimate;
+
+ /* we need transport mode ESP. */
+ algo = esp_algorithm_lookup(sav->alg_enc);
+ if (!algo)
+ goto estimate;
+ ivlen = sav->ivlen;
+ if (ivlen < 0)
+ goto estimate;
+
+ /*
+ * XXX
+ * right now we don't calcurate the padding size. simply
+ * treat the padding size as constant, for simplicity.
+ *
+ * XXX variable size padding support
+ */
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1827 */
+ hdrsiz = sizeof(struct esp) + ivlen + 9;
+ } else {
+ /* RFC 2406 */
+ 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;
+ }
+
+ return hdrsiz;
+
+ estimate:
+ /*
+ * ASSUMING:
+ * sizeof(struct newesp) > sizeof(struct esp).
+ * 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) + esp_max_ivlen() + 9 + 16;
+}
+
+/*
+ * Modify the packet so that the payload is encrypted.
+ * The mbuf (m) must start with IPv4 or IPv6 header.
+ * On failure, free the given mbuf and return NULL.
+ *
+ * on invocation:
+ * m nexthdrp md
+ * v v v
+ * IP ......... payload
+ * during the encryption:
+ * m nexthdrp mprev md
+ * v v v v
+ * IP ............... esp iv payload pad padlen nxthdr
+ * <--><-><------><--------------->
+ * esplen plen extendsiz
+ * ivlen
+ * <-----> esphlen
+ * <-> hlen
+ * <-----------------> espoff
+ */
+static int
+esp_output(m, nexthdrp, md, isr, af)
+ struct mbuf *m;
+ u_char *nexthdrp;
+ struct mbuf *md;
+ struct ipsecrequest *isr;
+ int af;
+{
+ struct mbuf *n;
+ struct mbuf *mprev;
+ struct esp *esp;
+ struct esptail *esptail;
+ struct secasvar *sav = isr->sav;
+ const struct esp_algorithm *algo;
+ u_int32_t spi;
+ u_int8_t nxt = 0;
+ size_t plen; /* payload length to be encrypted */
+ size_t espoff;
+ int ivlen;
+ 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:
+ ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af));
+ return 0; /* no change at all */
+ }
+
+ /* some sanity check */
+ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ {
+ struct ip *ip;
+
+ ip = mtod(m, struct ip *);
+ ipseclog((LOG_DEBUG, "esp4_output: internal error: "
+ "sav->replay is null: %x->%x, SPI=%u\n",
+ (u_int32_t)ntohl(ip->ip_src.s_addr),
+ (u_int32_t)ntohl(ip->ip_dst.s_addr),
+ (u_int32_t)ntohl(sav->spi)));
+ ipsecstat.out_inval++;
+ break;
+ }
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ ipseclog((LOG_DEBUG, "esp6_output: internal error: "
+ "sav->replay is null: SPI=%u\n",
+ (u_int32_t)ntohl(sav->spi)));
+ ipsec6stat.out_inval++;
+ break;
+#endif /* INET6 */
+ default:
+ panic("esp_output: should not reach here");
+ }
+ m_freem(m);
+ return EINVAL;
+ }
+
+ 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 */
+ if (ivlen < 0) {
+ panic("invalid ivlen");
+ }
+
+ {
+ /*
+ * insert ESP header.
+ * XXX inserts ESP header right after IPv4 header. should
+ * chase the header chain.
+ * XXX sequential number
+ */
+#ifdef INET
+ struct ip *ip = NULL;
+#endif
+#ifdef INET6
+ struct ip6_hdr *ip6 = NULL;
+#endif
+ size_t esplen; /* sizeof(struct esp/newesp) */
+ size_t esphlen; /* sizeof(struct esp/newesp) + ivlen */
+ size_t hlen = 0; /* ip header len */
+
+ if (sav->flags & SADB_X_EXT_OLD) {
+ /* RFC 1827 */
+ esplen = sizeof(struct esp);
+ } else {
+ /* RFC 2406 */
+ if (sav->flags & SADB_X_EXT_DERIV)
+ esplen = sizeof(struct esp);
+ else
+ esplen = sizeof(struct newesp);
+ }
+ esphlen = esplen + ivlen;
+
+ for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+ ;
+ if (mprev == NULL || mprev->m_next != md) {
+ ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n",
+ afnumber));
+ m_freem(m);
+ return EINVAL;
+ }
+
+ plen = 0;
+ for (n = md; n; n = n->m_next)
+ plen += n->m_len;
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ hlen = sizeof(*ip6);
+ break;
+#endif
+ }
+
+ /* make the packet over-writable */
+ mprev->m_next = NULL;
+ if ((md = ipsec_copypkt(md)) == NULL) {
+ m_freem(m);
+ error = ENOBUFS;
+ goto fail;
+ }
+ mprev->m_next = md;
+
+ espoff = m->m_pkthdr.len - plen;
+
+ /*
+ * grow the mbuf to accomodate ESP header.
+ * before: IP ... payload
+ * after: IP ... ESP IV payload
+ */
+ if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) {
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ error = ENOBUFS;
+ goto fail;
+ }
+ n->m_len = esphlen;
+ mprev->m_next = n;
+ n->m_next = md;
+ m->m_pkthdr.len += esphlen;
+ esp = mtod(n, struct esp *);
+ } else {
+ md->m_len += esphlen;
+ md->m_data -= esphlen;
+ m->m_pkthdr.len += esphlen;
+ esp = mtod(md, struct esp *);
+ }
+
+ nxt = *nexthdrp;
+ *nexthdrp = IPPROTO_ESP;
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
+ ip->ip_len = htons(ntohs(ip->ip_len) + esphlen);
+ else {
+ ipseclog((LOG_ERR,
+ "IPv4 ESP output: size exceeds limit\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ error = EMSGSIZE;
+ goto fail;
+ }
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ /* total packet length will be computed in ip6_output() */
+ break;
+#endif
+ }
+ }
+
+ /* initialize esp header. */
+ esp->esp_spi = spi;
+ if ((sav->flags & SADB_X_EXT_OLD) == 0) {
+ struct newesp *nesp;
+ nesp = (struct newesp *)esp;
+ if (sav->replay->count == ~0) {
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ /* XXX Is it noisy ? */
+ ipseclog((LOG_WARNING,
+ "replay counter overflowed. %s\n",
+ ipsec_logsastr(sav)));
+ stat->out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+ }
+ sav->replay->count++;
+ /*
+ * XXX sequence number must not be cycled, if the SA is
+ * installed by IKE daemon.
+ */
+ nesp->esp_seq = htonl(sav->replay->count);
+ }
+
+ {
+ /*
+ * find the last mbuf. make some room for ESP trailer.
+ */
+#ifdef INET
+ struct ip *ip = NULL;
+#endif
+ size_t padbound;
+ u_char *extend;
+ int i;
+ int randpadmax;
+
+ if (algo->padbound)
+ padbound = algo->padbound;
+ else
+ padbound = 4;
+ /* ESP packet, including nxthdr field, must be length of 4n */
+ if (padbound < 4)
+ padbound = 4;
+
+ extendsiz = padbound - (plen % padbound);
+ if (extendsiz == 1)
+ extendsiz = padbound + 1;
+
+ /* 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 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;
+ n->m_len += extendsiz;
+ m->m_pkthdr.len += extendsiz;
+ } else {
+ struct mbuf *nn;
+
+ MGET(nn, M_DONTWAIT, MT_DATA);
+ if (!nn) {
+ ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf",
+ afnumber));
+ m_freem(m);
+ error = ENOBUFS;
+ goto fail;
+ }
+ extend = mtod(nn, u_char *);
+ nn->m_len = extendsiz;
+ nn->m_next = NULL;
+ n->m_next = nn;
+ n = nn;
+ m->m_pkthdr.len += extendsiz;
+ }
+ switch (sav->flags & SADB_X_EXT_PMASK) {
+ case SADB_X_EXT_PRAND:
+ key_randomfill(extend, extendsiz);
+ break;
+ case SADB_X_EXT_PZERO:
+ bzero(extend, extendsiz);
+ break;
+ case SADB_X_EXT_PSEQ:
+ for (i = 0; i < extendsiz; i++)
+ extend[i] = (i + 1) & 0xff;
+ break;
+ }
+
+ /* initialize esp trailer. */
+ esptail = (struct esptail *)
+ (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail));
+ esptail->esp_nxt = nxt;
+ esptail->esp_padlen = extendsiz - 2;
+
+ /* modify IP header (for ESP header part only) */
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+ if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len)))
+ ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz);
+ else {
+ ipseclog((LOG_ERR,
+ "IPv4 ESP output: size exceeds limit\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ error = EMSGSIZE;
+ goto fail;
+ }
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ /* total packet length will be computed in ip6_output() */
+ break;
+#endif
+ }
+ }
+
+ /*
+ * 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"));
+ stat->out_inval++;
+ error = EINVAL;
+ goto fail;
+ }
+
+ /*
+ * calculate ICV if required.
+ */
+ if (!sav->replay)
+ goto noantireplay;
+ if (!sav->key_auth)
+ goto noantireplay;
+ 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
+
+ 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");
+
+ if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) {
+ ipseclog((LOG_ERR, "ESP checksum generation failure\n"));
+ m_freem(m);
+ error = EINVAL;
+ stat->out_inval++;
+ goto fail;
+ }
+
+ n = m;
+ while (n->m_next)
+ n = n->m_next;
+
+ if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /* XXX */
+ n->m_len += siz;
+ m->m_pkthdr.len += siz;
+ p = mtod(n, u_char *) + n->m_len - siz;
+ } else {
+ struct mbuf *nn;
+
+ MGET(nn, M_DONTWAIT, MT_DATA);
+ if (!nn) {
+ ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output",
+ afnumber));
+ m_freem(m);
+ error = ENOBUFS;
+ goto fail;
+ }
+ nn->m_len = siz;
+ nn->m_next = NULL;
+ n->m_next = nn;
+ n = nn;
+ m->m_pkthdr.len += siz;
+ p = mtod(nn, u_char *);
+ }
+ bcopy(authbuf, p, siz);
+
+ /* modify IP header (for ESP header part only) */
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+ if (siz < (IP_MAXPACKET - ntohs(ip->ip_len)))
+ ip->ip_len = htons(ntohs(ip->ip_len) + siz);
+ else {
+ ipseclog((LOG_ERR,
+ "IPv4 ESP output: size exceeds limit\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ error = EMSGSIZE;
+ goto fail;
+ }
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ /* total packet length will be computed in ip6_output() */
+ break;
+#endif
+ }
+ }
+
+noantireplay:
+ if (!m) {
+ ipseclog((LOG_ERR,
+ "NULL mbuf after encryption in esp%d_output", afnumber));
+ } else
+ stat->out_success++;
+ stat->out_esphist[sav->alg_enc]++;
+ key_sa_recordxfer(sav, m);
+ return 0;
+
+fail:
+#if 1
+ return error;
+#else
+ panic("something bad in esp_output");
+#endif
+}
+
+#ifdef INET
+int
+esp4_output(m, isr)
+ struct mbuf *m;
+ struct ipsecrequest *isr;
+{
+ struct ip *ip;
+ if (m->m_len < sizeof(struct ip)) {
+ ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n"));
+ m_freem(m);
+ return 0;
+ }
+ ip = mtod(m, struct ip *);
+ /* XXX assumes that m->m_next points to payload */
+ return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+esp6_output(m, nexthdrp, md, isr)
+ struct mbuf *m;
+ u_char *nexthdrp;
+ struct mbuf *md;
+ struct ipsecrequest *isr;
+{
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n"));
+ m_freem(m);
+ return 0;
+ }
+ return esp_output(m, nexthdrp, md, isr, AF_INET6);
+}
+#endif /* INET6 */
diff --git a/sys/netinet6/esp_rijndael.c b/sys/netinet6/esp_rijndael.c
new file mode 100644
index 0000000..950bbe8
--- /dev/null
+++ b/sys/netinet6/esp_rijndael.c
@@ -0,0 +1,117 @@
+/* $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 <netinet/in.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
new file mode 100644
index 0000000..515a9fb
--- /dev/null
+++ b/sys/netinet6/frag6.c
@@ -0,0 +1,695 @@
+/* $FreeBSD$ */
+/* $KAME: frag6.c,v 1.33 2002/01/07 11:34:48 kjc 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 <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>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Define it to get a correct behavior on per-interface statistics.
+ * You will need to perform an extra routing table lookup, per fragment,
+ * to do it. This may, or may not be, a performance hit.
+ */
+#define IN6_IFSTAT_STRICT
+
+static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *));
+static void frag6_deq __P((struct ip6asfrag *));
+static void frag6_insque __P((struct ip6q *, struct ip6q *));
+static void frag6_remque __P((struct ip6q *));
+static void frag6_freef __P((struct ip6q *));
+
+/* XXX we eventually need splreass6, or some real semaphore */
+int frag6_doing_reass;
+u_int frag6_nfragpackets;
+struct ip6q ip6q; /* ip6 reassemble queue */
+
+/* FreeBSD tweak */
+static MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header");
+
+/*
+ * Initialise reassembly queue and fragment identifier.
+ */
+void
+frag6_init()
+{
+ struct timeval tv;
+
+ ip6_maxfragpackets = nmbclusters / 4;
+
+ /*
+ * in many cases, random() here does NOT return random number
+ * as initialization during bootstrap time occur in fixed order.
+ */
+ microtime(&tv);
+ ip6_id = random() ^ tv.tv_usec;
+ ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
+}
+
+/*
+ * In RFC2460, fragment and reassembly rule do not agree with each other,
+ * in terms of next header field handling in fragment header.
+ * While the sender will use the same value for all of the fragmented packets,
+ * receiver is suggested not to check the consistency.
+ *
+ * fragment rule (p20):
+ * (2) A Fragment header containing:
+ * The Next Header value that identifies the first header of
+ * the Fragmentable Part of the original packet.
+ * -> next header field is same for all fragments
+ *
+ * reassembly rule (p21):
+ * The Next Header field of the last header of the Unfragmentable
+ * Part is obtained from the Next Header field of the first
+ * fragment's Fragment header.
+ * -> should grab it from the first fragment only
+ *
+ * The following note also contradicts with fragment rule - noone is going to
+ * send different fragment with different next header field.
+ *
+ * additional note (p22):
+ * The Next Header values in the Fragment headers of different
+ * fragments of the same original packet may differ. Only the value
+ * from the Offset zero fragment packet is used for reassembly.
+ * -> should grab it from the first fragment only
+ *
+ * There is no explicit reason given in the RFC. Historical reason maybe?
+ */
+/*
+ * Fragment input
+ */
+int
+frag6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp, *t;
+ struct ip6_hdr *ip6;
+ struct ip6_frag *ip6f;
+ struct ip6q *q6;
+ struct ip6asfrag *af6, *ip6af, *af6dwn;
+ int offset = *offp, nxt, i, next;
+ int first_frag = 0;
+ int fragoff, frgpartlen; /* must be larger than u_int16_t */
+ struct ifnet *dstifp;
+#ifdef IN6_IFSTAT_STRICT
+ static struct route_in6 ro;
+ struct sockaddr_in6 *dst;
+#endif
+
+ ip6 = mtod(m, struct ip6_hdr *);
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE);
+ ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset);
+#else
+ IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f));
+ if (ip6f == NULL)
+ return IPPROTO_DONE;
+#endif
+
+ dstifp = NULL;
+#ifdef IN6_IFSTAT_STRICT
+ /* find the destination interface of the packet. */
+ dst = (struct sockaddr_in6 *)&ro.ro_dst;
+ if (ro.ro_rt
+ && ((ro.ro_rt->rt_flags & RTF_UP) == 0
+ || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
+ RTFREE(ro.ro_rt);
+ ro.ro_rt = (struct rtentry *)0;
+ }
+ if (ro.ro_rt == NULL) {
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = ip6->ip6_dst;
+ }
+ rtalloc((struct route *)&ro);
+ if (ro.ro_rt != NULL && ro.ro_rt->rt_ifa != NULL)
+ dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp;
+#else
+ /* we are violating the spec, this is not the destination interface */
+ if ((m->m_flags & M_PKTHDR) != 0)
+ dstifp = m->m_pkthdr.rcvif;
+#endif
+
+ /* jumbo payload can't contain a fragment header */
+ if (ip6->ip6_plen == 0) {
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset);
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ return IPPROTO_DONE;
+ }
+
+ /*
+ * check whether fragment packet's fragment length is
+ * multiple of 8 octets.
+ * sizeof(struct ip6_frag) == 8
+ * sizeof(struct ip6_hdr) = 40
+ */
+ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) &&
+ (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) {
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ offsetof(struct ip6_hdr, ip6_plen));
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ return IPPROTO_DONE;
+ }
+
+ ip6stat.ip6s_fragments++;
+ in6_ifstat_inc(dstifp, ifs6_reass_reqd);
+
+ /* 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) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst))
+ break;
+
+ if (q6 == &ip6q) {
+ /*
+ * the first fragment to arrive, create a reassembly queue.
+ */
+ first_frag = 1;
+
+ /*
+ * Enforce upper bound on number of fragmented packets
+ * for which we attempt reassembly;
+ * If maxfrag is 0, never accept fragments.
+ * If maxfrag is -1, accept all fragments without limitation.
+ */
+ 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)
+ goto dropfrag;
+ bzero(q6, sizeof(*q6));
+
+ frag6_insque(q6, &ip6q);
+
+ /* ip6q_nxt will be filled afterwards, from 1st fragment */
+ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
+#ifdef notyet
+ q6->ip6q_nxtp = (u_char *)nxtp;
+#endif
+ q6->ip6q_ident = ip6f->ip6f_ident;
+ q6->ip6q_arrive = 0; /* Is it used anywhere? */
+ q6->ip6q_ttl = IPV6_FRAGTTL;
+ q6->ip6q_src = ip6->ip6_src;
+ q6->ip6q_dst = ip6->ip6_dst;
+ q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */
+ }
+
+ /*
+ * If it's the 1st fragment, record the length of the
+ * unfragmentable part and the next header of the fragment header.
+ */
+ fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
+ if (fragoff == 0) {
+ q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr)
+ - sizeof(struct ip6_frag);
+ q6->ip6q_nxt = ip6f->ip6f_nxt;
+ }
+
+ /*
+ * Check that the reassembled packet would not exceed 65535 bytes
+ * in size.
+ * If it would exceed, discard the fragment and return an ICMP error.
+ */
+ frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset;
+ if (q6->ip6q_unfrglen >= 0) {
+ /* The 1st fragment has already arrived. */
+ if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) {
+ 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);
+ }
+ }
+ else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
+ 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);
+ }
+ /*
+ * If it's the first fragment, do the above check for each
+ * fragment already stored in the reassembly queue.
+ */
+ if (fragoff == 0) {
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6dwn) {
+ af6dwn = af6->ip6af_down;
+
+ if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen >
+ IPV6_MAXPACKET) {
+ struct mbuf *merr = IP6_REASS_MBUF(af6);
+ struct ip6_hdr *ip6err;
+ int erroff = af6->ip6af_offset;
+
+ /* dequeue the fragment. */
+ frag6_deq(af6);
+ free(af6, M_FTABLE);
+
+ /* adjust pointer. */
+ ip6err = mtod(merr, struct ip6_hdr *);
+
+ /*
+ * Restore source and destination addresses
+ * in the erroneous IPv6 header.
+ */
+ ip6err->ip6_src = q6->ip6q_src;
+ ip6err->ip6_dst = q6->ip6q_dst;
+
+ icmp6_error(merr, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff - sizeof(struct ip6_frag) +
+ offsetof(struct ip6_frag, ip6f_offlg));
+ }
+ }
+ }
+
+ ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE,
+ M_DONTWAIT);
+ if (ip6af == NULL)
+ goto dropfrag;
+ bzero(ip6af, sizeof(*ip6af));
+ ip6af->ip6af_head = ip6->ip6_flow;
+ ip6af->ip6af_len = ip6->ip6_plen;
+ ip6af->ip6af_nxt = ip6->ip6_nxt;
+ ip6af->ip6af_hlim = ip6->ip6_hlim;
+ ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG;
+ ip6af->ip6af_off = fragoff;
+ ip6af->ip6af_frglen = frgpartlen;
+ ip6af->ip6af_offset = offset;
+ IP6_REASS_MBUF(ip6af) = m;
+
+ if (first_frag) {
+ af6 = (struct ip6asfrag *)q6;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6->ip6af_down)
+ if (af6->ip6af_off > ip6af->ip6af_off)
+ break;
+
+#if 0
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (af6->ip6af_up != (struct ip6asfrag *)q6) {
+ i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
+ - ip6af->ip6af_off;
+ if (i > 0) {
+ if (i >= ip6af->ip6af_frglen)
+ goto dropfrag;
+ m_adj(IP6_REASS_MBUF(ip6af), i);
+ ip6af->ip6af_off += i;
+ ip6af->ip6af_frglen -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (af6 != (struct ip6asfrag *)q6 &&
+ ip6af->ip6af_off + ip6af->ip6af_frglen > af6->ip6af_off) {
+ i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
+ if (i < af6->ip6af_frglen) {
+ af6->ip6af_frglen -= i;
+ af6->ip6af_off += i;
+ m_adj(IP6_REASS_MBUF(af6), i);
+ break;
+ }
+ af6 = af6->ip6af_down;
+ m_freem(IP6_REASS_MBUF(af6->ip6af_up));
+ frag6_deq(af6->ip6af_up);
+ }
+#else
+ /*
+ * If the incoming framgent overlaps some existing fragments in
+ * the reassembly queue, drop it, since it is dangerous to override
+ * existing fragments from a security point of view.
+ */
+ if (af6->ip6af_up != (struct ip6asfrag *)q6) {
+ i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
+ - ip6af->ip6af_off;
+ if (i > 0) {
+#if 0 /* suppress the noisy log */
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the previous fragment\n",
+ i, ip6_sprintf(&q6->ip6q_src));
+#endif
+ free(ip6af, M_FTABLE);
+ goto dropfrag;
+ }
+ }
+ if (af6 != (struct ip6asfrag *)q6) {
+ i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
+ if (i > 0) {
+#if 0 /* suppress the noisy log */
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the succeeding fragment",
+ i, ip6_sprintf(&q6->ip6q_src));
+#endif
+ free(ip6af, M_FTABLE);
+ goto dropfrag;
+ }
+ }
+#endif
+
+insert:
+
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ * Move to front of packet queue, as we are
+ * the most recently active fragmented packet.
+ */
+ frag6_enq(ip6af, af6->ip6af_up);
+#if 0 /* xxx */
+ if (q6 != ip6q.ip6q_next) {
+ frag6_remque(q6);
+ frag6_insque(q6, &ip6q);
+ }
+#endif
+ next = 0;
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = af6->ip6af_down) {
+ if (af6->ip6af_off != next) {
+ frag6_doing_reass = 0;
+ return IPPROTO_DONE;
+ }
+ next += af6->ip6af_frglen;
+ }
+ if (af6->ip6af_up->ip6af_mff) {
+ frag6_doing_reass = 0;
+ return IPPROTO_DONE;
+ }
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ ip6af = q6->ip6q_down;
+ t = m = IP6_REASS_MBUF(ip6af);
+ af6 = ip6af->ip6af_down;
+ frag6_deq(ip6af);
+ while (af6 != (struct ip6asfrag *)q6) {
+ af6dwn = af6->ip6af_down;
+ frag6_deq(af6);
+ while (t->m_next)
+ t = t->m_next;
+ t->m_next = IP6_REASS_MBUF(af6);
+ m_adj(t->m_next, af6->ip6af_offset);
+ free(af6, M_FTABLE);
+ af6 = af6dwn;
+ }
+
+ /* adjust offset to point where the original next header starts */
+ offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
+ free(ip6af, M_FTABLE);
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr));
+ ip6->ip6_src = q6->ip6q_src;
+ ip6->ip6_dst = q6->ip6q_dst;
+ nxt = q6->ip6q_nxt;
+#ifdef notyet
+ *q6->ip6q_nxtp = (u_char)(nxt & 0xff);
+#endif
+
+ /*
+ * Delete frag6 header with as a few cost as possible.
+ */
+ if (offset < m->m_len) {
+ ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag),
+ offset);
+ m->m_data += sizeof(struct ip6_frag);
+ m->m_len -= sizeof(struct ip6_frag);
+ } else {
+ /* this comes with no copy if the boundary is on cluster */
+ if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) {
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+ goto dropfrag;
+ }
+ m_adj(t, sizeof(struct ip6_frag));
+ m_cat(m, t);
+ }
+
+ /*
+ * Store NXT to the original.
+ */
+ {
+ char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */
+ *prvnxtp = nxt;
+ }
+
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+
+ if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */
+ int plen = 0;
+ for (t = m; t; t = t->m_next)
+ plen += t->m_len;
+ m->m_pkthdr.len = plen;
+ }
+
+ ip6stat.ip6s_reassembled++;
+ in6_ifstat_inc(dstifp, ifs6_reass_ok);
+
+ /*
+ * Tell launch routine the next header
+ */
+
+ *mp = m;
+ *offp = offset;
+
+ frag6_doing_reass = 0;
+ return nxt;
+
+ dropfrag:
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ ip6stat.ip6s_fragdropped++;
+ m_freem(m);
+ frag6_doing_reass = 0;
+ return IPPROTO_DONE;
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+void
+frag6_freef(q6)
+ struct ip6q *q6;
+{
+ struct ip6asfrag *af6, *down6;
+
+ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
+ af6 = down6) {
+ struct mbuf *m = IP6_REASS_MBUF(af6);
+
+ down6 = af6->ip6af_down;
+ frag6_deq(af6);
+
+ /*
+ * Return ICMP time exceeded error for the 1st fragment.
+ * Just free other fragments.
+ */
+ if (af6->ip6af_off == 0) {
+ struct ip6_hdr *ip6;
+
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* restoure source and destination addresses */
+ ip6->ip6_src = q6->ip6q_src;
+ ip6->ip6_dst = q6->ip6q_dst;
+
+ icmp6_error(m, ICMP6_TIME_EXCEEDED,
+ ICMP6_TIME_EXCEED_REASSEMBLY, 0);
+ } else
+ m_freem(m);
+ free(af6, M_FTABLE);
+ }
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+void
+frag6_enq(af6, up6)
+ struct ip6asfrag *af6, *up6;
+{
+ af6->ip6af_up = up6;
+ af6->ip6af_down = up6->ip6af_down;
+ up6->ip6af_down->ip6af_up = af6;
+ up6->ip6af_down = af6;
+}
+
+/*
+ * To frag6_enq as remque is to insque.
+ */
+void
+frag6_deq(af6)
+ struct ip6asfrag *af6;
+{
+ af6->ip6af_up->ip6af_down = af6->ip6af_down;
+ af6->ip6af_down->ip6af_up = af6->ip6af_up;
+}
+
+void
+frag6_insque(new, old)
+ struct ip6q *new, *old;
+{
+ new->ip6q_prev = old;
+ new->ip6q_next = old->ip6q_next;
+ old->ip6q_next->ip6q_prev= new;
+ old->ip6q_next = new;
+}
+
+void
+frag6_remque(p6)
+ struct ip6q *p6;
+{
+ p6->ip6q_prev->ip6q_next = p6->ip6q_next;
+ p6->ip6q_next->ip6q_prev = p6->ip6q_prev;
+}
+
+/*
+ * IPv6 reassembling timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+frag6_slowtimo()
+{
+ struct ip6q *q6;
+ int s = splnet();
+
+ frag6_doing_reass = 1;
+ q6 = ip6q.ip6q_next;
+ if (q6)
+ while (q6 != &ip6q) {
+ --q6->ip6q_ttl;
+ q6 = q6->ip6q_next;
+ if (q6->ip6q_prev->ip6q_ttl == 0) {
+ ip6stat.ip6s_fragtimeout++;
+ /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
+ frag6_freef(q6->ip6q_prev);
+ }
+ }
+ /*
+ * If we are over the maximum number of fragments
+ * (due to the limit being lowered), drain off
+ * enough to get down to the new limit.
+ */
+ 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);
+ }
+ frag6_doing_reass = 0;
+
+#if 0
+ /*
+ * Routing changes might produce a better route than we last used;
+ * make sure we notice eventually, even if forwarding only for one
+ * destination and the cache is never replaced.
+ */
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ if (ipsrcchk_rt.ro_rt) {
+ RTFREE(ipsrcchk_rt.ro_rt);
+ ipsrcchk_rt.ro_rt = 0;
+ }
+#endif
+
+ splx(s);
+}
+
+/*
+ * Drain off all datagram fragments.
+ */
+void
+frag6_drain()
+{
+ if (frag6_doing_reass)
+ return;
+ while (ip6q.ip6q_next != &ip6q) {
+ ip6stat.ip6s_fragdropped++;
+ /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
+ frag6_freef(ip6q.ip6q_next);
+ }
+}
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
new file mode 100644
index 0000000..d964b58
--- /dev/null
+++ b/sys/netinet6/icmp6.c
@@ -0,0 +1,2894 @@
+/* $FreeBSD$ */
+/* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/signalvar.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sx.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/mld6_var.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#endif
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/key.h>
+#define IPSEC
+#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;
+
+struct icmp6stat icmp6stat;
+
+extern struct inpcbhead ripcb;
+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 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 *));
+#define HAVE_PPSRATECHECK
+#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 **, 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()
+{
+ mld6_init();
+}
+
+static void
+icmp6_errcount(stat, type, code)
+ struct icmp6errstat *stat;
+ int type, code;
+{
+ switch (type) {
+ case ICMP6_DST_UNREACH:
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ stat->icp6errs_dst_unreach_noroute++;
+ return;
+ case ICMP6_DST_UNREACH_ADMIN:
+ stat->icp6errs_dst_unreach_admin++;
+ return;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ stat->icp6errs_dst_unreach_beyondscope++;
+ return;
+ case ICMP6_DST_UNREACH_ADDR:
+ stat->icp6errs_dst_unreach_addr++;
+ return;
+ case ICMP6_DST_UNREACH_NOPORT:
+ stat->icp6errs_dst_unreach_noport++;
+ return;
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ stat->icp6errs_packet_too_big++;
+ return;
+ case ICMP6_TIME_EXCEEDED:
+ switch (code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ stat->icp6errs_time_exceed_transit++;
+ return;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ stat->icp6errs_time_exceed_reassembly++;
+ return;
+ }
+ break;
+ case ICMP6_PARAM_PROB:
+ switch (code) {
+ case ICMP6_PARAMPROB_HEADER:
+ stat->icp6errs_paramprob_header++;
+ return;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ stat->icp6errs_paramprob_nextheader++;
+ return;
+ case ICMP6_PARAMPROB_OPTION:
+ stat->icp6errs_paramprob_option++;
+ return;
+ }
+ break;
+ case ND_REDIRECT:
+ stat->icp6errs_redirect++;
+ return;
+ }
+ stat->icp6errs_unknown++;
+}
+
+/*
+ * Generate an error packet of type error in response to bad IP6 packet.
+ */
+void
+icmp6_error(m, type, code, param)
+ struct mbuf *m;
+ int type, code, param;
+{
+ struct ip6_hdr *oip6, *nip6;
+ struct icmp6_hdr *icmp6;
+ u_int preplen;
+ int off;
+ int nxt;
+
+ icmp6stat.icp6s_error++;
+
+ /* count per-type-code statistics */
+ icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code);
+
+#ifdef M_DECRYPTED /*not openbsd*/
+ if (m->m_flags & M_DECRYPTED) {
+ icmp6stat.icp6s_canterror++;
+ goto freeit;
+ }
+#endif
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
+#else
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ m = m_pullup(m, sizeof(struct ip6_hdr));
+ if (m == NULL)
+ return;
+ }
+#endif
+ oip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * Multicast destination check. For unrecognized option errors,
+ * this check has already done in ip6_unknown_opt(), so we can
+ * check only for other errors.
+ */
+ if ((m->m_flags & (M_BCAST|M_MCAST) ||
+ IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
+ (type != ICMP6_PACKET_TOO_BIG &&
+ (type != ICMP6_PARAM_PROB ||
+ code != ICMP6_PARAMPROB_OPTION)))
+ goto freeit;
+
+ /* Source address check. XXX: the case of anycast source? */
+ if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
+ IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
+ goto freeit;
+
+ /*
+ * If we are about to send ICMPv6 against ICMPv6 error/redirect,
+ * don't do it.
+ */
+ nxt = -1;
+ off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
+ if (off >= 0 && nxt == IPPROTO_ICMPV6) {
+ struct icmp6_hdr *icp;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
+ icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
+ sizeof(*icp));
+ if (icp == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+ if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
+ icp->icmp6_type == ND_REDIRECT) {
+ /*
+ * ICMPv6 error
+ * Special case: for redirect (which is
+ * informational) we must not send icmp6 error.
+ */
+ icmp6stat.icp6s_canterror++;
+ goto freeit;
+ } else {
+ /* ICMPv6 informational - send the error */
+ }
+ } else {
+ /* non-ICMPv6 - send the error */
+ }
+
+ oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
+
+ /* Finally, do rate limitation check. */
+ if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
+ icmp6stat.icp6s_toofreq++;
+ goto freeit;
+ }
+
+ /*
+ * OK, ICMP6 can be generated.
+ */
+
+ if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
+ m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
+
+ preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ M_PREPEND(m, preplen, M_DONTWAIT);
+ if (m && m->m_len < preplen)
+ m = m_pullup(m, preplen);
+ if (m == NULL) {
+ nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
+ return;
+ }
+
+ nip6 = mtod(m, struct ip6_hdr *);
+ nip6->ip6_src = oip6->ip6_src;
+ nip6->ip6_dst = oip6->ip6_dst;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
+ oip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
+ oip6->ip6_dst.s6_addr16[1] = 0;
+
+ icmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ icmp6->icmp6_type = type;
+ 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 */
+
+ return;
+
+ freeit:
+ /*
+ * If we can't tell wheter or not we can generate ICMP6, free it.
+ */
+ m_freem(m);
+}
+
+/*
+ * Process a received ICMP6 message.
+ */
+int
+icmp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp, *n;
+ struct ip6_hdr *ip6, *nip6;
+ struct icmp6_hdr *icmp6, *nicmp6;
+ int off = *offp;
+ int icmp6len = m->m_pkthdr.len - *offp;
+ int code, sum, noff;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
+ /* m might change if M_LOOP. So, call mtod after this */
+#endif
+
+ /*
+ * Locate icmp6 structure in mbuf, and check
+ * that not corrupted and of at least minimum length
+ */
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (icmp6len < sizeof(struct icmp6_hdr)) {
+ icmp6stat.icp6s_tooshort++;
+ goto freeit;
+ }
+
+ /*
+ * calculate the checksum
+ */
+#ifndef PULLDOWN_TEST
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
+ if (icmp6 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ code = icmp6->icmp6_code;
+
+ if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
+ nd6log((LOG_ERR,
+ "ICMP6 checksum error(%d|%x) %s\n",
+ icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)));
+ icmp6stat.icp6s_checksum++;
+ goto freeit;
+ }
+
+ if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
+ /*
+ * Deliver very specific ICMP6 type only.
+ * This is important to deilver TOOBIG. Otherwise PMTUD
+ * will not work.
+ */
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ case ICMP6_PACKET_TOO_BIG:
+ case ICMP6_TIME_EXCEEDED:
+ break;
+ default:
+ goto freeit;
+ }
+ }
+
+ 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) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ code = PRC_UNREACH_NET;
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
+ code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ code = PRC_HOSTDEAD;
+ break;
+#ifdef COMPAT_RFC1885
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ code = PRC_UNREACH_SRCFAIL;
+ break;
+#else
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ /* I mean "source address was incorrect." */
+ code = PRC_PARAMPROB;
+ break;
+#endif
+ case ICMP6_DST_UNREACH_NOPORT:
+ code = PRC_UNREACH_PORT;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_PACKET_TOO_BIG:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
+ if (code != 0)
+ goto badcode;
+
+ code = PRC_MSGSIZE;
+
+ /*
+ * Updating the path MTU will be done after examining
+ * intermediate extension headers.
+ */
+ goto deliver;
+ break;
+
+ case ICMP6_TIME_EXCEEDED:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
+ switch (code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ code += PRC_TIMXCEED_INTRANS;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_PARAM_PROB:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
+ switch (code) {
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ code = PRC_UNREACH_PROTOCOL;
+ break;
+ case ICMP6_PARAMPROB_HEADER:
+ case ICMP6_PARAMPROB_OPTION:
+ code = PRC_PARAMPROB;
+ break;
+ default:
+ goto badcode;
+ }
+ goto deliver;
+ break;
+
+ case ICMP6_ECHO_REQUEST:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
+ if (code != 0)
+ goto badcode;
+ if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
+ /* Give up remote */
+ break;
+ }
+ if ((n->m_flags & M_EXT) != 0
+ || n->m_len < off + sizeof(struct icmp6_hdr)) {
+ struct mbuf *n0 = n;
+ const int maxlen = sizeof(*nip6) + sizeof(*nicmp6);
+ int n0len;
+
+ /*
+ * Prepare an internal mbuf. m_pullup() doesn't
+ * always copy the length we specified.
+ */
+ if (maxlen >= MCLBYTES) {
+ /* Give up remote */
+ m_freem(n0);
+ break;
+ }
+ MGETHDR(n, M_DONTWAIT, n0->m_type);
+ n0len = n0->m_pkthdr.len; /* save for use below */
+ if (n)
+ M_MOVE_PKTHDR(n, n0);
+ if (n && maxlen >= MHLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ /* Give up remote */
+ m_freem(n0);
+ break;
+ }
+ /*
+ * Copy IPv6 and ICMPv6 only.
+ */
+ nip6 = mtod(n, struct ip6_hdr *);
+ bcopy(ip6, nip6, sizeof(struct ip6_hdr));
+ nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
+ noff = sizeof(struct ip6_hdr);
+ /* new mbuf contains only ipv6+icmpv6 headers */
+ n->m_len = noff + sizeof(struct icmp6_hdr);
+ /*
+ * Adjust mbuf. ip6_plen will be adjusted in
+ * ip6_output().
+ */
+ m_adj(n0, off + sizeof(struct icmp6_hdr));
+ /* recalculate complete packet size */
+ n->m_pkthdr.len = n0len + (noff - off);
+ n->m_next = n0;
+ } else {
+ nip6 = mtod(n, struct ip6_hdr *);
+ nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
+ noff = off;
+ }
+ nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
+ nicmp6->icmp6_code = 0;
+ if (n) {
+ icmp6stat.icp6s_reflect++;
+ icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
+ icmp6_reflect(n, noff);
+ }
+ break;
+
+ case ICMP6_ECHO_REPLY:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
+ if (code != 0)
+ goto badcode;
+ break;
+
+ case MLD_LISTENER_QUERY:
+ case MLD_LISTENER_REPORT:
+ if (icmp6len < sizeof(struct mld_hdr))
+ goto badlen;
+ if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
+ else
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ mld6_input(m, off);
+ m = NULL;
+ goto freeit;
+ }
+ mld6_input(n, off);
+ /* m stays. */
+ break;
+
+ case MLD_LISTENER_DONE:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
+ if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */
+ goto badlen;
+ break; /* nothing to be done in kernel */
+
+ case MLD_MTRACE_RESP:
+ case MLD_MTRACE:
+ /* XXX: these two are experimental. not officially defind. */
+ /* XXX: per-interface statistics? */
+ break; /* just pass it to applications */
+
+ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
+ {
+ enum { WRU, FQDN } mode;
+
+ if (!icmp6_nodeinfo)
+ break;
+
+ if (icmp6len == sizeof(struct icmp6_hdr) + 4)
+ mode = WRU;
+ else if (icmp6len >= sizeof(struct icmp6_nodeinfo))
+ mode = FQDN;
+ else
+ goto badlen;
+
+#define hostnamelen strlen(hostname)
+ if (mode == FQDN) {
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
+ IPPROTO_DONE);
+#endif
+ n = m_copy(m, 0, M_COPYALL);
+ if (n)
+ n = ni6_input(n, off);
+ /* XXX meaningless if n == NULL */
+ noff = sizeof(struct ip6_hdr);
+ } else {
+ u_char *p;
+ int maxlen, maxhlen;
+
+ if ((icmp6_nodeinfo & 5) != 5)
+ break;
+
+ if (code != 0)
+ goto badcode;
+ maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4;
+ if (maxlen >= MCLBYTES) {
+ /* Give up remote */
+ break;
+ }
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n && maxlen > MHLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (!m_dup_pkthdr(n, m, M_DONTWAIT)) {
+ /*
+ * Previous code did a blind M_COPY_PKTHDR
+ * and said "just for rcvif". If true, then
+ * we could tolerate the dup failing (due to
+ * the deep copy of the tag chain). For now
+ * be conservative and just fail.
+ */
+ m_free(n);
+ n = NULL;
+ }
+ if (n == NULL) {
+ /* Give up remote */
+ break;
+ }
+ n->m_pkthdr.rcvif = NULL;
+ n->m_len = 0;
+ maxhlen = M_TRAILINGSPACE(n) - maxlen;
+ if (maxhlen > hostnamelen)
+ maxhlen = hostnamelen;
+ /*
+ * Copy IPv6 and ICMPv6 only.
+ */
+ nip6 = mtod(n, struct ip6_hdr *);
+ bcopy(ip6, nip6, sizeof(struct ip6_hdr));
+ nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
+ bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
+ p = (u_char *)(nicmp6 + 1);
+ bzero(p, 4);
+ bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */
+ noff = sizeof(struct ip6_hdr);
+ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_hdr) + 4 + maxhlen;
+ nicmp6->icmp6_type = ICMP6_WRUREPLY;
+ nicmp6->icmp6_code = 0;
+ }
+#undef hostnamelen
+ if (n) {
+ icmp6stat.icp6s_reflect++;
+ icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
+ icmp6_reflect(n, noff);
+ }
+ break;
+ }
+
+ case ICMP6_WRUREPLY:
+ if (code != 0)
+ goto badcode;
+ break;
+
+ case ND_ROUTER_SOLICIT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_router_solicit))
+ goto badlen;
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ nd6_rs_input(m, off, icmp6len);
+ m = NULL;
+ goto freeit;
+ }
+ nd6_rs_input(n, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_ROUTER_ADVERT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_router_advert))
+ goto badlen;
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ nd6_ra_input(m, off, icmp6len);
+ m = NULL;
+ goto freeit;
+ }
+ nd6_ra_input(n, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_NEIGHBOR_SOLICIT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_neighbor_solicit))
+ goto badlen;
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ nd6_ns_input(m, off, icmp6len);
+ m = NULL;
+ goto freeit;
+ }
+ nd6_ns_input(n, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_NEIGHBOR_ADVERT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_neighbor_advert))
+ goto badlen;
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ nd6_na_input(m, off, icmp6len);
+ m = NULL;
+ goto freeit;
+ }
+ nd6_na_input(n, off, icmp6len);
+ /* m stays. */
+ break;
+
+ case ND_REDIRECT:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
+ if (code != 0)
+ goto badcode;
+ if (icmp6len < sizeof(struct nd_redirect))
+ goto badlen;
+ if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
+ /* give up local */
+ icmp6_redirect_input(m, off);
+ m = NULL;
+ goto freeit;
+ }
+ icmp6_redirect_input(n, off);
+ /* m stays. */
+ break;
+
+ case ICMP6_ROUTER_RENUMBERING:
+ if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
+ code != ICMP6_ROUTER_RENUMBERING_RESULT)
+ goto badcode;
+ if (icmp6len < sizeof(struct icmp6_router_renum))
+ goto badlen;
+ break;
+
+ default:
+ 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;
+ /* deliver */
+ } else {
+ /* ICMPv6 informational: MUST not deliver */
+ break;
+ }
+ deliver:
+ 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, code;
+{
+ 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),
+ -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(-1);
+ }
+#endif
+ eip6 = (struct ip6_hdr *)(icmp6 + 1);
+
+ /* Detect the upper level protocol */
+ {
+ void (*ctlfunc) __P((int, struct sockaddr *, void *));
+ u_int8_t nxt = eip6->ip6_nxt;
+ int eoff = off + sizeof(struct icmp6_hdr) +
+ sizeof(struct ip6_hdr);
+ struct ip6ctlparam ip6cp;
+ struct in6_addr *finaldst = NULL;
+ int icmp6type = icmp6->icmp6_type;
+ struct ip6_frag *fh;
+ struct ip6_rthdr *rth;
+ struct ip6_rthdr0 *rth0;
+ int rthlen;
+
+ while (1) { /* XXX: should avoid infinite loop explicitly? */
+ struct ip6_ext *eh;
+
+ switch (nxt) {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_AH:
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, eoff +
+ sizeof(struct ip6_ext),
+ -1);
+ eh = (struct ip6_ext *)(mtod(m, caddr_t)
+ + eoff);
+#else
+ IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
+ eoff, sizeof(*eh));
+ if (eh == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return(-1);
+ }
+#endif
+
+ if (nxt == IPPROTO_AH)
+ eoff += (eh->ip6e_len + 2) << 2;
+ else
+ eoff += (eh->ip6e_len + 1) << 3;
+ nxt = eh->ip6e_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ /*
+ * When the erroneous packet contains a
+ * routing header, we should examine the
+ * header to determine the final destination.
+ * Otherwise, we can't properly update
+ * information that depends on the final
+ * destination (e.g. path MTU).
+ */
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth),
+ -1);
+ rth = (struct ip6_rthdr *)(mtod(m, caddr_t)
+ + eoff);
+#else
+ IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
+ eoff, sizeof(*rth));
+ if (rth == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return(-1);
+ }
+#endif
+ rthlen = (rth->ip6r_len + 1) << 3;
+ /*
+ * XXX: currently there is no
+ * officially defined type other
+ * than type-0.
+ * Note that if the segment left field
+ * is 0, all intermediate hops must
+ * have been passed.
+ */
+ if (rth->ip6r_segleft &&
+ rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
+ int hops;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
+ -1);
+ rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
+#else
+ IP6_EXTHDR_GET(rth0,
+ struct ip6_rthdr0 *, m,
+ eoff, rthlen);
+ if (rth0 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return(-1);
+ }
+#endif
+ /* just ignore a bogus header */
+ if ((rth0->ip6r0_len % 2) == 0 &&
+ (hops = rth0->ip6r0_len/2))
+ finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
+ }
+ eoff += rthlen;
+ nxt = rth->ip6r_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, eoff +
+ sizeof(struct ip6_frag),
+ -1);
+ fh = (struct ip6_frag *)(mtod(m, caddr_t)
+ + eoff);
+#else
+ IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
+ eoff, sizeof(*fh));
+ if (fh == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return(-1);
+ }
+#endif
+ /*
+ * Data after a fragment header is meaningless
+ * unless it is the first fragment, but
+ * we'll go to the notify label for path MTU
+ * discovery.
+ */
+ if (fh->ip6f_offlg & IP6F_OFF_MASK)
+ goto notify;
+
+ eoff += sizeof(struct ip6_frag);
+ nxt = fh->ip6f_nxt;
+ break;
+ default:
+ /*
+ * This case includes ESP and the No Next
+ * Header. In such cases going to the notify
+ * label does not have any meaning
+ * (i.e. ctlfunc will be NULL), but we go
+ * anyway since we might have to update
+ * path MTU information.
+ */
+ goto notify;
+ }
+ }
+ notify:
+#ifndef PULLDOWN_TEST
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
+ sizeof(*icmp6) + sizeof(struct ip6_hdr));
+ if (icmp6 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return(-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) {
+ 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) {
+ (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
+ &ip6cp);
+ }
+ }
+ return(0);
+
+ freeit:
+ m_freem(m);
+ return(-1);
+}
+
+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);
+
+ if (rt && (rt->rt_flags & RTF_HOST)
+ && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
+ if (mtu < IPV6_MMTU) {
+ /* xxx */
+ rt->rt_rmx.rmx_locks |= RTV_MTU;
+ } else if (mtu < rt->rt_ifp->if_mtu &&
+ rt->rt_rmx.rmx_mtu > mtu) {
+ icmp6stat.icp6s_pmtuchg++;
+ rt->rt_rmx.rmx_mtu = mtu;
+ }
+ }
+ if (rt) { /* XXX: need braces to avoid conflict with else in RTFREE. */
+ RTFREE(rt);
+ }
+}
+
+/*
+ * 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)
+ * - 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;
+ int off;
+{
+ struct icmp6_nodeinfo *ni6, *nni6;
+ struct mbuf *n = NULL;
+ u_int16_t qtype;
+ int subjlen;
+ int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
+ struct ni_reply_fqdn *fqdn;
+ int addrs; /* for NI_QTYPE_NODEADDR */
+ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
+ struct sockaddr_in6 sin6; /* 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 = NULL;
+ struct in6_ifaddr *ia6 = NULL;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+#ifndef PULLDOWN_TEST
+ ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6));
+ if (ni6 == NULL) {
+ /* m is already reclaimed */
+ return NULL;
+ }
+#endif
+
+ /*
+ * Validate IPv6 destination address.
+ *
+ * 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 ((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;
+
+ /* 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:
+ /* 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) {
+ case ICMP6_NI_SUBJ_IPV6:
+#if ICMP6_NI_SUBJ_IPV6 != 0
+ case 0:
+#endif
+ /*
+ * backward compatibility - try to accept 03 draft
+ * format, where no Subject is present.
+ */
+ if (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;
+
+ /*
+ * Validate Subject address.
+ *
+ * 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
+ * to the IPv6 destination address; validation for
+ * IPv6 destination address should have done enough
+ * check for us.
+ *
+ * We do not do proxy at this moment.
+ */
+ /* m_pulldown instead of copy? */
+ m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
+ subjlen, (caddr_t)&sin6.sin6_addr);
+ sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.rcvif,
+ &sin6.sin6_addr);
+#ifndef SCOPEDROUTING
+ in6_embedscope(&sin6.sin6_addr, &sin6, NULL, NULL);
+#endif
+ 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.
+ * basically, we should disallow queries toward IPv6
+ * destination X with subject Y, if scope(X) > scope(Y).
+ * if we allow scope(X) > scope(Y), it will result in
+ * information leakage across scope boundary.
+ */
+ goto bad;
+
+ case ICMP6_NI_SUBJ_FQDN:
+ /*
+ * Validate Subject name with gethostname(3).
+ *
+ * The behavior may need some debate, since:
+ * - we are not sure if the node has FQDN as
+ * hostname (returned by gethostname(3)).
+ * - the code does wildcard match for truncated names.
+ * however, we are not sure if we want to perform
+ * wildcard match, if gethostname(3) side has
+ * truncated hostname.
+ */
+ n = ni6_nametodns(hostname, hostnamelen, 0);
+ if (!n || n->m_next || n->m_len == 0)
+ goto bad;
+ IP6_EXTHDR_GET(subj, char *, m,
+ off + sizeof(struct icmp6_nodeinfo), subjlen);
+ if (subj == NULL)
+ goto bad;
+ if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *),
+ n->m_len)) {
+ goto bad;
+ }
+ m_freem(n);
+ n = NULL;
+ break;
+
+ case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */
+ default:
+ goto bad;
+ }
+ break;
+ }
+
+ /* 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:
+ /*
+ * 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 an mbuf to reply. */
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n == NULL) {
+ m_freem(m);
+ return(NULL);
+ }
+ M_MOVE_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...
+ */
+ goto bad;
+ }
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ goto bad;
+ }
+ }
+ n->m_pkthdr.len = n->m_len = replylen;
+
+ /* copy mbuf header and IPv6 + Node Information base headers */
+ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
+ nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
+ bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
+
+ /* qtype dependent procedure */
+ switch (qtype) {
+ case NI_QTYPE_NOOP:
+ nni6->ni_code = ICMP6_NI_SUCCESS;
+ nni6->ni_flags = 0;
+ break;
+ case NI_QTYPE_SUPTYPES:
+ {
+ 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));
+ nni6->ni_flags = 0; /* XXX: meaningless TTL */
+ fqdn->ni_fqdn_ttl = 0; /* ditto. */
+ /*
+ * XXX do we really have FQDN in variable "hostname"?
+ */
+ n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
+ if (n->m_next == NULL)
+ goto bad;
+ /* XXX we assume that n->m_next is not a chain */
+ if (n->m_next->m_next != NULL)
+ goto bad;
+ n->m_pkthdr.len += n->m_next->m_len;
+ break;
+ case NI_QTYPE_NODEADDR:
+ {
+ int lenlim, copied;
+
+ 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) +
+ sizeof(struct icmp6_nodeinfo) + copied;
+ break;
+ }
+ default:
+ break; /* XXX impossible! */
+ }
+
+ nni6->ni_type = ICMP6_NI_REPLY;
+ m_freem(m);
+ return(n);
+
+ bad:
+ m_freem(m);
+ if (n)
+ m_freem(n);
+ return(NULL);
+}
+#undef hostnamelen
+
+/*
+ * make a mbuf with DNS-encoded string. no compression support.
+ *
+ * XXX names with less than 2 dots (like "foo" or "foo.section") will be
+ * treated as truncated name (two \0 at the end). this is a wild guess.
+ */
+static struct mbuf *
+ni6_nametodns(name, namelen, old)
+ const char *name;
+ int namelen;
+ int old; /* return pascal string if non-zero */
+{
+ struct mbuf *m;
+ char *cp, *ep;
+ const char *p, *q;
+ int i, len, nterm;
+
+ if (old)
+ len = namelen + 1;
+ else
+ len = MCLBYTES;
+
+ /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m && len > MLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto fail;
+ }
+ if (!m)
+ goto fail;
+ m->m_next = NULL;
+
+ if (old) {
+ m->m_len = len;
+ *mtod(m, char *) = namelen;
+ bcopy(name, mtod(m, char *) + 1, namelen);
+ return m;
+ } else {
+ m->m_len = 0;
+ cp = mtod(m, char *);
+ ep = mtod(m, char *) + M_TRAILINGSPACE(m);
+
+ /* if not certain about my name, return empty buffer */
+ if (namelen == 0)
+ return m;
+
+ /*
+ * guess if it looks like shortened hostname, or FQDN.
+ * shortened hostname needs two trailing "\0".
+ */
+ i = 0;
+ for (p = name; p < name + namelen; p++) {
+ if (*p && *p == '.')
+ i++;
+ }
+ if (i < 2)
+ nterm = 2;
+ else
+ nterm = 1;
+
+ p = name;
+ while (cp < ep && p < name + namelen) {
+ i = 0;
+ for (q = p; q < name + namelen && *q && *q != '.'; q++)
+ i++;
+ /* result does not fit into mbuf */
+ if (cp + i + 1 >= ep)
+ goto fail;
+ /*
+ * DNS label length restriction, RFC1035 page 8.
+ * "i == 0" case is included here to avoid returning
+ * 0-length label on "foo..bar".
+ */
+ if (i <= 0 || i >= 64)
+ goto fail;
+ *cp++ = i;
+ bcopy(p, cp, i);
+ cp += i;
+ p = q;
+ if (p < name + namelen && *p == '.')
+ p++;
+ }
+ /* termination */
+ if (cp + nterm >= ep)
+ goto fail;
+ while (nterm-- > 0)
+ *cp++ = '\0';
+ m->m_len = cp - mtod(m, char *);
+ return m;
+ }
+
+ panic("should not reach here");
+ /* NOTREACHED */
+
+ fail:
+ if (m)
+ m_freem(m);
+ return NULL;
+}
+
+/*
+ * check if two DNS-encoded string matches. takes care of truncated
+ * form (with \0\0 at the end). no compression support.
+ * XXX upper/lowercase match (see RFC2065)
+ */
+static int
+ni6_dnsmatch(a, alen, b, blen)
+ const char *a;
+ int alen;
+ const char *b;
+ int blen;
+{
+ const char *a0, *b0;
+ int l;
+
+ /* simplest case - need validation? */
+ if (alen == blen && bcmp(a, b, alen) == 0)
+ return 1;
+
+ a0 = a;
+ b0 = b;
+
+ /* termination is mandatory */
+ if (alen < 2 || blen < 2)
+ return 0;
+ if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0')
+ return 0;
+ alen--;
+ blen--;
+
+ while (a - a0 < alen && b - b0 < blen) {
+ if (a - a0 + 1 > alen || b - b0 + 1 > blen)
+ return 0;
+
+ if ((signed char)a[0] < 0 || (signed char)b[0] < 0)
+ return 0;
+ /* we don't support compression yet */
+ if (a[0] >= 64 || b[0] >= 64)
+ return 0;
+
+ /* truncated case */
+ if (a[0] == 0 && a - a0 == alen - 1)
+ return 1;
+ if (b[0] == 0 && b - b0 == blen - 1)
+ return 1;
+ if (a[0] == 0 || b[0] == 0)
+ return 0;
+
+ if (a[0] != b[0])
+ return 0;
+ l = a[0];
+ if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen)
+ return 0;
+ if (bcmp(a + 1, b + 1, l) != 0)
+ return 0;
+
+ a += 1 + l;
+ b += 1 + l;
+ }
+
+ if (a - a0 == alen && b - b0 == blen)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * calculate the number of addresses to be returned in the node info reply.
+ */
+static int
+ni6_addrs(ni6, m, ifpp, subj)
+ struct icmp6_nodeinfo *ni6;
+ struct mbuf *m;
+ struct ifnet **ifpp;
+ char *subj;
+{
+ 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);
+ }
+ }
+
+ IFNET_RLOCK();
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ addrsofif = 0;
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 &&
+ IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr,
+ &ifa6->ia_addr.sin6_addr))
+ iffound = 1;
+
+ /*
+ * IPv4-mapped addresses can only be returned by a
+ * Node Information proxy, since they represent
+ * addresses of IPv4-only nodes, which perforce do
+ * not implement this protocol.
+ * [icmp-name-lookups-07, Section 5.4]
+ * So we don't support NI_NODEADDR_FLAG_COMPAT in
+ * this function at this moment.
+ */
+
+ /* What do we have to do about ::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_GLOBAL:
+ if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
+ continue;
+ break;
+ 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;
+ IFNET_RUNLOCK();
+ return(addrsofif);
+ }
+
+ addrs += addrsofif;
+ }
+ IFNET_RUNLOCK();
+
+ return(addrs);
+}
+
+static int
+ni6_store_addrs(ni6, nni6, ifp0, resid)
+ struct icmp6_nodeinfo *ni6, *nni6;
+ struct ifnet *ifp0;
+ int resid;
+{
+ 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 && !(niflags & NI_NODEADDR_FLAG_ALL))
+ return(0); /* needless to copy */
+
+ IFNET_RLOCK();
+ again:
+ for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ 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 ((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_GLOBAL:
+ if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
+ continue;
+ break;
+ 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;
+ 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;
+ IFNET_RUNLOCK();
+ 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;
+ }
+
+ IFNET_RUNLOCK();
+
+ return(copied);
+}
+
+/*
+ * XXX almost dup'ed code with rip6_input.
+ */
+static int
+icmp6_rip6_input(mp, off)
+ struct mbuf **mp;
+ int off;
+{
+ struct mbuf *m = *mp;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct in6pcb *in6p;
+ struct in6pcb *last = NULL;
+ struct sockaddr_in6 rip6src;
+ struct icmp6_hdr *icmp6;
+ struct mbuf *opts = NULL;
+
+#ifndef PULLDOWN_TEST
+ /* this is assumed to be safe. */
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
+ if (icmp6 == NULL) {
+ /* m is already reclaimed */
+ return IPPROTO_DONE;
+ }
+#endif
+
+ bzero(&rip6src, sizeof(rip6src));
+ rip6src.sin6_len = sizeof(struct sockaddr_in6);
+ rip6src.sin6_family = AF_INET6;
+ /* KAME hack: recover scopeid */
+ (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif);
+
+ LIST_FOREACH(in6p, &ripcb, inp_list)
+ {
+ if ((in6p->inp_vflag & INP_IPV6) == 0)
+ 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) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
+ continue;
+ if (in6p->in6p_icmp6filt
+ && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
+ in6p->in6p_icmp6filt))
+ continue;
+ if (last) {
+ struct mbuf *n;
+ if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS)
+ ip6_savecontrol(last, &opts, ip6, n);
+ /* strip intermediate headers */
+ m_adj(n, off);
+ 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);
+ }
+ } else
+ sorwakeup(last->in6p_socket);
+ opts = NULL;
+ }
+ }
+ last = in6p;
+ }
+ if (last) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS)
+ ip6_savecontrol(last, &opts, ip6, m);
+ /* strip intermediate headers */
+ m_adj(m, off);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src, m, opts) == 0) {
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+ } else
+ sorwakeup(last->in6p_socket);
+ } else {
+ m_freem(m);
+ ip6stat.ip6s_delivered--;
+ }
+ return IPPROTO_DONE;
+}
+
+/*
+ * Reflect the ip6 packet back to the source.
+ * OFF points to the icmp6 header, counted from the top of the mbuf.
+ */
+void
+icmp6_reflect(m, off)
+ struct mbuf *m;
+ size_t off;
+{
+ struct ip6_hdr *ip6;
+ struct icmp6_hdr *icmp6;
+ struct in6_ifaddr *ia;
+ struct in6_addr t, *src = 0;
+ 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;
+#endif
+
+ /* too short to reflect */
+ if (off < sizeof(struct ip6_hdr)) {
+ 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;
+ }
+
+ /*
+ * If there are extra headers between IPv6 and ICMPv6, strip
+ * off that header first.
+ */
+#ifdef DIAGNOSTIC
+ if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN)
+ panic("assumption failed in icmp6_reflect");
+#endif
+ if (off > sizeof(struct ip6_hdr)) {
+ size_t l;
+ struct ip6_hdr nip6;
+
+ l = off - sizeof(struct ip6_hdr);
+ m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
+ m_adj(m, l);
+ l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ if (m->m_len < l) {
+ if ((m = m_pullup(m, l)) == NULL)
+ return;
+ }
+ bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6));
+ } else /* off == sizeof(struct ip6_hdr) */ {
+ size_t l;
+ l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ if (m->m_len < l) {
+ if ((m = m_pullup(m, l)) == NULL)
+ return;
+ }
+ }
+ plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ icmp6 = (struct icmp6_hdr *)(ip6 + 1);
+ type = icmp6->icmp6_type; /* keep type for statistics */
+ code = icmp6->icmp6_code; /* ditto. */
+
+ t = ip6->ip6_dst;
+ /*
+ * ip6_input() drops a packet if its src is multicast.
+ * So, the src is never multicast.
+ */
+ ip6->ip6_dst = ip6->ip6_src;
+
+ /*
+ * 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
+ /*
+ * xxx guess MTU
+ * RFC 1885 requires that echo reply should be truncated if it
+ * does not fit in with (return) path MTU, but the description was
+ * removed in the new spec.
+ */
+ if (icmp6_reflect_rt.ro_rt == 0 ||
+ ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) {
+ if (icmp6_reflect_rt.ro_rt) {
+ RTFREE(icmp6_reflect_rt.ro_rt);
+ icmp6_reflect_rt.ro_rt = 0;
+ }
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_addr = ip6->ip6_dst;
+
+ rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
+ RTF_PRCLONING);
+ }
+
+ if (icmp6_reflect_rt.ro_rt == 0)
+ goto bad;
+
+ if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST)
+ && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu)
+ mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu;
+
+ if (mtu < m->m_pkthdr.len) {
+ plen -= (m->m_pkthdr.len - mtu);
+ m_adj(m, mtu - m->m_pkthdr.len);
+ }
+#endif
+ /*
+ * If the incoming packet was addressed directly to us(i.e. unicast),
+ * use dst as the src for the reply.
+ * The IN6_IFF_NOTREADY case would be VERY rare, but is possible
+ * (for example) when we encounter an error while forwarding procedure
+ * destined to a duplicated address of ours.
+ */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
+ (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
+ src = &t;
+ break;
+ }
+ if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
+ /*
+ * This is the case if the dst is our link-local address
+ * and the sender is also ourselves.
+ */
+ src = &t;
+ }
+
+ 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 based on the
+ * source address of the erroneous packet.
+ */
+ 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;
+
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ if (m->m_pkthdr.rcvif) {
+ /* XXX: This may not be the outgoing interface */
+ 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
+ */
+
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+
+#ifdef COMPAT_RFC1885
+ ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif, NULL);
+#else
+ ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL);
+#endif
+ if (outif)
+ icmp6_ifoutstat_inc(outif, type, code);
+
+ return;
+
+ bad:
+ m_freem(m);
+ return;
+}
+
+void
+icmp6_fasttimo()
+{
+
+ mld6_fasttimeo();
+}
+
+static const char *
+icmp6_redirect_diag(src6, dst6, tgt6)
+ struct in6_addr *src6;
+ struct in6_addr *dst6;
+ struct in6_addr *tgt6;
+{
+ static char buf[1024];
+ snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
+ ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
+ return buf;
+}
+
+void
+icmp6_redirect_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_redirect *nd_rd;
+ int icmp6len = ntohs(ip6->ip6_plen);
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+ u_char *redirhdr = NULL;
+ int redirhdrlen = 0;
+ struct rtentry *rt = NULL;
+ int is_router;
+ int is_onlink;
+ struct in6_addr src6 = ip6->ip6_src;
+ struct in6_addr redtgt6;
+ struct in6_addr reddst6;
+ union nd_opts ndopts;
+
+ if (!m || !ifp)
+ return;
+
+ /* XXX if we are router, we don't update route by icmp6 redirect */
+ if (ip6_forwarding)
+ goto freeit;
+ if (!icmp6_rediraccept)
+ goto freeit;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
+ if (nd_rd == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+ redtgt6 = nd_rd->nd_rd_target;
+ reddst6 = nd_rd->nd_rd_dst;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
+ redtgt6.s6_addr16[1] = htons(ifp->if_index);
+ if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
+ reddst6.s6_addr16[1] = htons(ifp->if_index);
+
+ /* validation */
+ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "must be from linklocal\n", ip6_sprintf(&src6)));
+ goto bad;
+ }
+ if (ip6->ip6_hlim != 255) {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "hlim=%d (must be 255)\n",
+ ip6_sprintf(&src6), ip6->ip6_hlim));
+ goto bad;
+ }
+ {
+ /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
+ struct sockaddr_in6 sin6;
+ struct in6_addr *gw6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+ if (rt) {
+ if (rt->rt_gateway == NULL ||
+ rt->rt_gateway->sa_family != AF_INET6) {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect rejected; no route "
+ "with inet6 gateway found for redirect dst: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+ RTFREE(rt);
+ goto bad;
+ }
+
+ gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
+ if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
+ 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)));
+ RTFREE(rt);
+ goto bad;
+ }
+ } else {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "no route found for redirect dst: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+ goto bad;
+ }
+ RTFREE(rt);
+ rt = NULL;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "redirect dst must be unicast: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+ goto bad;
+ }
+
+ is_router = is_onlink = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
+ is_router = 1; /* router case */
+ if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
+ is_onlink = 1; /* on-link destination case */
+ if (!is_router && !is_onlink) {
+ nd6log((LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "neither router case nor onlink case: %s\n",
+ 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) {
+ nd6log((LOG_INFO, "icmp6_redirect_input: "
+ "invalid ND option, rejected: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+ /* nd6_options have incremented stats */
+ goto freeit;
+ }
+
+ if (ndopts.nd_opts_tgt_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+ }
+
+ if (ndopts.nd_opts_rh) {
+ redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len;
+ redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ 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)));
+ goto bad;
+ }
+
+ /* RFC 2461 8.3 */
+ nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
+ is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
+
+ if (!is_onlink) { /* better router case. perform rtredirect. */
+ /* perform rtredirect */
+ struct sockaddr_in6 sdst;
+ struct sockaddr_in6 sgw;
+ struct sockaddr_in6 ssrc;
+
+ bzero(&sdst, sizeof(sdst));
+ bzero(&sgw, sizeof(sgw));
+ bzero(&ssrc, sizeof(ssrc));
+ sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
+ sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
+ sizeof(struct sockaddr_in6);
+ bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
+ bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
+ bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
+ rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
+ (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
+ (struct sockaddr *)&ssrc,
+ (struct rtentry **)NULL);
+ }
+ /* finally update cached route in each socket via pfctlinput */
+ {
+ struct sockaddr_in6 sdst;
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin6_family = AF_INET6;
+ sdst.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
+ pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
+#ifdef IPSEC
+ key_sa_routechange((struct sockaddr *)&sdst);
+#endif
+ }
+
+ freeit:
+ m_freem(m);
+ return;
+
+ bad:
+ icmp6stat.icp6s_badredirect++;
+ m_freem(m);
+}
+
+void
+icmp6_redirect_output(m0, rt)
+ struct mbuf *m0;
+ struct rtentry *rt;
+{
+ struct ifnet *ifp; /* my outgoing interface */
+ struct in6_addr *ifp_ll6;
+ struct in6_addr *router_ll6;
+ struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */
+ struct mbuf *m = NULL; /* newly allocated one */
+ struct ip6_hdr *ip6; /* m as struct ip6_hdr */
+ struct nd_redirect *nd_rd;
+ size_t maxlen;
+ u_char *p;
+ struct ifnet *outif = NULL;
+ struct sockaddr_in6 src_sa;
+
+ icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
+
+ /* if we are not router, we don't send icmp6 redirect */
+ if (!ip6_forwarding || ip6_accept_rtadv)
+ goto fail;
+
+ /* sanity check */
+ if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
+ goto fail;
+
+ /*
+ * Address check:
+ * the source address must identify a neighbor, and
+ * the destination address must not be a multicast address
+ * [RFC 2461, sec 8.2]
+ */
+ sip6 = mtod(m0, struct ip6_hdr *);
+ bzero(&src_sa, sizeof(src_sa));
+ src_sa.sin6_family = AF_INET6;
+ src_sa.sin6_len = sizeof(src_sa);
+ src_sa.sin6_addr = sip6->ip6_src;
+ /* we don't currently use sin6_scope_id, but eventually use it */
+ src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src);
+ if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
+ goto fail;
+ if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
+ goto fail; /* what should we do here? */
+
+ /* rate limit */
+ if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
+ goto fail;
+
+ /*
+ * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
+ * we almost always ask for an mbuf cluster for simplicity.
+ * (MHLEN < IPV6_MMTU is almost always true)
+ */
+#if IPV6_MMTU >= MCLBYTES
+# error assumption failed about IPV6_MMTU and MCLBYTES
+#endif
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m && IPV6_MMTU >= MHLEN)
+ MCLGET(m, M_DONTWAIT);
+ if (!m)
+ goto fail;
+ 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) +
+ ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
+ goto fail;
+ }
+
+ {
+ /* get ip6 linklocal address for ifp(my outgoing interface). */
+ struct in6_ifaddr *ia;
+ if ((ia = in6ifa_ifpforlinklocal(ifp,
+ IN6_IFF_NOTREADY|
+ IN6_IFF_ANYCAST)) == NULL)
+ goto fail;
+ ifp_ll6 = &ia->ia_addr.sin6_addr;
+ }
+
+ /* get ip6 linklocal address for the router. */
+ if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
+ router_ll6 = &sin6->sin6_addr;
+ if (!IN6_IS_ADDR_LINKLOCAL(router_ll6))
+ router_ll6 = (struct in6_addr *)NULL;
+ } else
+ router_ll6 = (struct in6_addr *)NULL;
+
+ /* ip6 */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ /* ip6->ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ /* ip6->ip6_src must be linklocal addr for my outgoing if. */
+ bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
+ bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
+
+ /* ND Redirect */
+ nd_rd = (struct nd_redirect *)(ip6 + 1);
+ nd_rd->nd_rd_type = ND_REDIRECT;
+ nd_rd->nd_rd_code = 0;
+ nd_rd->nd_rd_reserved = 0;
+ if (rt->rt_flags & RTF_GATEWAY) {
+ /*
+ * nd_rd->nd_rd_target must be a link-local address in
+ * better router cases.
+ */
+ if (!router_ll6)
+ goto fail;
+ bcopy(router_ll6, &nd_rd->nd_rd_target,
+ sizeof(nd_rd->nd_rd_target));
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
+ sizeof(nd_rd->nd_rd_dst));
+ } else {
+ /* make sure redtgt == reddst */
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
+ sizeof(nd_rd->nd_rd_target));
+ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
+ sizeof(nd_rd->nd_rd_dst));
+ }
+
+ p = (u_char *)(nd_rd + 1);
+
+ if (!router_ll6)
+ goto nolladdropt;
+
+ {
+ /* target lladdr option */
+ struct rtentry *rt_router = NULL;
+ int len;
+ struct sockaddr_dl *sdl;
+ struct nd_opt_hdr *nd_opt;
+ char *lladdr;
+
+ rt_router = nd6_lookup(router_ll6, 0, ifp);
+ if (!rt_router)
+ goto nolladdropt;
+ len = sizeof(*nd_opt) + ifp->if_addrlen;
+ len = (len + 7) & ~7; /* round by 8 */
+ /* safety check */
+ if (len + (p - (u_char *)ip6) > maxlen)
+ goto nolladdropt;
+ if (!(rt_router->rt_flags & RTF_GATEWAY) &&
+ (rt_router->rt_flags & RTF_LLINFO) &&
+ (rt_router->rt_gateway->sa_family == AF_LINK) &&
+ (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) &&
+ sdl->sdl_alen) {
+ nd_opt = (struct nd_opt_hdr *)p;
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ nd_opt->nd_opt_len = len >> 3;
+ lladdr = (char *)(nd_opt + 1);
+ bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
+ p += len;
+ }
+ }
+nolladdropt:;
+
+ m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+
+ /* just to be safe */
+#ifdef M_DECRYPTED /*not openbsd*/
+ if (m0->m_flags & M_DECRYPTED)
+ goto noredhdropt;
+#endif
+ if (p - (u_char *)ip6 > maxlen)
+ goto noredhdropt;
+
+ {
+ /* redirected header option */
+ int len;
+ struct nd_opt_rd_hdr *nd_opt_rh;
+
+ /*
+ * compute the maximum size for icmp6 redirect header option.
+ * XXX room for auth header?
+ */
+ len = maxlen - (p - (u_char *)ip6);
+ len &= ~7;
+
+ /* This is just for simplicity. */
+ if (m0->m_pkthdr.len != m0->m_len) {
+ if (m0->m_next) {
+ m_freem(m0->m_next);
+ m0->m_next = NULL;
+ }
+ m0->m_pkthdr.len = m0->m_len;
+ }
+
+ /*
+ * Redirected header option spec (RFC2461 4.6.3) talks nothing
+ * about padding/truncate rule for the original IP packet.
+ * From the discussion on IPv6imp in Feb 1999, the consensus was:
+ * - "attach as much as possible" is the goal
+ * - pad if not aligned (original size can be guessed by original
+ * ip6 header)
+ * Following code adds the padding if it is simple enough,
+ * and truncates if not.
+ */
+ if (m0->m_next || m0->m_pkthdr.len != m0->m_len)
+ panic("assumption failed in %s:%d", __FILE__, __LINE__);
+
+ if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
+ /* not enough room, truncate */
+ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
+ } else {
+ /* enough room, pad or truncate */
+ size_t extra;
+
+ extra = m0->m_pkthdr.len % 8;
+ if (extra) {
+ /* pad if easy enough, truncate if not */
+ if (8 - extra <= M_TRAILINGSPACE(m0)) {
+ /* pad */
+ m0->m_len += (8 - extra);
+ m0->m_pkthdr.len += (8 - extra);
+ } else {
+ /* truncate */
+ m0->m_pkthdr.len -= extra;
+ m0->m_len -= extra;
+ }
+ }
+ len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
+ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
+ }
+
+ nd_opt_rh = (struct nd_opt_rd_hdr *)p;
+ bzero(nd_opt_rh, sizeof(*nd_opt_rh));
+ nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
+ nd_opt_rh->nd_opt_rh_len = len >> 3;
+ p += sizeof(*nd_opt_rh);
+ m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
+
+ /* connect m0 to m */
+ m_tag_delete_chain(m0, NULL);
+ m0->m_flags &= ~M_PKTHDR;
+ m->m_next = m0;
+ m->m_pkthdr.len = m->m_len + m0->m_len;
+ m0 = NULL;
+ }
+noredhdropt:;
+ if (m0) {
+ m_freem(m0);
+ m0 = NULL;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
+ sip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
+ sip6->ip6_dst.s6_addr16[1] = 0;
+#if 0
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = 0;
+#endif
+ if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
+ nd_rd->nd_rd_target.s6_addr16[1] = 0;
+ if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
+ nd_rd->nd_rd_dst.s6_addr16[1] = 0;
+
+ ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
+
+ nd_rd->nd_rd_cksum = 0;
+ nd_rd->nd_rd_cksum
+ = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
+
+ /* send the packet to outside... */
+ ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_redirect);
+ }
+ icmp6stat.icp6s_outhist[ND_REDIRECT]++;
+
+ return;
+
+fail:
+ if (m)
+ m_freem(m);
+ if (m0)
+ m_freem(m0);
+}
+
+#ifdef HAVE_NRL_INPCB
+#define sotoin6pcb sotoinpcb
+#define in6pcb inpcb
+#define in6p_icmp6filt inp_icmp6filt
+#endif
+/*
+ * ICMPv6 socket option processing.
+ */
+int
+icmp6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error = 0;
+ int optlen;
+ struct inpcb *inp = sotoinpcb(so);
+ int level, op, optname;
+
+ if (sopt) {
+ level = sopt->sopt_level;
+ op = sopt->sopt_dir;
+ optname = sopt->sopt_name;
+ optlen = sopt->sopt_valsize;
+ } else
+ level = op = optname = optlen = 0;
+
+ if (level != IPPROTO_ICMPV6) {
+ return EINVAL;
+ }
+
+ switch (op) {
+ case PRCO_SETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+ struct icmp6_filter *p;
+
+ if (optlen != sizeof(*p)) {
+ error = EMSGSIZE;
+ break;
+ }
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
+ optlen);
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ case PRCO_GETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyout(sopt, inp->in6p_icmp6filt,
+ sizeof(struct icmp6_filter));
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+
+ 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
+
+/*
+ * ppsratecheck(): packets (or events) per second limitation.
+ */
+static int
+ppsratecheck(lasttime, curpps, maxpps)
+ struct timeval *lasttime;
+ int *curpps;
+ int maxpps; /* maximum pps allowed */
+{
+ struct timeval tv, delta;
+ int s, rv;
+
+ s = splclock();
+ microtime(&tv);
+ splx(s);
+
+ timersub(&tv, lasttime, &delta);
+
+ /*
+ * Check for 0,0 so that the message will be seen at least once.
+ * If more than one second has 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
+
+ return (rv);
+}
+#endif
+
+/*
+ * Perform rate limit check.
+ * Returns 0 if it is okay to send the icmp6 packet.
+ * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
+ * limitation.
+ *
+ * XXX per-destination/type check necessary?
+ */
+static int
+icmp6_ratelimit(dst, type, code)
+ const struct in6_addr *dst; /* not used at this moment */
+ const int type; /* not used at this moment */
+ const int code; /* not used at this moment */
+{
+ int ret;
+
+ ret = 0; /* okay to send */
+
+ /* PPS limit */
+ if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
+ icmp6errppslim)) {
+ /* The packet is subject to rate limit */
+ ret++;
+ }
+
+ return ret;
+}
diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h
new file mode 100644
index 0000000..a6414ef
--- /dev/null
+++ b/sys/netinet6/icmp6.h
@@ -0,0 +1,4 @@
+/* $FreeBSD$ */
+/* $KAME: icmp6.h,v 1.17 2000/06/11 17:23:40 jinmei Exp $ */
+
+#error "netinet6/icmp6.h is obsolete. use netinet/icmp6.h"
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
new file mode 100644
index 0000000..945ab65
--- /dev/null
+++ b/sys/netinet6/in6.c
@@ -0,0 +1,2455 @@
+/* $FreeBSD$ */
+/* $KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#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 <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/mld6_var.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/scope6_var.h>
+#ifndef SCOPEDROUTING
+#include <netinet6/in6_pcb.h>
+#endif
+
+#include <net/net_osdep.h>
+
+MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address");
+
+/*
+ * Definitions of some costant IP6 addresses.
+ */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+const struct in6_addr in6addr_nodelocal_allnodes =
+ IN6ADDR_NODELOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allnodes =
+ IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allrouters =
+ IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+
+const struct in6_addr in6mask0 = IN6MASK0;
+const struct in6_addr in6mask32 = IN6MASK32;
+const struct in6_addr in6mask64 = IN6MASK64;
+const struct in6_addr in6mask96 = IN6MASK96;
+const struct in6_addr in6mask128 = IN6MASK128;
+
+const struct 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 thread *));
+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 */
+
+int (*faithprefix_p)(struct in6_addr *);
+
+/*
+ * 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 all1_sa;
+ struct rtentry *nrt = NULL;
+ int e;
+
+ bzero(&all1_sa, sizeof(all1_sa));
+ all1_sa.sin6_family = AF_INET6;
+ all1_sa.sin6_len = sizeof(struct sockaddr_in6);
+ all1_sa.sin6_addr = in6mask128;
+
+ /*
+ * 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.
+ */
+ 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 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);
+ IFAREF(ifa);
+ nrt->rt_ifa = ifa;
+ }
+
+ /*
+ * 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) {
+ RTFREE(nrt);
+ } else {
+ /* the cmd must be RTM_ADD here */
+ nrt->rt_refcnt--;
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ 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--;
+}
+
+/*
+ * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
+ * if it exists.
+ */
+static void
+in6_ifremloop(struct ifaddr *ifa)
+{
+ struct in6_ifaddr *ia;
+ struct rtentry *rt;
+ int ia_count = 0;
+
+ /*
+ * Some of BSD variants do not remove cloned routes
+ * from an interface direct route, when removing the direct route
+ * (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().
+ */
+
+ /*
+ * 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) {
+ /*
+ * 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);
+ }
+ }
+}
+
+int
+in6_ifindex2scopeid(idx)
+ int idx;
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ struct sockaddr_in6 *sin6;
+
+ if (idx < 0 || if_index < idx)
+ return -1;
+ ifp = ifnet_byindex(idx);
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+ return sin6->sin6_scope_id & 0xffff;
+ }
+
+ return -1;
+}
+
+int
+in6_mask2len(mask, lim0)
+ struct in6_addr *mask;
+ u_char *lim0;
+{
+ 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 (p < lim) {
+ for (y = 0; y < 8; y++) {
+ 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;
+}
+
+void
+in6_len2mask(mask, len)
+ struct in6_addr *mask;
+ int len;
+{
+ int i;
+
+ bzero(mask, sizeof(*mask));
+ for (i = 0; i < len / 8; i++)
+ mask->s6_addr8[i] = 0xff;
+ if (len % 8)
+ mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff;
+}
+
+#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa))
+#define ia62ifa(ia6) (&((ia6)->ia_ifa))
+
+int
+in6_control(so, cmd, data, ifp, td)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct thread *td;
+{
+ struct in6_ifreq *ifr = (struct in6_ifreq *)data;
+ struct in6_ifaddr *ia = NULL;
+ struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
+ int privileged;
+
+ privileged = 0;
+ if (td == NULL || !suser(td))
+ privileged++;
+
+ switch (cmd) {
+ case SIOCGETSGCNT_IN6:
+ case SIOCGETMIFCNT_IN6:
+ return (mrt6_ioctl(cmd, data));
+ }
+
+ if (ifp == NULL)
+ return(EOPNOTSUPP);
+
+ switch (cmd) {
+ case SIOCSNDFLUSH_IN6:
+ case SIOCSPFXFLUSH_IN6:
+ case SIOCSRTRFLUSH_IN6:
+ case SIOCSDEFIFACE_IN6:
+ case SIOCSIFINFO_FLAGS:
+ if (!privileged)
+ return(EPERM);
+ /* fall through */
+ case OSIOCGIFINFO_IN6:
+ case SIOCGIFINFO_IN6:
+ case SIOCGDRLST_IN6:
+ case SIOCGPRLST_IN6:
+ case SIOCGNBRINFO_IN6:
+ case SIOCGDEFIFACE_IN6:
+ return(nd6_ioctl(cmd, data, ifp));
+ }
+
+ switch (cmd) {
+ case SIOCSIFPREFIX_IN6:
+ case SIOCDIFPREFIX_IN6:
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ case SIOCSGIFPREFIX_IN6:
+ case SIOCGIFPREFIX_IN6:
+ log(LOG_NOTICE,
+ "prefix ioctls are now invalidated. "
+ "please use ifconfig.\n");
+ return(EOPNOTSUPP);
+ }
+
+ switch (cmd) {
+ case SIOCSSCOPE6:
+ if (!privileged)
+ return(EPERM);
+ return(scope6_set(ifp, ifr->ifr_ifru.ifru_scope_id));
+ break;
+ case SIOCGSCOPE6:
+ return(scope6_get(ifp, ifr->ifr_ifru.ifru_scope_id));
+ break;
+ case SIOCGSCOPE6DEF:
+ return(scope6_get_default(ifr->ifr_ifru.ifru_scope_id));
+ break;
+ }
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ if (!privileged)
+ return(EPERM);
+ /* fall through */
+ case SIOCGLIFADDR:
+ return in6_lifaddr_ioctl(so, cmd, data, ifp, td);
+ }
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */
+ struct sockaddr_in6 *sa6 =
+ (struct sockaddr_in6 *)&ifra->ifra_addr;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
+ if (sa6->sin6_addr.s6_addr16[1] == 0) {
+ /* 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); /* link ID contradicts */
+ }
+ if (sa6->sin6_scope_id) {
+ if (sa6->sin6_scope_id !=
+ (u_int32_t)ifp->if_index)
+ return(EINVAL);
+ sa6->sin6_scope_id = 0; /* XXX: good way? */
+ }
+ }
+ ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
+ }
+
+ 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 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 preferable.
+ */
+ if (ia == NULL)
+ return(EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+ case SIOCAIFADDR_IN6:
+ /*
+ * We always require users to specify a valid IPv6 address for
+ * the corresponding operation.
+ */
+ if (ifra->ifra_addr.sin6_family != AF_INET6 ||
+ ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6))
+ return(EAFNOSUPPORT);
+ if (!privileged)
+ return(EPERM);
+
+ break;
+
+ case SIOCGIFADDR_IN6:
+ /* This interface is basically deprecated. use SIOCGIFCONF. */
+ /* fall through */
+ case SIOCGIFAFLAG_IN6:
+ case SIOCGIFNETMASK_IN6:
+ case SIOCGIFDSTADDR_IN6:
+ case SIOCGIFALIFETIME_IN6:
+ /* must think again about its semantics */
+ if (ia == NULL)
+ return(EADDRNOTAVAIL);
+ break;
+ case SIOCSIFALIFETIME_IN6:
+ {
+ struct in6_addrlifetime *lt;
+
+ if (!privileged)
+ return(EPERM);
+ if (ia == NULL)
+ return(EADDRNOTAVAIL);
+ /* sanity for overflow - beware unsigned */
+ lt = &ifr->ifr_ifru.ifru_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;
+ }
+ }
+
+ switch (cmd) {
+
+ case SIOCGIFADDR_IN6:
+ ifr->ifr_addr = ia->ia_addr;
+ break;
+
+ case SIOCGIFDSTADDR_IN6:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return(EINVAL);
+ /*
+ * XXX: should we check if ifa_dstaddr is NULL and return
+ * an error?
+ */
+ ifr->ifr_dstaddr = ia->ia_dstaddr;
+ break;
+
+ case SIOCGIFNETMASK_IN6:
+ ifr->ifr_addr = ia->ia_prefixmask;
+ break;
+
+ case SIOCGIFAFLAG_IN6:
+ ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags;
+ break;
+
+ case SIOCGIFSTAT_IN6:
+ if (ifp == NULL)
+ return EINVAL;
+ if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax
+ || in6_ifstat[ifp->if_index] == NULL) {
+ /* return EAFNOSUPPORT? */
+ bzero(&ifr->ifr_ifru.ifru_stat,
+ sizeof(ifr->ifr_ifru.ifru_stat));
+ } else
+ ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index];
+ break;
+
+ case SIOCGIFSTAT_ICMP6:
+ if (ifp == NULL)
+ return EINVAL;
+ if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax ||
+ icmp6_ifstat[ifp->if_index] == NULL) {
+ /* return EAFNOSUPPORT? */
+ bzero(&ifr->ifr_ifru.ifru_stat,
+ sizeof(ifr->ifr_ifru.ifru_icmp6stat));
+ } else
+ ifr->ifr_ifru.ifru_icmp6stat =
+ *icmp6_ifstat[ifp->if_index];
+ break;
+
+ case SIOCGIFALIFETIME_IN6:
+ ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
+ break;
+
+ case SIOCSIFALIFETIME_IN6:
+ ia->ia6_lifetime = ifr->ifr_ifru.ifru_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;
+ break;
+
+ 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);
+
+ /*
+ * 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.
+ */
+
+ /*
+ * 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 an API to set prefix (not address)
+ * lifetimes, we just use the same lifetimes as addresses.
+ * The (temporarily) installed lifetimes can be overridden by
+ * later advertised RAs (when accept_rtadv is non 0), which is
+ * an intended 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);
+ }
+ }
+ }
+
+ /*
+ * this might affect the status of autoconfigured
+ * addresses, that is, this address might make
+ * other addresses detached.
+ */
+ pfxlist_onlink_check();
+ }
+ break;
+ }
+
+ 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;
+ }
+
+ 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;
+
+ /* Validate parameters */
+ if (ifp == NULL || ifra == NULL) /* this maybe redundant */
+ return(EINVAL);
+
+ /*
+ * 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 {
+ /*
+ * In this case, ia must not be NULL. We just use its prefix
+ * length.
+ */
+ 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 following log might be noisy, but this is a typical
+ * configuration mistake or a tool's bug.
+ */
+ 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 this is a new address, allocate a new ifaddr and link it
+ * into chains.
+ */
+ if (ia == NULL) {
+ hostIsNew = 1;
+ /*
+ * When in6_update_ifa() is called in a process of a received
+ * RA, it is called under splnet(). So, we should call malloc
+ * with M_NOWAIT.
+ */
+ ia = (struct in6_ifaddr *)
+ malloc(sizeof(*ia), M_IFADDR, M_NOWAIT);
+ if (ia == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)ia, sizeof(*ia));
+ /* Initialize the address and masks */
+ IFA_LOCK_INIT(&ia->ia_ifa);
+ 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;
+ }
+ 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;
+
+ ia->ia_ifa.ifa_refcnt = 1;
+ 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;
+ }
+ 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... */
+ }
+ 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
+ */
+ 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] =
+ ifra->ifra_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+ (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);
+ }
+ }
+
+ 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);
+ 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);
+ }
+ }
+
+ /*
+ * join node information group address
+ */
+#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);
+ }
+ }
+ }
+#undef hostnamelen
+
+ /*
+ * 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_ifaddr *ia_loop;
+
+ struct in6_addr loop6 = in6addr_loopback;
+ ia_loop = in6ifa_ifpwithaddr(ifp, &loop6);
+
+ mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+
+ IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ if (in6m == NULL && ia_loop != NULL) {
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia_loop->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);
+ }
+ }
+ }
+ }
+
+ 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;
+
+ /*
+ * 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);
+
+ /*
+ * 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(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)
+ struct ifaddr *ifa;
+{
+ struct ifnet *ifp = ifa->ifa_ifp;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa;
+
+ /* 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) {
+ /*
+ * 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);
+ }
+
+ 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);
+
+ 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 {
+ /* search failed */
+ printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n");
+ }
+ }
+
+ 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);
+ }
+
+ /*
+ * 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().
+ */
+ 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.
+ * 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);
+}
+
+/*
+ * SIOC[GAD]LIFADDR.
+ * SIOCGLIFADDR: get first address. (?)
+ * SIOCGLIFADDR with IFLR_PREFIX:
+ * get first address that matches the specified prefix.
+ * SIOCALIFADDR: add the specified address.
+ * SIOCALIFADDR with IFLR_PREFIX:
+ * add the specified prefix, filling hostid part from
+ * the first link-local address. prefixlen must be <= 64.
+ * SIOCDLIFADDR: delete the specified address.
+ * SIOCDLIFADDR with IFLR_PREFIX:
+ * delete the first address that matches the specified prefix.
+ * return values:
+ * EINVAL on invalid parameters
+ * EADDRNOTAVAIL on prefix match failed/specified address not found
+ * other values may be returned from in6_ioctl()
+ *
+ * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64.
+ * this is to accomodate address naming scheme other than RFC2374,
+ * in the future.
+ * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374
+ * address encoding scheme. (see figure on page 8)
+ */
+static int
+in6_lifaddr_ioctl(so, cmd, data, ifp, td)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct thread *td;
+{
+ struct if_laddrreq *iflr = (struct if_laddrreq *)data;
+ struct ifaddr *ifa;
+ struct sockaddr *sa;
+
+ /* sanity checks */
+ if (!data || !ifp) {
+ panic("invalid argument to in6_lifaddr_ioctl");
+ /*NOTRECHED*/
+ }
+
+ switch (cmd) {
+ case SIOCGLIFADDR:
+ /* address must be specified on GET with IFLR_PREFIX */
+ if ((iflr->flags & IFLR_PREFIX) == 0)
+ break;
+ /* FALLTHROUGH */
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ /* address must be specified on ADD and DELETE */
+ sa = (struct sockaddr *)&iflr->addr;
+ if (sa->sa_family != AF_INET6)
+ return EINVAL;
+ if (sa->sa_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+ /* XXX need improvement */
+ sa = (struct sockaddr *)&iflr->dstaddr;
+ if (sa->sa_family && sa->sa_family != AF_INET6)
+ return EINVAL;
+ if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in6))
+ return EINVAL;
+ break;
+ default: /* shouldn't happen */
+#if 0
+ panic("invalid cmd to in6_lifaddr_ioctl");
+ /* NOTREACHED */
+#else
+ return EOPNOTSUPP;
+#endif
+ }
+ if (sizeof(struct in6_addr) * 8 < iflr->prefixlen)
+ return EINVAL;
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ {
+ struct in6_aliasreq ifra;
+ struct in6_addr *hostid = NULL;
+ int prefixlen;
+
+ if ((iflr->flags & IFLR_PREFIX) != 0) {
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * hostid is to fill in the hostid part of the
+ * address. hostid points to the first link-local
+ * address attached to the interface.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);
+ if (!ifa)
+ return EADDRNOTAVAIL;
+ hostid = IFA_IN6(ifa);
+
+ /* prefixlen must be <= 64. */
+ if (64 < iflr->prefixlen)
+ return EINVAL;
+ prefixlen = iflr->prefixlen;
+
+ /* hostid part must be zero. */
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ if (sin6->sin6_addr.s6_addr32[2] != 0
+ || sin6->sin6_addr.s6_addr32[3] != 0) {
+ return EINVAL;
+ }
+ } else
+ prefixlen = iflr->prefixlen;
+
+ /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */
+ bzero(&ifra, sizeof(ifra));
+ bcopy(iflr->iflr_name, ifra.ifra_name,
+ sizeof(ifra.ifra_name));
+
+ bcopy(&iflr->addr, &ifra.ifra_addr,
+ ((struct sockaddr *)&iflr->addr)->sa_len);
+ if (hostid) {
+ /* fill in hostid part */
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] =
+ hostid->s6_addr32[2];
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] =
+ hostid->s6_addr32[3];
+ }
+
+ if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/
+ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
+ ((struct sockaddr *)&iflr->dstaddr)->sa_len);
+ if (hostid) {
+ ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] =
+ hostid->s6_addr32[2];
+ ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] =
+ hostid->s6_addr32[3];
+ }
+ }
+
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen);
+
+ ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX;
+ return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, td);
+ }
+ case SIOCGLIFADDR:
+ case SIOCDLIFADDR:
+ {
+ struct in6_ifaddr *ia;
+ struct in6_addr mask, candidate, match;
+ struct sockaddr_in6 *sin6;
+ int cmp;
+
+ bzero(&mask, sizeof(mask));
+ if (iflr->flags & IFLR_PREFIX) {
+ /* lookup a prefix rather than address. */
+ in6_len2mask(&mask, iflr->prefixlen);
+
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ bcopy(&sin6->sin6_addr, &match, sizeof(match));
+ match.s6_addr32[0] &= mask.s6_addr32[0];
+ match.s6_addr32[1] &= mask.s6_addr32[1];
+ match.s6_addr32[2] &= mask.s6_addr32[2];
+ match.s6_addr32[3] &= mask.s6_addr32[3];
+
+ /* if you set extra bits, that's wrong */
+ if (bcmp(&match, &sin6->sin6_addr, sizeof(match)))
+ return EINVAL;
+
+ cmp = 1;
+ } else {
+ if (cmd == SIOCGLIFADDR) {
+ /* on getting an address, take the 1st match */
+ cmp = 0; /* XXX */
+ } else {
+ /* on deleting an address, do exact match */
+ in6_len2mask(&mask, 128);
+ sin6 = (struct sockaddr_in6 *)&iflr->addr;
+ bcopy(&sin6->sin6_addr, &match, sizeof(match));
+
+ cmp = 1;
+ }
+ }
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ 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];
+ candidate.s6_addr32[3] &= mask.s6_addr32[3];
+ if (IN6_ARE_ADDR_EQUAL(&candidate, &match))
+ break;
+ }
+ if (!ifa)
+ return EADDRNOTAVAIL;
+ 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,
+ NULL);
+
+ iflr->flags = ia->ia6_flags; /* XXX */
+
+ return 0;
+ } else {
+ struct in6_aliasreq ifra;
+
+ /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */
+ bzero(&ifra, sizeof(ifra));
+ bcopy(iflr->iflr_name, ifra.ifra_name,
+ sizeof(ifra.ifra_name));
+
+ bcopy(&ia->ia_addr, &ifra.ifra_addr,
+ ia->ia_addr.sin6_len);
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
+ ia->ia_dstaddr.sin6_len);
+ } else {
+ bzero(&ifra.ifra_dstaddr,
+ sizeof(ifra.ifra_dstaddr));
+ }
+ bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr,
+ ia->ia_prefixmask.sin6_len);
+
+ ifra.ifra_flags = ia->ia6_flags;
+ return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ ifp, td);
+ }
+ }
+ }
+
+ return EOPNOTSUPP; /* just for safety */
+}
+
+/*
+ * Initialize an interface's intetnet6 address
+ * and routing table entry.
+ */
+static int
+in6_ifinit(ifp, ia, sin6, newhost)
+ struct ifnet *ifp;
+ struct in6_ifaddr *ia;
+ struct sockaddr_in6 *sin6;
+ int newhost;
+{
+ int error = 0, plen, ifacount = 0;
+ int s = splimp();
+ struct ifaddr *ifa;
+
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ 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);
+ return(error);
+ }
+ splx(s);
+
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ /* we could do in(6)_socktrim here, but just omit it at this moment. */
+
+ /*
+ * 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.
+ */
+ 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;
+ }
+ 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). */
+ if (newhost) {
+ /* set the rtrequest function to create llinfo */
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ in6_ifaddloop(&(ia->ia_ifa));
+ }
+
+ return(error);
+}
+
+/*
+ * Add an address to the list of IP6 multicast addresses for a
+ * given interface.
+ */
+struct in6_multi *
+in6_addmulti(maddr6, ifp, errorp)
+ struct in6_addr *maddr6;
+ struct ifnet *ifp;
+ int *errorp;
+{
+ struct in6_multi *in6m;
+ struct sockaddr_in6 sin6;
+ struct ifmultiaddr *ifma;
+ int s = splnet();
+
+ *errorp = 0;
+
+ /*
+ * Call generic routine to add membership or increment
+ * refcount. It wants addresses in the form of a sockaddr,
+ * so we build one here (being careful to zero the unused bytes).
+ */
+ bzero(&sin6, sizeof sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof sin6;
+ sin6.sin6_addr = *maddr6;
+ *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma);
+ if (*errorp) {
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * If ifma->ifma_protospec is null, then if_addmulti() created
+ * a new record. Otherwise, we are done.
+ */
+ if (ifma->ifma_protospec != 0)
+ return ifma->ifma_protospec;
+
+ /* XXX - if_addmulti uses M_WAITOK. Can this really be called
+ at interrupt time? If so, need to fix if_addmulti. XXX */
+ in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
+ if (in6m == NULL) {
+ splx(s);
+ return (NULL);
+ }
+
+ bzero(in6m, sizeof *in6m);
+ in6m->in6m_addr = *maddr6;
+ in6m->in6m_ifp = ifp;
+ in6m->in6m_ifma = ifma;
+ ifma->ifma_protospec = in6m;
+ LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
+
+ /*
+ * Let MLD6 know that we have joined a new IP6 multicast
+ * group.
+ */
+ mld6_start_listening(in6m);
+ splx(s);
+ return(in6m);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+void
+in6_delmulti(in6m)
+ struct in6_multi *in6m;
+{
+ struct ifmultiaddr *ifma = in6m->in6m_ifma;
+ int s = splnet();
+
+ if (ifma->ifma_refcount == 1) {
+ /*
+ * No remaining claims to this record; let MLD6 know
+ * that we are leaving the multicast group.
+ */
+ mld6_stop_listening(in6m);
+ ifma->ifma_protospec = 0;
+ LIST_REMOVE(in6m, in6m_entry);
+ free(in6m, M_IPMADDR);
+ }
+ /* XXX - should be separate API for when we have an ifma? */
+ if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
+ splx(s);
+}
+
+/*
+ * Find an IPv6 interface link-local address specific to an interface.
+ */
+struct in6_ifaddr *
+in6ifa_ifpforlinklocal(ifp, ignoreflags)
+ struct ifnet *ifp;
+ int ignoreflags;
+{
+ struct ifaddr *ifa;
+
+ 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;
+ if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) {
+ if ((((struct in6_ifaddr *)ifa)->ia6_flags &
+ ignoreflags) != 0)
+ continue;
+ break;
+ }
+ }
+
+ return((struct in6_ifaddr *)ifa);
+}
+
+
+/*
+ * find the internet address corresponding to a given interface and address.
+ */
+struct in6_ifaddr *
+in6ifa_ifpwithaddr(ifp, addr)
+ struct ifnet *ifp;
+ struct in6_addr *addr;
+{
+ struct ifaddr *ifa;
+
+ 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;
+ if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa)))
+ break;
+ }
+
+ return((struct in6_ifaddr *)ifa);
+}
+
+/*
+ * Convert IP6 address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+static int ip6round = 0;
+char *
+ip6_sprintf(addr)
+ const struct in6_addr *addr;
+{
+ static char ip6buf[8][48];
+ int i;
+ char *cp;
+ const u_short *a = (const u_short *)addr;
+ const u_char *d;
+ int dcolon = 0;
+
+ ip6round = (ip6round + 1) & 7;
+ cp = ip6buf[ip6round];
+
+ for (i = 0; i < 8; i++) {
+ if (dcolon == 1) {
+ if (*a == 0) {
+ if (i == 7)
+ *cp++ = ':';
+ a++;
+ continue;
+ } else
+ dcolon = 2;
+ }
+ if (*a == 0) {
+ if (dcolon == 0 && *(a + 1) == 0) {
+ if (i == 0)
+ *cp++ = ':';
+ *cp++ = ':';
+ dcolon = 1;
+ } else {
+ *cp++ = '0';
+ *cp++ = ':';
+ }
+ a++;
+ continue;
+ }
+ d = (const u_char *)a;
+ *cp++ = digits[*d >> 4];
+ *cp++ = digits[*d++ & 0xf];
+ *cp++ = digits[*d >> 4];
+ *cp++ = digits[*d & 0xf];
+ *cp++ = ':';
+ a++;
+ }
+ *--cp = 0;
+ return(ip6buf[ip6round]);
+}
+
+int
+in6_localaddr(in6)
+ struct in6_addr *in6;
+{
+ struct in6_ifaddr *ia;
+
+ if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
+ return 1;
+
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
+ &ia->ia_prefixmask.sin6_addr))
+ return 1;
+
+ 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...
+ */
+int
+in6_matchlen(src, dst)
+struct in6_addr *src, *dst;
+{
+ int match = 0;
+ u_char *s = (u_char *)src, *d = (u_char *)dst;
+ u_char *lim = s + 16, r;
+
+ while (s < lim)
+ if ((r = (*d++ ^ *s++)) != 0) {
+ while (r < 128) {
+ match++;
+ r <<= 1;
+ }
+ break;
+ } else
+ match += 8;
+ return match;
+}
+
+/* XXX: to be scope conscious */
+int
+in6_are_prefix_equal(p1, p2, len)
+ struct in6_addr *p1, *p2;
+ int len;
+{
+ int bytelen, bitlen;
+
+ /* sanity check */
+ if (0 > len || len > 128) {
+ log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n",
+ len);
+ return(0);
+ }
+
+ bytelen = len / 8;
+ bitlen = len % 8;
+
+ if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen))
+ return(0);
+ if (p1->s6_addr[bytelen] >> (8 - bitlen) !=
+ p2->s6_addr[bytelen] >> (8 - bitlen))
+ return(0);
+
+ return(1);
+}
+
+void
+in6_prefixlen2mask(maskp, len)
+ struct in6_addr *maskp;
+ int len;
+{
+ u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
+ int bytelen, bitlen, i;
+
+ /* sanity check */
+ if (0 > len || len > 128) {
+ log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n",
+ len);
+ return;
+ }
+
+ bzero(maskp, sizeof(*maskp));
+ bytelen = len / 8;
+ bitlen = len % 8;
+ for (i = 0; i < bytelen; i++)
+ maskp->s6_addr[i] = 0xff;
+ if (bitlen)
+ maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
+}
+
+/*
+ * return the best address out of the same scope
+ */
+struct in6_ifaddr *
+in6_ifawithscope(oifp, dst)
+ struct ifnet *oifp;
+ struct in6_addr *dst;
+{
+ int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0;
+ int blen = -1;
+ struct ifaddr *ifa;
+ struct ifnet *ifp;
+ struct in6_ifaddr *ifa_best = NULL;
+
+ if (oifp == NULL) {
+#if 0
+ printf("in6_ifawithscope: output interface is not specified\n");
+#endif
+ return(NULL);
+ }
+
+ /*
+ * We search for all addresses on all interfaces from the beginning.
+ * Comparing an interface with the outgoing interface will be done
+ * only at the final stage of tiebreaking.
+ */
+ IFNET_RLOCK();
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ /*
+ * We can never take an address that breaks the scope zone
+ * of the destination.
+ */
+ if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst))
+ continue;
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ int tlen = -1, dscopecmp, bscopecmp, matchcmp;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+
+ src_scope = in6_addrscope(IFA_IN6(ifa));
+
+ /*
+ * Don't use an address before completing DAD
+ * nor a duplicated address.
+ */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_NOTREADY)
+ continue;
+
+ /* XXX: is there any case to allow anycasts? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_ANYCAST)
+ continue;
+
+ if (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_DETACHED)
+ continue;
+
+ /*
+ * If this is the first address we find,
+ * keep it anyway.
+ */
+ if (ifa_best == NULL)
+ goto replace;
+
+ /*
+ * ifa_best is never NULL beyond this line except
+ * within the block labeled "replace".
+ */
+
+ /*
+ * If ifa_best has a smaller scope than dst and
+ * the current address has a larger one than
+ * (or equal to) dst, always replace ifa_best.
+ * Also, if the current address has a smaller scope
+ * than dst, ignore it unless ifa_best also has a
+ * smaller scope.
+ * 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; /* (A) */
+ if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 &&
+ IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0)
+ continue; /* (B) */
+
+ /*
+ * A deprecated address SHOULD NOT be used in new
+ * communications if an alternate (non-deprecated)
+ * address is available and has sufficient scope.
+ * RFC 2462, Section 5.5.4.
+ */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_DEPRECATED) {
+ /*
+ * Ignore any deprecated addresses if
+ * specified by configuration.
+ */
+ if (!ip6_use_deprecated)
+ continue;
+
+ /*
+ * If we have already found a non-deprecated
+ * candidate, just ignore deprecated addresses.
+ */
+ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED)
+ == 0)
+ continue;
+ }
+
+ /*
+ * A non-deprecated address is always preferred
+ * to a deprecated one regardless of scopes and
+ * address matching (Note invariants ensured by the
+ * conditions (A) and (B) above.)
+ */
+ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) &&
+ (((struct in6_ifaddr *)ifa)->ia6_flags &
+ IN6_IFF_DEPRECATED) == 0)
+ 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.
+ * 2. we are looking at a deprecated address,
+ * and ifa_best is also deprecated.
+ * Also, we do not have to consider a case where
+ * the scope of if_best is larger(smaller) than dst and
+ * the scope of the current address is smaller(larger)
+ * than dst. Such a case has already been covered.
+ * Tiebreaking is done according to the following
+ * items:
+ * - the scope comparison between the address and
+ * dst (dscopecmp)
+ * - the scope comparison between the address and
+ * ifa_best (bscopecmp)
+ * - if the address match dst longer than ifa_best
+ * (matchcmp)
+ * - if the address is on the outgoing I/F (outI/F)
+ *
+ * Roughly speaking, the selection policy is
+ * - the most important item is scope. The same scope
+ * is best. Then search for a larger scope.
+ * Smaller scopes are the last resort.
+ * - A deprecated address is chosen only when we have
+ * no address that has an enough scope, but is
+ * prefered to any addresses of smaller scopes
+ * (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,
+ * longest address match against dst is considered.
+ *
+ * The precise decision table is as follows:
+ * 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 (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; /* (4) */
+ }
+ if (dscopecmp > 0) {
+ if (bscopecmp > 0) /* (5) */
+ continue;
+ goto replace; /* (6) */
+ }
+ if (dscopecmp < 0) {
+ if (bscopecmp > 0) /* (7) */
+ goto replace;
+ continue; /* (8) */
+ }
+
+ /* now dscopecmp must be 0 */
+ if (bscopecmp < 0)
+ goto replace; /* (9) */
+
+ replace:
+ ifa_best = (struct in6_ifaddr *)ifa;
+ blen = tlen >= 0 ? tlen :
+ in6_matchlen(IFA_IN6(ifa), dst);
+ best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr);
+ }
+ }
+ IFNET_RUNLOCK();
+
+ /* count statistics for future improvements */
+ if (ifa_best == NULL)
+ ip6stat.ip6s_sources_none++;
+ else {
+ if (oifp == ifa_best->ia_ifp)
+ ip6stat.ip6s_sources_sameif[best_scope]++;
+ else
+ ip6stat.ip6s_sources_otherif[best_scope]++;
+
+ if (best_scope == dst_scope)
+ ip6stat.ip6s_sources_samescope[best_scope]++;
+ else
+ ip6stat.ip6s_sources_otherscope[best_scope]++;
+
+ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0)
+ ip6stat.ip6s_sources_deprecated[best_scope]++;
+ }
+
+ return(ifa_best);
+}
+
+/*
+ * return the best address out of the same scope. if no address was
+ * found, return the first valid address from designated IF.
+ */
+struct in6_ifaddr *
+in6_ifawithifp(ifp, dst)
+ struct ifnet *ifp;
+ struct in6_addr *dst;
+{
+ int dst_scope = in6_addrscope(dst), blen = -1, tlen;
+ struct ifaddr *ifa;
+ struct in6_ifaddr *besta = 0;
+ struct in6_ifaddr *dep[2]; /* last-resort: deprecated */
+
+ dep[0] = dep[1] = NULL;
+
+ /*
+ * We first look for addresses in the same scope.
+ * If there is one, return it.
+ * If two or more, return one which matches the dst longest.
+ * If none, return one of global addresses assigned other ifs.
+ */
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* XXX: is there any case to allow anycast? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
+ continue; /* don't use this interface */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated)
+ dep[0] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+
+ if (dst_scope == in6_addrscope(IFA_IN6(ifa))) {
+ /*
+ * call in6_matchlen() as few as possible
+ */
+ if (besta) {
+ if (blen == -1)
+ blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst);
+ tlen = in6_matchlen(IFA_IN6(ifa), dst);
+ if (tlen > blen) {
+ blen = tlen;
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ } else
+ besta = (struct in6_ifaddr *)ifa;
+ }
+ }
+ if (besta)
+ return(besta);
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* XXX: is there any case to allow anycast? */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY)
+ continue; /* don't use this interface */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED)
+ continue;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated)
+ dep[1] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+
+ return (struct in6_ifaddr *)ifa;
+ }
+
+ /* use the last-resort values, that are, deprecated addresses */
+ if (dep[0])
+ return dep[0];
+ if (dep[1])
+ return dep[1];
+
+ return NULL;
+}
+
+/*
+ * perform DAD when interface becomes IFF_UP.
+ */
+void
+in6_if_up(ifp)
+ struct ifnet *ifp;
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia;
+ int dad_delay; /* delay ticks before DAD output */
+
+ /*
+ * special cases, like 6to4, are handled in in6_ifattach
+ */
+ in6_ifattach(ifp, NULL);
+
+ dad_delay = 0;
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ia = (struct in6_ifaddr *)ifa;
+ if (ia->ia6_flags & IN6_IFF_TENTATIVE)
+ nd6_dad_start(ifa, &dad_delay);
+ }
+}
+
+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.
+ */
+void
+in6_setmaxmtu()
+{
+ unsigned long maxmtu = 0;
+ struct ifnet *ifp;
+
+ IFNET_RLOCK();
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+ {
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ nd_ifinfo[ifp->if_index].linkmtu > maxmtu)
+ maxmtu = nd_ifinfo[ifp->if_index].linkmtu;
+ }
+ IFNET_RUNLOCK();
+ if (maxmtu) /* update only when maxmtu is positive */
+ in6_maxmtu = maxmtu;
+}
+
+/*
+ * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be
+ * v4 mapped addr or v4 compat addr
+ */
+void
+in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin, sizeof(*sin));
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
+}
+
+/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = sin->sin_port;
+ sin6->sin6_addr.s6_addr32[0] = 0;
+ sin6->sin6_addr.s6_addr32[1] = 0;
+ sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
+ sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+}
+
+/* Convert sockaddr_in6 into sockaddr_in. */
+void
+in6_sin6_2_sin_in_sock(struct sockaddr *nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 sin6;
+
+ /*
+ * Save original sockaddr_in6 addr and convert it
+ * to sockaddr_in.
+ */
+ sin6 = *(struct sockaddr_in6 *)nam;
+ sin_p = (struct sockaddr_in *)nam;
+ in6_sin6_2_sin(sin_p, &sin6);
+}
+
+/* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 *sin6_p;
+
+ MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME,
+ M_WAITOK);
+ sin_p = (struct sockaddr_in *)*nam;
+ in6_sin_2_v4mapsin6(sin_p, sin6_p);
+ FREE(*nam, M_SONAME);
+ *nam = (struct sockaddr *)sin6_p;
+}
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
new file mode 100644
index 0000000..a178371
--- /dev/null
+++ b/sys/netinet6/in6.h
@@ -0,0 +1,671 @@
+/* $FreeBSD$ */
+/* $KAME: in6.h,v 1.89 2001/05/27 13:28:35 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.h 8.3 (Berkeley) 1/3/94
+ */
+
+#ifndef __KAME_NETINET_IN_H_INCLUDED_
+#error "do not include netinet6/in6.h directly, include netinet/in.h. see RFC2553"
+#endif
+
+#ifndef _NETINET6_IN6_H_
+#define _NETINET6_IN6_H_
+
+/*
+ * 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 "20010528/FreeBSD"
+
+/*
+ * Local port number conventions:
+ *
+ * Ports < IPPORT_RESERVED are reserved for privileged processes (e.g. root),
+ * unless a kernel is compiled with IPNOPRIVPORTS defined.
+ *
+ * When a user does a bind(2) or connect(2) with a port number of zero,
+ * a non-conflicting local port address is chosen.
+ *
+ * The default range is IPPORT_ANONMIN to IPPORT_ANONMAX, although
+ * that is settable by sysctl(3); net.inet.ip.anonportmin and
+ * net.inet.ip.anonportmax respectively.
+ *
+ * A user may set the IPPROTO_IP option IP_PORTRANGE to change this
+ * default assignment range.
+ *
+ * The value IP_PORTRANGE_DEFAULT causes the default behavior.
+ *
+ * The value IP_PORTRANGE_HIGH is the same as IP_PORTRANGE_DEFAULT,
+ * and exists only for FreeBSD compatibility purposes.
+ *
+ * The value IP_PORTRANGE_LOW changes the range to the "low" are
+ * that is (by convention) restricted to privileged processes.
+ * This convention is based on "vouchsafe" principles only.
+ * It is only secure if you trust the remote host to restrict these ports.
+ * The range is IPPORT_RESERVEDMIN to IPPORT_RESERVEDMAX.
+ */
+#if __BSD_VISIBLE
+#define IPV6PORT_RESERVED 1024
+#define IPV6PORT_ANONMIN 49152
+#define IPV6PORT_ANONMAX 65535
+#define IPV6PORT_RESERVEDMIN 600
+#define IPV6PORT_RESERVEDMAX (IPV6PORT_RESERVED-1)
+#endif
+
+/*
+ * IPv6 address
+ */
+struct in6_addr {
+ union {
+ uint8_t __u6_addr8[16];
+ uint16_t __u6_addr16[8];
+ uint32_t __u6_addr32[4];
+ } __u6_addr; /* 128-bit IP6 address */
+};
+
+#define s6_addr __u6_addr.__u6_addr8
+#ifdef _KERNEL /* XXX nonstandard */
+#define s6_addr8 __u6_addr.__u6_addr8
+#define s6_addr16 __u6_addr.__u6_addr16
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+#define INET6_ADDRSTRLEN 46
+
+/*
+ * XXX missing POSIX.1-2001 macro IPPROTO_IPV6.
+ */
+
+/*
+ * Socket address for IPv6
+ */
+#if __BSD_VISIBLE
+#define SIN6_LEN
+#endif
+
+struct sockaddr_in6 {
+ uint8_t sin6_len; /* length of this struct */
+ sa_family_t sin6_family; /* AF_INET6 */
+ in_port_t sin6_port; /* Transport layer port # */
+ uint32_t sin6_flowinfo; /* IP6 flow information */
+ struct in6_addr sin6_addr; /* IP6 address */
+ uint32_t sin6_scope_id; /* scope zone index */
+};
+
+/*
+ * Local definition for masks
+ */
+#ifdef _KERNEL /* XXX nonstandard */
+#define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}}
+#define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
+#define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
+#define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}}
+#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
+#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;
+extern const struct in6_addr in6mask96;
+extern const struct in6_addr in6mask128;
+#endif /* _KERNEL */
+
+/*
+ * Macros started with IPV6_ADDR is KAME local
+ */
+#ifdef _KERNEL /* XXX nonstandard */
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define IPV6_ADDR_INT32_ONE 1
+#define IPV6_ADDR_INT32_TWO 2
+#define IPV6_ADDR_INT32_MNL 0xff010000
+#define IPV6_ADDR_INT32_MLL 0xff020000
+#define IPV6_ADDR_INT32_SMP 0x0000ffff
+#define IPV6_ADDR_INT16_ULL 0xfe80
+#define IPV6_ADDR_INT16_USL 0xfec0
+#define IPV6_ADDR_INT16_MLL 0xff02
+#elif _BYTE_ORDER == _LITTLE_ENDIAN
+#define IPV6_ADDR_INT32_ONE 0x01000000
+#define IPV6_ADDR_INT32_TWO 0x02000000
+#define IPV6_ADDR_INT32_MNL 0x000001ff
+#define IPV6_ADDR_INT32_MLL 0x000002ff
+#define IPV6_ADDR_INT32_SMP 0xffff0000
+#define IPV6_ADDR_INT16_ULL 0x80fe
+#define IPV6_ADDR_INT16_USL 0xc0fe
+#define IPV6_ADDR_INT16_MLL 0x02ff
+#endif
+#endif
+
+/*
+ * Definition of some useful macros to handle IP6 addresses
+ */
+#if __BSD_VISIBLE
+#define IN6ADDR_ANY_INIT \
+ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
+#define IN6ADDR_LOOPBACK_INIT \
+ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+#define IN6ADDR_NODELOCAL_ALLNODES_INIT \
+ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
+#define IN6ADDR_LINKLOCAL_ALLMDNS_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }}}
+#endif
+
+extern const struct in6_addr in6addr_any;
+extern const struct in6_addr in6addr_loopback;
+#if __BSD_VISIBLE
+extern const struct in6_addr in6addr_nodelocal_allnodes;
+extern const struct in6_addr in6addr_linklocal_allnodes;
+extern const struct in6_addr in6addr_linklocal_allrouters;
+#endif
+
+/*
+ * Equality
+ * NOTE: Some of kernel programming environment (for example, openbsd/sparc)
+ * does not supply memcmp(). For userland memcmp() is preferred as it is
+ * in ANSI standard.
+ */
+#ifdef _KERNEL
+#define IN6_ARE_ADDR_EQUAL(a, b) \
+ (bcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0)
+#else
+#if __BSD_VISIBLE
+#define IN6_ARE_ADDR_EQUAL(a, b) \
+ (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0)
+#endif
+#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) \
+ ((*(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) \
+ ((*(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) \
+ ((*(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) \
+ ((*(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
+ */
+
+#ifdef _KERNEL /* XXX nonstandard */
+#define IPV6_ADDR_SCOPE_NODELOCAL 0x01
+#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02
+#define IPV6_ADDR_SCOPE_SITELOCAL 0x05
+#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */
+#define IPV6_ADDR_SCOPE_GLOBAL 0x0e
+#else
+#define __IPV6_ADDR_SCOPE_NODELOCAL 0x01
+#define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02
+#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05
+#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */
+#define __IPV6_ADDR_SCOPE_GLOBAL 0x0e
+#endif
+
+/*
+ * Unicast Scope
+ * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373).
+ */
+#define IN6_IS_ADDR_LINKLOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80))
+#define IN6_IS_ADDR_SITELOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0))
+
+/*
+ * Multicast
+ */
+#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff)
+
+#ifdef _KERNEL /* XXX nonstandard */
+#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f)
+#else
+#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f)
+#endif
+
+/*
+ * Multicast Scope
+ */
+#ifdef _KERNEL /* refers nonstandard items */
+#define IN6_IS_ADDR_MC_NODELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL))
+#define IN6_IS_ADDR_MC_LINKLOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL))
+#define IN6_IS_ADDR_MC_SITELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL))
+#define IN6_IS_ADDR_MC_ORGLOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL))
+#define IN6_IS_ADDR_MC_GLOBAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL))
+#else
+#define IN6_IS_ADDR_MC_NODELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL))
+#define IN6_IS_ADDR_MC_LINKLOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL))
+#define IN6_IS_ADDR_MC_SITELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL))
+#define IN6_IS_ADDR_MC_ORGLOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL))
+#define IN6_IS_ADDR_MC_GLOBAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL))
+#endif
+
+#ifdef _KERNEL /* nonstandard */
+/*
+ * KAME Scope
+ */
+#define IN6_IS_SCOPE_LINKLOCAL(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)))
+
+#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
+ */
+#if __BSD_VISIBLE
+struct route_in6 {
+ struct rtentry *ro_rt;
+ struct sockaddr_in6 ro_dst;
+};
+#endif
+
+/*
+ * Options for use with [gs]etsockopt at the IPV6 level.
+ * First word of comment is data type; bool is stored in int.
+ */
+#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
+#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
+#define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */
+#define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */
+#define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */
+#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
+#define IPV6_V6ONLY 27 /* bool; only bind INET6 at wildcard bind */
+
+#if __BSD_VISIBLE
+/* no hdrincl */
+#if 0 /* the followings are relic in IPv4 and hence are disabled */
+#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */
+#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */
+#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */
+#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */
+#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */
+#endif
+#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */
+#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */
+#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */
+/* 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 */
+#ifndef _KERNEL
+#define IPV6_BINDV6ONLY IPV6_V6ONLY
+#endif
+
+#if 1 /* IPSEC */
+#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */
+#endif
+#define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */
+
+#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 */
+#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */
+#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */
+
+/*
+ * Defaults and limits for options
+ */
+#define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */
+#define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+
+/*
+ * Argument structure for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP.
+ */
+struct ipv6_mreq {
+ struct in6_addr ipv6mr_multiaddr;
+ unsigned int ipv6mr_interface;
+};
+
+/*
+ * IPV6_PKTINFO: Packet information(RFC2292 sec 5)
+ */
+struct in6_pktinfo {
+ struct in6_addr ipi6_addr; /* src/dst IPv6 address */
+ unsigned int ipi6_ifindex; /* send/recv interface index */
+};
+
+/*
+ * Argument for IPV6_PORTRANGE:
+ * - which range to search when port is unspecified at bind() or connect()
+ */
+#define IPV6_PORTRANGE_DEFAULT 0 /* default range */
+#define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */
+#define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */
+
+/*
+ * Definitions for inet6 sysctl operations.
+ *
+ * Third level is protocol number.
+ * Fourth level is desired variable within that protocol.
+ */
+#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */
+
+#define CTL_IPV6PROTO_NAMES { \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, \
+ { "tcp6", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "udp6", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, \
+ { "ip6", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, \
+ { "ipsec6", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "icmp6", CTLTYPE_NODE }, \
+ { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "pim6", CTLTYPE_NODE }, \
+}
+
+/*
+ * Names for IP sysctl objects
+ */
+#define IPV6CTL_FORWARDING 1 /* act as router */
+#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/
+#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */
+#ifdef notyet
+#define IPV6CTL_DEFMTU 4 /* default MTU */
+#endif
+#define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */
+#define IPV6CTL_STATS 6 /* stats */
+#define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */
+#define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */
+#define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */
+#define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */
+#define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */
+#define IPV6CTL_ACCEPT_RTADV 12
+#define IPV6CTL_KEEPFAITH 13
+#define IPV6CTL_LOG_INTERVAL 14
+#define IPV6CTL_HDRNESTLIMIT 15
+#define IPV6CTL_DAD_COUNT 16
+#define IPV6CTL_AUTO_FLOWLABEL 17
+#define IPV6CTL_DEFMCASTHLIM 18
+#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */
+#define IPV6CTL_KAME_VERSION 20
+#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */
+#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */
+#if 0 /* obsolete */
+#define IPV6CTL_MAPPED_ADDR 23
+#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 37
+
+/*
+ * Redefinition of mbuf flags
+ */
+#define M_AUTHIPHDR M_PROTO2
+#define M_DECRYPTED M_PROTO3
+#define M_LOOP M_PROTO4
+#define M_AUTHIPDGM M_PROTO5
+
+#ifdef _KERNEL
+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 u_char ip6_protox[];
+
+void in6_sin6_2_sin __P((struct sockaddr_in *sin,
+ struct sockaddr_in6 *sin6));
+void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin,
+ struct sockaddr_in6 *sin6));
+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))
+
+extern int (*faithprefix_p)(struct in6_addr *);
+#endif /* _KERNEL */
+
+#ifndef _SIZE_T_DECLARED
+typedef __size_t size_t;
+#define _SIZE_T_DECLARED
+#endif
+
+__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 uint8_t *,
+ int, int));
+extern uint8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int));
+extern int inet6_option_next __P((const struct cmsghdr *, uint8_t **));
+extern int inet6_option_find __P((const struct cmsghdr *, uint8_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, uint8_t,
+ size_t, uint8_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, uint8_t *,
+ size_t *, void **));
+extern int inet6_opt_find __P((void *, size_t, int, uint8_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 /* __BSD_VISIBLE */
+
+#endif /* !_NETINET6_IN6_H_ */
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
new file mode 100644
index 0000000..049c60d
--- /dev/null
+++ b/sys/netinet6/in6_cksum.c
@@ -0,0 +1,321 @@
+/* $FreeBSD$ */
+/* $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+/*
+ * m MUST contain a continuous IP6 header.
+ * off is an offset where TCP/UDP/ICMP6 header starts.
+ * len is a total length of a transport segment.
+ * (e.g. TCP header + TCP payload)
+ */
+
+int
+in6_cksum(m, nxt, off, len)
+ struct mbuf *m;
+ u_int8_t nxt;
+ u_int32_t off, len;
+{
+ u_int16_t *w;
+ int sum = 0;
+ int mlen = 0;
+ int byte_swapped = 0;
+#if 0
+ int srcifid = 0, dstifid = 0;
+#endif
+ struct ip6_hdr *ip6;
+ union {
+ u_int16_t phs[4];
+ struct {
+ u_int32_t ph_len;
+ u_int8_t ph_zero[3];
+ u_int8_t ph_nxt;
+ } ph __packed;
+ } uph;
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ /* sanity check */
+ if (m->m_pkthdr.len < off + len) {
+ panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)",
+ m->m_pkthdr.len, off, len);
+ }
+
+ bzero(&uph, sizeof(uph));
+
+ /*
+ * First create IP6 pseudo header and calculate a summary.
+ */
+ ip6 = mtod(m, struct ip6_hdr *);
+#if 0
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+ srcifid = ip6->ip6_src.s6_addr16[1];
+ ip6->ip6_src.s6_addr16[1] = 0;
+ }
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ dstifid = ip6->ip6_dst.s6_addr16[1];
+ ip6->ip6_dst.s6_addr16[1] = 0;
+ }
+#endif
+ w = (u_int16_t *)&ip6->ip6_src;
+ uph.ph.ph_len = htonl(len);
+ uph.ph.ph_nxt = nxt;
+
+ /* IPv6 source address */
+ sum += w[0];
+ if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ sum += w[1];
+ sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5];
+ sum += w[6]; sum += w[7];
+ /* IPv6 destination address */
+ sum += w[8];
+ if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ sum += w[9];
+ sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13];
+ sum += w[14]; sum += w[15];
+ /* Payload length and upper layer identifier */
+ sum += uph.phs[0]; sum += uph.phs[1];
+ sum += uph.phs[2]; sum += uph.phs[3];
+
+#if 0
+ if (srcifid)
+ ip6->ip6_src.s6_addr16[1] = srcifid;
+ if (dstifid)
+ ip6->ip6_dst.s6_addr16[1] = dstifid;
+#endif
+ /*
+ * Secondly calculate a summary of the first mbuf excluding offset.
+ */
+ while (m != NULL && off > 0) {
+ if (m->m_len <= off)
+ off -= m->m_len;
+ else
+ break;
+ m = m->m_next;
+ }
+ w = (u_int16_t *)(mtod(m, u_char *) + off);
+ mlen = m->m_len - off;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto next;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(char *)w;
+ next:
+ m = m->m_next;
+
+ /*
+ * Lastly calculate a summary of the rest of mbufs.
+ */
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = mtod(m, u_int16_t *);
+ if (mlen == -1) {
+ /*
+ * The first byte of this mbuf is the continuation
+ * of a word spanning between this mbuf and the
+ * last mbuf.
+ *
+ * s_util.c[0] is already saved when scanning previous
+ * mbuf.
+ */
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ } else
+ mlen = m->m_len;
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_char *)w;
+ w = (u_int16_t *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ continue;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(char *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(char *)w;
+ }
+ if (len)
+ panic("in6_cksum: out of data");
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c
new file mode 100644
index 0000000..b560075
--- /dev/null
+++ b/sys/netinet6/in6_gif.c
@@ -0,0 +1,398 @@
+/* $FreeBSD$ */
+/* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 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/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/syslog.h>
+#include <sys/protosw.h>
+
+#include <sys/malloc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifdef INET
+#include <netinet/ip.h>
+#endif
+#include <netinet/ip_encap.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_gif.h>
+#include <netinet6/in6_var.h>
+#endif
+#include <netinet6/ip6protosw.h>
+#include <netinet/ip_ecn.h>
+#ifdef INET6
+#include <netinet6/ip6_ecn.h>
+#endif
+
+#include <net/if_gif.h>
+
+#include <net/net_osdep.h>
+
+static int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
+ struct ifnet *);
+
+extern struct domain inet6domain;
+struct ip6protosw in6_gif_protosw =
+{ SOCK_RAW, &inet6domain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR,
+ in6_gif_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+};
+
+int
+in6_gif_output(ifp, family, m)
+ struct ifnet *ifp;
+ int family; /* family of the packet to be encapsulate. */
+ struct mbuf *m;
+{
+ struct gif_softc *sc = (struct gif_softc*)ifp;
+ struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
+ struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
+ struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
+ struct ip6_hdr *ip6;
+ int proto;
+ u_int8_t itos, otos;
+
+ if (sin6_src == NULL || sin6_dst == NULL ||
+ sin6_src->sin6_family != AF_INET6 ||
+ sin6_dst->sin6_family != AF_INET6) {
+ m_freem(m);
+ return EAFNOSUPPORT;
+ }
+
+ switch (family) {
+#ifdef INET
+ case AF_INET:
+ {
+ struct ip *ip;
+
+ proto = IPPROTO_IPV4;
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (!m)
+ return ENOBUFS;
+ }
+ ip = mtod(m, struct ip *);
+ itos = ip->ip_tos;
+ break;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct ip6_hdr *ip6;
+ proto = IPPROTO_IPV6;
+ if (m->m_len < sizeof(*ip6)) {
+ m = m_pullup(m, sizeof(*ip6));
+ if (!m)
+ return ENOBUFS;
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
+ break;
+ }
+#endif
+ default:
+#ifdef DEBUG
+ printf("in6_gif_output: warning: unknown family %d passed\n",
+ family);
+#endif
+ m_freem(m);
+ return EAFNOSUPPORT;
+ }
+
+ /* prepend new IP header */
+ M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
+ if (m && m->m_len < sizeof(struct ip6_hdr))
+ m = m_pullup(m, sizeof(struct ip6_hdr));
+ if (m == NULL) {
+ printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
+ return ENOBUFS;
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ ip6->ip6_plen = htons((u_short)m->m_pkthdr.len);
+ ip6->ip6_nxt = proto;
+ ip6->ip6_hlim = ip6_gif_hlim;
+ ip6->ip6_src = sin6_src->sin6_addr;
+ /* 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)
+ ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
+ 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)) {
+ /* cache route doesn't match */
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = sin6_dst->sin6_family;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = sin6_dst->sin6_addr;
+ if (sc->gif_ro6.ro_rt) {
+ RTFREE(sc->gif_ro6.ro_rt);
+ sc->gif_ro6.ro_rt = NULL;
+ }
+#if 0
+ sc->gif_if.if_mtu = GIF_MTU;
+#endif
+ }
+
+ if (sc->gif_ro6.ro_rt == NULL) {
+ rtalloc((struct route *)&sc->gif_ro6);
+ if (sc->gif_ro6.ro_rt == NULL) {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+
+ /* if it constitutes infinite encapsulation, punt. */
+ if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
+ m_freem(m);
+ return ENETUNREACH; /*XXX*/
+ }
+#if 0
+ ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
+ - sizeof(struct ip6_hdr);
+#endif
+ }
+
+#ifdef IPV6_MINMTU
+ /*
+ * force fragmentation to minimum MTU, to avoid path MTU discovery.
+ * it is too painful to ask for resend of inner packet, to achieve
+ * path MTU discovery for encapsulated packets.
+ */
+ return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL));
+#else
+ return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL));
+#endif
+}
+
+int in6_gif_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ struct ifnet *gifp = NULL;
+ struct ip6_hdr *ip6;
+ int af = 0;
+ u_int32_t otos;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ gifp = (struct ifnet *)encap_getarg(m);
+
+ if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ ip6stat.ip6s_nogif++;
+ return IPPROTO_DONE;
+ }
+
+ otos = ip6->ip6_flow;
+ m_adj(m, *offp);
+
+ switch (proto) {
+#ifdef INET
+ case IPPROTO_IPV4:
+ {
+ struct ip *ip;
+ u_int8_t otos8;
+ af = AF_INET;
+ otos8 = (ntohl(otos) >> 20) & 0xff;
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (!m)
+ return IPPROTO_DONE;
+ }
+ 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 */
+#ifdef INET6
+ case IPPROTO_IPV6:
+ {
+ struct ip6_hdr *ip6;
+ af = AF_INET6;
+ if (m->m_len < sizeof(*ip6)) {
+ m = m_pullup(m, sizeof(*ip6));
+ if (!m)
+ return IPPROTO_DONE;
+ }
+ 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
+ default:
+ ip6stat.ip6s_nogif++;
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+
+ gif_input(m, af, gifp);
+ return IPPROTO_DONE;
+}
+
+/*
+ * validate outer address.
+ */
+static int
+gif_validate6(ip6, sc, ifp)
+ const struct ip6_hdr *ip6;
+ struct gif_softc *sc;
+ struct ifnet *ifp;
+{
+ struct sockaddr_in6 *src, *dst;
+
+ src = (struct sockaddr_in6 *)sc->gif_psrc;
+ dst = (struct sockaddr_in6 *)sc->gif_pdst;
+
+ /*
+ * Check for address match. Note that the check is for an incoming
+ * packet. We should compare the *source* address in our configuration
+ * and the *destination* address of the packet, and vice versa.
+ */
+ if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
+ !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
+ return 0;
+
+ /* martian filters on outer source - done in ip6_input */
+
+ /* ingress filters on outer source */
+ if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
+ struct sockaddr_in6 sin6;
+ struct rtentry *rt;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = ip6->ip6_src;
+#ifndef SCOPEDROUTING
+ sin6.sin6_scope_id = 0; /* XXX */
+#endif
+
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+ if (!rt || rt->rt_ifp != ifp) {
+#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);
+ }
+
+ return 128 * 2;
+}
+
+/*
+ * we know that we are in IFF_UP, outer address available, and outer family
+ * matched the physical addr family. see gif_encapcheck().
+ * sanity check for arg should have been done in the caller.
+ */
+int
+gif_encapcheck6(m, off, proto, arg)
+ const struct mbuf *m;
+ int off;
+ int proto;
+ void *arg;
+{
+ struct ip6_hdr ip6;
+ struct gif_softc *sc;
+ struct ifnet *ifp;
+
+ /* sanity check done in caller */
+ sc = (struct gif_softc *)arg;
+
+ /* LINTED const cast */
+ m_copydata(m, 0, sizeof(ip6), (caddr_t)&ip6);
+ ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
+
+ return gif_validate6(&ip6, sc, ifp);
+}
+
+int
+in6_gif_attach(sc)
+ struct gif_softc *sc;
+{
+ sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
+ (struct protosw *)&in6_gif_protosw, sc);
+ if (sc->encap_cookie6 == NULL)
+ return EEXIST;
+ return 0;
+}
+
+int
+in6_gif_detach(sc)
+ struct gif_softc *sc;
+{
+ int error;
+
+ error = encap_detach(sc->encap_cookie6);
+ if (error == 0)
+ sc->encap_cookie6 = NULL;
+ return error;
+}
diff --git a/sys/netinet6/in6_gif.h b/sys/netinet6/in6_gif.h
new file mode 100644
index 0000000..83625e4
--- /dev/null
+++ b/sys/netinet6/in6_gif.h
@@ -0,0 +1,45 @@
+/* $FreeBSD$ */
+/* $KAME: in6_gif.h,v 1.5 2000/04/14 08:36:03 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * 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_IN6_GIF_H_
+#define _NETINET6_IN6_GIF_H_
+
+#define GIF_HLIM 30
+
+struct gif_softc;
+int in6_gif_input __P((struct mbuf **, int *, int));
+int in6_gif_output __P((struct ifnet *, int, struct mbuf *));
+int gif_encapcheck6 __P((const struct mbuf *, int, int, void *));
+int in6_gif_attach __P((struct gif_softc *));
+int in6_gif_detach __P((struct gif_softc *));
+
+#endif /*_NETINET6_IN6_GIF_H_*/
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
new file mode 100644
index 0000000..5abcca1
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.c
@@ -0,0 +1,1050 @@
+/* $FreeBSD$ */
+/* $KAME: in6_ifattach.c,v 1.118 2001/05/24 07:44:00 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/md5.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_pcb.h>
+
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/scope6_var.h>
+
+#include <net/net_osdep.h>
+
+struct in6_ifstat **in6_ifstat = NULL;
+struct icmp6_ifstat **icmp6_ifstat = NULL;
+size_t in6_ifstatmax = 0;
+size_t icmp6_ifstatmax = 0;
+unsigned long in6_maxmtu = 0;
+
+#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;
+
+extern struct inpcbinfo udbinfo;
+extern struct inpcbinfo ripcbinfo;
+
+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_linklocal __P((struct ifnet *, struct ifnet *));
+static int in6_ifattach_loopback __P((struct ifnet *));
+
+#define EUI64_GBIT 0x01
+#define EUI64_UBIT 0x02
+#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
+#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)
+#define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6))
+#define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT)
+#define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6))
+
+#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6))
+#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6))
+
+/*
+ * Generate a last-resort interface identifier, when the machine has no
+ * IEEE802/EUI64 address sources.
+ * The goal here is to get an interface identifier that is
+ * (1) random enough and (2) does not change across reboot.
+ * We currently use MD5(hostname) for it.
+ */
+static int
+get_rand_ifid(ifp, in6)
+ struct ifnet *ifp;
+ struct in6_addr *in6; /* upper 64bits are preserved */
+{
+ MD5_CTX ctxt;
+ u_int8_t digest[16];
+ int hostnamelen = strlen(hostname);
+
+#if 0
+ /* we need at least several letters as seed for ifid */
+ if (hostnamelen < 3)
+ return -1;
+#endif
+
+ /* generate 8 bytes of pseudo-random value. */
+ bzero(&ctxt, sizeof(ctxt));
+ MD5Init(&ctxt);
+ MD5Update(&ctxt, hostname, hostnamelen);
+ MD5Final(digest, &ctxt);
+
+ /* assumes sizeof(digest) > sizeof(ifid) */
+ bcopy(digest, &in6->s6_addr[8], 8);
+
+ /* make sure to set "u" bit to local, and "g" bit to individual. */
+ in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
+ in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
+
+ /* convert EUI64 into IPv6 interface identifier */
+ EUI64_TO_IFID(in6);
+
+ return 0;
+}
+
+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
+ */
+static int
+get_hw_ifid(ifp, in6)
+ struct ifnet *ifp;
+ struct in6_addr *in6; /* upper 64bits are preserved */
+{
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ u_int8_t *addr;
+ size_t addrlen;
+ static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static u_int8_t allone[8] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl == NULL)
+ continue;
+ if (sdl->sdl_alen == 0)
+ continue;
+
+ goto found;
+ }
+
+ return -1;
+
+found:
+ addr = LLADDR(sdl);
+ addrlen = sdl->sdl_alen;
+
+ /* get EUI64 */
+ switch (ifp->if_type) {
+ 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)
+ return -1;
+
+ /*
+ * check for invalid MAC address - on bsdi, we see it a lot
+ * since wildboar configures all-zero MAC on pccard before
+ * card insertion.
+ */
+ if (bcmp(addr, allzero, addrlen) == 0)
+ return -1;
+ if (bcmp(addr, allone, addrlen) == 0)
+ return -1;
+
+ /* make EUI64 address */
+ if (addrlen == 8)
+ bcopy(addr, &in6->s6_addr[8], 8);
+ else if (addrlen == 6) {
+ in6->s6_addr[8] = addr[0];
+ in6->s6_addr[9] = addr[1];
+ in6->s6_addr[10] = addr[2];
+ in6->s6_addr[11] = 0xff;
+ in6->s6_addr[12] = 0xfe;
+ in6->s6_addr[13] = addr[3];
+ in6->s6_addr[14] = addr[4];
+ in6->s6_addr[15] = addr[5];
+ }
+ break;
+
+ case IFT_ARCNET:
+ if (addrlen != 1)
+ return -1;
+ if (!addr[0])
+ return -1;
+
+ bzero(&in6->s6_addr[8], 8);
+ in6->s6_addr[15] = addr[0];
+
+ /*
+ * due to insufficient bitwidth, we mark it local.
+ */
+ in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */
+ in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */
+ break;
+
+ case IFT_GIF:
+#ifdef IFT_STF
+ case IFT_STF:
+#endif
+ /*
+ * 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.
+ */
+ return -1;
+
+ default:
+ return -1;
+ }
+
+ /* sanity check: g bit must not indicate "group" */
+ if (EUI64_GROUP(in6))
+ return -1;
+
+ /* convert EUI64 into IPv6 interface identifier */
+ EUI64_TO_IFID(in6);
+
+ /*
+ * sanity check: ifid must not be all zero, avoid conflict with
+ * subnet router anycast
+ */
+ if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
+ bcmp(&in6->s6_addr[9], allzero, 7) == 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Get interface identifier for the specified interface. If it is not
+ * available on ifp0, borrow interface identifier from other information
+ * sources.
+ */
+static int
+get_ifid(ifp0, altifp, in6)
+ struct ifnet *ifp0;
+ struct ifnet *altifp; /* secondary EUI64 source */
+ struct in6_addr *in6;
+{
+ struct ifnet *ifp;
+
+ /* first, try to get it from the interface itself */
+ if (get_hw_ifid(ifp0, in6) == 0) {
+ 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) {
+ nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n",
+ if_name(ifp0), if_name(altifp)));
+ goto success;
+ }
+
+ /* next, try to get it from some other hardware interface */
+ IFNET_RLOCK();
+ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
+ {
+ if (ifp == ifp0)
+ continue;
+ if (get_hw_ifid(ifp, in6) != 0)
+ continue;
+
+ /*
+ * to borrow ifid from other interface, ifid needs to be
+ * globally unique
+ */
+ if (IFID_UNIVERSAL(in6)) {
+ nd6log((LOG_DEBUG,
+ "%s: borrow interface identifier from %s\n",
+ if_name(ifp0), if_name(ifp)));
+ IFNET_RUNLOCK();
+ goto success;
+ }
+ }
+ IFNET_RUNLOCK();
+
+ /* last resort: get from random number source */
+ if (get_rand_ifid(ifp, in6) == 0) {
+ nd6log((LOG_DEBUG,
+ "%s: interface identifier generated by random number\n",
+ if_name(ifp0)));
+ goto success;
+ }
+
+ printf("%s: failed to get interface identifier\n", if_name(ifp0));
+ return -1;
+
+success:
+ 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]));
+ return 0;
+}
+
+static int
+in6_ifattach_linklocal(ifp, altifp)
+ struct ifnet *ifp;
+ struct ifnet *altifp; /* secondary EUI64 source */
+{
+ struct in6_ifaddr *ia;
+ struct in6_aliasreq ifra;
+ struct nd_prefix pr0;
+ int i, error;
+
+ /*
+ * configure link-local address.
+ */
+ 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);
+ 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;
+ }
+ }
+#ifdef SCOPEDROUTING
+ ifra.ifra_addr.sin6_scope_id =
+ in6_addr2scopeid(ifp, &ifra.ifra_addr.sin6_addr);
+#endif
+
+ 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
+ /* link-local addresses should NEVER expire. */
+ ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ /*
+ * Do not let in6_update_ifa() do DAD, since we need a random delay
+ * before sending an NS at the first time the interface becomes up.
+ * Instead, in6_if_up() will start DAD with a proper random delay.
+ */
+ ifra.ifra_flags |= IN6_IFF_NODAD;
+
+ /*
+ * 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
+ * and therefore we are adding one (instead of updating one).
+ */
+ if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
+ /*
+ * 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)
+ */
+ 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);
+ }
+
+ /*
+ * 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 = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */
+#ifdef DIAGNOSTIC
+ if (!ia) {
+ panic("ia == NULL in in6_ifattach_linklocal");
+ /* NOTREACHED */
+ }
+#endif
+ if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ ia->ia6_flags &= ~IN6_IFF_NODAD;
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ }
+
+ /*
+ * 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;
+}
+
+static int
+in6_ifattach_loopback(ifp)
+ struct ifnet *ifp; /* must be IFT_LOOP */
+{
+ struct in6_aliasreq ifra;
+ int error;
+
+ 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_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. Follows IPv4 practice - see in_ifinit().
+ */
+ ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_dstaddr.sin6_family = AF_INET6;
+ ifra.ifra_dstaddr.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;
+
+ /* the loopback address should NEVER expire. */
+ ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ /* we don't need to perform 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 are sure that this is a newly assigned address, so we can set
+ * NULL to the 3rd arg.
+ */
+ 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;
+}
+
+/*
+ * compute NI group address, based on the current hostname setting.
+ * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later).
+ *
+ * when ifp == NULL, the caller is responsible for filling scopeid.
+ */
+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;
+
+ p = name;
+ while (p && *p && *p != '.' && p - name < namelen)
+ p++;
+ 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));
+ MD5Update(&ctxt, n, l);
+ MD5Final(digest, &ctxt);
+
+ bzero(in6, sizeof(*in6));
+ in6->s6_addr16[0] = htons(0xff02);
+ if (ifp)
+ in6->s6_addr16[1] = htons(ifp->if_index);
+ in6->s6_addr8[11] = 2;
+ bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3]));
+
+ return 0;
+}
+
+void
+in6_nigroup_attach(name, namelen)
+ const char *name;
+ int namelen;
+{
+ struct ifnet *ifp;
+ struct sockaddr_in6 mltaddr;
+ struct in6_multi *in6m;
+ int error;
+
+ bzero(&mltaddr, sizeof(mltaddr));
+ mltaddr.sin6_family = AF_INET6;
+ mltaddr.sin6_len = sizeof(struct sockaddr_in6);
+ if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0)
+ return;
+
+ IFNET_RLOCK();
+ 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) {
+ 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));
+ }
+ }
+ }
+ IFNET_RUNLOCK();
+}
+
+void
+in6_nigroup_detach(name, namelen)
+ const char *name;
+ int namelen;
+{
+ struct ifnet *ifp;
+ struct sockaddr_in6 mltaddr;
+ struct in6_multi *in6m;
+
+ bzero(&mltaddr, sizeof(mltaddr));
+ mltaddr.sin6_family = AF_INET6;
+ mltaddr.sin6_len = sizeof(struct sockaddr_in6);
+ if (in6_nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0)
+ return;
+
+ IFNET_RLOCK();
+ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
+ {
+ mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ IFNET_RUNLOCK();
+}
+
+/*
+ * XXX multiple loopback interface needs more care. for instance,
+ * nodelocal address needs to be configured onto only one of them.
+ * XXX multiple link-local address case
+ */
+void
+in6_ifattach(ifp, altifp)
+ struct ifnet *ifp;
+ struct ifnet *altifp; /* secondary EUI64 source */
+{
+ static size_t if_indexlim = 8;
+ struct in6_ifaddr *ia;
+ struct in6_addr in6;
+
+ /* 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.
+ * since if_index will grow dynamically, they should grow too.
+ * struct in6_ifstat **in6_ifstat
+ * struct icmp6_ifstat **icmp6_ifstat
+ */
+ if (in6_ifstat == NULL || icmp6_ifstat == NULL ||
+ if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+ size_t olim;
+
+ olim = if_indexlim;
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow in6_ifstat */
+ n = if_indexlim * sizeof(struct in6_ifstat *);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (in6_ifstat) {
+ bcopy((caddr_t)in6_ifstat, q,
+ olim * sizeof(struct in6_ifstat *));
+ free((caddr_t)in6_ifstat, M_IFADDR);
+ }
+ in6_ifstat = (struct in6_ifstat **)q;
+ in6_ifstatmax = if_indexlim;
+
+ /* grow icmp6_ifstat */
+ n = if_indexlim * sizeof(struct icmp6_ifstat *);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (icmp6_ifstat) {
+ bcopy((caddr_t)icmp6_ifstat, q,
+ olim * sizeof(struct icmp6_ifstat *));
+ free((caddr_t)icmp6_ifstat, M_IFADDR);
+ }
+ icmp6_ifstat = (struct icmp6_ifstat **)q;
+ icmp6_ifstatmax = if_indexlim;
+ }
+
+ /* initialize scope identifiers */
+ scope6_ifattach(ifp);
+
+ /*
+ * quirks based on interface type
+ */
+ switch (ifp->if_type) {
+#ifdef IFT_STF
+ case IFT_STF:
+ /*
+ * 6to4 interface is a very special kind of beast.
+ * no multicast, no linklocal. RFC2529 specifies how to make
+ * linklocals for 6to4 interface, but there's no use and
+ * it is rather harmful to have one.
+ */
+ goto statinit;
+#endif
+ default:
+ break;
+ }
+
+ /*
+ * usually, we require multicast capability to the interface
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ log(LOG_INFO, "in6_ifattach: "
+ "%s is not multicast capable, IPv6 not enabled\n",
+ if_name(ifp));
+ return;
+ }
+
+ /*
+ * assign loopback address for loopback interface.
+ * XXX multiple loopback interface case.
+ */
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ in6 = in6addr_loopback;
+ if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) {
+ if (in6_ifattach_loopback(ifp) != 0)
+ return;
+ }
+ }
+
+ /*
+ * assign a link-local address, if there's none.
+ */
+ 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? */
+ }
+ }
+ }
+
+#ifdef IFT_STF /* XXX */
+statinit:
+#endif
+
+ /* update dynamically. */
+ if (in6_maxmtu < ifp->if_mtu)
+ in6_maxmtu = ifp->if_mtu;
+
+ if (in6_ifstat[ifp->if_index] == NULL) {
+ in6_ifstat[ifp->if_index] = (struct in6_ifstat *)
+ malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK);
+ bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat));
+ }
+ if (icmp6_ifstat[ifp->if_index] == NULL) {
+ icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *)
+ malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK);
+ bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat));
+ }
+
+ /* initialize NDP variables */
+ nd6_ifattach(ifp);
+}
+
+/*
+ * 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)
+ struct ifnet *ifp;
+{
+ struct in6_ifaddr *ia, *oia;
+ struct ifaddr *ifa, *next;
+ struct rtentry *rt;
+ short rtflags;
+ struct sockaddr_in6 sin6;
+ struct in6_multi *in6m;
+ struct in6_multi *in6m_next;
+
+ /* nuke prefix list. this may try to remove some of ifaddrs as well */
+ in6_purgeprefix(ifp);
+
+ /* remove neighbor management table */
+ nd6_purge(ifp);
+
+ /* nuke any of IPv6 addresses we have */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next)
+ {
+ next = ifa->ifa_list.tqe_next;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ in6_purgeaddr(ifa);
+ }
+
+ /* undo everything done by in6_ifattach(), just in case */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next)
+ {
+ next = ifa->ifa_list.tqe_next;
+
+
+ if (ifa->ifa_addr->sa_family != AF_INET6
+ || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
+ continue;
+ }
+
+ ia = (struct in6_ifaddr *)ifa;
+
+ /* remove from the routing table */
+ if ((ia->ia_flags & IFA_ROUTE)
+ && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) {
+ rtflags = rt->rt_flags;
+ rtfree(rt);
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_prefixmask,
+ rtflags, (struct rtentry **)0);
+ }
+
+ /* remove from the linked list */
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ IFAFREE(&ia->ia_ifa);
+
+ /* also remove from the IPv6 address chain(itojun&jinmei) */
+ oia = ia;
+ 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 {
+ nd6log((LOG_ERR,
+ "%s: didn't unlink in6ifaddr from "
+ "list\n", if_name(ifp)));
+ }
+ }
+
+ IFAFREE(&oia->ia_ifa);
+ }
+
+ /* leave from all multicast groups joined */
+ in6_pcbpurgeif0(LIST_FIRST(udbinfo.listhead), ifp);
+ in6_pcbpurgeif0(LIST_FIRST(ripcbinfo.listhead), ifp);
+ for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) {
+ in6m_next = LIST_NEXT(in6m, in6m_entry);
+ if (in6m->in6m_ifp != ifp)
+ continue;
+ in6_delmulti(in6m);
+ in6m = NULL;
+ }
+
+ /*
+ * remove neighbor management table. 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) */
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = in6addr_linklocal_allnodes;
+ sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+ if (rt && rt->rt_ifp == ifp) {
+ rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
+ 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
new file mode 100644
index 0000000..fa4434f
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $KAME: in6_ifattach.h,v 1.14 2001/02/08 12:48:39 jinmei 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.
+ */
+
+#ifndef _NETINET6_IN6_IFATTACH_H_
+#define _NETINET6_IN6_IFATTACH_H_
+
+#ifdef _KERNEL
+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
new file mode 100644
index 0000000..84ea6a9
--- /dev/null
+++ b/sys/netinet6/in6_pcb.c
@@ -0,0 +1,1158 @@
+/* $FreeBSD$ */
+/* $KAME: in6_pcb.c,v 1.31 2001/05/21 05:45:10 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/jail.h>
+
+#include <vm/uma.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/tcp_var.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.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
+#include <netkey/key.h>
+#endif /* IPSEC */
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#include <netipsec/key.h>
+#define IPSEC
+#endif /* FAST_IPSEC */
+
+struct in6_addr zeroin6_addr;
+
+int
+in6_pcbbind(inp, nam, td)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct thread *td;
+{
+ struct socket *so = inp->inp_socket;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL;
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ u_short lport = 0;
+ int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
+
+ if (!in6_ifaddr) /* XXX broken! */
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
+ return(EINVAL);
+ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
+ wild = 1;
+ if (nam) {
+ sin6 = (struct sockaddr_in6 *)nam;
+ if (nam->sa_len != sizeof(*sin6))
+ return(EINVAL);
+ /*
+ * family check.
+ */
+ if (nam->sa_family != AF_INET6)
+ return(EAFNOSUPPORT);
+
+ /* KAME hack: embed scopeid */
+ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0)
+ return EINVAL;
+ /* this must be cleared for ifa_ifwithaddr() */
+ sin6->sin6_scope_id = 0;
+
+ lport = sin6->sin6_port;
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /*
+ * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
+ * allow compepte duplication of binding if
+ * SO_REUSEPORT is set, or if SO_REUSEADDR is set
+ * and a multicast address is bound on both
+ * new and duplicated sockets.
+ */
+ if (so->so_options & SO_REUSEADDR)
+ reuseport = SO_REUSEADDR|SO_REUSEPORT;
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ struct ifaddr *ia = NULL;
+
+ sin6->sin6_port = 0; /* yech... */
+ if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0)
+ return(EADDRNOTAVAIL);
+
+ /*
+ * 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)) {
+ return(EADDRNOTAVAIL);
+ }
+ }
+ if (lport) {
+ struct inpcb *t;
+
+ /* GROSS */
+ if (ntohs(lport) < IPV6PORT_RESERVED && td &&
+ suser_cred(td->td_ucred, PRISON_ROOT))
+ return(EACCES);
+ if (so->so_cred->cr_uid != 0 &&
+ !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ t = in6_pcblookup_local(pcbinfo,
+ &sin6->sin6_addr, lport,
+ INPLOOKUP_WILDCARD);
+ if (t && (t->inp_vflag & INP_TIMEWAIT)) {
+ if ((!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
+ !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
+ !(intotw(t)->tw_so_options & SO_REUSEPORT))
+ && so->so_cred->cr_uid !=
+ intotw(t)->tw_cred->cr_uid)
+ return (EADDRINUSE);
+ } else if (t &&
+ (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
+ !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
+ (t->inp_socket->so_options & SO_REUSEPORT)
+ == 0) && (so->so_cred->cr_uid !=
+ t->inp_socket->so_cred->cr_uid))
+ return (EADDRINUSE);
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ struct sockaddr_in sin;
+
+ in6_sin6_2_sin(&sin, sin6);
+ t = in_pcblookup_local(pcbinfo,
+ sin.sin_addr, lport,
+ INPLOOKUP_WILDCARD);
+ if (t && (t->inp_vflag & INP_TIMEWAIT)) {
+ if (so->so_cred->cr_uid !=
+ intotw(t)->tw_cred->cr_uid &&
+ (ntohl(t->inp_laddr.s_addr) !=
+ INADDR_ANY ||
+ ((inp->inp_vflag &
+ INP_IPV6PROTO) ==
+ (t->inp_vflag &
+ INP_IPV6PROTO))))
+ return (EADDRINUSE);
+ } else if (t &&
+ (so->so_cred->cr_uid !=
+ t->inp_socket->so_cred->cr_uid) &&
+ (ntohl(t->inp_laddr.s_addr) !=
+ INADDR_ANY ||
+ INP_SOCKAF(so) ==
+ INP_SOCKAF(t->inp_socket)))
+ return (EADDRINUSE);
+ }
+ }
+ t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
+ lport, wild);
+ if (t && (reuseport & ((t->inp_vflag & INP_TIMEWAIT) ?
+ intotw(t)->tw_so_options :
+ t->inp_socket->so_options)) == 0)
+ return(EADDRINUSE);
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ struct sockaddr_in sin;
+
+ in6_sin6_2_sin(&sin, sin6);
+ t = in_pcblookup_local(pcbinfo, sin.sin_addr,
+ lport, wild);
+ if (t && t->inp_vflag & INP_TIMEWAIT) {
+ if ((reuseport &
+ intotw(t)->tw_so_options) == 0 &&
+ (ntohl(t->inp_laddr.s_addr) !=
+ INADDR_ANY || ((inp->inp_vflag &
+ INP_IPV6PROTO) ==
+ (t->inp_vflag & INP_IPV6PROTO))))
+ return (EADDRINUSE);
+ }
+ else if (t &&
+ (reuseport & t->inp_socket->so_options)
+ == 0 && (ntohl(t->inp_laddr.s_addr) !=
+ INADDR_ANY || INP_SOCKAF(so) ==
+ INP_SOCKAF(t->inp_socket)))
+ return (EADDRINUSE);
+ }
+ }
+ inp->in6p_laddr = sin6->sin6_addr;
+ }
+ if (lport == 0) {
+ int e;
+ if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, td)) != 0)
+ return(e);
+ }
+ else {
+ inp->inp_lport = lport;
+ if (in_pcbinshash(inp) != 0) {
+ inp->in6p_laddr = in6addr_any;
+ inp->inp_lport = 0;
+ return (EAGAIN);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Transform old in6_pcbconnect() into an inner subroutine for new
+ * in6_pcbconnect(): Do some validity-checking on the remote
+ * address (in mbuf 'nam') and then determine local host address
+ * (i.e., which interface) to use to access that remote host.
+ *
+ * This preserves definition of in6_pcbconnect(), while supporting a
+ * slightly different version for T/TCP. (This is more than
+ * a bit of a kludge, but cleaning up the internal interfaces would
+ * have forced minor changes in every protocol).
+ */
+
+int
+in6_pcbladdr(inp, nam, plocal_addr6)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct in6_addr **plocal_addr6;
+{
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ struct ifnet *ifp = NULL;
+ int error = 0;
+
+ if (nam->sa_len != sizeof (*sin6))
+ return (EINVAL);
+ if (sin6->sin6_family != AF_INET6)
+ return (EAFNOSUPPORT);
+ if (sin6->sin6_port == 0)
+ return (EADDRNOTAVAIL);
+
+ /* KAME hack: embed scopeid */
+ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0)
+ return EINVAL;
+
+ if (in6_ifaddr) {
+ /*
+ * If the destination address is UNSPECIFIED addr,
+ * use the loopback addr, e.g ::1.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+ sin6->sin6_addr = in6addr_loopback;
+ }
+ {
+ /*
+ * XXX: in6_selectsrc might replace the bound local address
+ * with the address specified by setsockopt(IPV6_PKTINFO).
+ * Is it the intended behavior?
+ */
+ *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts,
+ inp->in6p_moptions,
+ &inp->in6p_route,
+ &inp->in6p_laddr, &error);
+ if (*plocal_addr6 == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return(error);
+ }
+ /*
+ * Don't do pcblookup call here; return interface in
+ * plocal_addr6
+ * and exit to caller, that will do the lookup.
+ */
+ }
+
+ if (inp->in6p_route.ro_rt)
+ ifp = inp->in6p_route.ro_rt->rt_ifp;
+
+ return(0);
+}
+
+/*
+ * Outer subroutine:
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in6_pcbconnect(inp, nam, td)
+ register struct inpcb *inp;
+ struct sockaddr *nam;
+ struct thread *td;
+{
+ struct in6_addr *addr6;
+ register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
+ int error;
+
+ /*
+ * Call inner routine, to assign local interface address.
+ * in6_pcbladdr() may automatically fill in sin6_scope_id.
+ */
+ if ((error = in6_pcbladdr(inp, nam, &addr6)) != 0)
+ return(error);
+
+ if (in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr,
+ sin6->sin6_port,
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)
+ ? addr6 : &inp->in6p_laddr,
+ inp->inp_lport, 0, NULL) != NULL) {
+ return (EADDRINUSE);
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
+ if (inp->inp_lport == 0) {
+ error = in6_pcbbind(inp, (struct sockaddr *)0, td);
+ if (error)
+ return (error);
+ }
+ inp->in6p_laddr = *addr6;
+ }
+ inp->in6p_faddr = sin6->sin6_addr;
+ inp->inp_fport = sin6->sin6_port;
+ /* 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);
+
+ in_pcbrehash(inp);
+ return (0);
+}
+
+#if 0
+/*
+ * Return an IPv6 address, which is the most appropriate for given
+ * destination and user specified options.
+ * If necessary, this function lookups the routing table and return
+ * an entry to the caller for later use.
+ */
+struct in6_addr *
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct in6_addr *laddr;
+ int *errorp;
+{
+ struct in6_addr *dst;
+ struct in6_ifaddr *ia6 = 0;
+ struct in6_pktinfo *pi = NULL;
+
+ dst = &dstsock->sin6_addr;
+ *errorp = 0;
+
+ /*
+ * If the source address is explicitly specified by the caller,
+ * use it.
+ */
+ if (opts && (pi = opts->ip6po_pktinfo) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+ return(&pi->ipi6_addr);
+
+ /*
+ * If the source address is not specified but the socket(if any)
+ * is already bound, use the bound address.
+ */
+ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
+ return(laddr);
+
+ /*
+ * If the caller doesn't specify the source address but
+ * the outgoing interface, use an address associated with
+ * the interface.
+ */
+ if (pi && pi->ipi6_ifindex) {
+ /* XXX boundary check is assumed to be already done. */
+ ia6 = in6_ifawithscope(ifnet_byindex(pi->ipi6_ifindex), dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a link-local unicast address or
+ * a multicast address, and if the outgoing interface is specified
+ * by the sin6_scope_id filed, use an address associated with the
+ * interface.
+ * XXX: We're now trying to define more specific semantics of
+ * sin6_scope_id field, so this part will be rewritten in
+ * the near future.
+ */
+ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
+ dstsock->sin6_scope_id) {
+ /*
+ * I'm not sure if boundary check for scope_id is done
+ * somewhere...
+ */
+ if (dstsock->sin6_scope_id < 0 ||
+ if_index < dstsock->sin6_scope_id) {
+ *errorp = ENXIO; /* XXX: better error? */
+ return(0);
+ }
+ ia6 = in6_ifawithscope(ifnet_byindex(dstsock->sin6_scope_id),
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a multicast address and
+ * the outgoing interface for the address is specified
+ * by the caller, use an address associated with the interface.
+ * There is a sanity check here; if the destination has node-local
+ * scope, the outgoing interfacde should be a loopback address.
+ * Even if the outgoing interface is not specified, we also
+ * choose a loopback interface as the outgoing interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
+
+ if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
+ ifp = &loif[0];
+ }
+
+ if (ifp) {
+ ia6 = in6_ifawithscope(ifp, dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&ia6->ia_addr.sin6_addr);
+ }
+ }
+
+ /*
+ * If the next hop address for the packet is specified
+ * by caller, use an address associated with the route
+ * to the next hop.
+ */
+ {
+ struct sockaddr_in6 *sin6_next;
+ struct rtentry *rt;
+
+ if (opts && opts->ip6po_nexthop) {
+ sin6_next = satosin6(opts->ip6po_nexthop);
+ rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
+ if (rt) {
+ ia6 = in6_ifawithscope(rt->rt_ifp, dst);
+ if (ia6 == 0)
+ ia6 = ifatoia6(rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro) {
+ if (ro->ro_rt &&
+ !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0) {
+ struct sockaddr_in6 *dst6;
+
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
+ 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);
+ } else {
+ rtalloc((struct route *)ro);
+ }
+ }
+
+ /*
+ * in_pcbconnect() checks out IFF_LOOPBACK to skip using
+ * the address. But we don't know why it does so.
+ * It is necessary to ensure the scope even for lo0
+ * so doesn't check out IFF_LOOPBACK.
+ */
+
+ if (ro->ro_rt) {
+ ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
+ if (ia6 == 0) /* xxx scope error ?*/
+ ia6 = ifatoia6(ro->ro_rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EHOSTUNREACH; /* no route */
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+}
+
+/*
+ * Default hop limit selection. The precedence is as follows:
+ * 1. Hoplimit valued specified via ioctl.
+ * 2. (If the outgoing interface is detected) the current
+ * hop limit of the interface specified by router advertisement.
+ * 3. The system default hoplimit.
+*/
+int
+in6_selecthlim(in6p, ifp)
+ struct in6pcb *in6p;
+ struct ifnet *ifp;
+{
+ if (in6p && in6p->in6p_hops >= 0)
+ return(in6p->in6p_hops);
+ else if (ifp)
+ return(nd_ifinfo[ifp->if_index].chlim);
+ else
+ return(ip6_defhlim);
+}
+#endif
+
+void
+in6_pcbdisconnect(inp)
+ struct inpcb *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);
+}
+
+void
+in6_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+ struct inpcbinfo *ipi = inp->inp_pcbinfo;
+
+#ifdef IPSEC
+ if (inp->in6p_sp != NULL)
+ ipsec6_delete_pcbpolicy(inp);
+#endif /* IPSEC */
+ inp->inp_gencnt = ++ipi->ipi_gencnt;
+ in_pcbremlists(inp);
+ if (so) {
+ so->so_pcb = NULL;
+ sotryfree(so);
+ }
+ if (inp->in6p_options)
+ m_freem(inp->in6p_options);
+ ip6_freepcbopts(inp->in6p_outputopts);
+ ip6_freemoptions(inp->in6p_moptions);
+ if (inp->in6p_route.ro_rt)
+ rtfree(inp->in6p_route.ro_rt);
+ /* Check and free IPv4 related resources in case of mapped addr */
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ ip_freemoptions(inp->inp_moptions);
+ inp->inp_vflag = 0;
+ INP_LOCK_DESTROY(inp);
+ uma_zfree(ipi->ipi_zone, inp);
+}
+
+struct sockaddr *
+in6_sockaddr(port, addr_p)
+ in_port_t port;
+ struct in6_addr *addr_p;
+{
+ struct sockaddr_in6 *sin6;
+
+ MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK);
+ bzero(sin6, sizeof *sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_port = port;
+ sin6->sin6_addr = *addr_p;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ else
+ sin6->sin6_scope_id = 0; /*XXX*/
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_addr.s6_addr16[1] = 0;
+
+ return (struct sockaddr *)sin6;
+}
+
+struct sockaddr *
+in6_v4mapsin6_sockaddr(port, addr_p)
+ in_port_t port;
+ struct in_addr *addr_p;
+{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 *sin6_p;
+
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = port;
+ sin.sin_addr = *addr_p;
+
+ MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME,
+ M_WAITOK);
+ in6_sin_2_v4mapsin6(&sin, sin6_p);
+
+ return (struct sockaddr *)sin6_p;
+}
+
+/*
+ * The calling convention of in6_setsockaddr() and in6_setpeeraddr() was
+ * modified to match the pru_sockaddr() and pru_peeraddr() entry points
+ * in struct pr_usrreqs, so that protocols can just reference then directly
+ * without the need for a wrapper function. The socket must have a valid
+ * (i.e., non-nil) PCB, but it should be impossible to get an invalid one
+ * except through a kernel programming error, so it is acceptable to panic
+ * (or in this case trap) if the PCB is invalid. (Actually, we don't trap
+ * because there actually /is/ a programming error somewhere... XXX)
+ */
+int
+in6_setsockaddr(so, nam)
+ struct socket *so;
+ struct sockaddr **nam;
+{
+ int s;
+ register struct inpcb *inp;
+ struct in6_addr addr;
+ in_port_t port;
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (!inp) {
+ splx(s);
+ return EINVAL;
+ }
+ port = inp->inp_lport;
+ addr = inp->in6p_laddr;
+ splx(s);
+
+ *nam = in6_sockaddr(port, &addr);
+ return 0;
+}
+
+int
+in6_setpeeraddr(so, nam)
+ struct socket *so;
+ struct sockaddr **nam;
+{
+ int s;
+ struct inpcb *inp;
+ struct in6_addr addr;
+ in_port_t port;
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (!inp) {
+ splx(s);
+ return EINVAL;
+ }
+ port = inp->inp_fport;
+ addr = inp->in6p_faddr;
+ splx(s);
+
+ *nam = in6_sockaddr(port, &addr);
+ return 0;
+}
+
+int
+in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error;
+
+ if (inp == NULL)
+ return EINVAL;
+ if (inp->inp_vflag & INP_IPV4) {
+ error = in_setsockaddr(so, nam, &tcbinfo);
+ if (error == 0)
+ in6_sin_2_v4mapsin6_in_sock(nam);
+ } else
+ /* scope issues will be handled in in6_setsockaddr(). */
+ error = in6_setsockaddr(so, nam);
+
+ return error;
+}
+
+int
+in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error;
+
+ if (inp == NULL)
+ return EINVAL;
+ if (inp->inp_vflag & INP_IPV4) {
+ error = in_setpeeraddr(so, nam, &tcbinfo);
+ if (error == 0)
+ in6_sin_2_v4mapsin6_in_sock(nam);
+ } else
+ /* scope issues will be handled in in6_setpeeraddr(). */
+ error = in6_setpeeraddr(so, nam);
+
+ return error;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+void
+in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, notify)
+ struct inpcbhead *head;
+ struct sockaddr *dst;
+ const struct sockaddr *src;
+ u_int fport_arg, lport_arg;
+ int cmd;
+ struct inpcb *(*notify) __P((struct inpcb *, int));
+{
+ struct inpcb *inp, *ninp;
+ struct sockaddr_in6 sa6_src, *sa6_dst;
+ u_short fport = fport_arg, lport = lport_arg;
+ u_int32_t flowinfo;
+ int errno, s;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6)
+ return;
+
+ 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 : *(const 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
+ * the cache, and deliver the error to all the sockets.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ fport = 0;
+ lport = 0;
+ bzero((caddr_t)&sa6_src.sin6_addr, sizeof(sa6_src.sin6_addr));
+
+ 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) == 0)
+ continue;
+
+ /*
+ * 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);
+ }
+ splx(s);
+}
+
+/*
+ * Lookup a PCB based on the local address and port.
+ */
+struct inpcb *
+in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay)
+ struct inpcbinfo *pcbinfo;
+ struct in6_addr *laddr;
+ u_int lport_arg;
+ int wild_okay;
+{
+ register struct inpcb *inp;
+ int matchwild = 3, wildcard;
+ u_short lport = lport_arg;
+
+ if (!wild_okay) {
+ struct inpcbhead *head;
+ /*
+ * Look for an unconnected (wildcard foreign addr) PCB that
+ * matches the local address and port we're looking for.
+ */
+ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+ if ((inp->inp_vflag & INP_IPV6) == 0)
+ continue;
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
+ IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
+ inp->inp_lport == lport) {
+ /*
+ * Found.
+ */
+ return (inp);
+ }
+ }
+ /*
+ * Not found.
+ */
+ return (NULL);
+ } else {
+ struct inpcbporthead *porthash;
+ struct inpcbport *phd;
+ struct inpcb *match = NULL;
+ /*
+ * Best fit PCB lookup.
+ *
+ * First see if this local port is in use by looking on the
+ * port hash list.
+ */
+ porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport,
+ pcbinfo->porthashmask)];
+ LIST_FOREACH(phd, porthash, phd_hash) {
+ if (phd->phd_port == lport)
+ break;
+ }
+ if (phd != NULL) {
+ /*
+ * Port is in use by one or more PCBs. Look for best
+ * fit.
+ */
+ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) {
+ wildcard = 0;
+ if ((inp->inp_vflag & INP_IPV6) == 0)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ wildcard++;
+ if (!IN6_IS_ADDR_UNSPECIFIED(
+ &inp->in6p_laddr)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(laddr))
+ wildcard++;
+ else if (!IN6_ARE_ADDR_EQUAL(
+ &inp->in6p_laddr, laddr))
+ continue;
+ } else {
+ if (!IN6_IS_ADDR_UNSPECIFIED(laddr))
+ wildcard++;
+ }
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0) {
+ break;
+ }
+ }
+ }
+ }
+ return (match);
+ }
+}
+
+void
+in6_pcbpurgeif0(head, ifp)
+ struct in6pcb *head;
+ struct ifnet *ifp;
+{
+ struct in6pcb *in6p;
+ struct ip6_moptions *im6o;
+ struct in6_multi_mship *imm, *nimm;
+
+ for (in6p = head; in6p != NULL; in6p = LIST_NEXT(in6p, inp_list)) {
+ im6o = in6p->in6p_moptions;
+ if ((in6p->inp_vflag & INP_IPV6) &&
+ im6o) {
+ /*
+ * Unselect the outgoing interface if it is being
+ * detached.
+ */
+ if (im6o->im6o_multicast_ifp == ifp)
+ im6o->im6o_multicast_ifp = NULL;
+
+ /*
+ * Drop multicast group membership if we joined
+ * through the interface being detached.
+ * XXX controversial - is it really legal for kernel
+ * to force this?
+ */
+ for (imm = im6o->im6o_memberships.lh_first;
+ imm != NULL; imm = nimm) {
+ nimm = imm->i6mm_chain.le_next;
+ if (imm->i6mm_maddr->in6m_ifp == ifp) {
+ LIST_REMOVE(imm, i6mm_chain);
+ in6_delmulti(imm->i6mm_maddr);
+ free(imm, M_IPMADDR);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+void
+in6_losing(in6p)
+ struct inpcb *in6p;
+{
+ struct rtentry *rt;
+ struct rt_addrinfo info;
+
+ if ((rt = in6p->in6p_route.ro_rt) != NULL) {
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_flags = rt->rt_flags;
+ info.rti_info[RTAX_DST] = rt_key(rt);
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void)rtrequest1(RTM_DELETE, &info, NULL);
+ in6p->in6p_route.ro_rt = NULL;
+ rtfree(rt);
+ /*
+ * A new route can be allocated
+ * the next time output is attempted.
+ */
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+struct inpcb *
+in6_rtchange(inp, errno)
+ struct inpcb *inp;
+ int errno;
+{
+ if (inp->in6p_route.ro_rt) {
+ rtfree(inp->in6p_route.ro_rt);
+ inp->in6p_route.ro_rt = 0;
+ /*
+ * A new route can be allocated the next time
+ * output is attempted.
+ */
+ }
+ return inp;
+}
+
+/*
+ * Lookup PCB in hash list.
+ */
+struct inpcb *
+in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp)
+ struct inpcbinfo *pcbinfo;
+ struct in6_addr *faddr, *laddr;
+ u_int fport_arg, lport_arg;
+ int wildcard;
+ struct ifnet *ifp;
+{
+ struct inpcbhead *head;
+ register struct inpcb *inp;
+ u_short fport = fport_arg, lport = lport_arg;
+ int faith;
+
+ if (faithprefix_p != NULL)
+ faith = (*faithprefix_p)(laddr);
+ else
+ faith = 0;
+
+ /*
+ * First look for an exact match.
+ */
+ head = &pcbinfo->hashbase[INP_PCBHASH(faddr->s6_addr32[3] /* XXX */,
+ lport, fport,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+ if ((inp->inp_vflag & INP_IPV6) == 0)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) &&
+ IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
+ inp->inp_fport == fport &&
+ inp->inp_lport == lport) {
+ /*
+ * Found.
+ */
+ return (inp);
+ }
+ }
+ if (wildcard) {
+ struct inpcb *local_wild = NULL;
+
+ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
+ pcbinfo->hashmask)];
+ LIST_FOREACH(inp, head, inp_hash) {
+ if ((inp->inp_vflag & INP_IPV6) == 0)
+ continue;
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
+ inp->inp_lport == lport) {
+ if (faith && (inp->inp_flags & INP_FAITH) == 0)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
+ laddr))
+ return (inp);
+ else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
+ local_wild = inp;
+ }
+ }
+ return (local_wild);
+ }
+
+ /*
+ * Not found.
+ */
+ return (NULL);
+}
+
+void
+init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
+{
+ struct ip6_hdr *ip;
+
+ ip = mtod(m, struct ip6_hdr *);
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = ip->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id =
+ (m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ ? m->m_pkthdr.rcvif->if_index : 0;
+
+ return;
+}
diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h
new file mode 100644
index 0000000..6d5c609
--- /dev/null
+++ b/sys/netinet6/in6_pcb.h
@@ -0,0 +1,116 @@
+/* $FreeBSD$ */
+/* $KAME: in6_pcb.h,v 1.13 2001/02/06 09:16:53 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IN6_PCB_H_
+#define _NETINET6_IN6_PCB_H_
+
+#ifdef _KERNEL
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+
+void in6_pcbpurgeif0 __P((struct in6pcb *, struct ifnet *));
+void in6_losing __P((struct inpcb *));
+int in6_pcballoc __P((struct socket *, struct inpcbinfo *, struct thread *));
+int in6_pcbbind __P((struct inpcb *, struct sockaddr *, struct thread *));
+int in6_pcbconnect __P((struct inpcb *, struct sockaddr *, struct thread *));
+void in6_pcbdetach __P((struct inpcb *));
+void in6_pcbdisconnect __P((struct inpcb *));
+int in6_pcbladdr __P((struct inpcb *, struct sockaddr *,
+ struct in6_addr **));
+struct inpcb *
+ in6_pcblookup_local __P((struct inpcbinfo *,
+ struct in6_addr *, u_int, int));
+struct inpcb *
+ in6_pcblookup_hash __P((struct inpcbinfo *,
+ struct in6_addr *, u_int, struct in6_addr *,
+ u_int, int, struct ifnet *));
+void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *,
+ u_int, const struct sockaddr *, u_int, int,
+ struct inpcb *(*)(struct inpcb *, int)));
+struct inpcb *
+ in6_rtchange __P((struct inpcb *, int));
+struct sockaddr *
+ in6_sockaddr __P((in_port_t port, struct in6_addr *addr_p));
+struct sockaddr *
+ in6_v4mapsin6_sockaddr __P((in_port_t port, struct in_addr *addr_p));
+int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam));
+int in6_setsockaddr __P((struct socket *so, struct sockaddr **nam));
+int in6_mapped_sockaddr __P((struct socket *so, struct sockaddr **nam));
+int in6_mapped_peeraddr __P((struct socket *so, struct sockaddr **nam));
+struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *,
+ struct ip6_pktopts *,
+ struct ip6_moptions *,
+ struct route_in6 *,
+ struct in6_addr *, int *));
+int in6_selecthlim __P((struct in6pcb *, struct ifnet *));
+int in6_pcbsetport __P((struct in6_addr *, struct inpcb *, struct thread *));
+void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m));
+#endif /* _KERNEL */
+
+#endif /* !_NETINET6_IN6_PCB_H_ */
diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c
new file mode 100644
index 0000000..4292cd4
--- /dev/null
+++ b/sys/netinet6/in6_prefix.c
@@ -0,0 +1,1203 @@
+/* $FreeBSD$ */
+/* $KAME: in6_prefix.c,v 1.47 2001/03/25 08:41:39 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.2 (Berkeley) 11/15/93
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/ip6_var.h>
+
+static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix");
+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,
+ struct rp_addr *rap));
+static int create_ra_entry __P((struct rp_addr **rapp));
+static int add_each_prefix __P((struct socket *so, struct rr_prefix *rpp));
+static void free_rp_entries __P((struct rr_prefix *rpp));
+static int link_stray_ia6s __P((struct rr_prefix *rpp));
+static void rp_remove __P((struct rr_prefix *rpp));
+
+/*
+ * Copy bits from src to tgt, from off bit for len bits.
+ * Caller must specify collect tgtsize and srcsize.
+ */
+static void
+bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize,
+ u_int off, u_int len)
+{
+ char *sp, *tp;
+
+ /* arg values check */
+ if (srcsize < off || srcsize < (off + len) ||
+ tgtsize < off || tgtsize < (off + len)) {
+ log(LOG_ERR,
+ "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n"
+ "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off,
+ len);
+ return;
+ }
+
+ /* search start point */
+ for (sp = src, tp = tgt; off >= 8; sp++, tp++)
+ off-=8;
+ /* copy starting bits */
+ if (off) {
+ char setbit;
+ int startbits;
+
+ startbits = min((8 - off), len);
+
+ for (setbit = (0x80 >> off); startbits;
+ setbit >>= 1, startbits--, len--)
+ *tp |= (setbit & *sp);
+ tp++;
+ sp++;
+ }
+ /* copy midium bits */
+ for (; len >= 8; sp++, tp++) {
+ *tp = *sp;
+ len-=8;
+ }
+ /* copy ending bits */
+ if (len) {
+ char setbit;
+
+ for (setbit = 0x80; len; setbit >>= 1, len--)
+ *tp |= (setbit & *sp);
+ }
+}
+
+static struct ifprefix *
+in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr)))
+ break;
+ }
+ return (ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt
+ */
+static struct rr_prefix *
+search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ struct rr_prefix *rpp;
+
+ /* search matched prefix */
+ ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen,
+ &ipr->ipr_prefix.sin6_addr);
+ if (ifpr != NULL)
+ return ifpr2rp(ifpr);
+
+ /*
+ * search matched addr, and then search prefix
+ * which matches the addr
+ */
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (ipr->ipr_plen <=
+ in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa)))
+ break;
+ }
+ if (ifa == NULL)
+ return NULL;
+
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0)
+ return rpp;
+
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa),
+ IFPR_IN6(ifpr)))
+ break;
+ }
+ if (ifpr != NULL)
+ log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s"
+ "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)),
+ ip6_sprintf(IFPR_IN6(ifpr)));
+ return ifpr2rp(ifpr);
+}
+
+/*
+ * Search prefix which matches arg prefix as specified in
+ * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists.
+ * Return 1 if anything matched, and 0 if nothing matched.
+ */
+static int
+mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+ struct ifaddr *ifa;
+ int matchlen, matched = 0;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFPR_IN6(ifpr));
+ if (irr->irr_m_minlen > ifpr->ifpr_plen ||
+ irr->irr_m_maxlen < ifpr->ifpr_plen ||
+ irr->irr_m_len > matchlen)
+ continue;
+ matched = 1;
+ ifpr2rp(ifpr)->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+
+ /*
+ * search matched addr, and then search prefixes
+ * which matche the addr
+ */
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
+ {
+ struct rr_prefix *rpp;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
+ IFA_IN6(ifa));
+ if (irr->irr_m_minlen > matchlen ||
+ irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen)
+ continue;
+ rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (rpp != 0) {
+ matched = 1;
+ rpp->rp_statef_addmark = 1;
+ if (cmd == SIOCCIFPREFIX_IN6)
+ rpp->rp_statef_delmark = 1;
+ } else
+ log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:"
+ "no back pointer to ifprefix for %s. "
+ "ND autoconfigured addr?\n",
+ ip6_sprintf(IFA_IN6(ifa)));
+ }
+ return matched;
+}
+
+/*
+ * Mark global prefixes as to be deleted.
+ */
+static void
+delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr;
+
+ /* search matched prefixes */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* mark delete global prefix */
+ if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) ==
+ IPV6_ADDR_SCOPE_GLOBAL)
+ ifpr2rp(ifpr)->rp_statef_delmark = 1;
+ }
+}
+
+/* Unmark prefixes */
+static void
+unmark_prefixes(struct ifnet *ifp)
+{
+ struct ifprefix *ifpr;
+
+ /* unmark all prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ /* unmark prefix */
+ ifpr2rp(ifpr)->rp_statef_addmark = 0;
+ ifpr2rp(ifpr)->rp_statef_delmark = 0;
+ }
+}
+
+static void
+init_prefix_ltimes(struct rr_prefix *rpp)
+{
+
+ if (rpp->rp_pltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrprefd == 0)
+ rpp->rp_preferred = 0;
+ else
+ rpp->rp_preferred = time_second + rpp->rp_pltime;
+ if (rpp->rp_vltime == RR_INFINITE_LIFETIME ||
+ rpp->rp_rrf_decrvalid == 0)
+ rpp->rp_expire = 0;
+ else
+ rpp->rp_expire = time_second + rpp->rp_vltime;
+}
+
+static int
+rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len)
+{
+ int ii_bytelen, ii_bitlen;
+ int p_bytelen, p_bitlen;
+
+ /* sanity check */
+ if (1 > ii_len ||
+ ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */
+ log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n",
+ ii_len);
+ return(0);
+ }
+
+ ii_bytelen = ii_len / 8;
+ ii_bitlen = ii_len % 8;
+
+ p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1;
+ p_bitlen = 8 - ii_bitlen;
+
+ if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1,
+ ii_bytelen))
+ return(0);
+ if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) !=
+ ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff))
+ return(0);
+
+ return(1);
+}
+
+static struct rp_addr *
+search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid)
+{
+ struct rp_addr *rap;
+
+ LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry)
+ {
+ if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
+ (sizeof(struct in6_addr) << 3) -
+ rpp->rp_plen))
+ break;
+ }
+ return rap;
+}
+
+static int
+assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
+{
+ int error = 0;
+ struct rp_addr *rap;
+ int s;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IA6_IN6(ia),
+ sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen);
+ /* link to ia, and put into list */
+ rap->ra_addr = ia;
+ 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
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+
+ return 0;
+}
+
+/*
+ * add a link-local address to an interface. we will add new interface address
+ * (prefix database + new interface id).
+ */
+static int
+in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia)
+{
+ struct rr_prefix *rpp;
+ struct rp_addr *rap;
+ struct socket so;
+ int error, s;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return(error);
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3,
+ 64, (sizeof(rap->ra_ifid) << 3) - 64);
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+ /* insert into list */
+ LIST_FOREACH(rpp, &rr_prefix, rp_entry)
+ {
+ /*
+ * do not attempt to add an address, if ifp does not match
+ */
+ if (rpp->rp_ifp != ia->ia_ifp)
+ continue;
+
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+ add_each_addr(&so, rpp, rap);
+ }
+ return 0;
+}
+
+/*
+ * add an address to an interface. if the interface id portion is new,
+ * we will add new interface address (prefix database + new interface id).
+ */
+int
+in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int error = 0;
+
+ if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia)))
+ return(in6_prefix_add_llifid(iilen, ia));
+ ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia));
+ if (ifpr == NULL) {
+ struct rr_prefix rp;
+ struct socket so;
+ int pplen = (plen == 128) ? 64 : plen; /* XXX hardcoded 64 is bad */
+
+ /* allocate a prefix for ia, with default properties */
+
+ /* init rp */
+ bzero(&rp, sizeof(rp));
+ rp.rp_type = IN6_PREFIX_RR;
+ rp.rp_ifp = ia->ia_ifp;
+ rp.rp_plen = pplen;
+ rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix);
+ rp.rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3,
+ (char *)&ia->ia_addr.sin6_addr,
+ sizeof(ia->ia_addr.sin6_addr) << 3,
+ 0, pplen);
+ rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME;
+ rp.rp_raf_onlink = 1;
+ rp.rp_raf_auto = 1;
+ /* Is some FlagMasks for rrf necessary? */
+ rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0;
+ rp.rp_origin = PR_ORIG_RR; /* can be renumbered */
+
+ /* create ra_entry */
+ error = link_stray_ia6s(&rp);
+ if (error != 0) {
+ free_rp_entries(&rp);
+ return error;
+ }
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+
+ error = add_each_prefix(&so, &rp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ if (error != 0)
+ return error;
+
+ /* search again */
+ ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia));
+ if (ifpr == NULL)
+ return 0;
+ }
+ rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ if (rap->ra_addr == NULL) {
+ rap->ra_addr = ia;
+ 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"
+ " 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)));
+ return EADDRINUSE /* XXX */;
+ }
+ ia->ia6_ifpr = ifpr;
+ return 0;
+ }
+ error = assign_ra_entry(ifpr2rp(ifpr), iilen, ia);
+ if (error == 0)
+ ia->ia6_ifpr = ifpr;
+ return (error);
+}
+
+void
+in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia)
+{
+ struct rp_addr *rap;
+
+ if (ia->ia6_ifpr == NULL)
+ return;
+ rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia));
+ if (rap != NULL) {
+ int s = splnet();
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ if (rap->ra_addr)
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ free(rap, M_RR_ADDR);
+ }
+
+ if (LIST_EMPTY(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead))
+ rp_remove(ifpr2rp(ia->ia6_ifpr));
+}
+
+void
+in6_purgeprefix(ifp)
+ struct ifnet *ifp;
+{
+ struct ifprefix *ifpr, *nextifpr;
+
+ /* delete prefixes before ifnet goes away */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr;
+ ifpr = nextifpr)
+ {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ (void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL);
+ }
+}
+
+static void
+add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)
+{
+ struct in6_ifaddr *ia6;
+ struct in6_aliasreq ifra;
+ int error;
+
+ /* init ifra */
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name));
+ ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family =
+ AF_INET6;
+ ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len =
+ sizeof(ifra.ifra_addr);
+ /* copy prefix part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ 0, rpp->rp_plen);
+ /* copy interface id part */
+ bit_copy((char *)&ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr) << 3,
+ (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
+ rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen);
+ 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) {
+ /* link this addr and the prefix each other */
+ if (rap->ra_addr)
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ rap->ra_addr = ia6;
+ IFAREF(&rap->ra_addr->ia_ifa);
+ ia6->ia6_ifpr = rp2ifpr(rpp);
+ return;
+ }
+ if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
+ if (rap->ra_addr)
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ rap->ra_addr = ia6;
+ IFAREF(&rap->ra_addr->ia_ifa);
+ return;
+ }
+ /*
+ * The addr is already assigned to other
+ * prefix.
+ * There may be some inconsistencies between
+ * prefixes.
+ * e.g. overraped prefixes with common starting
+ * part and different plefixlen.
+ * 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",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ ip6_sprintf(IA6_IN6(ia6)),
+ 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,
+ curthread);
+ 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
+ * in in6_prefix_add_ifid
+ */
+}
+
+static int
+rrpr_update(struct socket *so, struct rr_prefix *new)
+{
+ struct rr_prefix *rpp;
+ struct ifprefix *ifpr;
+ struct rp_addr *rap;
+ int s;
+
+ /* search existing prefix */
+ for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr;
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ {
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr->ifpr_plen == new->rp_plen &&
+ in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new),
+ ifpr->ifpr_plen))
+ break;
+ }
+ rpp = ifpr2rp(ifpr);
+ if (rpp != NULL) {
+ /*
+ * We got a prefix which we have seen in the past.
+ */
+ /*
+ * If the origin of the already-installed prefix is more
+ * preferable than the new one, ignore installation request.
+ */
+ if (rpp->rp_origin > new->rp_origin)
+ return(EPERM);
+
+ /* update prefix information */
+ rpp->rp_flags.prf_ra = new->rp_flags.prf_ra;
+ if (rpp->rp_origin >= PR_ORIG_RR)
+ rpp->rp_flags.prf_rr = new->rp_flags.prf_rr;
+ rpp->rp_vltime = new->rp_vltime;
+ rpp->rp_pltime = new->rp_pltime;
+ rpp->rp_expire = new->rp_expire;
+ rpp->rp_preferred = new->rp_preferred;
+ rpp->rp_statef_delmark = 0; /* cancel deletion */
+ /*
+ * Interface id related update.
+ * add rp_addr entries in new into rpp, if they have not
+ * been already included in rpp.
+ */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ if (search_ifidwithprefix(rpp, &rap->ra_ifid)
+ != NULL) {
+ if (rap->ra_addr)
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ s = splnet();
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ splx(s);
+ }
+ } else {
+ /*
+ * We got a fresh prefix.
+ */
+ /* create new prefix */
+ rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR,
+ M_NOWAIT);
+ if (rpp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: rrpr_update:%d"
+ ": ENOBUFS for rr_prefix\n", __LINE__);
+ return(ENOBUFS);
+ }
+ /* initilization */
+ *rpp = *new;
+ LIST_INIT(&rpp->rp_addrhead);
+ /* move rp_addr entries of new to rpp */
+ while (!LIST_EMPTY(&new->rp_addrhead))
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+
+ /* let rp_ifpr.ifpr_prefix point rr_prefix. */
+ rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix;
+ /* link rr_prefix entry to if_prefixlist */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead))
+ != NULL) {
+ for ( ; TAILQ_NEXT(ifpr, ifpr_list);
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list))
+ continue;
+ TAILQ_NEXT(ifpr, ifpr_list) = rp2ifpr(rpp);
+ } else
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ rp2ifpr(rpp);
+ rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR;
+ }
+ /* link rr_prefix entry to rr_prefix list */
+ s = splnet();
+ LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry);
+ splx(s);
+ }
+
+ if (!new->rp_raf_auto)
+ return 0;
+
+ /*
+ * Add an address for each interface id, if it is not yet
+ * If it existed but not pointing to the prefix yet,
+ * init the prefix pointer.
+ */
+ LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry)
+ {
+ if (rap->ra_addr != NULL) {
+ if (rap->ra_addr->ia6_ifpr == NULL)
+ rap->ra_addr->ia6_ifpr = rp2ifpr(rpp);
+ continue;
+ }
+ add_each_addr(so, rpp, rap);
+ }
+ return 0;
+}
+
+static int
+add_each_prefix(struct socket *so, struct rr_prefix *rpp)
+{
+ init_prefix_ltimes(rpp);
+ return(rrpr_update(so, rpp));
+}
+
+static void
+rp_remove(struct rr_prefix *rpp)
+{
+ int s;
+
+ s = splnet();
+ /* unlink rp_entry from if_prefixlist */
+ {
+ struct ifnet *ifp = rpp->rp_ifp;
+ struct ifprefix *ifpr;
+
+ if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) == rp2ifpr(rpp))
+ TAILQ_FIRST(&ifp->if_prefixhead) =
+ TAILQ_NEXT(ifpr, ifpr_list);
+ else {
+ while (TAILQ_NEXT(ifpr, ifpr_list) != NULL &&
+ (TAILQ_NEXT(ifpr, ifpr_list) != rp2ifpr(rpp)))
+ ifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (TAILQ_NEXT(ifpr, ifpr_list))
+ TAILQ_NEXT(ifpr, ifpr_list) =
+ TAILQ_NEXT(rp2ifpr(rpp), ifpr_list);
+ else
+ printf("Couldn't unlink rr_prefix from ifp\n");
+ }
+ }
+ /* unlink rp_entry from rr_prefix list */
+ LIST_REMOVE(rpp, rp_entry);
+ splx(s);
+ free(rpp, M_IP6RR);
+}
+
+static int
+create_ra_entry(struct rp_addr **rapp)
+{
+ *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR,
+ M_NOWAIT);
+ if (*rapp == NULL) {
+ log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS"
+ "for rp_addr\n", __LINE__);
+ return ENOBUFS;
+ }
+ bzero(*rapp, sizeof(*(*rapp)));
+
+ return 0;
+}
+
+static int
+init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
+ struct rr_prefix *rpp)
+{
+ struct rp_addr *orap;
+
+ /* init rp */
+ bzero(rpp, sizeof(*rpp));
+ rpp->rp_type = IN6_PREFIX_RR;
+ rpp->rp_ifp = ifpr->ifpr_ifp;
+ rpp->rp_plen = ifpr->ifpr_plen;
+ rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix);
+ rpp->rp_prefix.sin6_family = AF_INET6;
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)&irr->irr_useprefix.sin6_addr,
+ sizeof(irr->irr_useprefix.sin6_addr) << 3,
+ 0, irr->irr_u_uselen);
+ /* copy keeplen part if necessary as necessary len */
+ if (irr->irr_u_uselen < ifpr->ifpr_plen)
+ bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
+ (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3,
+ irr->irr_u_uselen,
+ min(ifpr->ifpr_plen - irr->irr_u_uselen,
+ irr->irr_u_keeplen));
+ LIST_FOREACH(orap, &(ifpr2rp(ifpr)->rp_addrhead), ra_entry)
+ {
+ struct rp_addr *rap;
+ int error = 0;
+
+ if ((error = create_ra_entry(&rap)) != 0)
+ return error;
+ rap->ra_ifid = orap->ra_ifid;
+ rap->ra_flags.anycast = (orap->ra_addr != NULL &&
+ (orap->ra_addr->ia6_flags &
+ IN6_IFF_ANYCAST) != 0) ? 1 : 0;
+ LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
+ }
+ rpp->rp_vltime = irr->irr_vltime;
+ rpp->rp_pltime = irr->irr_pltime;
+ rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink :
+ ifpr2rp(ifpr)->rp_raf_onlink;
+ rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto :
+ ifpr2rp(ifpr)->rp_raf_auto;
+ /* Is some FlagMasks for rrf necessary? */
+ rpp->rp_rrf = irr->irr_rrf;
+ rpp->rp_origin = irr->irr_origin;
+
+ return 0;
+}
+
+static void
+free_rp_entries(struct rr_prefix *rpp)
+{
+ /*
+ * This func is only called with rpp on stack(not on list).
+ * So no splnet() here
+ */
+ while (!LIST_EMPTY(&rpp->rp_addrhead))
+ {
+ struct rp_addr *rap;
+
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ if (rap->ra_addr)
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+static int
+add_useprefixes(struct socket *so, struct ifnet *ifp,
+ struct in6_rrenumreq *irr)
+{
+ struct ifprefix *ifpr, *nextifpr;
+ struct rr_prefix rp;
+ int error = 0;
+
+ /* add prefixes to each of marked prefix */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr)
+ {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_addmark) {
+ if ((error = init_newprefix(irr, ifpr, &rp)) != 0)
+ break;
+ error = add_each_prefix(so, &rp);
+ }
+ }
+ /* free each rp_addr entry */
+ free_rp_entries(&rp);
+
+ return error;
+}
+
+static void
+unprefer_prefix(struct rr_prefix *rpp)
+{
+ struct rp_addr *rap;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ if (rap->ra_addr == NULL)
+ continue;
+ rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second;
+ rap->ra_addr->ia6_lifetime.ia6t_pltime = 0;
+ }
+}
+
+int
+delete_each_prefix(struct rr_prefix *rpp, u_char origin)
+{
+ int error = 0;
+
+ if (rpp->rp_origin > origin)
+ return(EPERM);
+
+ while (rpp->rp_addrhead.lh_first != NULL) {
+ struct rp_addr *rap;
+ int s;
+
+ s = splnet();
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ if (rap == NULL) {
+ splx(s);
+ break;
+ }
+ LIST_REMOVE(rap, ra_entry);
+ splx(s);
+ if (rap->ra_addr == NULL) {
+ free(rap, M_RR_ADDR);
+ continue;
+ }
+ rap->ra_addr->ia6_ifpr = NULL;
+
+ in6_purgeaddr(&rap->ra_addr->ia_ifa);
+ IFAFREE(&rap->ra_addr->ia_ifa);
+ free(rap, M_RR_ADDR);
+ }
+ rp_remove(rpp);
+
+ return error;
+}
+
+static void
+delete_prefixes(struct ifnet *ifp, u_char origin)
+{
+ struct ifprefix *ifpr, *nextifpr;
+
+ /* delete prefixes marked as tobe deleted */
+ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr)
+ {
+ nextifpr = TAILQ_NEXT(ifpr, ifpr_list);
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_delmark)
+ (void)delete_each_prefix(ifpr2rp(ifpr), origin);
+ }
+}
+
+static int
+link_stray_ia6s(struct rr_prefix *rpp)
+{
+ struct ifaddr *ifa;
+
+ for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ struct rp_addr *rap;
+ struct rr_prefix *orpp;
+ int error = 0;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa)))
+ continue;
+
+ orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
+ if (orpp != NULL) {
+ if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp),
+ rpp->rp_plen))
+ log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:"
+ "addr %s/%d already linked to a prefix"
+ "and it matches also %s/%d\n",
+ ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
+ ip6_sprintf(RP_IN6(rpp)),
+ rpp->rp_plen);
+ continue;
+ }
+ if ((error = assign_ra_entry(rpp,
+ (sizeof(rap->ra_ifid) << 3) -
+ rpp->rp_plen,
+ (struct in6_ifaddr *)ifa)) != 0)
+ return error;
+ }
+ return 0;
+}
+
+/* XXX assumes that permission is already checked by the caller */
+int
+in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp)
+{
+ struct rr_prefix *rpp, rp_tmp;
+ struct rp_addr *rap;
+ struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
+ struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
+ struct ifaddr *ifa;
+ int error = 0;
+
+ /*
+ * Failsafe for errneous address config program.
+ * Let's hope rrenumd don't make a mistakes.
+ */
+ if (ipr->ipr_origin <= PR_ORIG_RA)
+ ipr->ipr_origin = PR_ORIG_STATIC;
+
+ switch (cmd) {
+ case SIOCSGIFPREFIX_IN6:
+ delmark_global_prefixes(ifp, irr);
+ /* FALL THROUGH */
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (irr->irr_pltime > irr->irr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)\n",
+ (u_long)irr->irr_pltime, (u_long)irr->irr_vltime);
+ error = EINVAL;
+ break;
+ }
+ if (mark_matched_prefixes(cmd, ifp, irr)) {
+ if (irr->irr_u_uselen != 0)
+ if ((error = add_useprefixes(so, ifp, irr))
+ != 0)
+ goto failed;
+ if (cmd != SIOCAIFPREFIX_IN6)
+ delete_prefixes(ifp, irr->irr_origin);
+ } else
+ return (EADDRNOTAVAIL);
+ failed:
+ unmark_prefixes(ifp);
+ break;
+ case SIOCGIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ ipr->ipr_origin = rpp->rp_origin;
+ ipr->ipr_plen = rpp->rp_plen;
+ ipr->ipr_vltime = rpp->rp_vltime;
+ ipr->ipr_pltime = rpp->rp_pltime;
+ ipr->ipr_flags = rpp->rp_flags;
+ ipr->ipr_prefix = rpp->rp_prefix;
+
+ break;
+ case SIOCSIFPREFIX_IN6:
+ /* check if preferred lifetime > valid lifetime */
+ if (ipr->ipr_pltime > ipr->ipr_vltime) {
+ log(LOG_NOTICE,
+ "in6_prefix_ioctl: preferred lifetime"
+ "(%ld) is greater than valid lifetime(%ld)\n",
+ (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime);
+ error = EINVAL;
+ break;
+ }
+
+ /* init rp_tmp */
+ bzero((caddr_t)&rp_tmp, sizeof(rp_tmp));
+ rp_tmp.rp_ifp = ifp;
+ rp_tmp.rp_plen = ipr->ipr_plen;
+ rp_tmp.rp_prefix = ipr->ipr_prefix;
+ rp_tmp.rp_vltime = ipr->ipr_vltime;
+ rp_tmp.rp_pltime = ipr->ipr_pltime;
+ rp_tmp.rp_flags = ipr->ipr_flags;
+ rp_tmp.rp_origin = ipr->ipr_origin;
+
+ /* create rp_addr entries, usually at least for lladdr */
+ if ((error = link_stray_ia6s(&rp_tmp)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr == NULL)
+ continue; /* just for safety */
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)) == 0)
+ continue;
+
+ if ((error = create_ra_entry(&rap)) != 0) {
+ free_rp_entries(&rp_tmp);
+ goto bad;
+ }
+ /* copy interface id part */
+ bit_copy((caddr_t)&rap->ra_ifid,
+ sizeof(rap->ra_ifid) << 3,
+ (caddr_t)IFA_IN6(ifa),
+ sizeof(*IFA_IN6(ifa)) << 3,
+ rp_tmp.rp_plen,
+ (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen);
+ /* insert into list */
+ LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry);
+ }
+
+ error = add_each_prefix(so, &rp_tmp);
+
+ /* free each rp_addr entry */
+ free_rp_entries(&rp_tmp);
+
+ break;
+ case SIOCDIFPREFIX_IN6:
+ rpp = search_matched_prefix(ifp, ipr);
+ if (rpp == NULL || ifp != rpp->rp_ifp)
+ return (EADDRNOTAVAIL);
+
+ error = delete_each_prefix(rpp, ipr->ipr_origin);
+ break;
+ }
+ bad:
+ return error;
+}
+
+void
+in6_rr_timer(void *ignored_arg)
+{
+ int s;
+ struct rr_prefix *rpp;
+
+ callout_reset(&in6_rr_timer_ch, ip6_rr_prune * hz,
+ in6_rr_timer, NULL);
+
+ s = splnet();
+ /* expire */
+ rpp = LIST_FIRST(&rr_prefix);
+ while (rpp) {
+ if (rpp->rp_expire && rpp->rp_expire < time_second) {
+ struct rr_prefix *next_rpp;
+
+ next_rpp = LIST_NEXT(rpp, rp_entry);
+ delete_each_prefix(rpp, PR_ORIG_KERNEL);
+ rpp = next_rpp;
+ continue;
+ }
+ if (rpp->rp_preferred && rpp->rp_preferred < time_second)
+ unprefer_prefix(rpp);
+ rpp = LIST_NEXT(rpp, rp_entry);
+ }
+ splx(s);
+}
diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h
new file mode 100644
index 0000000..3ae6a63
--- /dev/null
+++ b/sys/netinet6/in6_prefix.h
@@ -0,0 +1,91 @@
+/* $FreeBSD$ */
+/* $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.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/callout.h>
+
+struct rr_prefix {
+ struct ifprefix rp_ifpr;
+ LIST_ENTRY(rr_prefix) rp_entry;
+ LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead;
+ struct sockaddr_in6 rp_prefix; /* prefix */
+ u_int32_t rp_vltime; /* advertised valid lifetime */
+ u_int32_t rp_pltime; /* advertised preferred lifetime */
+ time_t rp_expire; /* expiration time of the prefix */
+ time_t rp_preferred; /* preferred time of the prefix */
+ struct in6_prflags rp_flags;
+ u_char rp_origin; /* from where this prefix info is obtained */
+ struct rp_stateflags {
+ /* if some prefix should be added to this prefix */
+ u_char addmark : 1;
+ u_char delmark : 1; /* if this prefix will be deleted */
+ } rp_stateflags;
+};
+
+#define rp_type rp_ifpr.ifpr_type
+#define rp_ifp rp_ifpr.ifpr_ifp
+#define rp_plen rp_ifpr.ifpr_plen
+
+#define rp_raf rp_flags.prf_ra
+#define rp_raf_onlink rp_flags.prf_ra.onlink
+#define rp_raf_auto rp_flags.prf_ra.autonomous
+
+#define rp_statef_addmark rp_stateflags.addmark
+#define rp_statef_delmark rp_stateflags.delmark
+
+#define rp_rrf rp_flags.prf_rr
+#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid
+#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd
+
+struct rp_addr {
+ LIST_ENTRY(rp_addr) ra_entry;
+ struct in6_addr ra_ifid;
+ struct in6_ifaddr *ra_addr;
+ struct ra_flags {
+ u_char anycast : 1;
+ } ra_flags;
+};
+
+#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr))
+#define rp2ifpr(rp) ((struct ifprefix *)(rp))
+
+#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr)
+
+#define RR_INFINITE_LIFETIME 0xffffffff
+
+
+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
new file mode 100644
index 0000000..1e8aeac
--- /dev/null
+++ b/sys/netinet6/in6_proto.c
@@ -0,0 +1,460 @@
+/* $FreeBSD$ */
+/* $KAME: in6_proto.c,v 1.91 2001/05/27 13:28:35 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_proto.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/kernel.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/radix.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_encap.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#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 */
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec6.h>
+#define IPSEC
+#define IPSEC_ESP
+#define ah6_input ipsec6_common_input
+#define esp6_input ipsec6_common_input
+#define ipcomp6_input ipsec6_common_input
+#endif /* FAST_IPSEC */
+
+#include <netinet6/ip6protosw.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * TCP/IP protocol family: IP6, ICMP6, UDP, TCP.
+ */
+
+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,
+ 0,
+ ip6_init, 0, frag6_slowtimo, frag6_drain,
+ &nousrreqs,
+},
+{ 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|PR_LISTEN,
+ tcp6_input, 0, tcp6_ctlinput, tcp_ctloutput,
+ 0,
+#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,
+ rip6_input, rip6_output, rip6_ctlinput, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+},
+{ 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
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR,
+ dest6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR,
+ route6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR,
+ frag6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs
+},
+#ifdef IPSEC
+{ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
+ ah6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#ifdef IPSEC_ESP
+{ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
+ esp6_input, 0,
+ esp6_ctlinput,
+ 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#endif
+{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR,
+ ipcomp6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+ &nousrreqs,
+},
+#endif /* IPSEC */
+#ifdef INET
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR|PR_LASTHDR,
+ encap6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ encap_init, 0, 0, 0,
+ &rip6_usrreqs
+},
+#endif /* INET */
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR,
+ encap6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ encap_init, 0, 0, 0,
+ &rip6_usrreqs
+},
+{ 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,
+ rip6_input, rip6_output, 0, rip6_ctloutput,
+ 0,
+ 0, 0, 0, 0,
+ &rip6_usrreqs
+},
+};
+
+extern int in6_inithead __P((void **, int));
+
+struct domain inet6domain =
+ { AF_INET6, "internet6", 0, 0, 0,
+ (struct protosw *)inet6sw,
+ (struct protosw *)&inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
+ in6_inithead,
+ offsetof(struct sockaddr_in6, sin6_addr) << 3,
+ sizeof(struct sockaddr_in6) };
+
+DOMAIN_SET(inet6);
+
+/*
+ * Internet configuration info
+ */
+#ifndef IPV6FORWARDING
+#ifdef GATEWAY6
+#define IPV6FORWARDING 1 /* forward IP6 packets not for us */
+#else
+#define IPV6FORWARDING 0 /* don't forward IP6 packets not for us */
+#endif /* GATEWAY6 */
+#endif /* !IPV6FORWARDING */
+
+#ifndef IPV6_SENDREDIRECTS
+#define IPV6_SENDREDIRECTS 1
+#endif
+
+int ip6_forwarding = IPV6FORWARDING; /* act as router? */
+int ip6_sendredirects = IPV6_SENDREDIRECTS;
+int ip6_defhlim = IPV6_DEFHLIM;
+int ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS;
+int ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */
+int ip6_maxfragpackets; /* initialized in frag6.c:frag6_init() */
+int ip6_log_interval = 5;
+int ip6_hdrnestlimit = 50; /* appropriate? */
+int ip6_dad_count = 1; /* DupAddrDetectionTransmits */
+u_int32_t ip6_flow_seq;
+int ip6_auto_flowlabel = 1;
+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_v6only = 1;
+
+u_int32_t ip6_id = 0UL;
+int ip6_keepfaith = 0;
+time_t ip6_log_time = (time_t)0L;
+
+/* icmp6 */
+/*
+ * BSDI4 defines these variables in in_proto.c...
+ * XXX: what if we don't define INET? Should we define pmtu6_expire
+ * or so? (jinmei@kame.net 19990310)
+ */
+int pmtu_expire = 60*10;
+int pmtu_probe = 60*2;
+
+/* raw IP6 parameters */
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+#define RIPV6SNDQ 8192
+#define RIPV6RCVQ 8192
+
+u_long rip6_sendspace = RIPV6SNDQ;
+u_long rip6_recvspace = RIPV6RCVQ;
+
+/* ICMPV6 parameters */
+int icmp6_rediraccept = 1; /* accept and process redirects */
+int icmp6_redirtimeout = 10 * 60; /* 10 minutes */
+int icmp6errppslim = 100; /* 100pps */
+int icmp6_nodeinfo = 3; /* enable/disable NI response */
+
+/* UDP on IP6 parameters */
+int udp6_sendspace = 9216; /* really max datagram size */
+int udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6));
+ /* 40 1K datagrams */
+
+/*
+ * sysctl related items.
+ */
+SYSCTL_NODE(_net, PF_INET6, inet6, CTLFLAG_RW, 0,
+ "Internet6 Family");
+
+/* net.inet6 */
+SYSCTL_NODE(_net_inet6, IPPROTO_IPV6, ip6, CTLFLAG_RW, 0, "IP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_ICMPV6, icmp6, CTLFLAG_RW, 0, "ICMP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_UDP, udp6, CTLFLAG_RW, 0, "UDP6");
+SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG_RW, 0, "TCP6");
+#ifdef IPSEC
+SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6");
+#endif /* IPSEC */
+
+/* net.inet6.ip6 */
+static int
+sysctl_ip6_temppltime(SYSCTL_HANDLER_ARGS)
+{
+ int error = 0;
+ int old;
+
+ error = SYSCTL_OUT(req, arg1, sizeof(int));
+ if (error || !req->newptr)
+ return (error);
+ old = ip6_temp_preferred_lifetime;
+ error = SYSCTL_IN(req, arg1, sizeof(int));
+ if (ip6_temp_preferred_lifetime <
+ ip6_desync_factor + ip6_temp_regen_advance) {
+ ip6_temp_preferred_lifetime = old;
+ return(EINVAL);
+ }
+ return(error);
+}
+
+static int
+sysctl_ip6_tempvltime(SYSCTL_HANDLER_ARGS)
+{
+ int error = 0;
+ int old;
+
+ error = SYSCTL_OUT(req, arg1, sizeof(int));
+ if (error || !req->newptr)
+ return (error);
+ 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_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,
+ hlim, CTLFLAG_RW, &ip6_defhlim, 0, "");
+SYSCTL_STRUCT(_net_inet6_ip6, IPV6CTL_STATS, stats, CTLFLAG_RD,
+ &ip6stat, ip6stat, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS,
+ maxfragpackets, CTLFLAG_RW, &ip6_maxfragpackets, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_ACCEPT_RTADV,
+ accept_rtadv, CTLFLAG_RW, &ip6_accept_rtadv, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_KEEPFAITH,
+ keepfaith, CTLFLAG_RW, &ip6_keepfaith, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_LOG_INTERVAL,
+ log_interval, CTLFLAG_RW, &ip6_log_interval, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_HDRNESTLIMIT,
+ hdrnestlimit, CTLFLAG_RW, &ip6_hdrnestlimit, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DAD_COUNT,
+ dad_count, CTLFLAG_RW, &ip6_dad_count, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_FLOWLABEL,
+ auto_flowlabel, CTLFLAG_RW, &ip6_auto_flowlabel, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFMCASTHLIM,
+ defmcasthlim, CTLFLAG_RW, &ip6_defmcasthlim, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM,
+ gifhlim, CTLFLAG_RW, &ip6_gif_hlim, 0, "");
+SYSCTL_STRING(_net_inet6_ip6, IPV6CTL_KAME_VERSION,
+ kame_version, CTLFLAG_RD, __KAME_VERSION, 0, "");
+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_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,
+ rediraccept, CTLFLAG_RW, &icmp6_rediraccept, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT,
+ redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, "");
+SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD,
+ &icmp6stat, icmp6stat, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE,
+ nd6_prune, CTLFLAG_RW, &nd6_prune, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY,
+ nd6_delay, CTLFLAG_RW, &nd6_delay, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_UMAXTRIES,
+ nd6_umaxtries, CTLFLAG_RW, &nd6_umaxtries, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MMAXTRIES,
+ nd6_mmaxtries, CTLFLAG_RW, &nd6_mmaxtries, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_USELOOPBACK,
+ nd6_useloopback, CTLFLAG_RW, &nd6_useloopback, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_NODEINFO,
+ nodeinfo, CTLFLAG_RW, &icmp6_nodeinfo, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT,
+ errppslimit, CTLFLAG_RW, &icmp6errppslim, 0, "");
+SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXNUDHINT,
+ nd6_maxnudhint, CTLFLAG_RW, &nd6_maxnudhint, 0, "");
+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
new file mode 100644
index 0000000..14587bd
--- /dev/null
+++ b/sys/netinet6/in6_rmx.c
@@ -0,0 +1,487 @@
+/* $FreeBSD$ */
+/* $KAME: in6_rmx.c,v 1.11 2001/07/26 06:53:16 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 1994, 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. 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.
+ *
+ */
+
+/*
+ * This code does two things necessary for the enhanced TCP metrics to
+ * function in a useful manner:
+ * 1) It marks all non-host routes as `cloning', thus ensuring that
+ * every actual reference to such a route actually gets turned
+ * into a reference to a host route to the specific destination
+ * requested.
+ * 2) When such routes lose all their references, it arranges for them
+ * to be deleted in some random collection of circumstances, so that
+ * a large quantity of stale routing data is not kept in kernel memory
+ * indefinitely. See in6_rtqtimo() below for the exact mechanism.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+
+#include <netinet/icmp6.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+
+extern int in6_inithead __P((void **head, int off));
+
+#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */
+
+/*
+ * Do what we need to do when inserting a route.
+ */
+static struct radix_node *
+in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
+ struct radix_node *treenodes)
+{
+ struct rtentry *rt = (struct rtentry *)treenodes;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
+ struct radix_node *ret;
+
+ /*
+ * For IPv6, all unicast non-host routes are automatically cloning.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ rt->rt_flags |= RTF_MULTICAST;
+
+ if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
+ rt->rt_flags |= RTF_PRCLONING;
+ }
+
+ /*
+ * A little bit of help for both IPv6 output and input:
+ * For local addresses, we make sure that RTF_LOCAL is set,
+ * with the thought that this might one day be used to speed up
+ * ip_input().
+ *
+ * We also mark routes to multicast addresses as such, because
+ * it's easy to do and might be useful (but this is much more
+ * dubious since it's so easy to inspect the address). (This
+ * is done above.)
+ *
+ * XXX
+ * should elaborate the code.
+ */
+ if (rt->rt_flags & RTF_HOST) {
+ if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr)
+ ->sin6_addr,
+ &sin6->sin6_addr)) {
+ rt->rt_flags |= RTF_LOCAL;
+ }
+ }
+
+ 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;
+
+ ret = rn_addroute(v_arg, n_arg, head, treenodes);
+ if (ret == NULL && rt->rt_flags & RTF_HOST) {
+ struct rtentry *rt2;
+ /*
+ * We are trying to add a host route, but can't.
+ * Find out if it is because of an
+ * ARP entry and delete it if so.
+ */
+ rt2 = rtalloc1((struct sockaddr *)sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+ if (rt2) {
+ if (rt2->rt_flags & RTF_LLINFO &&
+ rt2->rt_flags & RTF_HOST &&
+ rt2->rt_gateway &&
+ rt2->rt_gateway->sa_family == AF_LINK) {
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt2),
+ rt2->rt_gateway,
+ rt_mask(rt2), rt2->rt_flags, 0);
+ ret = rn_addroute(v_arg, n_arg, head,
+ treenodes);
+ }
+ RTFREE(rt2);
+ }
+ } else if (ret == NULL && rt->rt_flags & RTF_CLONING) {
+ struct rtentry *rt2;
+ /*
+ * We are trying to add a net route, but can't.
+ * The following case should be allowed, so we'll make a
+ * special check for this:
+ * Two IPv6 addresses with the same prefix is assigned
+ * to a single interrface.
+ * # ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1)
+ * # ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2)
+ * In this case, (*1) and (*2) want to add the same
+ * net route entry, 3ffe:0501:: -> if0.
+ * This case should not raise an error.
+ */
+ rt2 = rtalloc1((struct sockaddr *)sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+ if (rt2) {
+ if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY))
+ == RTF_CLONING
+ && rt2->rt_gateway
+ && rt2->rt_gateway->sa_family == AF_LINK
+ && rt2->rt_ifp == rt->rt_ifp) {
+ ret = rt2->rt_nodes;
+ }
+ RTFREE(rt2);
+ }
+ }
+ return ret;
+}
+
+/*
+ * This code is the inverse of in6_clsroute: on first reference, if we
+ * were managing the route, stop doing so and set the expiration timer
+ * back off again.
+ */
+static struct radix_node *
+in6_matroute(void *v_arg, struct radix_node_head *head)
+{
+ struct radix_node *rn = rn_match(v_arg, head);
+ struct rtentry *rt = (struct rtentry *)rn;
+
+ if (rt && rt->rt_refcnt == 0) { /* this is first reference */
+ if (rt->rt_flags & RTPRF_OURS) {
+ rt->rt_flags &= ~RTPRF_OURS;
+ rt->rt_rmx.rmx_expire = 0;
+ }
+ }
+ return rn;
+}
+
+SYSCTL_DECL(_net_inet6_ip6);
+
+static int rtq_reallyold = 60*60;
+ /* one hour is ``really old'' */
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RTEXPIRE, rtexpire,
+ CTLFLAG_RW, &rtq_reallyold , 0, "");
+
+static int rtq_minreallyold = 10;
+ /* never automatically crank down to less */
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RTMINEXPIRE, rtminexpire,
+ CTLFLAG_RW, &rtq_minreallyold , 0, "");
+
+static int rtq_toomany = 128;
+ /* 128 cached routes is ``too many'' */
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RTMAXCACHE, rtmaxcache,
+ CTLFLAG_RW, &rtq_toomany , 0, "");
+
+
+/*
+ * On last reference drop, mark the route as belong to us so that it can be
+ * timed out.
+ */
+static void
+in6_clsroute(struct radix_node *rn, struct radix_node_head *head)
+{
+ struct rtentry *rt = (struct rtentry *)rn;
+
+ if (!(rt->rt_flags & RTF_UP))
+ return; /* prophylactic measures */
+
+ if ((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST)
+ return;
+
+ if ((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS)) != RTF_WASCLONED)
+ return;
+
+ /*
+ * As requested by David Greenman:
+ * If rtq_reallyold is 0, just delete the route without
+ * waiting for a timeout cycle to kill it.
+ */
+ if (rtq_reallyold != 0) {
+ rt->rt_flags |= RTPRF_OURS;
+ rt->rt_rmx.rmx_expire = time_second + rtq_reallyold;
+ } else {
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt),
+ rt->rt_flags, 0);
+ }
+}
+
+struct rtqk_arg {
+ struct radix_node_head *rnh;
+ int mode;
+ int updating;
+ int draining;
+ int killed;
+ int found;
+ time_t nextstop;
+};
+
+/*
+ * Get rid of old routes. When draining, this deletes everything, even when
+ * the timeout is not expired yet. When updating, this makes sure that
+ * nothing has a timeout longer than the current value of rtq_reallyold.
+ */
+static int
+in6_rtqkill(struct radix_node *rn, void *rock)
+{
+ struct rtqk_arg *ap = rock;
+ struct rtentry *rt = (struct rtentry *)rn;
+ int err;
+
+ if (rt->rt_flags & RTPRF_OURS) {
+ ap->found++;
+
+ if (ap->draining || rt->rt_rmx.rmx_expire <= time_second) {
+ if (rt->rt_refcnt > 0)
+ panic("rtqkill route really not free");
+
+ err = rtrequest(RTM_DELETE,
+ (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt),
+ rt->rt_flags, 0);
+ if (err) {
+ log(LOG_WARNING, "in6_rtqkill: error %d", err);
+ } else {
+ ap->killed++;
+ }
+ } else {
+ if (ap->updating
+ && (rt->rt_rmx.rmx_expire - time_second
+ > rtq_reallyold)) {
+ rt->rt_rmx.rmx_expire = time_second
+ + rtq_reallyold;
+ }
+ ap->nextstop = lmin(ap->nextstop,
+ rt->rt_rmx.rmx_expire);
+ }
+ }
+
+ return 0;
+}
+
+#define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */
+static int rtq_timeout = RTQ_TIMEOUT;
+
+static void
+in6_rtqtimo(void *rock)
+{
+ struct radix_node_head *rnh = rock;
+ struct rtqk_arg arg;
+ struct timeval atv;
+ static time_t last_adjusted_timeout = 0;
+ int s;
+
+ arg.found = arg.killed = 0;
+ arg.rnh = rnh;
+ arg.nextstop = time_second + rtq_timeout;
+ arg.draining = arg.updating = 0;
+ s = splnet();
+ RADIX_NODE_HEAD_LOCK(rnh);
+ rnh->rnh_walktree(rnh, in6_rtqkill, &arg);
+ RADIX_NODE_HEAD_UNLOCK(rnh);
+ splx(s);
+
+ /*
+ * Attempt to be somewhat dynamic about this:
+ * If there are ``too many'' routes sitting around taking up space,
+ * then crank down the timeout, and see if we can't make some more
+ * go away. However, we make sure that we will never adjust more
+ * than once in rtq_timeout seconds, to keep from cranking down too
+ * hard.
+ */
+ if ((arg.found - arg.killed > rtq_toomany)
+ && (time_second - last_adjusted_timeout >= rtq_timeout)
+ && rtq_reallyold > rtq_minreallyold) {
+ rtq_reallyold = 2*rtq_reallyold / 3;
+ if (rtq_reallyold < rtq_minreallyold) {
+ rtq_reallyold = rtq_minreallyold;
+ }
+
+ last_adjusted_timeout = time_second;
+#ifdef DIAGNOSTIC
+ log(LOG_DEBUG, "in6_rtqtimo: adjusted rtq_reallyold to %d",
+ rtq_reallyold);
+#endif
+ arg.found = arg.killed = 0;
+ arg.updating = 1;
+ s = splnet();
+ RADIX_NODE_HEAD_LOCK(rnh);
+ rnh->rnh_walktree(rnh, in6_rtqkill, &arg);
+ RADIX_NODE_HEAD_UNLOCK(rnh);
+ splx(s);
+ }
+
+ atv.tv_usec = 0;
+ atv.tv_sec = arg.nextstop;
+ timeout(in6_rtqtimo, rock, tvtohz(&atv));
+}
+
+/*
+ * Age old PMTUs.
+ */
+struct mtuex_arg {
+ struct radix_node_head *rnh;
+ time_t nextstop;
+};
+
+static int
+in6_mtuexpire(struct radix_node *rn, void *rock)
+{
+ struct rtentry *rt = (struct rtentry *)rn;
+ struct mtuex_arg *ap = rock;
+
+ /* sanity */
+ if (!rt)
+ panic("rt == NULL in in6_mtuexpire");
+
+ if (rt->rt_rmx.rmx_expire && !(rt->rt_flags & RTF_PROBEMTU)) {
+ if (rt->rt_rmx.rmx_expire <= time_second) {
+ rt->rt_flags |= RTF_PROBEMTU;
+ } else {
+ ap->nextstop = lmin(ap->nextstop,
+ rt->rt_rmx.rmx_expire);
+ }
+ }
+
+ return 0;
+}
+
+#define MTUTIMO_DEFAULT (60*1)
+
+static void
+in6_mtutimo(void *rock)
+{
+ struct radix_node_head *rnh = rock;
+ struct mtuex_arg arg;
+ struct timeval atv;
+ int s;
+
+ arg.rnh = rnh;
+ arg.nextstop = time_second + MTUTIMO_DEFAULT;
+ s = splnet();
+ RADIX_NODE_HEAD_LOCK(rnh);
+ rnh->rnh_walktree(rnh, in6_mtuexpire, &arg);
+ RADIX_NODE_HEAD_UNLOCK(rnh);
+ splx(s);
+
+ atv.tv_usec = 0;
+ atv.tv_sec = arg.nextstop;
+ if (atv.tv_sec < time_second) {
+ printf("invalid mtu expiration time on routing table\n");
+ arg.nextstop = time_second + 30; /* last resort */
+ }
+ timeout(in6_mtutimo, rock, tvtohz(&atv));
+}
+
+#if 0
+void
+in6_rtqdrain()
+{
+ struct radix_node_head *rnh = rt_tables[AF_INET6];
+ struct rtqk_arg arg;
+ int s;
+ arg.found = arg.killed = 0;
+ arg.rnh = rnh;
+ arg.nextstop = 0;
+ arg.draining = 1;
+ arg.updating = 0;
+ s = splnet();
+ RADIX_NODE_HEAD_LOCK(rnh);
+ rnh->rnh_walktree(rnh, in6_rtqkill, &arg);
+ RADIX_NODE_HEAD_UNLOCK(rnh);
+ splx(s);
+}
+#endif
+
+/*
+ * Initialize our routing tree.
+ */
+int
+in6_inithead(void **head, int off)
+{
+ struct radix_node_head *rnh;
+
+ if (!rn_inithead(head, off))
+ return 0;
+
+ if (head != (void **)&rt_tables[AF_INET6]) /* BOGUS! */
+ return 1; /* only do this for the real routing table */
+
+ rnh = *head;
+ rnh->rnh_addaddr = in6_addroute;
+ rnh->rnh_matchaddr = in6_matroute;
+ rnh->rnh_close = in6_clsroute;
+ in6_rtqtimo(rnh); /* kick off timeout first time */
+ in6_mtutimo(rnh); /* kick off timeout first time */
+ return 1;
+}
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
new file mode 100644
index 0000000..3dd2212
--- /dev/null
+++ b/sys/netinet6/in6_src.c
@@ -0,0 +1,558 @@
+/* $FreeBSD$ */
+/* $KAME: in6_src.c,v 1.37 2001/03/29 05:34:31 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#ifdef ENABLE_DEFAULT_SCOPE
+#include <netinet6/scope6_var.h>
+#endif
+
+#include <net/net_osdep.h>
+
+/*
+ * 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 returns
+ * an entry to the caller for later use.
+ */
+struct in6_addr *
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct in6_addr *laddr;
+ int *errorp;
+{
+ struct in6_addr *dst;
+ struct in6_ifaddr *ia6 = 0;
+ struct in6_pktinfo *pi = NULL;
+
+ dst = &dstsock->sin6_addr;
+ *errorp = 0;
+
+ /*
+ * If the source address is explicitly specified by the caller,
+ * use it.
+ */
+ if (opts && (pi = opts->ip6po_pktinfo) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+ return(&pi->ipi6_addr);
+
+ /*
+ * If the source address is not specified but the socket(if any)
+ * is already bound, use the bound address.
+ */
+ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
+ return(laddr);
+
+ /*
+ * If the caller doesn't specify the source address but
+ * the outgoing interface, use an address associated with
+ * the interface.
+ */
+ if (pi && pi->ipi6_ifindex) {
+ /* XXX boundary check is assumed to be already done. */
+ ia6 = in6_ifawithscope(ifnet_byindex(pi->ipi6_ifindex), dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a link-local unicast address or
+ * a multicast address, and if the outgoing interface is specified
+ * by the sin6_scope_id filed, use an address associated with the
+ * interface.
+ * XXX: We're now trying to define more specific semantics of
+ * sin6_scope_id field, so this part will be rewritten in
+ * the near future.
+ */
+ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
+ dstsock->sin6_scope_id) {
+ /*
+ * I'm not sure if boundary check for scope_id is done
+ * somewhere...
+ */
+ if (dstsock->sin6_scope_id < 0 ||
+ if_index < dstsock->sin6_scope_id) {
+ *errorp = ENXIO; /* XXX: better error? */
+ return(0);
+ }
+ ia6 = in6_ifawithscope(ifnet_byindex(dstsock->sin6_scope_id),
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a multicast address and
+ * the outgoing interface for the address is specified
+ * by the caller, use an address associated with the interface.
+ * There is a sanity check here; if the destination has node-local
+ * scope, the outgoing interfacde should be a loopback address.
+ * Even if the outgoing interface is not specified, we also
+ * choose a loopback interface as the outgoing interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
+
+ if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
+ ifp = &loif[0];
+ }
+
+ if (ifp) {
+ ia6 = in6_ifawithscope(ifp, dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If the next hop address for the packet is specified
+ * by caller, use an address associated with the route
+ * to the next hop.
+ */
+ {
+ struct sockaddr_in6 *sin6_next;
+ struct rtentry *rt;
+
+ if (opts && opts->ip6po_nexthop) {
+ sin6_next = satosin6(opts->ip6po_nexthop);
+ rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
+ if (rt) {
+ ia6 = in6_ifawithscope(rt->rt_ifp, dst);
+ if (ia6 == 0)
+ ia6 = ifatoia6(rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro) {
+ if (ro->ro_rt &&
+ (!(ro->ro_rt->rt_flags & RTF_UP) ||
+ satosin6(&ro->ro_dst)->sin6_family != AF_INET6 ||
+ !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr,
+ dst))) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0) {
+ struct sockaddr_in6 *sa6;
+
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
+ 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);
+ } else {
+ rtalloc((struct route *)ro);
+ }
+ }
+
+ /*
+ * in_pcbconnect() checks out IFF_LOOPBACK to skip using
+ * the address. But we don't know why it does so.
+ * It is necessary to ensure the scope even for lo0
+ * so doesn't check out IFF_LOOPBACK.
+ */
+
+ if (ro->ro_rt) {
+ ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst);
+ if (ia6 == 0) /* xxx scope error ?*/
+ ia6 = ifatoia6(ro->ro_rt->rt_ifa);
+ }
+#if 0
+ /*
+ * xxx The followings are necessary? (kazu)
+ * I don't think so.
+ * It's for SO_DONTROUTE option in IPv4.(jinmei)
+ */
+ if (ia6 == 0) {
+ struct sockaddr_in6 sin6 = {sizeof(sin6), AF_INET6, 0};
+
+ sin6->sin6_addr = *dst;
+
+ ia6 = ifatoia6(ifa_ifwithdstaddr(sin6tosa(&sin6)));
+ if (ia6 == 0)
+ ia6 = ifatoia6(ifa_ifwithnet(sin6tosa(&sin6)));
+ if (ia6 == 0)
+ return(0);
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+#endif /* 0 */
+ if (ia6 == 0) {
+ *errorp = EHOSTUNREACH; /* no route */
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+}
+
+/*
+ * Default hop limit selection. The precedence is as follows:
+ * 1. Hoplimit value specified via ioctl.
+ * 2. (If the outgoing interface is detected) the current
+ * hop limit of the interface specified by router advertisement.
+ * 3. The system default hoplimit.
+*/
+int
+in6_selecthlim(in6p, ifp)
+ struct in6pcb *in6p;
+ struct ifnet *ifp;
+{
+ if (in6p && in6p->in6p_hops >= 0)
+ return(in6p->in6p_hops);
+ else if (ifp)
+ return(nd_ifinfo[ifp->if_index].chlim);
+ else
+ return(ip6_defhlim);
+}
+
+/*
+ * XXX: this is borrowed from in6_pcbbind(). If possible, we should
+ * share this function by all *bsd*...
+ */
+int
+in6_pcbsetport(laddr, inp, td)
+ struct in6_addr *laddr;
+ struct inpcb *inp;
+ struct thread *td;
+{
+ struct socket *so = inp->inp_socket;
+ u_int16_t lport = 0, first, last, *lastport;
+ int count, error = 0, wild = 0;
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+
+ /* XXX: this is redundant when called from in6_pcbbind */
+ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
+ wild = INPLOOKUP_WILDCARD;
+
+ inp->inp_flags |= INP_ANONPORT;
+
+ if (inp->inp_flags & INP_HIGHPORT) {
+ first = ipport_hifirstauto; /* sysctl */
+ last = ipport_hilastauto;
+ lastport = &pcbinfo->lasthi;
+ } else if (inp->inp_flags & INP_LOWPORT) {
+ if (td && (error = suser(td)))
+ return error;
+ first = ipport_lowfirstauto; /* 1023 */
+ last = ipport_lowlastauto; /* 600 */
+ lastport = &pcbinfo->lastlow;
+ } else {
+ first = ipport_firstauto; /* sysctl */
+ last = ipport_lastauto;
+ lastport = &pcbinfo->lastport;
+ }
+ /*
+ * Simple check to ensure all ports are not used up causing
+ * a deadlock here.
+ *
+ * We split the two cases (up and down) so that the direction
+ * is not being tested on each round of the loop.
+ */
+ if (first > last) {
+ /*
+ * counting down
+ */
+ count = first - last;
+
+ do {
+ if (count-- < 0) { /* completely used? */
+ /*
+ * Undo any address bind that may have
+ * occurred above.
+ */
+ inp->in6p_laddr = in6addr_any;
+ return (EAGAIN);
+ }
+ --*lastport;
+ if (*lastport > first || *lastport < last)
+ *lastport = first;
+ lport = htons(*lastport);
+ } while (in6_pcblookup_local(pcbinfo,
+ &inp->in6p_laddr, lport, wild));
+ } else {
+ /*
+ * counting up
+ */
+ count = last - first;
+
+ do {
+ if (count-- < 0) { /* completely used? */
+ /*
+ * Undo any address bind that may have
+ * occurred above.
+ */
+ inp->in6p_laddr = in6addr_any;
+ return (EAGAIN);
+ }
+ ++*lastport;
+ if (*lastport < first || *lastport > last)
+ *lastport = first;
+ lport = htons(*lastport);
+ } while (in6_pcblookup_local(pcbinfo,
+ &inp->in6p_laddr, lport, wild));
+ }
+
+ inp->inp_lport = lport;
+ if (in_pcbinshash(inp) != 0) {
+ inp->in6p_laddr = in6addr_any;
+ inp->inp_lport = 0;
+ return (EAGAIN);
+ }
+
+ return(0);
+}
+
+/*
+ * generate kernel-internal form (scopeid embedded into s6_addr16[1]).
+ * If the address scope of is link-local, embed the interface index in the
+ * address. The routine determines our precedence
+ * between advanced API scope/interface specification and basic API
+ * specification.
+ *
+ * this function should be nuked in the future, when we get rid of
+ * embedded scopeid thing.
+ *
+ * XXX actually, it is over-specification to return ifp against sin6_scope_id.
+ * there can be multiple interfaces that belong to a particular scope zone
+ * (in specification, we have 1:N mapping between a scope zone and interfaces).
+ * we may want to change the function to return something other than ifp.
+ */
+int
+in6_embedscope(in6, sin6, in6p, ifpp)
+ struct in6_addr *in6;
+ const struct sockaddr_in6 *sin6;
+#ifdef HAVE_NRL_INPCB
+ struct inpcb *in6p;
+#define in6p_outputopts inp_outputopts6
+#define in6p_moptions inp_moptions6
+#else
+ struct in6pcb *in6p;
+#endif
+ struct ifnet **ifpp;
+{
+ struct ifnet *ifp = NULL;
+ u_int32_t scopeid;
+
+ *in6 = sin6->sin6_addr;
+ scopeid = sin6->sin6_scope_id;
+ if (ifpp)
+ *ifpp = NULL;
+
+ /*
+ * don't try to read sin6->sin6_addr beyond here, since the caller may
+ * ask us to overwrite existing sockaddr_in6
+ */
+
+#ifdef ENABLE_DEFAULT_SCOPE
+ if (scopeid == 0)
+ scopeid = scope6_addr2default(in6);
+#endif
+
+ if (IN6_IS_SCOPE_LINKLOCAL(in6)) {
+ struct in6_pktinfo *pi;
+
+ /*
+ * KAME assumption: link id == interface id
+ */
+
+ if (in6p && in6p->in6p_outputopts &&
+ (pi = in6p->in6p_outputopts->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
+ ifp = ifnet_byindex(pi->ipi6_ifindex);
+ in6->s6_addr16[1] = htons(pi->ipi6_ifindex);
+ } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) &&
+ in6p->in6p_moptions &&
+ in6p->in6p_moptions->im6o_multicast_ifp) {
+ ifp = in6p->in6p_moptions->im6o_multicast_ifp;
+ in6->s6_addr16[1] = htons(ifp->if_index);
+ } else if (scopeid) {
+ /* boundary check */
+ if (scopeid < 0 || if_index < scopeid)
+ return ENXIO; /* XXX EINVAL? */
+ ifp = ifnet_byindex(scopeid);
+ /*XXX assignment to 16bit from 32bit variable */
+ in6->s6_addr16[1] = htons(scopeid & 0xffff);
+ }
+
+ if (ifpp)
+ *ifpp = ifp;
+ }
+
+ return 0;
+}
+#ifdef HAVE_NRL_INPCB
+#undef in6p_outputopts
+#undef in6p_moptions
+#endif
+
+/*
+ * generate standard sockaddr_in6 from embedded form.
+ * touches sin6_addr and sin6_scope_id only.
+ *
+ * this function should be nuked in the future, when we get rid of
+ * embedded scopeid thing.
+ */
+int
+in6_recoverscope(sin6, in6, ifp)
+ struct sockaddr_in6 *sin6;
+ const struct in6_addr *in6;
+ struct ifnet *ifp;
+{
+ u_int32_t scopeid;
+
+ sin6->sin6_addr = *in6;
+
+ /*
+ * don't try to read *in6 beyond here, since the caller may
+ * ask us to overwrite existing sockaddr_in6
+ */
+
+ sin6->sin6_scope_id = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(in6)) {
+ /*
+ * KAME assumption: link id == interface id
+ */
+ scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ if (scopeid) {
+ /* sanity check */
+ if (scopeid < 0 || if_index < scopeid)
+ return ENXIO;
+ if (ifp && ifp->if_index != scopeid)
+ return ENXIO;
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = scopeid;
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..35f9e5b
--- /dev/null
+++ b/sys/netinet6/in6_var.h
@@ -0,0 +1,607 @@
+/* $FreeBSD$ */
+/* $KAME: in6_var.h,v 1.56 2001/03/29 05:34:31 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1985, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IN6_VAR_H_
+#define _NETINET6_IN6_VAR_H_
+
+/*
+ * Interface address, Internet version. One of these structures
+ * is allocated for each interface with an Internet address.
+ * The ifaddr structure contains the protocol-independent part
+ * of the structure and is assumed to be first.
+ */
+
+/*
+ * pltime/vltime are just for future reference (required to implements 2
+ * hour rule for hosts). they should never be modified by nd6_timeout or
+ * anywhere else.
+ * userland -> kernel: accept pltime/vltime
+ * kernel -> userland: throw up everything
+ * in kernel: modify preferred/expire only
+ */
+struct in6_addrlifetime {
+ time_t ia6t_expire; /* valid lifetime expiration time */
+ time_t ia6t_preferred; /* preferred lifetime expiration time */
+ u_int32_t ia6t_vltime; /* valid lifetime */
+ u_int32_t ia6t_pltime; /* prefix lifetime */
+};
+
+struct in6_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ struct sockaddr_in6 ia_addr; /* interface address */
+ struct sockaddr_in6 ia_net; /* network number of interface */
+ struct sockaddr_in6 ia_dstaddr; /* space for destination addr */
+ struct sockaddr_in6 ia_prefixmask; /* prefix mask */
+ u_int32_t ia_plen; /* prefix length */
+ struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */
+ int ia6_flags;
+
+ 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)
+ */
+};
+
+/*
+ * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12).
+ */
+struct in6_ifstat {
+ u_quad_t ifs6_in_receive; /* # of total input datagram */
+ u_quad_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */
+ u_quad_t ifs6_in_toobig; /* # of datagrams exceeded MTU */
+ u_quad_t ifs6_in_noroute; /* # of datagrams with no route */
+ u_quad_t ifs6_in_addrerr; /* # of datagrams with invalid dst */
+ u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */
+ /* NOTE: increment on final dst if */
+ u_quad_t ifs6_in_truncated; /* # of truncated datagrams */
+ u_quad_t ifs6_in_discard; /* # of discarded datagrams */
+ /* NOTE: fragment timeout is not here */
+ u_quad_t ifs6_in_deliver; /* # of datagrams delivered to ULP */
+ /* NOTE: increment on final dst if */
+ u_quad_t ifs6_out_forward; /* # of datagrams forwarded */
+ /* NOTE: increment on outgoing if */
+ u_quad_t ifs6_out_request; /* # of outgoing datagrams from ULP */
+ /* NOTE: does not include forwrads */
+ u_quad_t ifs6_out_discard; /* # of discarded datagrams */
+ u_quad_t ifs6_out_fragok; /* # of datagrams fragmented */
+ u_quad_t ifs6_out_fragfail; /* # of datagrams failed on fragment */
+ u_quad_t ifs6_out_fragcreat; /* # of fragment datagrams */
+ /* NOTE: this is # after fragment */
+ u_quad_t ifs6_reass_reqd; /* # of incoming fragmented packets */
+ /* NOTE: increment on final dst if */
+ u_quad_t ifs6_reass_ok; /* # of reassembled packets */
+ /* NOTE: this is # after reass */
+ /* NOTE: increment on final dst if */
+ u_quad_t ifs6_reass_fail; /* # of reass failures */
+ /* NOTE: may not be packet count */
+ /* NOTE: increment on final dst if */
+ u_quad_t ifs6_in_mcast; /* # of inbound multicast datagrams */
+ u_quad_t ifs6_out_mcast; /* # of outbound multicast datagrams */
+};
+
+/*
+ * ICMPv6 interface statistics, as defined in RFC2466 Ipv6IfIcmpEntry.
+ * XXX: I'm not sure if this file is the right place for this structure...
+ */
+struct icmp6_ifstat {
+ /*
+ * Input statistics
+ */
+ /* ipv6IfIcmpInMsgs, total # of input messages */
+ u_quad_t ifs6_in_msg;
+ /* ipv6IfIcmpInErrors, # of input error messages */
+ u_quad_t ifs6_in_error;
+ /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */
+ u_quad_t ifs6_in_dstunreach;
+ /* ipv6IfIcmpInAdminProhibs, # of input administratively prohibited errs */
+ u_quad_t ifs6_in_adminprohib;
+ /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */
+ u_quad_t ifs6_in_timeexceed;
+ /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */
+ u_quad_t ifs6_in_paramprob;
+ /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */
+ u_quad_t ifs6_in_pkttoobig;
+ /* ipv6IfIcmpInEchos, # of input echo requests */
+ u_quad_t ifs6_in_echo;
+ /* ipv6IfIcmpInEchoReplies, # of input echo replies */
+ u_quad_t ifs6_in_echoreply;
+ /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */
+ u_quad_t ifs6_in_routersolicit;
+ /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */
+ u_quad_t ifs6_in_routeradvert;
+ /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */
+ u_quad_t ifs6_in_neighborsolicit;
+ /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advertisements */
+ u_quad_t ifs6_in_neighboradvert;
+ /* ipv6IfIcmpInRedirects, # of input redirects */
+ u_quad_t ifs6_in_redirect;
+ /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */
+ u_quad_t ifs6_in_mldquery;
+ /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */
+ u_quad_t ifs6_in_mldreport;
+ /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */
+ u_quad_t ifs6_in_mlddone;
+
+ /*
+ * Output statistics. We should solve unresolved routing problem...
+ */
+ /* ipv6IfIcmpOutMsgs, total # of output messages */
+ u_quad_t ifs6_out_msg;
+ /* ipv6IfIcmpOutErrors, # of output error messages */
+ u_quad_t ifs6_out_error;
+ /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */
+ u_quad_t ifs6_out_dstunreach;
+ /* ipv6IfIcmpOutAdminProhibs, # of output administratively prohibited errs */
+ u_quad_t ifs6_out_adminprohib;
+ /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */
+ u_quad_t ifs6_out_timeexceed;
+ /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */
+ u_quad_t ifs6_out_paramprob;
+ /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */
+ u_quad_t ifs6_out_pkttoobig;
+ /* ipv6IfIcmpOutEchos, # of output echo requests */
+ u_quad_t ifs6_out_echo;
+ /* ipv6IfIcmpOutEchoReplies, # of output echo replies */
+ u_quad_t ifs6_out_echoreply;
+ /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */
+ u_quad_t ifs6_out_routersolicit;
+ /* ipv6IfIcmpOutRouterAdvertisements, # of output router advertisements */
+ u_quad_t ifs6_out_routeradvert;
+ /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */
+ u_quad_t ifs6_out_neighborsolicit;
+ /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advertisements */
+ u_quad_t ifs6_out_neighboradvert;
+ /* ipv6IfIcmpOutRedirects, # of output redirects */
+ u_quad_t ifs6_out_redirect;
+ /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */
+ u_quad_t ifs6_out_mldquery;
+ /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */
+ u_quad_t ifs6_out_mldreport;
+ /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */
+ u_quad_t ifs6_out_mlddone;
+};
+
+struct in6_ifreq {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr_in6 ifru_addr;
+ struct sockaddr_in6 ifru_dstaddr;
+ int ifru_flags;
+ int ifru_flags6;
+ int ifru_metric;
+ caddr_t ifru_data;
+ struct in6_addrlifetime ifru_lifetime;
+ struct in6_ifstat ifru_stat;
+ struct icmp6_ifstat ifru_icmp6stat;
+ u_int32_t ifru_scope_id[16];
+ } ifr_ifru;
+};
+
+struct in6_aliasreq {
+ char ifra_name[IFNAMSIZ];
+ struct sockaddr_in6 ifra_addr;
+ struct sockaddr_in6 ifra_dstaddr;
+ struct sockaddr_in6 ifra_prefixmask;
+ int ifra_flags;
+ struct in6_addrlifetime ifra_lifetime;
+};
+
+/* prefix type macro */
+#define IN6_PREFIX_ND 1
+#define IN6_PREFIX_RR 2
+
+/*
+ * prefix related flags passed between kernel(NDP related part) and
+ * user land command(ifconfig) and daemon(rtadvd).
+ */
+struct in6_prflags {
+ struct prf_ra {
+ u_char onlink : 1;
+ u_char autonomous : 1;
+ u_char reserved : 6;
+ } prf_ra;
+ u_char prf_reserved1;
+ u_short prf_reserved2;
+ /* want to put this on 4byte offset */
+ struct prf_rr {
+ u_char decrvalid : 1;
+ u_char decrprefd : 1;
+ u_char reserved : 6;
+ } prf_rr;
+ u_char prf_reserved3;
+ u_short prf_reserved4;
+};
+
+struct in6_prefixreq {
+ char ipr_name[IFNAMSIZ];
+ u_char ipr_origin;
+ u_char ipr_plen;
+ u_int32_t ipr_vltime;
+ u_int32_t ipr_pltime;
+ struct in6_prflags ipr_flags;
+ struct sockaddr_in6 ipr_prefix;
+};
+
+#define PR_ORIG_RA 0
+#define PR_ORIG_RR 1
+#define PR_ORIG_STATIC 2
+#define PR_ORIG_KERNEL 3
+
+#define ipr_raf_onlink ipr_flags.prf_ra.onlink
+#define ipr_raf_auto ipr_flags.prf_ra.autonomous
+
+#define ipr_statef_onlink ipr_flags.prf_state.onlink
+
+#define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid
+#define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd
+
+struct in6_rrenumreq {
+ char irr_name[IFNAMSIZ];
+ u_char irr_origin;
+ u_char irr_m_len; /* match len for matchprefix */
+ u_char irr_m_minlen; /* minlen for matching prefix */
+ u_char irr_m_maxlen; /* maxlen for matching prefix */
+ u_char irr_u_uselen; /* uselen for adding prefix */
+ u_char irr_u_keeplen; /* keeplen from matching prefix */
+ struct irr_raflagmask {
+ u_char onlink : 1;
+ u_char autonomous : 1;
+ u_char reserved : 6;
+ } irr_raflagmask;
+ u_int32_t irr_vltime;
+ u_int32_t irr_pltime;
+ struct in6_prflags irr_flags;
+ struct sockaddr_in6 irr_matchprefix;
+ struct sockaddr_in6 irr_useprefix;
+};
+
+#define irr_raf_mask_onlink irr_raflagmask.onlink
+#define irr_raf_mask_auto irr_raflagmask.autonomous
+#define irr_raf_mask_reserved irr_raflagmask.reserved
+
+#define irr_raf_onlink irr_flags.prf_ra.onlink
+#define irr_raf_auto irr_flags.prf_ra.autonomous
+
+#define irr_statef_onlink irr_flags.prf_state.onlink
+
+#define irr_rrf irr_flags.prf_rr
+#define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid
+#define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd
+
+/*
+ * Given a pointer to an in6_ifaddr (ifaddr),
+ * return a pointer to the addr as a sockaddr_in6
+ */
+#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr))
+#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr))
+#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr))
+#define IA6_SIN6(ia) (&((ia)->ia_addr))
+#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr))
+#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr)
+#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr)
+
+#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr)
+
+#ifdef _KERNEL
+#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
+ (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
+ (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
+ (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
+ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
+#endif
+
+#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq)
+#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq)
+
+#ifdef _KERNEL
+/*
+ * SIOCSxxx ioctls should be unused (see comments in in6.c), but
+ * we do not shift numbers for binary compatibility.
+ */
+#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq)
+#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq)
+#endif
+
+#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq)
+#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq)
+
+#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq)
+#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq)
+
+#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq)
+#define SIOCGIFPSRCADDR_IN6 _IOWR('i', 71, struct in6_ifreq)
+#define SIOCGIFPDSTADDR_IN6 _IOWR('i', 72, struct in6_ifreq)
+
+#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
+
+#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist)
+#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist)
+#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)
+#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq)
+
+#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq)
+#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq)
+#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq)
+#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq)
+
+#define SIOCSDEFIFACE_IN6 _IOWR('i', 85, struct in6_ndifreq)
+#define SIOCGDEFIFACE_IN6 _IOWR('i', 86, struct in6_ndifreq)
+
+#define SIOCSIFINFO_FLAGS _IOWR('i', 87, struct in6_ndireq) /* XXX */
+
+#define SIOCSSCOPE6 _IOW('i', 88, struct in6_ifreq)
+#define SIOCGSCOPE6 _IOWR('i', 89, struct in6_ifreq)
+#define SIOCGSCOPE6DEF _IOWR('i', 90, struct in6_ifreq)
+
+#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */
+#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */
+#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */
+#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */
+#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \
+ struct in6_rrenumreq) /* change */
+#define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \
+ struct in6_rrenumreq) /* set global */
+
+#define SIOCGETSGCNT_IN6 _IOWR('u', 106, \
+ struct sioc_sg_req6) /* get s,g pkt cnt */
+#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \
+ struct sioc_mif_req6) /* get pkt cnt per if */
+
+#define IN6_IFF_ANYCAST 0x01 /* anycast address */
+#define IN6_IFF_TENTATIVE 0x02 /* tentative address */
+#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */
+#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */
+#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */
+#define IN6_IFF_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)
+
+#ifdef _KERNEL
+#define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b))
+#define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b))
+#endif
+
+#ifdef _KERNEL
+extern struct in6_ifaddr *in6_ifaddr;
+
+extern struct in6_ifstat **in6_ifstat;
+extern size_t in6_ifstatmax;
+extern struct icmp6stat icmp6stat;
+extern struct icmp6_ifstat **icmp6_ifstat;
+extern size_t icmp6_ifstatmax;
+#define in6_ifstat_inc(ifp, tag) \
+do { \
+ if ((ifp) && (ifp)->if_index <= if_index \
+ && (ifp)->if_index < in6_ifstatmax \
+ && in6_ifstat && in6_ifstat[(ifp)->if_index]) { \
+ in6_ifstat[(ifp)->if_index]->tag++; \
+ } \
+} while (0)
+
+extern struct in6_addr zeroin6_addr;
+extern u_char inet6ctlerrmap[];
+extern unsigned long in6_maxmtu;
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_IPMADDR);
+#endif
+
+/*
+ * Macro for finding the internet address structure (in6_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+
+#define IFP_TO_IA6(ifp, ia) \
+/* struct ifnet *ifp; */ \
+/* struct in6_ifaddr *ia; */ \
+do { \
+ struct ifaddr *ifa; \
+ for (ifa = (ifp)->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { \
+ if (!ifa->ifa_addr) \
+ continue; \
+ if (ifa->ifa_addr->sa_family == AF_INET6) \
+ break; \
+ } \
+ (ia) = (struct in6_ifaddr *)ifa; \
+} while (0)
+
+#endif /* _KERNEL */
+
+/*
+ * Multi-cast membership entry. One for each group/ifp that a PCB
+ * belongs to.
+ */
+struct in6_multi_mship {
+ struct in6_multi *i6mm_maddr; /* Multicast address pointer */
+ LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */
+};
+
+struct in6_multi {
+ LIST_ENTRY(in6_multi) in6m_entry; /* list glue */
+ struct in6_addr in6m_addr; /* IP6 multicast address */
+ struct ifnet *in6m_ifp; /* back pointer to ifnet */
+ struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */
+ u_int in6m_refcount; /* # membership claims by sockets */
+ u_int in6m_state; /* state of the membership */
+ u_int in6m_timer; /* MLD6 listener report timer */
+};
+
+#ifdef _KERNEL
+extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead;
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the in6_multi records.
+ */
+struct in6_multistep {
+ struct in6_ifaddr *i_ia;
+ struct in6_multi *i_in6m;
+};
+
+/*
+ * Macros for looking up the in6_multi record for a given IP6 multicast
+ * address on a given interface. If no matching record is found, "in6m"
+ * returns NLL.
+ */
+
+#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
+/* struct in6_addr addr; */ \
+/* struct ifnet *ifp; */ \
+/* struct in6_multi *in6m; */ \
+do { \
+ 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, \
+ &(addr))) \
+ break; \
+ } \
+ (in6m) = (struct in6_multi *)(ifma ? ifma->ifma_protospec : 0); \
+} while(0)
+
+/*
+ * Macro to step through all of the in6_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. IN6_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "in6m" when there
+ * are no remaining records.
+ */
+#define IN6_NEXT_MULTI(step, in6m) \
+/* struct in6_multistep step; */ \
+/* struct in6_multi *in6m; */ \
+do { \
+ if (((in6m) = (step).i_in6m) != NULL) \
+ (step).i_in6m = (step).i_in6m->in6m_entry.le_next; \
+} while(0)
+
+#define IN6_FIRST_MULTI(step, in6m) \
+/* struct in6_multistep step; */ \
+/* struct in6_multi *in6m */ \
+do { \
+ (step).i_in6m = in6_multihead.lh_first; \
+ IN6_NEXT_MULTI((step), (in6m)); \
+} while(0)
+
+struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *,
+ int *));
+void in6_delmulti __P((struct in6_multi *));
+extern int in6_ifindex2scopeid __P((int));
+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 thread *));
+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 *));
+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((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,
+ int len));
+void in6_prefixlen2mask __P((struct in6_addr *maskp, int len));
+int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data,
+ struct ifnet *ifp));
+int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia));
+void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia));
+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.h b/sys/netinet6/ip6.h
new file mode 100644
index 0000000..9eec13f
--- /dev/null
+++ b/sys/netinet6/ip6.h
@@ -0,0 +1,4 @@
+/* $FreeBSD$ */
+/* $KAME: ip6.h,v 1.7 2000/03/25 07:23:36 sumikawa Exp $ */
+
+#error "netinet6/ip6.h is obsolete. use netinet/ip6.h"
diff --git a/sys/netinet6/ip6_ecn.h b/sys/netinet6/ip6_ecn.h
new file mode 100644
index 0000000..4107cf0
--- /dev/null
+++ b/sys/netinet6/ip6_ecn.h
@@ -0,0 +1,41 @@
+/* $FreeBSD$ */
+/* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ * ECN consideration on tunnel ingress/egress operation.
+ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
+ */
+
+#ifdef _KERNEL
+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
new file mode 100644
index 0000000..1e6dde8
--- /dev/null
+++ b/sys/netinet6/ip6_forward.c
@@ -0,0 +1,595 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 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_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+#endif
+
+#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 */
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#include <netipsec/key.h>
+#define IPSEC
+#endif /* FAST_IPSEC */
+
+#include <netinet6/ip6_fw.h>
+
+#include <net/net_osdep.h>
+
+#include <netinet6/ip6protosw.h>
+
+struct route_in6 ip6_forward_rt;
+
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repertoire
+ * of codes and types.
+ *
+ * If not forwarding, just drop the packet. This could be confusing
+ * if ipforwarding was zero but some routing protocol was advancing
+ * us as a gateway to somewhere. However, we must let the routing
+ * protocol deal with that.
+ *
+ */
+
+void
+ip6_forward(m, srcrt)
+ struct mbuf *m;
+ int srcrt;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt;
+ int error, type = 0, code = 0;
+ struct mbuf *mcopy = NULL;
+ struct ifnet *origifp; /* maybe unnecessary */
+#ifdef PFIL_HOOKS
+ struct packet_filter_hook *pfh;
+ struct mbuf *m1;
+ int rv;
+#endif /* PFIL_HOOKS */
+#ifdef IPSEC
+ struct secpolicy *sp = NULL;
+#endif
+
+#ifdef IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ /*
+ * Don't increment ip6s_cantforward because this is the check
+ * before forwarding packet actually.
+ */
+ if (ipsec6_in_reject(m, NULL)) {
+#if !defined(FAST_IPSEC)
+ ipsec6stat.in_polvio++;
+#endif
+ m_freem(m);
+ return;
+ }
+#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_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) {
+ ip6_log_time = time_second;
+ log(LOG_DEBUG,
+ "cannot forward "
+ "from %s to %s nxt %d received on %s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ ip6->ip6_nxt,
+ if_name(m->m_pkthdr.rcvif));
+ }
+ m_freem(m);
+ return;
+ }
+
+ if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
+ icmp6_error(m, ICMP6_TIME_EXCEEDED,
+ ICMP6_TIME_EXCEED_TRANSIT, 0);
+ return;
+ }
+ ip6->ip6_hlim -= IPV6_HLIMDEC;
+
+ /*
+ * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
+ * size of IPv6 + ICMPv6 headers) bytes of the packet in case
+ * we need to generate an ICMP6 message to the src.
+ * Thanks to M_EXT, in most cases copy will not occur.
+ *
+ * It is important to save it before IPsec processing as IPsec
+ * processing may modify the mbuf.
+ */
+ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
+
+#ifdef IPSEC
+ /* get a security policy for this packet */
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
+ &error);
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ ip6stat.ip6s_cantforward++;
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ ip6stat.ip6s_cantforward++;
+ key_freesp(sp);
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ key_freesp(sp);
+ goto skip_ipsec;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* XXX should be panic ? */
+ printf("ip6_forward: No IPsec request specified.\n");
+ ip6stat.ip6s_cantforward++;
+ key_freesp(sp);
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+ /* do IPsec */
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ /* should be panic ?? */
+ printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
+ key_freesp(sp);
+ goto skip_ipsec;
+ }
+
+ {
+ struct ipsec_output_state state;
+
+ /*
+ * All the extension headers will become inaccessible
+ * (since they can be encrypted).
+ * Don't panic, we need no more updates to extension headers
+ * on inner IPv6 packet (since they are now encapsulated).
+ *
+ * IPv6 [ESP|AH] IPv6 [extension headers] payload
+ */
+ bzero(&state, sizeof(state));
+ state.m = m;
+ state.ro = NULL; /* update at ipsec6_output_tunnel() */
+ state.dst = NULL; /* update at ipsec6_output_tunnel() */
+
+ error = ipsec6_output_tunnel(&state, sp, 0);
+
+ m = state.m;
+ key_freesp(sp);
+
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /* fall through */
+ case ENOENT:
+ /* don't show these error codes to the user */
+ break;
+ }
+ ip6stat.ip6s_cantforward++;
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+ }
+ skip_ipsec:
+#endif /* IPSEC */
+
+ dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst;
+ if (!srcrt) {
+ /*
+ * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
+ */
+ if (ip6_forward_rt.ro_rt == 0 ||
+ (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ /* this probably fails but give it a try again */
+ rtalloc_ign((struct route *)&ip6_forward_rt,
+ RTF_PRCLONING);
+ }
+
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
+ if (mcopy) {
+ icmp6_error(mcopy, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_NOROUTE, 0);
+ }
+ m_freem(m);
+ return;
+ }
+ } else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ bzero(dst, sizeof(*dst));
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_family = AF_INET6;
+ dst->sin6_addr = ip6->ip6_dst;
+
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
+ if (mcopy) {
+ icmp6_error(mcopy, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_NOROUTE, 0);
+ }
+ m_freem(m);
+ return;
+ }
+ }
+ rt = ip6_forward_rt.ro_rt;
+
+ /*
+ * Scope check: if a packet can't be delivered to its destination
+ * for the reason that the destination is beyond the scope of the
+ * source address, discard the packet and return an icmp6 destination
+ * unreachable error with Code 2 (beyond scope of source address).
+ * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1]
+ */
+ if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
+ in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
+ ip6stat.ip6s_cantforward++;
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
+
+ if (ip6_log_time + ip6_log_interval < time_second) {
+ ip6_log_time = time_second;
+ log(LOG_DEBUG,
+ "cannot forward "
+ "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ ip6->ip6_nxt,
+ if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
+ }
+ if (mcopy)
+ icmp6_error(mcopy, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
+ m_freem(m);
+ return;
+ }
+
+ if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
+ in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
+ if (mcopy) {
+ u_long mtu;
+#ifdef IPSEC
+ struct secpolicy *sp;
+ int ipsecerror;
+ size_t ipsechdrsiz;
+#endif
+
+ mtu = rt->rt_ifp->if_mtu;
+#ifdef IPSEC
+ /*
+ * When we do IPsec tunnel ingress, we need to play
+ * with if_mtu value (decrement IPsec header size
+ * from mtu value). The code is much simpler than v4
+ * case, as we have the outgoing interface for
+ * encapsulated packet as "rt->rt_ifp".
+ */
+ sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
+ IP_FORWARDING, &ipsecerror);
+ if (sp) {
+ ipsechdrsiz = ipsec6_hdrsiz(mcopy,
+ IPSEC_DIR_OUTBOUND, NULL);
+ if (ipsechdrsiz < mtu)
+ mtu -= ipsechdrsiz;
+ }
+
+ /*
+ * if mtu becomes less than minimum MTU,
+ * tell minimum MTU (and I'll need to fragment it).
+ */
+ if (mtu < IPV6_MMTU)
+ mtu = IPV6_MMTU;
+#endif
+ icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
+ }
+ m_freem(m);
+ return;
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in6 *)rt->rt_gateway;
+
+ /*
+ * If we are to forward the packet using the same interface
+ * as one we got the packet from, perhaps we should send a redirect
+ * to sender to shortcut a hop.
+ * Only send redirect if source is sending directly to us,
+ * and if packet was not source routed (or has any options).
+ * Also, don't send redirect if forwarding using a route
+ * modified by a redirect.
+ */
+ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
+ (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...
+ */
+ if (ip6_fw_enable && ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
+ m_freem(m);
+ goto freecopy;
+ }
+ if (!m)
+ goto freecopy;
+ }
+
+ /*
+ * Fake scoped addresses. Note that even link-local source or
+ * destinaion can appear, if the originating node just sends the
+ * packet to us (without address resolution for the destination).
+ * Since both icmp6_error and icmp6_redirect_output fill the embedded
+ * link identifiers, we can do this stuff after making a copy for
+ * returning an error.
+ */
+ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
+ /*
+ * See corresponding comments in ip6_output.
+ * XXX: but is it possible that ip6_forward() sends a packet
+ * to a loopback interface? I don't think so, and thus
+ * I bark here. (jinmei@kame.net)
+ * XXX: it is common to route invalid packets to loopback.
+ * also, the codepath will be visited on use of ::1 in
+ * rthdr. (itojun)
+ */
+#if 1
+ if (0)
+#else
+ if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
+#endif
+ {
+ printf("ip6_forward: outgoing interface is loopback. "
+ "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
+ if_name(rt->rt_ifp));
+ }
+
+ /* we can just use rcvif in forwarding. */
+ origifp = m->m_pkthdr.rcvif;
+ }
+ else
+ origifp = rt->rt_ifp;
+#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
+
+#ifdef PFIL_HOOKS
+ /*
+ * Run through list of hooks for output packets.
+ */
+ m1 = m;
+ pfh = pfil_hook_get(PFIL_OUT, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh);
+ for (; pfh; pfh = pfh->pfil_link.tqe_next)
+ if (pfh->pfil_func) {
+ rv = pfh->pfil_func(ip6, sizeof(*ip6),
+ rt->rt_ifp, 1, &m1);
+ if (rv) {
+ error = EHOSTUNREACH;
+ goto freecopy;
+ }
+ m = m1;
+ if (m == NULL)
+ goto freecopy;
+ ip6 = mtod(m, struct ip6_hdr *);
+ }
+#endif /* PFIL_HOOKS */
+
+ error = nd6_output(rt->rt_ifp, origifp, m, dst, rt);
+ if (error) {
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
+ ip6stat.ip6s_cantforward++;
+ } else {
+ ip6stat.ip6s_forward++;
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
+ if (type)
+ ip6stat.ip6s_redirectsent++;
+ else {
+ if (mcopy)
+ goto freecopy;
+ }
+ }
+ if (mcopy == NULL)
+ return;
+ switch (error) {
+ case 0:
+#if 1
+ if (type == ND_REDIRECT) {
+ icmp6_redirect_output(mcopy, rt);
+ return;
+ }
+#endif
+ goto freecopy;
+
+ case EMSGSIZE:
+ /* xxx MTU is constant in PPP? */
+ goto freecopy;
+
+ case ENOBUFS:
+ /* Tell source to slow down like source quench in IP? */
+ goto freecopy;
+
+ case ENETUNREACH: /* shouldn't happen, checked above */
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case EHOSTDOWN:
+ default:
+ type = ICMP6_DST_UNREACH;
+ code = ICMP6_DST_UNREACH_ADDR;
+ break;
+ }
+ icmp6_error(mcopy, type, code, 0);
+ return;
+
+ freecopy:
+ m_freem(mcopy);
+ return;
+}
diff --git a/sys/netinet6/ip6_fw.c b/sys/netinet6/ip6_fw.c
new file mode 100644
index 0000000..0ef2c52
--- /dev/null
+++ b/sys/netinet6/ip6_fw.c
@@ -0,0 +1,1299 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_fw.c,v 1.21 2001/01/24 01:25:32 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998, 1999, 2000 and 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.
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ * Copyright (c) 1996 Alex Nash
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+/*
+ * Implement IPv6 packet firewall
+ */
+
+#if !defined(KLD_MODULE)
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#endif
+
+#ifdef IP6DIVERT
+#error "NOT SUPPORTED IPV6 DIVERT"
+#endif
+#ifdef IP6FW_DIVERT_RESTART
+#error "NOT SUPPORTED IPV6 DIVERT"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet/icmp6.h>
+
+#include <netinet/in_pcb.h>
+
+#include <netinet6/ip6_fw.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/udp.h>
+
+#include <sys/sysctl.h>
+
+#include <net/net_osdep.h>
+
+MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's");
+
+static int fw6_debug = 1;
+#ifdef IPV6FIREWALL_VERBOSE
+static int fw6_verbose = 1;
+#else
+static int fw6_verbose = 0;
+#endif
+#ifdef IPV6FIREWALL_VERBOSE_LIMIT
+static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT;
+#else
+static int fw6_verbose_limit = 0;
+#endif
+
+static LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain;
+
+#ifdef SYSCTL_NODE
+SYSCTL_DECL(_net_inet6_ip6);
+SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW | CTLFLAG_SECURE,
+ 0, "Firewall");
+SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, enable, CTLFLAG_RW | CTLFLAG_SECURE,
+ &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 | CTLFLAG_SECURE,
+ &fw6_verbose, 0, "");
+SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, "");
+#endif
+
+#define dprintf(a) do { \
+ if (fw6_debug) \
+ printf a; \
+ } while (0)
+#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
+
+static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl));
+static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number));
+static int zero_entry6 __P((struct mbuf *m));
+static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m));
+static struct ip6_fw *check_ip6fw_mbuf __P((struct mbuf *fw));
+static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f,
+ struct mbuf **m,
+ int *off, int *nxt, u_short *offset));
+static int port_match6 __P((u_short *portptr, int nports, u_short port,
+ int range_flag));
+static int tcp6flg_match __P((struct tcphdr *tcp6, struct ip6_fw *f));
+static int icmp6type_match __P((struct icmp6_hdr * icmp, struct ip6_fw * f));
+static void ip6fw_report __P((struct ip6_fw *f, struct ip6_hdr *ip6,
+ struct ifnet *rif, struct ifnet *oif, int off, int nxt));
+
+static int ip6_fw_chk __P((struct ip6_hdr **pip6,
+ struct ifnet *oif, u_int16_t *cookie, struct mbuf **m));
+static int ip6_fw_ctl __P((int stage, struct mbuf **mm));
+
+static char err_prefix[] = "ip6_fw_ctl:";
+
+/*
+ * Returns 1 if the port is matched by the vector, 0 otherwise
+ */
+static
+__inline int
+port_match6(u_short *portptr, int nports, u_short port, int range_flag)
+{
+ if (!nports)
+ return 1;
+ if (range_flag) {
+ if (portptr[0] <= port && port <= portptr[1]) {
+ return 1;
+ }
+ nports -= 2;
+ portptr += 2;
+ }
+ while (nports-- > 0) {
+ if (*portptr++ == port) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f)
+{
+ u_char flg_set, flg_clr;
+
+ /*
+ * If an established connection is required, reject packets that
+ * have only SYN of RST|ACK|SYN set. Otherwise, fall through to
+ * other flag requirements.
+ */
+ if ((f->fw_ipflg & IPV6_FW_IF_TCPEST) &&
+ ((tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK |
+ IPV6_FW_TCPF_SYN)) == IPV6_FW_TCPF_SYN))
+ return 0;
+
+ flg_set = tcp6->th_flags & f->fw_tcpf;
+ flg_clr = tcp6->th_flags & f->fw_tcpnf;
+
+ if (flg_set != f->fw_tcpf)
+ return 0;
+ if (flg_clr)
+ return 0;
+
+ return 1;
+}
+
+static int
+icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f)
+{
+ int type;
+
+ if (!(f->fw_flg & IPV6_FW_F_ICMPBIT))
+ return(1);
+
+ type = icmp6->icmp6_type;
+
+ /* check for matching type in the bitmap */
+ if (type < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 &&
+ (f->fw_icmp6types[type / (sizeof(unsigned) * 8)] &
+ (1U << (type % (8 * sizeof(unsigned))))))
+ return(1);
+
+ return(0); /* no match */
+}
+
+static int
+is_icmp6_query(struct ip6_hdr *ip6, int off)
+{
+ const struct icmp6_hdr *icmp6;
+ int icmp6_type;
+
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+ icmp6_type = icmp6->icmp6_type;
+
+ if (icmp6_type == ICMP6_ECHO_REQUEST ||
+ icmp6_type == ICMP6_MEMBERSHIP_QUERY ||
+ icmp6_type == ICMP6_WRUREQUEST ||
+ icmp6_type == ICMP6_FQDN_QUERY ||
+ icmp6_type == ICMP6_NI_QUERY)
+ return(1);
+
+ return(0);
+}
+
+static int
+ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m,
+ int *off, int *nxt, u_short *offset)
+{
+ int len;
+ struct ip6_hdr *ip6 = *pip6;
+ struct ip6_ext *ip6e;
+ u_char opts, nopts, nopts_sve;
+
+ opts = f->fw_ip6opt;
+ nopts = nopts_sve = f->fw_ip6nopt;
+
+ *nxt = ip6->ip6_nxt;
+ *off = sizeof(struct ip6_hdr);
+ len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr);
+ while (*off < len) {
+ ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off);
+ if ((*m)->m_len < *off + sizeof(*ip6e))
+ goto opts_check; /* XXX */
+
+ switch(*nxt) {
+ case IPPROTO_FRAGMENT:
+ if ((*m)->m_len >= *off + sizeof(struct ip6_frag)) {
+ struct ip6_frag *ip6f;
+
+ ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off);
+ *offset = ip6f->ip6f_offlg & IP6F_OFF_MASK;
+ }
+ opts &= ~IPV6_FW_IP6OPT_FRAG;
+ nopts &= ~IPV6_FW_IP6OPT_FRAG;
+ *off += sizeof(struct ip6_frag);
+ break;
+ case IPPROTO_AH:
+ opts &= ~IPV6_FW_IP6OPT_AH;
+ nopts &= ~IPV6_FW_IP6OPT_AH;
+ *off += (ip6e->ip6e_len + 2) << 2;
+ break;
+ default:
+ switch (*nxt) {
+ case IPPROTO_HOPOPTS:
+ opts &= ~IPV6_FW_IP6OPT_HOPOPT;
+ nopts &= ~IPV6_FW_IP6OPT_HOPOPT;
+ break;
+ case IPPROTO_ROUTING:
+ opts &= ~IPV6_FW_IP6OPT_ROUTE;
+ nopts &= ~IPV6_FW_IP6OPT_ROUTE;
+ break;
+ case IPPROTO_ESP:
+ opts &= ~IPV6_FW_IP6OPT_ESP;
+ nopts &= ~IPV6_FW_IP6OPT_ESP;
+ break;
+ case IPPROTO_NONE:
+ opts &= ~IPV6_FW_IP6OPT_NONXT;
+ nopts &= ~IPV6_FW_IP6OPT_NONXT;
+ goto opts_check;
+ break;
+ case IPPROTO_DSTOPTS:
+ opts &= ~IPV6_FW_IP6OPT_OPTS;
+ nopts &= ~IPV6_FW_IP6OPT_OPTS;
+ break;
+ default:
+ goto opts_check;
+ break;
+ }
+ *off += (ip6e->ip6e_len + 1) << 3;
+ break;
+ }
+ *nxt = ip6e->ip6e_nxt;
+
+ }
+ opts_check:
+ if (f->fw_ip6opt == f->fw_ip6nopt) /* XXX */
+ return 1;
+
+ if (opts == 0 && nopts == nopts_sve)
+ return 1;
+ else
+ return 0;
+}
+
+static
+__inline int
+iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname)
+{
+ /* Check by name or by IP address */
+ if (byname) {
+ /* Check unit number (-1 is wildcard) */
+ if (ifu->fu_via_if.unit != -1
+ && ifp->if_unit != ifu->fu_via_if.unit)
+ return(0);
+ /* Check name */
+ if (strncmp(ifp->if_name, ifu->fu_via_if.name, IP6FW_IFNLEN))
+ return(0);
+ return(1);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) { /* Zero == wildcard */
+ struct ifaddr *ia;
+
+ for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next)
+ {
+
+ if (ia->ifa_addr == NULL)
+ continue;
+ if (ia->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!IN6_ARE_ADDR_EQUAL(&ifu->fu_via_ip6,
+ &(((struct sockaddr_in6 *)
+ (ia->ifa_addr))->sin6_addr)))
+ continue;
+ return(1);
+ }
+ return(0);
+ }
+ return(1);
+}
+
+static void
+ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6,
+ struct ifnet *rif, struct ifnet *oif, int off, int nxt)
+{
+ static int counter;
+ struct tcphdr *const tcp6 = (struct tcphdr *) ((caddr_t) ip6+ off);
+ struct udphdr *const udp = (struct udphdr *) ((caddr_t) ip6+ off);
+ struct icmp6_hdr *const icmp6 = (struct icmp6_hdr *) ((caddr_t) ip6+ off);
+ int count;
+ char *action;
+ char action2[32], proto[102], name[18];
+ int len;
+
+ count = f ? f->fw_pcnt : ++counter;
+ if (fw6_verbose_limit != 0 && count > fw6_verbose_limit)
+ return;
+
+ /* Print command name */
+ snprintf(SNPARGS(name, 0), "ip6fw: %d", f ? f->fw_number : -1);
+
+ action = action2;
+ if (!f)
+ action = "Refuse";
+ else {
+ switch (f->fw_flg & IPV6_FW_F_COMMAND) {
+ case IPV6_FW_F_DENY:
+ action = "Deny";
+ break;
+ case IPV6_FW_F_REJECT:
+ if (f->fw_reject_code == IPV6_FW_REJECT_RST)
+ action = "Reset";
+ else
+ action = "Unreach";
+ break;
+ case IPV6_FW_F_ACCEPT:
+ action = "Accept";
+ break;
+ case IPV6_FW_F_COUNT:
+ action = "Count";
+ break;
+ case IPV6_FW_F_DIVERT:
+ snprintf(SNPARGS(action2, 0), "Divert %d",
+ f->fw_divert_port);
+ break;
+ case IPV6_FW_F_TEE:
+ snprintf(SNPARGS(action2, 0), "Tee %d",
+ f->fw_divert_port);
+ break;
+ case IPV6_FW_F_SKIPTO:
+ snprintf(SNPARGS(action2, 0), "SkipTo %d",
+ f->fw_skipto_rule);
+ break;
+ default:
+ action = "UNKNOWN";
+ break;
+ }
+ }
+
+ switch (nxt) {
+ case IPPROTO_TCP:
+ len = snprintf(SNPARGS(proto, 0), "TCP [%s]",
+ ip6_sprintf(&ip6->ip6_src));
+ if (off > 0)
+ len += snprintf(SNPARGS(proto, len), ":%d ",
+ ntohs(tcp6->th_sport));
+ else
+ len += snprintf(SNPARGS(proto, len), " ");
+ len += snprintf(SNPARGS(proto, len), "[%s]",
+ ip6_sprintf(&ip6->ip6_dst));
+ if (off > 0)
+ snprintf(SNPARGS(proto, len), ":%d",
+ ntohs(tcp6->th_dport));
+ break;
+ case IPPROTO_UDP:
+ len = snprintf(SNPARGS(proto, 0), "UDP [%s]",
+ ip6_sprintf(&ip6->ip6_src));
+ if (off > 0)
+ len += snprintf(SNPARGS(proto, len), ":%d ",
+ ntohs(udp->uh_sport));
+ else
+ len += snprintf(SNPARGS(proto, len), " ");
+ len += snprintf(SNPARGS(proto, len), "[%s]",
+ ip6_sprintf(&ip6->ip6_dst));
+ if (off > 0)
+ snprintf(SNPARGS(proto, len), ":%d",
+ ntohs(udp->uh_dport));
+ break;
+ case IPPROTO_ICMPV6:
+ if (off > 0)
+ len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP:%u.%u ",
+ icmp6->icmp6_type, icmp6->icmp6_code);
+ else
+ len = snprintf(SNPARGS(proto, 0), "IPV6-ICMP ");
+ len += snprintf(SNPARGS(proto, len), "[%s]",
+ ip6_sprintf(&ip6->ip6_src));
+ snprintf(SNPARGS(proto, len), " [%s]",
+ ip6_sprintf(&ip6->ip6_dst));
+ break;
+ default:
+ len = snprintf(SNPARGS(proto, 0), "P:%d [%s]", nxt,
+ ip6_sprintf(&ip6->ip6_src));
+ snprintf(SNPARGS(proto, len), " [%s]",
+ ip6_sprintf(&ip6->ip6_dst));
+ break;
+ }
+
+ if (oif)
+ log(LOG_SECURITY | LOG_INFO, "%s %s %s out via %s\n",
+ name, action, proto, if_name(oif));
+ else if (rif)
+ log(LOG_SECURITY | LOG_INFO, "%s %s %s in via %s\n",
+ name, action, proto, if_name(rif));
+ else
+ log(LOG_SECURITY | LOG_INFO, "%s %s %s",
+ name, action, proto);
+ if (fw6_verbose_limit != 0 && count == fw6_verbose_limit)
+ log(LOG_SECURITY | LOG_INFO, "ip6fw: limit reached on entry %d\n",
+ f ? f->fw_number : -1);
+}
+
+/*
+ * Parameters:
+ *
+ * ip Pointer to packet header (struct ip6_hdr *)
+ * hlen Packet header length
+ * oif Outgoing interface, or NULL if packet is incoming
+ * #ifndef IP6FW_DIVERT_RESTART
+ * *cookie Ignore all divert/tee rules to this port (if non-zero)
+ * #else
+ * *cookie Skip up to the first rule past this rule number;
+ * #endif
+ * *m The packet; we set to NULL when/if we nuke it.
+ *
+ * Return value:
+ *
+ * 0 The packet is to be accepted and routed normally OR
+ * the packet was denied/rejected and has been dropped;
+ * in the latter case, *m is equal to NULL upon return.
+ * port Divert the packet to port.
+ */
+
+static int
+ip6_fw_chk(struct ip6_hdr **pip6,
+ struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)
+{
+ struct ip6_fw_chain *chain;
+ struct ip6_fw *rule = NULL;
+ struct ip6_hdr *ip6 = *pip6;
+ struct ifnet *const rif = (*m)->m_pkthdr.rcvif;
+ u_short offset = 0;
+ int off = sizeof(struct ip6_hdr), nxt = ip6->ip6_nxt;
+ u_short src_port, dst_port;
+#ifdef IP6FW_DIVERT_RESTART
+ u_int16_t skipto = *cookie;
+#else
+ u_int16_t ignport = ntohs(*cookie);
+#endif
+
+ *cookie = 0;
+ /*
+ * Go down the chain, looking for enlightment
+ * #ifdef IP6FW_DIVERT_RESTART
+ * If we've been asked to start at a given rule immediatly, do so.
+ * #endif
+ */
+ chain = LIST_FIRST(&ip6_fw_chain);
+#ifdef IP6FW_DIVERT_RESTART
+ if (skipto) {
+ if (skipto >= 65535)
+ goto dropit;
+ while (chain && (chain->rule->fw_number <= skipto)) {
+ chain = LIST_NEXT(chain, chain);
+ }
+ if (! chain) goto dropit;
+ }
+#endif /* IP6FW_DIVERT_RESTART */
+ for (; chain; chain = LIST_NEXT(chain, chain)) {
+ struct ip6_fw *const f = chain->rule;
+
+ if (oif) {
+ /* Check direction outbound */
+ if (!(f->fw_flg & IPV6_FW_F_OUT))
+ continue;
+ } else {
+ /* Check direction inbound */
+ if (!(f->fw_flg & IPV6_FW_F_IN))
+ continue;
+ }
+
+#define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\
+ (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \
+ (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \
+ (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \
+ (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3]))
+
+ /* If src-addr doesn't match, not this rule. */
+ if (((f->fw_flg & IPV6_FW_F_INVSRC) != 0) ^
+ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_src,&f->fw_smsk,&f->fw_src)))
+ continue;
+
+ /* If dest-addr doesn't match, not this rule. */
+ if (((f->fw_flg & IPV6_FW_F_INVDST) != 0) ^
+ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_dst,&f->fw_dmsk,&f->fw_dst)))
+ continue;
+
+#undef IN6_ARE_ADDR_MASKEQUAL
+ /* Interface check */
+ if ((f->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
+ struct ifnet *const iface = oif ? oif : rif;
+
+ /* Backwards compatibility hack for "via" */
+ if (!iface || !iface_match(iface,
+ &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME))
+ continue;
+ } else {
+ /* Check receive interface */
+ if ((f->fw_flg & IPV6_FW_F_IIFACE)
+ && (!rif || !iface_match(rif,
+ &f->fw_in_if, f->fw_flg & IPV6_FW_F_IIFNAME)))
+ continue;
+ /* Check outgoing interface */
+ if ((f->fw_flg & IPV6_FW_F_OIFACE)
+ && (!oif || !iface_match(oif,
+ &f->fw_out_if, f->fw_flg & IPV6_FW_F_OIFNAME)))
+ continue;
+ }
+
+ /* Check IP options */
+ if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset))
+ continue;
+
+ /* Fragments */
+ if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset)
+ continue;
+
+ /* Check protocol; if wildcard, match */
+ if (f->fw_prot == IPPROTO_IPV6)
+ goto got_match;
+
+ /* If different, don't match */
+ if (nxt != f->fw_prot)
+ continue;
+
+#define PULLUP_TO(len) do { \
+ if ((*m)->m_len < (len) \
+ && (*m = m_pullup(*m, (len))) == 0) { \
+ goto dropit; \
+ } \
+ *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \
+ } while (0)
+
+ /* Protocol specific checks */
+ switch (nxt) {
+ case IPPROTO_TCP:
+ {
+ struct tcphdr *tcp6;
+
+ if (offset == 1) { /* cf. RFC 1858 */
+ PULLUP_TO(off + 4); /* XXX ? */
+ goto bogusfrag;
+ }
+ if (offset != 0) {
+ /*
+ * TCP flags and ports aren't available in this
+ * packet -- if this rule specified either one,
+ * we consider the rule a non-match.
+ */
+ if (f->fw_nports != 0 ||
+ f->fw_tcpf != f->fw_tcpnf)
+ continue;
+
+ break;
+ }
+ PULLUP_TO(off + 14);
+ tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off);
+ if (((f->fw_tcpf != f->fw_tcpnf) ||
+ (f->fw_ipflg & IPV6_FW_IF_TCPEST)) &&
+ !tcp6flg_match(tcp6, f))
+ continue;
+ src_port = ntohs(tcp6->th_sport);
+ dst_port = ntohs(tcp6->th_dport);
+ goto check_ports;
+ }
+
+ case IPPROTO_UDP:
+ {
+ struct udphdr *udp;
+
+ if (offset != 0) {
+ /*
+ * Port specification is unavailable -- if this
+ * rule specifies a port, we consider the rule
+ * a non-match.
+ */
+ if (f->fw_nports != 0)
+ continue;
+
+ break;
+ }
+ PULLUP_TO(off + 4);
+ udp = (struct udphdr *) ((caddr_t)ip6 + off);
+ src_port = ntohs(udp->uh_sport);
+ dst_port = ntohs(udp->uh_dport);
+check_ports:
+ if (!port_match6(&f->fw_pts[0],
+ IPV6_FW_GETNSRCP(f), src_port,
+ f->fw_flg & IPV6_FW_F_SRNG))
+ continue;
+ if (!port_match6(&f->fw_pts[IPV6_FW_GETNSRCP(f)],
+ IPV6_FW_GETNDSTP(f), dst_port,
+ f->fw_flg & IPV6_FW_F_DRNG))
+ continue;
+ break;
+ }
+
+ case IPPROTO_ICMPV6:
+ {
+ struct icmp6_hdr *icmp;
+
+ if (offset != 0) /* Type isn't valid */
+ break;
+ PULLUP_TO(off + 2);
+ icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off);
+ if (!icmp6type_match(icmp, f))
+ continue;
+ break;
+ }
+#undef PULLUP_TO
+
+bogusfrag:
+ if (fw6_verbose)
+ ip6fw_report(NULL, ip6, rif, oif, off, nxt);
+ goto dropit;
+ }
+
+got_match:
+#ifndef IP6FW_DIVERT_RESTART
+ /* Ignore divert/tee rule if socket port is "ignport" */
+ switch (f->fw_flg & IPV6_FW_F_COMMAND) {
+ case IPV6_FW_F_DIVERT:
+ case IPV6_FW_F_TEE:
+ if (f->fw_divert_port == ignport)
+ continue; /* ignore this rule */
+ break;
+ }
+
+#endif /* IP6FW_DIVERT_RESTART */
+ /* Update statistics */
+ f->fw_pcnt += 1;
+ f->fw_bcnt += ntohs(ip6->ip6_plen);
+ f->timestamp = time_second;
+
+ /* Log to console if desired */
+ if ((f->fw_flg & IPV6_FW_F_PRN) && fw6_verbose)
+ ip6fw_report(f, ip6, rif, oif, off, nxt);
+
+ /* Take appropriate action */
+ switch (f->fw_flg & IPV6_FW_F_COMMAND) {
+ case IPV6_FW_F_ACCEPT:
+ return(0);
+ case IPV6_FW_F_COUNT:
+ continue;
+ case IPV6_FW_F_DIVERT:
+#ifdef IP6FW_DIVERT_RESTART
+ *cookie = f->fw_number;
+#else
+ *cookie = htons(f->fw_divert_port);
+#endif /* IP6FW_DIVERT_RESTART */
+ return(f->fw_divert_port);
+ case IPV6_FW_F_TEE:
+ /*
+ * XXX someday tee packet here, but beware that you
+ * can't use m_copym() or m_copypacket() because
+ * the divert input routine modifies the mbuf
+ * (and these routines only increment reference
+ * counts in the case of mbuf clusters), so need
+ * to write custom routine.
+ */
+ continue;
+ case IPV6_FW_F_SKIPTO:
+#ifdef DIAGNOSTIC
+ while (chain->chain.le_next
+ && chain->chain.le_next->rule->fw_number
+ < f->fw_skipto_rule)
+#else
+ while (chain->chain.le_next->rule->fw_number
+ < f->fw_skipto_rule)
+#endif
+ chain = chain->chain.le_next;
+ continue;
+ }
+
+ /* Deny/reject this packet using this rule */
+ rule = f;
+ break;
+ }
+
+#ifdef DIAGNOSTIC
+ /* Rule 65535 should always be there and should always match */
+ if (!chain)
+ panic("ip6_fw: chain");
+#endif
+
+ /*
+ * At this point, we're going to drop the packet.
+ * Send a reject notice if all of the following are true:
+ *
+ * - The packet matched a reject rule
+ * - The packet is not an ICMP packet, or is an ICMP query packet
+ * - The packet is not a multicast or broadcast packet
+ */
+ if ((rule->fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT
+ && (nxt != IPPROTO_ICMPV6 || is_icmp6_query(ip6, off))
+ && !((*m)->m_flags & (M_BCAST|M_MCAST))
+ && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ switch (rule->fw_reject_code) {
+ case IPV6_FW_REJECT_RST:
+ {
+ struct tcphdr *const tcp =
+ (struct tcphdr *) ((caddr_t)ip6 + off);
+ struct {
+ struct ip6_hdr ip6;
+ struct tcphdr th;
+ } ti;
+ tcp_seq ack, seq;
+ int flags;
+
+ if (offset != 0 || (tcp->th_flags & TH_RST))
+ break;
+
+ ti.ip6 = *ip6;
+ ti.th = *tcp;
+ ti.th.th_seq = ntohl(ti.th.th_seq);
+ ti.th.th_ack = ntohl(ti.th.th_ack);
+ ti.ip6.ip6_nxt = IPPROTO_TCP;
+ if (ti.th.th_flags & TH_ACK) {
+ ack = 0;
+ seq = ti.th.th_ack;
+ flags = TH_RST;
+ } else {
+ ack = ti.th.th_seq;
+ if (((*m)->m_flags & M_PKTHDR) != 0) {
+ ack += (*m)->m_pkthdr.len - off
+ - (ti.th.th_off << 2);
+ } else if (ip6->ip6_plen) {
+ ack += ntohs(ip6->ip6_plen) + sizeof(*ip6)
+ - off - (ti.th.th_off << 2);
+ } else {
+ m_freem(*m);
+ *m = 0;
+ break;
+ }
+ seq = 0;
+ flags = TH_RST|TH_ACK;
+ }
+ bcopy(&ti, ip6, sizeof(ti));
+ tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1),
+ *m, ack, seq, flags);
+ *m = NULL;
+ break;
+ }
+ default: /* Send an ICMP unreachable using code */
+ if (oif)
+ (*m)->m_pkthdr.rcvif = oif;
+ icmp6_error(*m, ICMP6_DST_UNREACH,
+ rule->fw_reject_code, 0);
+ *m = NULL;
+ break;
+ }
+ }
+
+dropit:
+ /*
+ * Finally, drop the packet.
+ */
+ if (*m) {
+ m_freem(*m);
+ *m = NULL;
+ }
+ return(0);
+}
+
+static int
+add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl)
+{
+ struct ip6_fw *ftmp = 0;
+ struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0;
+ u_short nbr = 0;
+ int s;
+
+ fwc = malloc(sizeof *fwc, M_IP6FW, M_NOWAIT);
+ ftmp = malloc(sizeof *ftmp, M_IP6FW, M_NOWAIT);
+ if (!fwc || !ftmp) {
+ dprintf(("%s malloc said no\n", err_prefix));
+ if (fwc) free(fwc, M_IP6FW);
+ if (ftmp) free(ftmp, M_IP6FW);
+ return (ENOSPC);
+ }
+
+ bcopy(frwl, ftmp, sizeof(struct ip6_fw));
+ ftmp->fw_in_if.fu_via_if.name[IP6FW_IFNLEN - 1] = '\0';
+ ftmp->fw_pcnt = 0L;
+ ftmp->fw_bcnt = 0L;
+ fwc->rule = ftmp;
+
+ s = splnet();
+
+ if (!chainptr->lh_first) {
+ LIST_INSERT_HEAD(chainptr, fwc, chain);
+ splx(s);
+ return(0);
+ } else if (ftmp->fw_number == (u_short)-1) {
+ if (fwc) free(fwc, M_IP6FW);
+ if (ftmp) free(ftmp, M_IP6FW);
+ splx(s);
+ dprintf(("%s bad rule number\n", err_prefix));
+ return (EINVAL);
+ }
+
+ /* If entry number is 0, find highest numbered rule and add 100 */
+ if (ftmp->fw_number == 0) {
+ for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
+ if (fcp->rule->fw_number != (u_short)-1)
+ nbr = fcp->rule->fw_number;
+ else
+ break;
+ }
+ if (nbr < (u_short)-1 - 100)
+ nbr += 100;
+ ftmp->fw_number = nbr;
+ }
+
+ /* Got a valid number; now insert it, keeping the list ordered */
+ for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) {
+ if (fcp->rule->fw_number > ftmp->fw_number) {
+ if (fcpl) {
+ LIST_INSERT_AFTER(fcpl, fwc, chain);
+ } else {
+ LIST_INSERT_HEAD(chainptr, fwc, chain);
+ }
+ break;
+ } else {
+ fcpl = fcp;
+ }
+ }
+
+ splx(s);
+ return (0);
+}
+
+static int
+del_entry6(struct ip6_fw_head *chainptr, u_short number)
+{
+ struct ip6_fw_chain *fcp;
+ int s;
+
+ s = splnet();
+
+ fcp = chainptr->lh_first;
+ if (number != (u_short)-1) {
+ for (; fcp; fcp = fcp->chain.le_next) {
+ if (fcp->rule->fw_number == number) {
+ LIST_REMOVE(fcp, chain);
+ splx(s);
+ free(fcp->rule, M_IP6FW);
+ free(fcp, M_IP6FW);
+ return 0;
+ }
+ }
+ }
+
+ splx(s);
+ return (EINVAL);
+}
+
+static int
+zero_entry6(struct mbuf *m)
+{
+ struct ip6_fw *frwl;
+ struct ip6_fw_chain *fcp;
+ int s;
+
+ if (m && m->m_len != 0) {
+ if (m->m_len != sizeof(struct ip6_fw))
+ return(EINVAL);
+ frwl = mtod(m, struct ip6_fw *);
+ }
+ else
+ frwl = NULL;
+
+ /*
+ * It's possible to insert multiple chain entries with the
+ * same number, so we don't stop after finding the first
+ * match if zeroing a specific entry.
+ */
+ s = splnet();
+ for (fcp = ip6_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next)
+ if (!frwl || frwl->fw_number == fcp->rule->fw_number) {
+ fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0;
+ fcp->rule->timestamp = 0;
+ }
+ splx(s);
+
+ if (fw6_verbose) {
+ if (frwl)
+ log(LOG_SECURITY | LOG_NOTICE,
+ "ip6fw: Entry %d cleared.\n", frwl->fw_number);
+ else
+ log(LOG_SECURITY | LOG_NOTICE,
+ "ip6fw: Accounting cleared.\n");
+ }
+
+ return(0);
+}
+
+static struct ip6_fw *
+check_ip6fw_mbuf(struct mbuf *m)
+{
+ /* Check length */
+ if (m->m_len != sizeof(struct ip6_fw)) {
+ dprintf(("%s len=%d, want %zu\n", err_prefix, m->m_len,
+ sizeof(struct ip6_fw)));
+ return (NULL);
+ }
+ return(check_ip6fw_struct(mtod(m, struct ip6_fw *)));
+}
+
+static struct ip6_fw *
+check_ip6fw_struct(struct ip6_fw *frwl)
+{
+ /* Check for invalid flag bits */
+ if ((frwl->fw_flg & ~IPV6_FW_F_MASK) != 0) {
+ dprintf(("%s undefined flag bits set (flags=%x)\n",
+ err_prefix, frwl->fw_flg));
+ return (NULL);
+ }
+ /* Must apply to incoming or outgoing (or both) */
+ if (!(frwl->fw_flg & (IPV6_FW_F_IN | IPV6_FW_F_OUT))) {
+ dprintf(("%s neither in nor out\n", err_prefix));
+ return (NULL);
+ }
+ /* Empty interface name is no good */
+ if (((frwl->fw_flg & IPV6_FW_F_IIFNAME)
+ && !*frwl->fw_in_if.fu_via_if.name)
+ || ((frwl->fw_flg & IPV6_FW_F_OIFNAME)
+ && !*frwl->fw_out_if.fu_via_if.name)) {
+ dprintf(("%s empty interface name\n", err_prefix));
+ return (NULL);
+ }
+ /* Sanity check interface matching */
+ if ((frwl->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) {
+ ; /* allow "via" backwards compatibility */
+ } else if ((frwl->fw_flg & IPV6_FW_F_IN)
+ && (frwl->fw_flg & IPV6_FW_F_OIFACE)) {
+ dprintf(("%s outgoing interface check on incoming\n",
+ err_prefix));
+ return (NULL);
+ }
+ /* Sanity check port ranges */
+ if ((frwl->fw_flg & IPV6_FW_F_SRNG) && IPV6_FW_GETNSRCP(frwl) < 2) {
+ dprintf(("%s src range set but n_src_p=%d\n",
+ err_prefix, IPV6_FW_GETNSRCP(frwl)));
+ return (NULL);
+ }
+ if ((frwl->fw_flg & IPV6_FW_F_DRNG) && IPV6_FW_GETNDSTP(frwl) < 2) {
+ dprintf(("%s dst range set but n_dst_p=%d\n",
+ err_prefix, IPV6_FW_GETNDSTP(frwl)));
+ return (NULL);
+ }
+ if (IPV6_FW_GETNSRCP(frwl) + IPV6_FW_GETNDSTP(frwl) > IPV6_FW_MAX_PORTS) {
+ dprintf(("%s too many ports (%d+%d)\n",
+ err_prefix, IPV6_FW_GETNSRCP(frwl), IPV6_FW_GETNDSTP(frwl)));
+ return (NULL);
+ }
+ /*
+ * Protocols other than TCP/UDP don't use port range
+ */
+ if ((frwl->fw_prot != IPPROTO_TCP) &&
+ (frwl->fw_prot != IPPROTO_UDP) &&
+ (IPV6_FW_GETNSRCP(frwl) || IPV6_FW_GETNDSTP(frwl))) {
+ dprintf(("%s port(s) specified for non TCP/UDP rule\n",
+ err_prefix));
+ return(NULL);
+ }
+
+ /*
+ * Rather than modify the entry to make such entries work,
+ * we reject this rule and require user level utilities
+ * to enforce whatever policy they deem appropriate.
+ */
+ if ((frwl->fw_src.s6_addr32[0] & (~frwl->fw_smsk.s6_addr32[0])) ||
+ (frwl->fw_src.s6_addr32[1] & (~frwl->fw_smsk.s6_addr32[1])) ||
+ (frwl->fw_src.s6_addr32[2] & (~frwl->fw_smsk.s6_addr32[2])) ||
+ (frwl->fw_src.s6_addr32[3] & (~frwl->fw_smsk.s6_addr32[3])) ||
+ (frwl->fw_dst.s6_addr32[0] & (~frwl->fw_dmsk.s6_addr32[0])) ||
+ (frwl->fw_dst.s6_addr32[1] & (~frwl->fw_dmsk.s6_addr32[1])) ||
+ (frwl->fw_dst.s6_addr32[2] & (~frwl->fw_dmsk.s6_addr32[2])) ||
+ (frwl->fw_dst.s6_addr32[3] & (~frwl->fw_dmsk.s6_addr32[3]))) {
+ dprintf(("%s rule never matches\n", err_prefix));
+ return(NULL);
+ }
+
+ if ((frwl->fw_flg & IPV6_FW_F_FRAG) &&
+ (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) {
+ if (frwl->fw_nports) {
+ dprintf(("%s cannot mix 'frag' and ports\n", err_prefix));
+ return(NULL);
+ }
+ if (frwl->fw_prot == IPPROTO_TCP &&
+ frwl->fw_tcpf != frwl->fw_tcpnf) {
+ dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix));
+ return(NULL);
+ }
+ }
+
+ /* Check command specific stuff */
+ switch (frwl->fw_flg & IPV6_FW_F_COMMAND)
+ {
+ case IPV6_FW_F_REJECT:
+ if (frwl->fw_reject_code >= 0x100
+ && !(frwl->fw_prot == IPPROTO_TCP
+ && frwl->fw_reject_code == IPV6_FW_REJECT_RST)) {
+ dprintf(("%s unknown reject code\n", err_prefix));
+ return(NULL);
+ }
+ break;
+ case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */
+ case IPV6_FW_F_TEE:
+ if (frwl->fw_divert_port == 0) {
+ dprintf(("%s can't divert to port 0\n", err_prefix));
+ return (NULL);
+ }
+ break;
+ case IPV6_FW_F_DENY:
+ case IPV6_FW_F_ACCEPT:
+ case IPV6_FW_F_COUNT:
+ case IPV6_FW_F_SKIPTO:
+ break;
+ default:
+ dprintf(("%s invalid command\n", err_prefix));
+ return(NULL);
+ }
+
+ return frwl;
+}
+
+static int
+ip6_fw_ctl(int stage, struct mbuf **mm)
+{
+ int error;
+ struct mbuf *m;
+
+ if (stage == IPV6_FW_GET) {
+ struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first;
+ *mm = m = m_get(M_TRYWAIT, MT_DATA); /* XXX */
+ if (!m)
+ return(ENOBUFS);
+ if (sizeof *(fcp->rule) > MLEN) {
+ MCLGET(m, M_TRYWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return(ENOBUFS);
+ }
+ }
+ for (; fcp; fcp = fcp->chain.le_next) {
+ 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) {
+ m_freem(*mm);
+ return(ENOBUFS);
+ }
+ m = m->m_next;
+ if (sizeof *(fcp->rule) > MLEN) {
+ MCLGET(m, M_TRYWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(*mm);
+ return(ENOBUFS);
+ }
+ }
+ m->m_len = 0;
+ }
+ return (0);
+ }
+ m = *mm;
+ /* only allow get calls if secure mode > 2 */
+ if (securelevel > 2) {
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return(EPERM);
+ }
+ if (stage == IPV6_FW_FLUSH) {
+ while (ip6_fw_chain.lh_first != NULL &&
+ ip6_fw_chain.lh_first->rule->fw_number != (u_short)-1) {
+ struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first;
+ int s = splnet();
+ LIST_REMOVE(ip6_fw_chain.lh_first, chain);
+ splx(s);
+ free(fcp->rule, M_IP6FW);
+ free(fcp, M_IP6FW);
+ }
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return (0);
+ }
+ if (stage == IPV6_FW_ZERO) {
+ error = zero_entry6(m);
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return (error);
+ }
+ if (m == NULL) {
+ printf("%s NULL mbuf ptr\n", err_prefix);
+ return (EINVAL);
+ }
+
+ if (stage == IPV6_FW_ADD) {
+ struct ip6_fw *frwl = check_ip6fw_mbuf(m);
+
+ if (!frwl)
+ error = EINVAL;
+ else
+ error = add_entry6(&ip6_fw_chain, frwl);
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return error;
+ }
+ if (stage == IPV6_FW_DEL) {
+ if (m->m_len != sizeof(struct ip6_fw)) {
+ dprintf(("%s len=%d, want %zu\n", err_prefix, m->m_len,
+ sizeof(struct ip6_fw)));
+ error = EINVAL;
+ } else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) {
+ dprintf(("%s can't delete rule 65535\n", err_prefix));
+ error = EINVAL;
+ } else
+ error = del_entry6(&ip6_fw_chain,
+ mtod(m, struct ip6_fw *)->fw_number);
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return error;
+ }
+
+ dprintf(("%s unknown request %d\n", err_prefix, stage));
+ if (m) {
+ (void)m_freem(m);
+ *mm = 0;
+ }
+ return (EINVAL);
+}
+
+void
+ip6_fw_init(void)
+{
+ struct ip6_fw default_rule;
+
+ ip6_fw_chk_ptr = ip6_fw_chk;
+ ip6_fw_ctl_ptr = ip6_fw_ctl;
+ LIST_INIT(&ip6_fw_chain);
+
+ bzero(&default_rule, sizeof default_rule);
+ default_rule.fw_prot = IPPROTO_IPV6;
+ default_rule.fw_number = (u_short)-1;
+#ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
+ default_rule.fw_flg |= IPV6_FW_F_ACCEPT;
+#else
+ default_rule.fw_flg |= IPV6_FW_F_DENY;
+#endif
+ default_rule.fw_flg |= IPV6_FW_F_IN | IPV6_FW_F_OUT;
+ if (check_ip6fw_struct(&default_rule) == NULL ||
+ add_entry6(&ip6_fw_chain, &default_rule))
+ panic(__func__);
+
+ printf("IPv6 packet filtering initialized, ");
+#ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT
+ printf("default to accept, ");
+#endif
+#ifndef IPV6FIREWALL_VERBOSE
+ printf("logging disabled\n");
+#else
+ if (fw6_verbose_limit == 0)
+ printf("unlimited logging\n");
+ else
+ printf("logging limited to %d packets/entry\n",
+ fw6_verbose_limit);
+#endif
+}
+
+static ip6_fw_chk_t *old_chk_ptr;
+static ip6_fw_ctl_t *old_ctl_ptr;
+
+static int
+ip6fw_modevent(module_t mod, int type, void *unused)
+{
+ int s;
+
+ switch (type) {
+ case MOD_LOAD:
+ s = splnet();
+
+ old_chk_ptr = ip6_fw_chk_ptr;
+ old_ctl_ptr = ip6_fw_ctl_ptr;
+
+ ip6_fw_init();
+ splx(s);
+ return 0;
+ case MOD_UNLOAD:
+ s = splnet();
+ ip6_fw_chk_ptr = old_chk_ptr;
+ ip6_fw_ctl_ptr = old_ctl_ptr;
+ while (LIST_FIRST(&ip6_fw_chain) != NULL) {
+ struct ip6_fw_chain *fcp = LIST_FIRST(&ip6_fw_chain);
+ LIST_REMOVE(LIST_FIRST(&ip6_fw_chain), chain);
+ free(fcp->rule, M_IP6FW);
+ free(fcp, M_IP6FW);
+ }
+
+ splx(s);
+ printf("IPv6 firewall unloaded\n");
+ return 0;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static moduledata_t ip6fwmod = {
+ "ip6fw",
+ ip6fw_modevent,
+ 0
+};
+DECLARE_MODULE(ip6fw, ip6fwmod, SI_SUB_PSEUDO, SI_ORDER_ANY);
diff --git a/sys/netinet6/ip6_fw.h b/sys/netinet6/ip6_fw.h
new file mode 100644
index 0000000..3c1dcd0
--- /dev/null
+++ b/sys/netinet6/ip6_fw.h
@@ -0,0 +1,231 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_fw.h,v 1.9 2001/08/01 04:29:57 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1998, 1999, 2000 and 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.
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ */
+
+#ifndef _IP6_FW_H
+#define _IP6_FW_H
+
+#include <net/if.h>
+
+/*
+ * This union structure identifies an interface, either explicitly
+ * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME
+ * and IP_FW_F_OIFNAME say how to interpret this structure. An
+ * interface unit number of -1 matches any unit number, while an
+ * IP address of 0.0.0.0 indicates matches any interface.
+ *
+ * The receive and transmit interfaces are only compared against the
+ * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE)
+ * is set. Note some packets lack a receive or transmit interface
+ * (in which case the missing "interface" never matches).
+ */
+
+union ip6_fw_if {
+ struct in6_addr fu_via_ip6; /* Specified by IPv6 address */
+ struct { /* Specified by interface name */
+#define IP6FW_IFNLEN IFNAMSIZ
+ char name[IP6FW_IFNLEN];
+ short unit; /* -1 means match any unit */
+ } fu_via_if;
+};
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order.
+ * fw_flg and fw_n*p are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108)
+ */
+
+struct ip6_fw {
+ u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */
+ struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */
+ struct in6_addr fw_smsk, fw_dmsk; /* Mask for src and dest IPv6 addr */
+ u_short fw_number; /* Rule number */
+ u_short fw_flg; /* Flags word */
+#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */
+ u_int fw_ipflg; /* IP flags word */
+ u_short fw_pts[IPV6_FW_MAX_PORTS]; /* Array of port numbers to match */
+ u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */
+ u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */
+#define IPV6_FW_ICMPTYPES_DIM (256 / (sizeof(unsigned) * 8))
+ unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */
+ long timestamp; /* timestamp (tv_sec) of last match */
+ union ip6_fw_if fw_in_if, fw_out_if;/* Incoming and outgoing interfaces */
+ union {
+ u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */
+ u_short fu_skipto_rule; /* SKIPTO command rule number */
+ u_short fu_reject_code; /* REJECT response code */
+ } fw_un;
+ u_char fw_prot; /* IPv6 protocol */
+ u_char fw_nports; /* N'of src ports and # of dst ports */
+ /* in ports array (dst ports follow */
+ /* src ports; max of 10 ports in all; */
+ /* count of 0 means match all ports) */
+};
+
+#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f)
+#define IPV6_FW_SETNSRCP(rule, n) do { \
+ (rule)->fw_nports &= ~0x0f; \
+ (rule)->fw_nports |= (n); \
+ } while (0)
+#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4)
+#define IPV6_FW_SETNDSTP(rule, n) do { \
+ (rule)->fw_nports &= ~0xf0; \
+ (rule)->fw_nports |= (n) << 4;\
+ } while (0)
+
+#define fw_divert_port fw_un.fu_divert_port
+#define fw_skipto_rule fw_un.fu_skipto_rule
+#define fw_reject_code fw_un.fu_reject_code
+
+struct ip6_fw_chain {
+ LIST_ENTRY(ip6_fw_chain) chain;
+ struct ip6_fw *rule;
+};
+
+/*
+ * Values for "flags" field .
+ */
+#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */
+#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */
+#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */
+#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */
+
+#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */
+#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */
+#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */
+#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */
+#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */
+#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */
+#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */
+#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */
+
+#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */
+
+#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min *
+ * and max range (stored in host byte *
+ * order). */
+
+#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min *
+ * and max range (stored in host byte *
+ * order). */
+
+#define IPV6_FW_F_IIFNAME 0x0400 /* In interface by name/unit (not IP) */
+#define IPV6_FW_F_OIFNAME 0x0800 /* Out interface by name/unit (not IP) */
+
+#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */
+#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */
+
+#define IPV6_FW_F_FRAG 0x4000 /* Fragment */
+
+#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */
+
+#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */
+
+/*
+ * Flags for the 'fw_ipflg' field, for comparing values of ip and its protocols. */
+#define IPV6_FW_IF_TCPEST 0x00000020 /* established TCP connection */
+#define IPV6_FW_IF_TCPMSK 0x00000020 /* mask of all TCP values */
+
+/*
+ * For backwards compatibility with rules specifying "via iface" but
+ * not restricted to only "in" or "out" packets, we define this combination
+ * of bits to represent this configuration.
+ */
+
+#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|IPV6_FW_F_OIFACE)
+
+/*
+ * Definitions for REJECT response codes.
+ * Values less than 256 correspond to ICMP unreachable codes.
+ */
+#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */
+
+/*
+ * Definitions for IPv6 option names.
+ */
+#define IPV6_FW_IP6OPT_HOPOPT 0x01
+#define IPV6_FW_IP6OPT_ROUTE 0x02
+#define IPV6_FW_IP6OPT_FRAG 0x04
+#define IPV6_FW_IP6OPT_ESP 0x08
+#define IPV6_FW_IP6OPT_AH 0x10
+#define IPV6_FW_IP6OPT_NONXT 0x20
+#define IPV6_FW_IP6OPT_OPTS 0x40
+
+/*
+ * Definitions for TCP flags.
+ */
+#define IPV6_FW_TCPF_FIN TH_FIN
+#define IPV6_FW_TCPF_SYN TH_SYN
+#define IPV6_FW_TCPF_RST TH_RST
+#define IPV6_FW_TCPF_PSH TH_PUSH
+#define IPV6_FW_TCPF_ACK TH_ACK
+#define IPV6_FW_TCPF_URG TH_URG
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+#ifdef _KERNEL
+
+/*
+ * Function definitions.
+ */
+void ip6_fw_init(void);
+
+/* Firewall hooks */
+struct ip6_hdr;
+typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*,
+ u_short *, struct mbuf**));
+typedef int ip6_fw_ctl_t __P((int, struct mbuf**));
+extern ip6_fw_chk_t *ip6_fw_chk_ptr;
+extern ip6_fw_ctl_t *ip6_fw_ctl_ptr;
+extern int ip6_fw_enable;
+
+#endif /* _KERNEL */
+
+#endif /* _IP6_FW_H */
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
new file mode 100644
index 0000000..dcb74fb
--- /dev/null
+++ b/sys/netinet6/ip6_input.c
@@ -0,0 +1,1643 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_input.c,v 1.259 2002/01/21 04:58:09 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+#include "opt_pfil_hooks.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifdef INET
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#endif /* INET */
+#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/icmp6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#endif
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#define IPSEC
+#endif /* FAST_IPSEC */
+
+#include <netinet6/ip6_fw.h>
+
+#include <netinet6/ip6protosw.h>
+
+#include <net/net_osdep.h>
+
+extern struct domain inet6domain;
+
+u_char ip6_protox[IPPROTO_MAX];
+static struct ifqueue ip6intrq;
+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 */
+
+int ip6_ours_check_algorithm;
+
+
+/* firewall hooks */
+ip6_fw_chk_t *ip6_fw_chk_ptr;
+ip6_fw_ctl_t *ip6_fw_ctl_ptr;
+int ip6_fw_enable = 1;
+
+struct ip6stat ip6stat;
+
+static void ip6_init2 __P((void *));
+static struct ip6aux *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.
+ */
+void
+ip6_init()
+{
+ 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");
+ for (i = 0; i < IPPROTO_MAX; i++)
+ ip6_protox[i] = pr - inet6sw;
+ for (pr = (struct ip6protosw *)inet6domain.dom_protosw;
+ pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++)
+ if (pr->pr_domain->dom_family == PF_INET6 &&
+ pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
+ ip6_protox[pr->pr_protocol] = pr - inet6sw;
+ ip6intrq.ifq_maxlen = ip6qmaxlen;
+ mtx_init(&ip6intrq.ifq_mtx, "ip6_inq", NULL, MTX_DEF);
+ netisr_register(NETISR_IPV6, ip6_input, &ip6intrq);
+ nd6_init();
+ frag6_init();
+ /*
+ * in many cases, random() here does NOT return random number
+ * as initialization during bootstrap time occur in fixed order.
+ */
+ microtime(&tv);
+ ip6_flow_seq = random() ^ tv.tv_usec;
+ microtime(&tv);
+ ip6_desync_factor = (random() ^ tv.tv_usec) % MAX_TEMP_DESYNC_FACTOR;
+}
+
+static void
+ip6_init2(dummy)
+ void *dummy;
+{
+
+ /*
+ * to route local address of p2p link to loopback,
+ * assign loopback address first.
+ */
+ in6_ifattach(&loif[0], NULL);
+
+ /* nd6_timer_init */
+ callout_init(&nd6_timer_ch, 0);
+ callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL);
+
+ /* router renumbering prefix list maintenance */
+ 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 */
+/* This must be after route_init(), which is now SI_ORDER_THIRD */
+SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL);
+
+extern struct route_in6 ip6_forward_rt;
+
+void
+ip6_input(m)
+ struct mbuf *m;
+{
+ struct ip6_hdr *ip6;
+ int off = sizeof(struct ip6_hdr), nest;
+ u_int32_t plen;
+ u_int32_t rtalert = ~0;
+ int nxt, ours = 0;
+ struct ifnet *deliverifp = NULL;
+#ifdef PFIL_HOOKS
+ struct packet_filter_hook *pfh;
+ struct mbuf *m0;
+ int rv;
+#endif /* PFIL_HOOKS */
+
+#ifdef IPSEC
+ /*
+ * should the inner packet be considered authentic?
+ * see comment in ah4_input().
+ */
+ if (m) {
+ m->m_flags &= ~M_AUTHIPHDR;
+ m->m_flags &= ~M_AUTHIPDGM;
+ }
+#endif
+
+ /*
+ * make sure we don't have onion peering information into m_aux.
+ */
+ ip6_delaux(m);
+
+ /*
+ * mbuf statistics
+ */
+ if (m->m_flags & M_EXT) {
+ if (m->m_next)
+ ip6stat.ip6s_mext2m++;
+ 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 < 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);
+ ip6stat.ip6s_total++;
+
+#ifndef PULLDOWN_TEST
+ /*
+ * L2 bridge code and some other code can return mbuf chain
+ * that does not conform to KAME requirement. too bad.
+ * XXX: fails to join if interface MTU > MCLBYTES. jumbogram?
+ */
+ if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) {
+ struct mbuf *n;
+
+ MGETHDR(n, M_DONTWAIT, MT_HEADER);
+ if (n)
+ M_MOVE_PKTHDR(n, m);
+ if (n && n->m_pkthdr.len > MHLEN) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_freem(n);
+ n = NULL;
+ }
+ }
+ if (n == NULL) {
+ m_freem(m);
+ return; /*ENOBUFS*/
+ }
+
+ m_copydata(m, 0, n->m_pkthdr.len, mtod(n, caddr_t));
+ n->m_len = n->m_pkthdr.len;
+ m_freem(m);
+ m = n;
+ }
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/);
+#endif
+
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ struct ifnet *inifp;
+ inifp = m->m_pkthdr.rcvif;
+ if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) {
+ ip6stat.ip6s_toosmall++;
+ in6_ifstat_inc(inifp, ifs6_in_hdrerr);
+ return;
+ }
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
+ ip6stat.ip6s_badvers++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+ goto bad;
+ }
+
+#ifdef PFIL_HOOKS
+ /*
+ * Run through list of hooks for input packets. If there are any
+ * filters which require that additional packets in the flow are
+ * not fast-forwarded, they must clear the M_CANFASTFWD flag.
+ * Note that filters must _never_ set this flag, as another filter
+ * in the list may have previously cleared it.
+ */
+ m0 = m;
+ pfh = pfil_hook_get(PFIL_IN, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh);
+ for (; pfh; pfh = pfh->pfil_link.tqe_next)
+ if (pfh->pfil_func) {
+ rv = pfh->pfil_func(ip6, sizeof(*ip6),
+ m->m_pkthdr.rcvif, 0, &m0);
+ if (rv)
+ return;
+ m = m0;
+ if (m == NULL)
+ return;
+ ip6 = mtod(m, struct ip6_hdr *);
+ }
+#endif /* PFIL_HOOKS */
+
+ ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
+
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_enable && ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ /* use port as a dummy argument */
+ if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) {
+ m_freem(m);
+ m = NULL;
+ }
+ if (!m)
+ return;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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).
+ *
+ * The code forbids auto tunnel relay case in RFC1933 (the check is
+ * stronger than RFC1933). We may want to re-enable it if mech-xx
+ * is revised to forbid relaying case.
+ */
+ if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) ||
+ IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+ goto bad;
+ }
+#endif
+
+ /* 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++;
+ goto bad;
+ }
+ }
+
+ 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) */
+ /*
+ * 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 &&
+ 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
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ struct in6_multi *in6m = 0;
+
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast);
+ /*
+ * See if we belong to the destination multicast group on the
+ * arrival interface.
+ */
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m);
+ if (in6m)
+ ours = 1;
+ else if (!ip6_mrouter) {
+ ip6stat.ip6s_notmember++;
+ ip6stat.ip6s_cantforward++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ goto bad;
+ }
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ }
+
+ /*
+ * 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,
+ &((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++;
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+
+ bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6));
+ 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);
+#endif
+
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+ }
+
+#define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key))
+
+ /*
+ * Accept the packet if the forwarding interface to the destination
+ * according to the routing table is the loopback interface,
+ * unless the associated route has a gateway.
+ * Note that this approach causes to accept a packet if there is a
+ * 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
+ * the destination and the key of the rtentry has
+ * already done through looking up the routing table.
+ */
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr)
+#endif
+ ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) {
+ struct in6_ifaddr *ia6 =
+ (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa;
+
+ /*
+ * record address information into m_aux.
+ */
+ (void)ip6_setdstifaddr(m, ia6);
+
+ /*
+ * packets to a tentative, duplicated, or somehow invalid
+ * address must not be accepted.
+ */
+ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) {
+ /* 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. */
+ nd6log((LOG_INFO,
+ "ip6_input: packet to an unready address %s->%s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst)));
+
+ goto bad;
+ }
+ }
+ } /* XXX indentation (see above) */
+
+ /*
+ * FAITH(Firewall Aided Internet Translator)
+ */
+ if (ip6_keepfaith) {
+ if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp
+ && ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) {
+ /* XXX do we need more sanity checks? */
+ ours = 1;
+ deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /* faith */
+ goto hbhcheck;
+ }
+ }
+
+ /*
+ * Now there is no reason to process the packet if it's not our own
+ * and we're not a router.
+ */
+ if (!ip6_forwarding) {
+ ip6stat.ip6s_cantforward++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ goto bad;
+ }
+
+ 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.
+ */
+ plen = (u_int32_t)ntohs(ip6->ip6_plen);
+ if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
+ struct ip6_hbh *hbh;
+
+ if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) {
+#if 0 /*touches NULL pointer*/
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+#endif
+ return; /* m have already been freed */
+ }
+
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * if the payload length field is 0 and the next header field
+ * indicates Hop-by-Hop Options header, then a Jumbo Payload
+ * option MUST be included.
+ */
+ if (ip6->ip6_plen == 0 && plen == 0) {
+ /*
+ * Note that if a valid jumbo payload option is
+ * contained, ip6_hoptops_input() must set a valid
+ * (non-zero) payload length to the variable plen.
+ */
+ ip6stat.ip6s_badoptions++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+ return;
+ }
+#ifndef PULLDOWN_TEST
+ /* ip6_hopopts_input() ensures that mbuf is contiguous */
+ hbh = (struct ip6_hbh *)(ip6 + 1);
+#else
+ IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr),
+ sizeof(struct ip6_hbh));
+ if (hbh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+#endif
+ nxt = hbh->ip6h_nxt;
+
+ /*
+ * accept the packet if a router alert option is included
+ * and we act as an IPv6 router.
+ */
+ if (rtalert != ~0 && ip6_forwarding)
+ ours = 1;
+ } else
+ nxt = ip6->ip6_nxt;
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IPv6 header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) {
+ ip6stat.ip6s_tooshort++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
+ goto bad;
+ }
+ if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) {
+ if (m->m_len == m->m_pkthdr.len) {
+ m->m_len = sizeof(struct ip6_hdr) + plen;
+ m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen;
+ } else
+ m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len);
+ }
+
+ /*
+ * Forward if desirable.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ /*
+ * If we are acting as a multicast router, all
+ * incoming multicast packets are passed to the
+ * kernel-level multicast forwarding function.
+ * The packet is returned (relatively) intact; if
+ * ip6_mforward() returns a non-zero value, the packet
+ * must be discarded, else it may be accepted below.
+ */
+ if (ip6_mrouter && ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) {
+ ip6stat.ip6s_cantforward++;
+ m_freem(m);
+ return;
+ }
+ if (!ours) {
+ m_freem(m);
+ return;
+ }
+ } else if (!ours) {
+ ip6_forward(m, 0);
+ return;
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * Malicious party may be able to use IPv4 mapped addr to confuse
+ * tcp/udp stack and bypass security checks (act as if it was from
+ * 127.0.0.1 by using IPv6 src ::ffff:127.0.0.1). Be cautious.
+ *
+ * For SIIT end node behavior, you may want to disable the check.
+ * However, you will become vulnerable to attacks using IPv4 mapped
+ * source.
+ */
+ if (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;
+ }
+
+ /*
+ * Tell launch routine the next header
+ */
+ 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++;
+ goto bad;
+ }
+
+ /*
+ * protection against faulty packet - there should be
+ * more sanity checks in header chain processing.
+ */
+ if (m->m_pkthdr.len < off) {
+ ip6stat.ip6s_tooshort++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
+ 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;
+ bad:
+ m_freem(m);
+}
+
+/*
+ * set/grab in6_ifaddr correspond to IPv6 destination address.
+ * XXX backward compatibility wrapper
+ */
+static struct ip6aux *
+ip6_setdstifaddr(m, ia6)
+ struct mbuf *m;
+ struct in6_ifaddr *ia6;
+{
+ struct ip6aux *n;
+
+ n = ip6_addaux(m);
+ if (n)
+ n->ip6a_dstia6 = ia6;
+ return n; /* NULL if failed to set */
+}
+
+struct in6_ifaddr *
+ip6_getdstifaddr(m)
+ struct mbuf *m;
+{
+ struct ip6aux *n;
+
+ n = ip6_findaux(m);
+ if (n)
+ return n->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.
+ */
+static int
+ip6_hopopts_input(plenp, rtalertp, mp, offp)
+ u_int32_t *plenp;
+ u_int32_t *rtalertp; /* XXX: should be stored more smart way */
+ struct mbuf **mp;
+ int *offp;
+{
+ struct mbuf *m = *mp;
+ int off = *offp, hbhlen;
+ struct ip6_hbh *hbh;
+ u_int8_t *opt;
+
+ /* validation of the length of the header */
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1);
+ hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off);
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+
+ IP6_EXTHDR_CHECK(m, off, hbhlen, -1);
+ hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m,
+ sizeof(struct ip6_hdr), sizeof(struct ip6_hbh));
+ if (hbh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return -1;
+ }
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+ IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr),
+ hbhlen);
+ if (hbh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return -1;
+ }
+#endif
+ off += hbhlen;
+ hbhlen -= sizeof(struct ip6_hbh);
+ opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh);
+
+ if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh),
+ hbhlen, rtalertp, plenp) < 0)
+ return(-1);
+
+ *offp = off;
+ *mp = m;
+ return(0);
+}
+
+/*
+ * Search header for all Hop-by-hop options and process each option.
+ * 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)
+ struct mbuf *m;
+ u_int8_t *opthead;
+ int hbhlen;
+ u_int32_t *rtalertp;
+ u_int32_t *plenp;
+{
+ struct ip6_hdr *ip6;
+ int optlen = 0;
+ 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 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 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 payload option.
+ */
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (ip6->ip6_plen) {
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt - opthead);
+ return(-1);
+ }
+
+ /*
+ * We may see jumbolen in unaligned location, so
+ * we'd need to perform bcopy().
+ */
+ bcopy(opt + 2, &jumboplen, sizeof(jumboplen));
+ jumboplen = (u_int32_t)htonl(jumboplen);
+
+#if 1
+ /*
+ * if there are multiple jumbo payload options,
+ * *plenp will be non-zero and the packet will be
+ * rejected.
+ * the behavior may need some debate in ipngwg -
+ * multiple options does not make sense, however,
+ * there's no explicit mention in specification.
+ */
+ if (*plenp != 0) {
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 2 - opthead);
+ return(-1);
+ }
+#endif
+
+ /*
+ * jumbo payload length must be larger than 65535.
+ */
+ if (jumboplen <= IPV6_MAXPACKET) {
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 2 - opthead);
+ return(-1);
+ }
+ *plenp = jumboplen;
+
+ 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;
+ }
+ }
+
+ return(0);
+
+ bad:
+ m_freem(m);
+ return(-1);
+}
+
+/*
+ * Unknown option processing.
+ * The third argument `off' is the offset from the IPv6 header to the option,
+ * which is necessary if the IPv6 header the and option header and IPv6 header
+ * is not continuous in order to return an ICMPv6 error.
+ */
+int
+ip6_unknown_opt(optp, m, off)
+ u_int8_t *optp;
+ struct mbuf *m;
+ int 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);
+ }
+
+ m_freem(m); /* XXX: NOTREACHED */
+ return(-1);
+}
+
+/*
+ * 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.
+ */
+void
+ip6_savecontrol(in6p, mp, ip6, m)
+ struct inpcb *in6p;
+ struct mbuf **mp;
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
+{
+#if __FreeBSD_version >= 500000
+ struct thread *td = curthread; /* XXX */
+#else
+ struct proc *td = curproc; /* XXX */
+#endif
+ int privileged = 0;
+ int rthdr_exist = 0;
+
+
+ if (td && !suser(td))
+ privileged++;
+
+#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) {
+ mp = &(*mp)->m_next;
+ }
+ }
+#endif
+
+ /* RFC 2292 sec. 5 */
+ 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))
+ pi6.ipi6_addr.s6_addr16[1] = 0;
+ pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif)
+ ? m->m_pkthdr.rcvif->if_index
+ : 0;
+ *mp = sbcreatecontrol((caddr_t) &pi6,
+ sizeof(struct in6_pktinfo), IPV6_PKTINFO,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
+ 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;
+ }
+
+ /*
+ * IPV6_HOPOPTS socket option. We require super-user privilege
+ * for the option, but it might be too strict, since there might
+ * be some hop-by-hop options which can be returned to normal user.
+ * See RFC 2292 section 6.
+ */
+ 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
+ * data. Note that a hop-by-hop options header must be
+ * just after the IPv6 header, which fact is assured through
+ * the IPv6 input processing.
+ */
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
+ struct ip6_hbh *hbh;
+ 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
+ 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;
+ if (hbhlen != ext->m_len) {
+ m_freem(ext);
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+#endif
+
+ /*
+ * 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.
+ * 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_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);
+
+ /*
+ * Search for destination options headers or routing
+ * header(s) through the header chain, and stores each
+ * header as ancillary data.
+ * 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 = 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
+ 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;
+ if (elen != ext->m_len) {
+ m_freem(ext);
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+#endif
+
+ 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:
+ ;
+ }
+
+}
+
+#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 (!n)
+ return NULL;
+
+ n->m_len = 0;
+ if (elen >= M_TRAILINGSPACE(n)) {
+ m_free(n);
+ return NULL;
+ }
+
+ 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
+ * currently processed.
+ * XXX: This function supposes that
+ * M includes all headers,
+ * the next header field and the header length field of each header
+ * are valid, and
+ * the sum of each header length equals to OFF.
+ * Because of these assumptions, this function must be called very
+ * carefully. Moreover, it will not be used in the near future when
+ * we develop `neater' mechanism to process extension headers.
+ */
+char *
+ip6_get_prevhdr(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+
+ if (off == sizeof(struct ip6_hdr))
+ return(&ip6->ip6_nxt);
+ else {
+ int len, nxt;
+ struct ip6_ext *ip6e = NULL;
+
+ nxt = ip6->ip6_nxt;
+ len = sizeof(struct ip6_hdr);
+ while (len < off) {
+ ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len);
+
+ switch (nxt) {
+ case IPPROTO_FRAGMENT:
+ len += sizeof(struct ip6_frag);
+ break;
+ case IPPROTO_AH:
+ len += (ip6e->ip6e_len + 2) << 2;
+ break;
+ default:
+ len += (ip6e->ip6e_len + 1) << 3;
+ break;
+ }
+ nxt = ip6e->ip6e_nxt;
+ }
+ if (ip6e)
+ return(&ip6e->ip6e_nxt);
+ else
+ return NULL;
+ }
+}
+
+/*
+ * get next header offset. m will be retained.
+ */
+int
+ip6_nexthdr(m, off, proto, nxtp)
+ struct mbuf *m;
+ int off;
+ int proto;
+ int *nxtp;
+{
+ struct ip6_hdr ip6;
+ struct ip6_ext ip6e;
+ struct ip6_frag fh;
+
+ /* just in case */
+ if (m == NULL)
+ panic("ip6_nexthdr: m == NULL");
+ if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off)
+ return -1;
+
+ switch (proto) {
+ case IPPROTO_IPV6:
+ if (m->m_pkthdr.len < off + sizeof(ip6))
+ return -1;
+ m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6);
+ if (nxtp)
+ *nxtp = ip6.ip6_nxt;
+ off += sizeof(ip6);
+ return off;
+
+ case IPPROTO_FRAGMENT:
+ /*
+ * terminate parsing if it is not the first fragment,
+ * it does not make sense to parse through it.
+ */
+ if (m->m_pkthdr.len < off + sizeof(fh))
+ return -1;
+ m_copydata(m, off, sizeof(fh), (caddr_t)&fh);
+ if ((ntohs(fh.ip6f_offlg) & IP6F_OFF_MASK) != 0)
+ return -1;
+ if (nxtp)
+ *nxtp = fh.ip6f_nxt;
+ off += sizeof(struct ip6_frag);
+ return off;
+
+ case IPPROTO_AH:
+ if (m->m_pkthdr.len < off + sizeof(ip6e))
+ return -1;
+ m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+ if (nxtp)
+ *nxtp = ip6e.ip6e_nxt;
+ off += (ip6e.ip6e_len + 2) << 2;
+ return off;
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_DSTOPTS:
+ if (m->m_pkthdr.len < off + sizeof(ip6e))
+ return -1;
+ m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+ if (nxtp)
+ *nxtp = ip6e.ip6e_nxt;
+ off += (ip6e.ip6e_len + 1) << 3;
+ return off;
+
+ case IPPROTO_NONE:
+ case IPPROTO_ESP:
+ case IPPROTO_IPCOMP:
+ /* give up */
+ return -1;
+
+ default:
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * get offset for the last header in the chain. m will be kept untainted.
+ */
+int
+ip6_lasthdr(m, off, proto, nxtp)
+ struct mbuf *m;
+ int off;
+ int proto;
+ int *nxtp;
+{
+ int newoff;
+ int nxt;
+
+ if (!nxtp) {
+ nxt = -1;
+ nxtp = &nxt;
+ }
+ while (1) {
+ newoff = ip6_nexthdr(m, off, proto, nxtp);
+ if (newoff < 0)
+ return off;
+ else if (newoff < off)
+ return -1; /* invalid */
+ else if (newoff == off)
+ return newoff;
+
+ off = newoff;
+ proto = *nxtp;
+ }
+}
+
+struct ip6aux *
+ip6_addaux(m)
+ struct mbuf *m;
+{
+ struct m_tag *tag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL);
+ if (!tag) {
+ tag = m_tag_get(PACKET_TAG_IPV6_INPUT,
+ sizeof (struct ip6aux),
+ M_NOWAIT);
+ if (tag)
+ m_tag_prepend(m, tag);
+ }
+ if (tag)
+ bzero(tag+1, sizeof (struct ip6aux));
+ return tag ? (struct ip6aux*)(tag+1) : NULL;
+}
+
+struct ip6aux *
+ip6_findaux(m)
+ struct mbuf *m;
+{
+ struct m_tag *tag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL);
+ return tag ? (struct ip6aux*)(tag+1) : NULL;
+}
+
+void
+ip6_delaux(m)
+ struct mbuf *m;
+{
+ struct m_tag *tag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL);
+ if (tag)
+ m_tag_delete(m, tag);
+}
+
+/*
+ * System control for IP6
+ */
+
+u_char inet6ctlerrmap[PRC_NCMDS] = {
+ 0, 0, 0, 0,
+ 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
+ EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
+ EMSGSIZE, EHOSTUNREACH, 0, 0,
+ 0, 0, 0, 0,
+ ENOPROTOOPT
+};
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
new file mode 100644
index 0000000..f1257d3
--- /dev/null
+++ b/sys/netinet6/ip6_mroute.c
@@ -0,0 +1,1814 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_mroute.c,v 1.58 2001/12/18 02:36:31 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* BSDI ip_mroute.c,v 2.10 1996/11/14 00:29:52 jch Exp */
+
+/*
+ * IP multicast forwarding procedures
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ * Modified by Mark J. Steiglitz, Stanford, May, 1991
+ * Modified by Van Jacobson, LBL, January 1993
+ * Modified by Ajit Thyagarajan, PARC, August 1993
+ * Modified by Bill Fenenr, PARC, April 1994
+ *
+ * MROUTING Revision: 3.5.1.2 + PIM-SMv2 (pimd) Support
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/callout.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/signalvar.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/sx.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/raw_cb.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/pim6.h>
+#include <netinet6/pim6_var.h>
+
+#include <net/net_osdep.h>
+
+static MALLOC_DEFINE(M_MRTABLE, "mf6c", "multicast forwarding cache entry");
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+
+static int ip6_mdq __P((struct mbuf *, struct ifnet *, struct mf6c *));
+static void phyint_send __P((struct ip6_hdr *, struct mif6 *, struct mbuf *));
+
+static int set_pim6 __P((int *));
+static int socket_send __P((struct socket *, struct mbuf *,
+ struct sockaddr_in6 *));
+static int register_send __P((struct ip6_hdr *, struct mif6 *,
+ struct mbuf *));
+
+/*
+ * Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static,
+ * except for netstat or debugging purposes.
+ */
+struct socket *ip6_mrouter = NULL;
+int ip6_mrouter_ver = 0;
+int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */
+struct mrt6stat mrt6stat;
+
+#define NO_RTE_FOUND 0x1
+#define RTE_FOUND 0x2
+
+struct mf6c *mf6ctable[MF6CTBLSIZ];
+u_char n6expire[MF6CTBLSIZ];
+static struct mif6 mif6table[MAXMIFS];
+#ifdef MRT6DEBUG
+u_int mrt6debug = 0; /* debug level */
+#define DEBUG_MFC 0x02
+#define DEBUG_FORWARD 0x04
+#define DEBUG_EXPIRE 0x08
+#define DEBUG_XMIT 0x10
+#define DEBUG_REG 0x20
+#define DEBUG_PIM 0x40
+#endif
+
+static void expire_upcalls __P((void *));
+#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */
+#define UPCALL_EXPIRE 6 /* number of timeouts */
+
+#ifdef INET
+#ifdef MROUTING
+extern struct socket *ip_mrouter;
+#endif
+#endif
+
+/*
+ * 'Interfaces' associated with decapsulator (so we can tell
+ * packets that went through it from ones that get reflected
+ * by a broken gateway). These interfaces are never linked into
+ * the system ifnet list & no routes point to them. I.e., packets
+ * can't be sent this way. They only exist as a placeholder for
+ * multicast source verification.
+ */
+struct ifnet multicast_register_if;
+
+#define ENCAP_HOPS 64
+
+/*
+ * Private variables.
+ */
+static mifi_t nummifs = 0;
+static mifi_t reg_mif_num = (mifi_t)-1;
+
+static struct pim6stat pim6stat;
+static int pim6;
+
+/*
+ * Hash function for a source, group entry
+ */
+#define MF6CHASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \
+ (a).s6_addr32[2] ^ (a).s6_addr32[3] ^ \
+ (g).s6_addr32[0] ^ (g).s6_addr32[1] ^ \
+ (g).s6_addr32[2] ^ (g).s6_addr32[3])
+
+/*
+ * Find a route for a given origin IPv6 address and Multicast group address.
+ * Quality of service parameter to be added in the future!!!
+ */
+
+#define MF6CFIND(o, g, rt) do { \
+ struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \
+ rt = NULL; \
+ mrt6stat.mrt6s_mfc_lookups++; \
+ while (_rt) { \
+ if (IN6_ARE_ADDR_EQUAL(&_rt->mf6c_origin.sin6_addr, &(o)) && \
+ IN6_ARE_ADDR_EQUAL(&_rt->mf6c_mcastgrp.sin6_addr, &(g)) && \
+ (_rt->mf6c_stall == NULL)) { \
+ rt = _rt; \
+ break; \
+ } \
+ _rt = _rt->mf6c_next; \
+ } \
+ if (rt == NULL) { \
+ mrt6stat.mrt6s_mfc_misses++; \
+ } \
+} while (0)
+
+/*
+ * Macros to compute elapsed time efficiently
+ * Borrowed from Van Jacobson's scheduling code
+ */
+#define TV_DELTA(a, b, delta) do { \
+ int xxs; \
+ \
+ delta = (a).tv_usec - (b).tv_usec; \
+ if ((xxs = (a).tv_sec - (b).tv_sec)) { \
+ switch (xxs) { \
+ case 2: \
+ delta += 1000000; \
+ /* fall through */ \
+ case 1: \
+ delta += 1000000; \
+ break; \
+ default: \
+ delta += (1000000 * xxs); \
+ } \
+ } \
+} while (0)
+
+#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \
+ (a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec)
+
+#ifdef UPCALL_TIMING
+#define UPCALL_MAX 50
+u_long upcall_data[UPCALL_MAX + 1];
+static void collate();
+#endif /* UPCALL_TIMING */
+
+static int get_sg_cnt __P((struct sioc_sg_req6 *));
+static int get_mif6_cnt __P((struct sioc_mif_req6 *));
+static int ip6_mrouter_init __P((struct socket *, struct mbuf *, int));
+static int add_m6if __P((struct mif6ctl *));
+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.
+ */
+int
+ip6_mrouter_set(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error = 0;
+ struct mbuf *m;
+
+ if (so != ip6_mrouter && sopt->sopt_name != MRT6_INIT)
+ return (EACCES);
+
+ if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
+ return (error);
+ if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
+ return (error);
+
+ switch (sopt->sopt_name) {
+ case MRT6_INIT:
+#ifdef 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;
+ }
+
+ (void)m_freem(m);
+ return(error);
+}
+
+/*
+ * Handle MRT getsockopt commands
+ */
+int
+ip6_mrouter_get(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error = 0;
+
+ if (so != ip6_mrouter) return EACCES;
+
+ switch (sopt->sopt_name) {
+ case MRT6_PIM:
+ error = sooptcopyout(sopt, &pim6, sizeof(pim6));
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Handle ioctl commands to obtain information from the cache
+ */
+int
+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;
+}
+
+/*
+ * returns the packet, byte, rpf-failure count for the source group provided
+ */
+static int
+get_sg_cnt(req)
+ struct sioc_sg_req6 *req;
+{
+ struct mf6c *rt;
+ int s;
+
+ s = splnet();
+ MF6CFIND(req->src.sin6_addr, req->grp.sin6_addr, rt);
+ splx(s);
+ if (rt != NULL) {
+ req->pktcnt = rt->mf6c_pkt_cnt;
+ req->bytecnt = rt->mf6c_byte_cnt;
+ req->wrong_if = rt->mf6c_wrong_if;
+ } else
+ return(ESRCH);
+#if 0
+ req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff;
+#endif
+
+ return 0;
+}
+
+/*
+ * returns the input and output packet and byte counts on the mif provided
+ */
+static int
+get_mif6_cnt(req)
+ struct sioc_mif_req6 *req;
+{
+ mifi_t mifi = req->mifi;
+
+ if (mifi >= nummifs)
+ return EINVAL;
+
+ req->icount = mif6table[mifi].m6_pkt_in;
+ req->ocount = mif6table[mifi].m6_pkt_out;
+ req->ibytes = mif6table[mifi].m6_bytes_in;
+ req->obytes = mif6table[mifi].m6_bytes_out;
+
+ return 0;
+}
+
+static int
+set_pim6(i)
+ int *i;
+{
+ if ((*i != 1) && (*i != 0))
+ return EINVAL;
+
+ pim6 = *i;
+
+ return 0;
+}
+
+/*
+ * Enable multicast routing
+ */
+static int
+ip6_mrouter_init(so, m, cmd)
+ struct socket *so;
+ struct mbuf *m;
+ int cmd;
+{
+ int *v;
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG,
+ "ip6_mrouter_init: so_type = %d, pr_protocol = %d\n",
+ so->so_type, so->so_proto->pr_protocol);
+#endif
+
+ if (so->so_type != SOCK_RAW ||
+ so->so_proto->pr_protocol != IPPROTO_ICMPV6)
+ return EOPNOTSUPP;
+
+ if (!m || (m->m_len != sizeof(int *)))
+ return ENOPROTOOPT;
+
+ v = mtod(m, int *);
+ if (*v != 1)
+ return ENOPROTOOPT;
+
+ if (ip6_mrouter != NULL) return EADDRINUSE;
+
+ ip6_mrouter = so;
+ ip6_mrouter_ver = cmd;
+
+ bzero((caddr_t)mf6ctable, sizeof(mf6ctable));
+ bzero((caddr_t)n6expire, sizeof(n6expire));
+
+ pim6 = 0;/* used for stubbing out/in pim stuff */
+
+ callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT,
+ expire_upcalls, NULL);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG, "ip6_mrouter_init\n");
+#endif
+
+ return 0;
+}
+
+/*
+ * Disable multicast routing
+ */
+int
+ip6_mrouter_done()
+{
+ mifi_t mifi;
+ int i;
+ struct ifnet *ifp;
+ struct in6_ifreq ifr;
+ struct mf6c *rt;
+ struct rtdetq *rte;
+ int s;
+
+ s = splnet();
+
+ /*
+ * For each phyint in use, disable promiscuous reception of all IPv6
+ * multicasts.
+ */
+#ifdef INET
+#ifdef MROUTING
+ /*
+ * If there is still IPv4 multicast routing daemon,
+ * we remain interfaces to receive all muliticasted packets.
+ * XXX: there may be an interface in which the IPv4 multicast
+ * daemon is not interested...
+ */
+ if (!ip_mrouter)
+#endif
+#endif
+ {
+ for (mifi = 0; mifi < nummifs; mifi++) {
+ if (mif6table[mifi].m6_ifp &&
+ !(mif6table[mifi].m6_flags & MIFF_REGISTER)) {
+ ifr.ifr_addr.sin6_family = AF_INET6;
+ ifr.ifr_addr.sin6_addr= in6addr_any;
+ ifp = mif6table[mifi].m6_ifp;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+ }
+ }
+ }
+#ifdef notyet
+ bzero((caddr_t)qtable, sizeof(qtable));
+ bzero((caddr_t)tbftable, sizeof(tbftable));
+#endif
+ bzero((caddr_t)mif6table, sizeof(mif6table));
+ nummifs = 0;
+
+ pim6 = 0; /* used to stub out/in pim specific code */
+
+ callout_stop(&expire_upcalls_ch);
+
+ /*
+ * Free all multicast forwarding cache entries.
+ */
+ for (i = 0; i < MF6CTBLSIZ; i++) {
+ rt = mf6ctable[i];
+ while (rt) {
+ struct mf6c *frt;
+
+ for (rte = rt->mf6c_stall; rte != NULL; ) {
+ struct rtdetq *n = rte->next;
+
+ m_free(rte->m);
+ free(rte, M_MRTABLE);
+ rte = n;
+ }
+ frt = rt;
+ rt = rt->mf6c_next;
+ free(frt, M_MRTABLE);
+ }
+ }
+
+ bzero((caddr_t)mf6ctable, sizeof(mf6ctable));
+
+ /*
+ * Reset de-encapsulation cache
+ */
+ reg_mif_num = -1;
+
+ ip6_mrouter = NULL;
+ ip6_mrouter_ver = 0;
+
+ splx(s);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG, "ip6_mrouter_done\n");
+#endif
+
+ return 0;
+}
+
+static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 };
+
+/*
+ * Add a mif to the mif table
+ */
+static int
+add_m6if(mifcp)
+ struct mif6ctl *mifcp;
+{
+ struct mif6 *mifp;
+ struct ifnet *ifp;
+ int error, s;
+#ifdef notyet
+ struct tbf *m_tbf = tbftable + mifcp->mif6c_mifi;
+#endif
+
+ if (mifcp->mif6c_mifi >= MAXMIFS)
+ return EINVAL;
+ mifp = mif6table + mifcp->mif6c_mifi;
+ if (mifp->m6_ifp)
+ return EADDRINUSE; /* XXX: is it appropriate? */
+ if (mifcp->mif6c_pifi == 0 || mifcp->mif6c_pifi > if_index)
+ return ENXIO;
+ ifp = ifnet_byindex(mifcp->mif6c_pifi);
+
+ if (mifcp->mif6c_flags & MIFF_REGISTER) {
+ if (reg_mif_num == (mifi_t)-1) {
+ multicast_register_if.if_name = "register_mif";
+ multicast_register_if.if_flags |= IFF_LOOPBACK;
+ multicast_register_if.if_index = mifcp->mif6c_mifi;
+ reg_mif_num = mifcp->mif6c_mifi;
+ }
+
+ ifp = &multicast_register_if;
+
+ } /* if REGISTER */
+ else {
+ /* Make sure the interface supports multicast */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0)
+ return EOPNOTSUPP;
+
+ s = splnet();
+ error = if_allmulti(ifp, 1);
+ splx(s);
+ if (error)
+ return error;
+ }
+
+ s = splnet();
+ mifp->m6_flags = mifcp->mif6c_flags;
+ mifp->m6_ifp = ifp;
+#ifdef notyet
+ /* scaling up here allows division by 1024 in critical code */
+ mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000;
+#endif
+ /* initialize per mif pkt counters */
+ mifp->m6_pkt_in = 0;
+ mifp->m6_pkt_out = 0;
+ mifp->m6_bytes_in = 0;
+ mifp->m6_bytes_out = 0;
+ splx(s);
+
+ /* Adjust nummifs up if the mifi is higher than nummifs */
+ if (nummifs <= mifcp->mif6c_mifi)
+ nummifs = mifcp->mif6c_mifi + 1;
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG,
+ "add_mif #%d, phyint %s%d\n",
+ mifcp->mif6c_mifi,
+ ifp->if_name, ifp->if_unit);
+#endif
+
+ return 0;
+}
+
+/*
+ * Delete a mif from the mif table
+ */
+static int
+del_m6if(mifip)
+ mifi_t *mifip;
+{
+ struct mif6 *mifp = mif6table + *mifip;
+ mifi_t mifi;
+ struct ifnet *ifp;
+ int s;
+
+ if (*mifip >= nummifs)
+ return EINVAL;
+ if (mifp->m6_ifp == NULL)
+ return EINVAL;
+
+ s = splnet();
+
+ if (!(mifp->m6_flags & MIFF_REGISTER)) {
+ /*
+ * XXX: what if there is yet IPv4 multicast daemon
+ * using the interface?
+ */
+ ifp = mifp->m6_ifp;
+
+ if_allmulti(ifp, 0);
+ }
+
+#ifdef notyet
+ bzero((caddr_t)qtable[*mifip], sizeof(qtable[*mifip]));
+ bzero((caddr_t)mifp->m6_tbf, sizeof(*(mifp->m6_tbf)));
+#endif
+ bzero((caddr_t)mifp, sizeof (*mifp));
+
+ /* Adjust nummifs down */
+ for (mifi = nummifs; mifi > 0; mifi--)
+ if (mif6table[mifi - 1].m6_ifp)
+ break;
+ nummifs = mifi;
+
+ splx(s);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG, "del_m6if %d, nummifs %d\n", *mifip, nummifs);
+#endif
+
+ return 0;
+}
+
+/*
+ * Add an mfc entry
+ */
+static int
+add_m6fc(mfccp)
+ struct mf6cctl *mfccp;
+{
+ struct mf6c *rt;
+ u_long hash;
+ struct rtdetq *rte;
+ u_short nstl;
+ int s;
+
+ MF6CFIND(mfccp->mf6cc_origin.sin6_addr,
+ mfccp->mf6cc_mcastgrp.sin6_addr, rt);
+
+ /* If an entry already exists, just update the fields */
+ if (rt) {
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_MFC)
+ log(LOG_DEBUG,
+ "add_m6fc no upcall h %d o %s g %s p %x\n",
+ ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr),
+ ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr),
+ mfccp->mf6cc_parent);
+#endif
+
+ s = splnet();
+ rt->mf6c_parent = mfccp->mf6cc_parent;
+ rt->mf6c_ifset = mfccp->mf6cc_ifset;
+ splx(s);
+ return 0;
+ }
+
+ /*
+ * Find the entry for which the upcall was made and update
+ */
+ s = splnet();
+ hash = MF6CHASH(mfccp->mf6cc_origin.sin6_addr,
+ mfccp->mf6cc_mcastgrp.sin6_addr);
+ for (rt = mf6ctable[hash], nstl = 0; rt; rt = rt->mf6c_next) {
+ if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr,
+ &mfccp->mf6cc_origin.sin6_addr) &&
+ IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr,
+ &mfccp->mf6cc_mcastgrp.sin6_addr) &&
+ (rt->mf6c_stall != NULL)) {
+
+ if (nstl++)
+ log(LOG_ERR,
+ "add_m6fc: %s o %s g %s p %x dbx %p\n",
+ "multiple kernel entries",
+ ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr),
+ ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr),
+ mfccp->mf6cc_parent, rt->mf6c_stall);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_MFC)
+ log(LOG_DEBUG,
+ "add_m6fc o %s g %s p %x dbg %x\n",
+ ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr),
+ ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr),
+ mfccp->mf6cc_parent, rt->mf6c_stall);
+#endif
+
+ rt->mf6c_origin = mfccp->mf6cc_origin;
+ rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp;
+ rt->mf6c_parent = mfccp->mf6cc_parent;
+ rt->mf6c_ifset = mfccp->mf6cc_ifset;
+ /* initialize pkt counters per src-grp */
+ rt->mf6c_pkt_cnt = 0;
+ rt->mf6c_byte_cnt = 0;
+ rt->mf6c_wrong_if = 0;
+
+ rt->mf6c_expire = 0; /* Don't clean this guy up */
+ n6expire[hash]--;
+
+ /* free packets Qed at the end of this entry */
+ for (rte = rt->mf6c_stall; rte != NULL; ) {
+ struct rtdetq *n = rte->next;
+ ip6_mdq(rte->m, rte->ifp, rt);
+ m_freem(rte->m);
+#ifdef UPCALL_TIMING
+ collate(&(rte->t));
+#endif /* UPCALL_TIMING */
+ free(rte, M_MRTABLE);
+ rte = n;
+ }
+ rt->mf6c_stall = NULL;
+ }
+ }
+
+ /*
+ * It is possible that an entry is being inserted without an upcall
+ */
+ if (nstl == 0) {
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_MFC)
+ log(LOG_DEBUG,"add_mfc no upcall h %d o %s g %s p %x\n",
+ hash,
+ ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr),
+ ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr),
+ mfccp->mf6cc_parent);
+#endif
+
+ for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) {
+
+ if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr,
+ &mfccp->mf6cc_origin.sin6_addr)&&
+ IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr,
+ &mfccp->mf6cc_mcastgrp.sin6_addr)) {
+
+ rt->mf6c_origin = mfccp->mf6cc_origin;
+ rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp;
+ rt->mf6c_parent = mfccp->mf6cc_parent;
+ rt->mf6c_ifset = mfccp->mf6cc_ifset;
+ /* initialize pkt counters per src-grp */
+ rt->mf6c_pkt_cnt = 0;
+ rt->mf6c_byte_cnt = 0;
+ rt->mf6c_wrong_if = 0;
+
+ if (rt->mf6c_expire)
+ n6expire[hash]--;
+ rt->mf6c_expire = 0;
+ }
+ }
+ if (rt == NULL) {
+ /* no upcall, so make a new entry */
+ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE,
+ M_NOWAIT);
+ if (rt == NULL) {
+ splx(s);
+ return ENOBUFS;
+ }
+
+ /* insert new entry at head of hash chain */
+ rt->mf6c_origin = mfccp->mf6cc_origin;
+ rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp;
+ rt->mf6c_parent = mfccp->mf6cc_parent;
+ rt->mf6c_ifset = mfccp->mf6cc_ifset;
+ /* initialize pkt counters per src-grp */
+ rt->mf6c_pkt_cnt = 0;
+ rt->mf6c_byte_cnt = 0;
+ rt->mf6c_wrong_if = 0;
+ rt->mf6c_expire = 0;
+ rt->mf6c_stall = NULL;
+
+ /* link into table */
+ rt->mf6c_next = mf6ctable[hash];
+ mf6ctable[hash] = rt;
+ }
+ }
+ splx(s);
+ return 0;
+}
+
+#ifdef UPCALL_TIMING
+/*
+ * collect delay statistics on the upcalls
+ */
+static void
+collate(t)
+ struct timeval *t;
+{
+ u_long d;
+ struct timeval tp;
+ u_long delta;
+
+ GET_TIME(tp);
+
+ if (TV_LT(*t, tp))
+ {
+ TV_DELTA(tp, *t, delta);
+
+ d = delta >> 10;
+ if (d > UPCALL_MAX)
+ d = UPCALL_MAX;
+
+ ++upcall_data[d];
+ }
+}
+#endif /* UPCALL_TIMING */
+
+/*
+ * Delete an mfc entry
+ */
+static int
+del_m6fc(mfccp)
+ struct mf6cctl *mfccp;
+{
+ struct sockaddr_in6 origin;
+ struct sockaddr_in6 mcastgrp;
+ struct mf6c *rt;
+ struct mf6c **nptr;
+ u_long hash;
+ int s;
+
+ origin = mfccp->mf6cc_origin;
+ mcastgrp = mfccp->mf6cc_mcastgrp;
+ hash = MF6CHASH(origin.sin6_addr, mcastgrp.sin6_addr);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_MFC)
+ log(LOG_DEBUG,"del_m6fc orig %s mcastgrp %s\n",
+ ip6_sprintf(&origin.sin6_addr),
+ ip6_sprintf(&mcastgrp.sin6_addr));
+#endif
+
+ s = splnet();
+
+ nptr = &mf6ctable[hash];
+ while ((rt = *nptr) != NULL) {
+ if (IN6_ARE_ADDR_EQUAL(&origin.sin6_addr,
+ &rt->mf6c_origin.sin6_addr) &&
+ IN6_ARE_ADDR_EQUAL(&mcastgrp.sin6_addr,
+ &rt->mf6c_mcastgrp.sin6_addr) &&
+ rt->mf6c_stall == NULL)
+ break;
+
+ nptr = &rt->mf6c_next;
+ }
+ if (rt == NULL) {
+ splx(s);
+ return EADDRNOTAVAIL;
+ }
+
+ *nptr = rt->mf6c_next;
+ free(rt, M_MRTABLE);
+
+ splx(s);
+
+ return 0;
+}
+
+static int
+socket_send(s, mm, src)
+ struct socket *s;
+ struct mbuf *mm;
+ struct sockaddr_in6 *src;
+{
+ if (s) {
+ if (sbappendaddr(&s->so_rcv,
+ (struct sockaddr *)src,
+ mm, (struct mbuf *)0) != 0) {
+ sorwakeup(s);
+ return 0;
+ }
+ }
+ m_freem(mm);
+ return -1;
+}
+
+/*
+ * IPv6 multicast forwarding function. This function assumes that the packet
+ * pointed to by "ip6" has arrived on (or is about to be sent to) the interface
+ * pointed to by "ifp", and the packet is to be relayed to other networks
+ * that have members of the packet's destination IPv6 multicast group.
+ *
+ * The packet is returned unscathed to the caller, unless it is
+ * erroneous, in which case a non-zero return value tells the caller to
+ * discard it.
+ */
+
+int
+ip6_mforward(ip6, ifp, m)
+ struct ip6_hdr *ip6;
+ struct ifnet *ifp;
+ struct mbuf *m;
+{
+ struct mf6c *rt;
+ struct mif6 *mifp;
+ struct mbuf *mm;
+ int s;
+ mifi_t mifi;
+
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_FORWARD)
+ log(LOG_DEBUG, "ip6_mforward: src %s, dst %s, ifindex %d\n",
+ ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst),
+ ifp->if_index);
+#endif
+
+ /*
+ * Don't forward a packet with Hop limit of zero or one,
+ * or a packet destined to a local-only group.
+ */
+ if (ip6->ip6_hlim <= 1 || IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst))
+ return 0;
+ ip6->ip6_hlim--;
+
+ /*
+ * Source address check: do not forward packets with unspecified
+ * source. It was discussed in July 2000, on ipngwg mailing list.
+ * This is rather more serious than unicast cases, because some
+ * MLD packets can be sent with the unspecified source address
+ * (although such packets must normally set 1 to the hop limit field).
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
+ ip6stat.ip6s_cantforward++;
+ if (ip6_log_time + ip6_log_interval < time_second) {
+ ip6_log_time = time_second;
+ log(LOG_DEBUG,
+ "cannot forward "
+ "from %s to %s nxt %d received on %s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ ip6->ip6_nxt,
+ if_name(m->m_pkthdr.rcvif));
+ }
+ return 0;
+ }
+
+ /*
+ * Determine forwarding mifs from the forwarding cache table
+ */
+ s = splnet();
+ MF6CFIND(ip6->ip6_src, ip6->ip6_dst, rt);
+
+ /* Entry exists, so forward if necessary */
+ if (rt) {
+ splx(s);
+ return (ip6_mdq(m, ifp, rt));
+ } else {
+ /*
+ * If we don't have a route for packet's origin,
+ * Make a copy of the packet &
+ * send message to routing daemon
+ */
+
+ struct mbuf *mb0;
+ struct rtdetq *rte;
+ u_long hash;
+/* int i, npkts;*/
+#ifdef UPCALL_TIMING
+ struct timeval tp;
+
+ GET_TIME(tp);
+#endif /* UPCALL_TIMING */
+
+ mrt6stat.mrt6s_no_route++;
+#ifdef MRT6DEBUG
+ if (mrt6debug & (DEBUG_FORWARD | DEBUG_MFC))
+ log(LOG_DEBUG, "ip6_mforward: no rte s %s g %s\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst));
+#endif
+
+ /*
+ * Allocate mbufs early so that we don't do extra work if we
+ * are just going to fail anyway.
+ */
+ rte = (struct rtdetq *)malloc(sizeof(*rte), M_MRTABLE,
+ M_NOWAIT);
+ if (rte == NULL) {
+ splx(s);
+ return ENOBUFS;
+ }
+ mb0 = m_copy(m, 0, M_COPYALL);
+ /*
+ * Pullup packet header if needed before storing it,
+ * as other references may modify it in the meantime.
+ */
+ if (mb0 &&
+ (M_HASCL(mb0) || mb0->m_len < sizeof(struct ip6_hdr)))
+ mb0 = m_pullup(mb0, sizeof(struct ip6_hdr));
+ if (mb0 == NULL) {
+ free(rte, M_MRTABLE);
+ splx(s);
+ return ENOBUFS;
+ }
+
+ /* is there an upcall waiting for this packet? */
+ hash = MF6CHASH(ip6->ip6_src, ip6->ip6_dst);
+ for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) {
+ if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src,
+ &rt->mf6c_origin.sin6_addr) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &rt->mf6c_mcastgrp.sin6_addr) &&
+ (rt->mf6c_stall != NULL))
+ break;
+ }
+
+ if (rt == NULL) {
+ struct mrt6msg *im;
+#ifdef MRT6_OINIT
+ struct omrt6msg *oim;
+#endif
+
+ /* no upcall, so make a new entry */
+ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE,
+ M_NOWAIT);
+ if (rt == NULL) {
+ free(rte, M_MRTABLE);
+ m_freem(mb0);
+ splx(s);
+ return ENOBUFS;
+ }
+ /*
+ * Make a copy of the header to send to the user
+ * level process
+ */
+ mm = m_copy(mb0, 0, sizeof(struct ip6_hdr));
+
+ if (mm == NULL) {
+ free(rte, M_MRTABLE);
+ m_freem(mb0);
+ free(rt, M_MRTABLE);
+ splx(s);
+ return ENOBUFS;
+ }
+
+ /*
+ * Send message to routing daemon
+ */
+ sin6.sin6_addr = ip6->ip6_src;
+
+ im = NULL;
+#ifdef MRT6_OINIT
+ oim = NULL;
+#endif
+ switch (ip6_mrouter_ver) {
+#ifdef MRT6_OINIT
+ case MRT6_OINIT:
+ oim = mtod(mm, struct omrt6msg *);
+ oim->im6_msgtype = MRT6MSG_NOCACHE;
+ oim->im6_mbz = 0;
+ break;
+#endif
+ case MRT6_INIT:
+ im = mtod(mm, struct mrt6msg *);
+ im->im6_msgtype = MRT6MSG_NOCACHE;
+ im->im6_mbz = 0;
+ break;
+ default:
+ free(rte, M_MRTABLE);
+ m_freem(mb0);
+ free(rt, M_MRTABLE);
+ splx(s);
+ return EINVAL;
+ }
+
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_FORWARD)
+ log(LOG_DEBUG,
+ "getting the iif info in the kernel\n");
+#endif
+
+ for (mifp = mif6table, mifi = 0;
+ mifi < nummifs && mifp->m6_ifp != ifp;
+ mifp++, mifi++)
+ ;
+
+ switch (ip6_mrouter_ver) {
+#ifdef MRT6_OINIT
+ case MRT6_OINIT:
+ oim->im6_mif = mifi;
+ break;
+#endif
+ case MRT6_INIT:
+ im->im6_mif = mifi;
+ break;
+ }
+
+ if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
+ log(LOG_WARNING, "ip6_mforward: ip6_mrouter "
+ "socket queue full\n");
+ mrt6stat.mrt6s_upq_sockfull++;
+ free(rte, M_MRTABLE);
+ m_freem(mb0);
+ free(rt, M_MRTABLE);
+ splx(s);
+ return ENOBUFS;
+ }
+
+ mrt6stat.mrt6s_upcalls++;
+
+ /* insert new entry at head of hash chain */
+ bzero(rt, sizeof(*rt));
+ rt->mf6c_origin.sin6_family = AF_INET6;
+ rt->mf6c_origin.sin6_len = sizeof(struct sockaddr_in6);
+ rt->mf6c_origin.sin6_addr = ip6->ip6_src;
+ rt->mf6c_mcastgrp.sin6_family = AF_INET6;
+ rt->mf6c_mcastgrp.sin6_len = sizeof(struct sockaddr_in6);
+ rt->mf6c_mcastgrp.sin6_addr = ip6->ip6_dst;
+ rt->mf6c_expire = UPCALL_EXPIRE;
+ n6expire[hash]++;
+ rt->mf6c_parent = MF6C_INCOMPLETE_PARENT;
+
+ /* link into table */
+ rt->mf6c_next = mf6ctable[hash];
+ mf6ctable[hash] = rt;
+ /* Add this entry to the end of the queue */
+ rt->mf6c_stall = rte;
+ } else {
+ /* determine if q has overflowed */
+ struct rtdetq **p;
+ int npkts = 0;
+
+ for (p = &rt->mf6c_stall; *p != NULL; p = &(*p)->next)
+ if (++npkts > MAX_UPQ6) {
+ mrt6stat.mrt6s_upq_ovflw++;
+ free(rte, M_MRTABLE);
+ m_freem(mb0);
+ splx(s);
+ return 0;
+ }
+
+ /* Add this entry to the end of the queue */
+ *p = rte;
+ }
+
+ rte->next = NULL;
+ rte->m = mb0;
+ rte->ifp = ifp;
+#ifdef UPCALL_TIMING
+ rte->t = tp;
+#endif /* UPCALL_TIMING */
+
+ splx(s);
+
+ return 0;
+ }
+}
+
+/*
+ * Clean up cache entries if upcalls are not serviced
+ * Call from the Slow Timeout mechanism, every half second.
+ */
+static void
+expire_upcalls(unused)
+ void *unused;
+{
+ struct rtdetq *rte;
+ struct mf6c *mfc, **nptr;
+ int i;
+ int s;
+
+ s = splnet();
+ for (i = 0; i < MF6CTBLSIZ; i++) {
+ if (n6expire[i] == 0)
+ continue;
+ nptr = &mf6ctable[i];
+ while ((mfc = *nptr) != NULL) {
+ rte = mfc->mf6c_stall;
+ /*
+ * Skip real cache entries
+ * Make sure it wasn't marked to not expire (shouldn't happen)
+ * If it expires now
+ */
+ if (rte != NULL &&
+ mfc->mf6c_expire != 0 &&
+ --mfc->mf6c_expire == 0) {
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_EXPIRE)
+ log(LOG_DEBUG, "expire_upcalls: expiring (%s %s)\n",
+ ip6_sprintf(&mfc->mf6c_origin.sin6_addr),
+ ip6_sprintf(&mfc->mf6c_mcastgrp.sin6_addr));
+#endif
+ /*
+ * drop all the packets
+ * free the mbuf with the pkt, if, timing info
+ */
+ do {
+ struct rtdetq *n = rte->next;
+ m_freem(rte->m);
+ free(rte, M_MRTABLE);
+ rte = n;
+ } while (rte != NULL);
+ mrt6stat.mrt6s_cache_cleanups++;
+ n6expire[i]--;
+
+ *nptr = mfc->mf6c_next;
+ free(mfc, M_MRTABLE);
+ } else {
+ nptr = &mfc->mf6c_next;
+ }
+ }
+ }
+ splx(s);
+ callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT,
+ expire_upcalls, NULL);
+}
+
+/*
+ * Packet forwarding routine once entry in the cache is made
+ */
+static int
+ip6_mdq(m, ifp, rt)
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct mf6c *rt;
+{
+ 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
+ * input, they shouldn't get counted on output, so statistics keeping is
+ * separate.
+ */
+
+#define MC6_SEND(ip6, mifp, m) do { \
+ if ((mifp)->m6_flags & MIFF_REGISTER) \
+ register_send((ip6), (mifp), (m)); \
+ else \
+ phyint_send((ip6), (mifp), (m)); \
+} while (0)
+
+ /*
+ * Don't forward if it didn't arrive from the parent mif
+ * for its origin.
+ */
+ mifi = rt->mf6c_parent;
+ if ((mifi >= nummifs) || (mif6table[mifi].m6_ifp != ifp)) {
+ /* came in the wrong interface */
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_FORWARD)
+ log(LOG_DEBUG,
+ "wrong if: ifid %d mifi %d mififid %x\n",
+ ifp->if_index, mifi,
+ mif6table[mifi].m6_ifp->if_index);
+#endif
+ mrt6stat.mrt6s_wrong_if++;
+ rt->mf6c_wrong_if++;
+ /*
+ * If we are doing PIM processing, and we are forwarding
+ * packets on this interface, send a message to the
+ * routing daemon.
+ */
+ /* have to make sure this is a valid mif */
+ if (mifi < nummifs && mif6table[mifi].m6_ifp)
+ if (pim6 && (m->m_flags & M_LOOP) == 0) {
+ /*
+ * Check the M_LOOP flag to avoid an
+ * unnecessary PIM assert.
+ * XXX: M_LOOP is an ad-hoc hack...
+ */
+ static struct sockaddr_in6 sin6 =
+ { sizeof(sin6), AF_INET6 };
+
+ struct mbuf *mm;
+ struct mrt6msg *im;
+#ifdef MRT6_OINIT
+ struct omrt6msg *oim;
+#endif
+
+ mm = m_copy(m, 0, sizeof(struct ip6_hdr));
+ if (mm &&
+ (M_HASCL(mm) ||
+ mm->m_len < sizeof(struct ip6_hdr)))
+ mm = m_pullup(mm, sizeof(struct ip6_hdr));
+ if (mm == NULL)
+ return ENOBUFS;
+
+#ifdef MRT6_OINIT
+ oim = NULL;
+#endif
+ im = NULL;
+ switch (ip6_mrouter_ver) {
+#ifdef MRT6_OINIT
+ case MRT6_OINIT:
+ oim = mtod(mm, struct omrt6msg *);
+ oim->im6_msgtype = MRT6MSG_WRONGMIF;
+ oim->im6_mbz = 0;
+ break;
+#endif
+ case MRT6_INIT:
+ im = mtod(mm, struct mrt6msg *);
+ im->im6_msgtype = MRT6MSG_WRONGMIF;
+ im->im6_mbz = 0;
+ break;
+ default:
+ m_freem(mm);
+ return EINVAL;
+ }
+
+ for (mifp = mif6table, iif = 0;
+ iif < nummifs && mifp &&
+ mifp->m6_ifp != ifp;
+ mifp++, iif++)
+ ;
+
+ switch (ip6_mrouter_ver) {
+#ifdef MRT6_OINIT
+ case MRT6_OINIT:
+ oim->im6_mif = iif;
+ sin6.sin6_addr = oim->im6_src;
+ break;
+#endif
+ case MRT6_INIT:
+ im->im6_mif = iif;
+ sin6.sin6_addr = im->im6_src;
+ break;
+ }
+
+ mrt6stat.mrt6s_upcalls++;
+
+ if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n");
+#endif
+ ++mrt6stat.mrt6s_upq_sockfull;
+ return ENOBUFS;
+ } /* if socket Q full */
+ } /* if PIM */
+ return 0;
+ } /* if wrong iif */
+
+ /* If I sourced this packet, it counts as output, else it was input. */
+ if (m->m_pkthdr.rcvif == NULL) {
+ /* XXX: is rcvif really NULL when output?? */
+ mif6table[mifi].m6_pkt_out++;
+ mif6table[mifi].m6_bytes_out += plen;
+ } else {
+ mif6table[mifi].m6_pkt_in++;
+ mif6table[mifi].m6_bytes_in += plen;
+ }
+ rt->mf6c_pkt_cnt++;
+ rt->mf6c_byte_cnt += plen;
+
+ /*
+ * For each mif, forward a copy of the packet if there are group
+ * members downstream on the interface.
+ */
+ for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++)
+ if (IF_ISSET(mifi, &rt->mf6c_ifset)) {
+ /*
+ * check if the outgoing packet is going to break
+ * a scope boundary.
+ * XXX For packets through PIM register tunnel
+ * interface, we believe a routing daemon.
+ */
+ if ((mif6table[rt->mf6c_parent].m6_flags &
+ MIFF_REGISTER) == 0 &&
+ (mif6table[mifi].m6_flags & MIFF_REGISTER) == 0 &&
+ (in6_addr2scopeid(ifp, &ip6->ip6_dst) !=
+ in6_addr2scopeid(mif6table[mifi].m6_ifp,
+ &ip6->ip6_dst) ||
+ in6_addr2scopeid(ifp, &ip6->ip6_src) !=
+ in6_addr2scopeid(mif6table[mifi].m6_ifp,
+ &ip6->ip6_src))) {
+ ip6stat.ip6s_badscope++;
+ continue;
+ }
+
+ mifp->m6_pkt_out++;
+ mifp->m6_bytes_out += plen;
+ MC6_SEND(ip6, mifp, m);
+ }
+ return 0;
+}
+
+static void
+phyint_send(ip6, mifp, m)
+ struct ip6_hdr *ip6;
+ struct mif6 *mifp;
+ struct mbuf *m;
+{
+ struct mbuf *mb_copy;
+ struct ifnet *ifp = mifp->m6_ifp;
+ int error = 0;
+ 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
+ * the IPv6 header is actually copied, not just referenced,
+ * so that ip6_output() only scribbles on the copy.
+ */
+ mb_copy = m_copy(m, 0, M_COPYALL);
+ 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) {
+ splx(s);
+ return;
+ }
+ /* set MCAST flag to the outgoing packet */
+ mb_copy->m_flags |= M_MCAST;
+
+ /*
+ * If we sourced the packet, call ip6_output since we may devide
+ * the packet into fragments when the packet is too big for the
+ * outgoing interface.
+ * Otherwise, we can simply send the packet to the interface
+ * sending queue.
+ */
+ if (m->m_pkthdr.rcvif == NULL) {
+ struct ip6_moptions im6o;
+
+ im6o.im6o_multicast_ifp = ifp;
+ /* 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, &ro,
+ IPV6_FORWARDING, &im6o, NULL, NULL);
+
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_XMIT)
+ log(LOG_DEBUG, "phyint_send on mif %d err %d\n",
+ mifp - mif6table, error);
+#endif
+ splx(s);
+ return;
+ }
+
+ /*
+ * 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) {
+ 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) {
+ 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 *)&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 {
+#ifdef MULTICAST_PMTUD
+ icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu);
+#else
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_XMIT)
+ log(LOG_DEBUG,
+ "phyint_send: packet too big on %s o %s g %s"
+ " size %d(discarded)\n",
+ 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 */
+#endif
+ }
+
+ splx(s);
+}
+
+static int
+register_send(ip6, mif, m)
+ struct ip6_hdr *ip6;
+ struct mif6 *mif;
+ struct mbuf *m;
+{
+ struct mbuf *mm;
+ int i, len = m->m_pkthdr.len;
+ static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 };
+ struct mrt6msg *im6;
+
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_DEBUG, "** IPv6 register_send **\n src %s dst %s\n",
+ ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst));
+#endif
+ ++pim6stat.pim6s_snd_registers;
+
+ /* Make a copy of the packet to send to the user level process */
+ 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);
+
+ if ((mm->m_next = m_copy(m, 0, M_COPYALL)) == NULL) {
+ m_freem(mm);
+ return ENOBUFS;
+ }
+ i = MHLEN - M_LEADINGSPACE(mm);
+ if (i > len)
+ i = len;
+ mm = m_pullup(mm, i);
+ if (mm == NULL)
+ return ENOBUFS;
+/* TODO: check it! */
+ mm->m_pkthdr.len = len + sizeof(struct ip6_hdr);
+
+ /*
+ * Send message to routing daemon
+ */
+ sin6.sin6_addr = ip6->ip6_src;
+
+ im6 = mtod(mm, struct mrt6msg *);
+ im6->im6_msgtype = MRT6MSG_WHOLEPKT;
+ im6->im6_mbz = 0;
+
+ im6->im6_mif = mif - mif6table;
+
+ /* iif info is not given for reg. encap.n */
+ mrt6stat.mrt6s_upcalls++;
+
+ if (socket_send(ip6_mrouter, mm, &sin6) < 0) {
+#ifdef MRT6DEBUG
+ if (mrt6debug)
+ log(LOG_WARNING,
+ "register_send: ip6_mrouter socket queue full\n");
+#endif
+ ++mrt6stat.mrt6s_upq_sockfull;
+ return ENOBUFS;
+ }
+ return 0;
+}
+
+/*
+ * PIM sparse mode hook
+ * Receives the pim control messages, and passes them up to the listening
+ * socket, using rip6_input.
+ * The only message processed is the REGISTER pim message; the pim header
+ * is stripped off, and the inner packet is passed to register_mforward.
+ */
+int
+pim6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct pim *pim; /* pointer to a pim struct */
+ struct ip6_hdr *ip6;
+ int pimlen;
+ struct mbuf *m = *mp;
+ int minlen;
+ int off = *offp;
+
+ ++pim6stat.pim6s_rcv_total;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ pimlen = m->m_pkthdr.len - *offp;
+
+ /*
+ * Validate lengths
+ */
+ if (pimlen < PIM_MINLEN) {
+ ++pim6stat.pim6s_rcv_tooshort;
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM)
+ log(LOG_DEBUG,"pim6_input: PIM packet too short\n");
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ /*
+ * if the packet is at least as big as a REGISTER, go ahead
+ * and grab the PIM REGISTER header size, to avoid another
+ * possible m_pullup() later.
+ *
+ * PIM_MINLEN == pimhdr + u_int32 == 8
+ * PIM6_REG_MINLEN == pimhdr + reghdr + eip6hdr == 4 + 4 + 40
+ */
+ minlen = (pimlen >= PIM6_REG_MINLEN) ? PIM6_REG_MINLEN : PIM_MINLEN;
+
+ /*
+ * Make sure that the IP6 and PIM headers in contiguous memory, and
+ * possibly the PIM REGISTER header
+ */
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, minlen, IPPROTO_DONE);
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* adjust mbuf to point to the PIM header */
+ pim = (struct pim *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(pim, struct pim *, m, off, minlen);
+ if (pim == NULL) {
+ pim6stat.pim6s_rcv_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+
+#define PIM6_CHECKSUM
+#ifdef PIM6_CHECKSUM
+ {
+ int cksumlen;
+
+ /*
+ * Validate checksum.
+ * If PIM REGISTER, exclude the data packet
+ */
+ if (pim->pim_type == PIM_REGISTER)
+ cksumlen = PIM_MINLEN;
+ else
+ cksumlen = pimlen;
+
+ if (in6_cksum(m, IPPROTO_PIM, off, cksumlen)) {
+ ++pim6stat.pim6s_rcv_badsum;
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM)
+ log(LOG_DEBUG,
+ "pim6_input: invalid checksum\n");
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+ }
+#endif /* PIM_CHECKSUM */
+
+ /* PIM version check */
+ if (pim->pim_ver != PIM_VERSION) {
+ ++pim6stat.pim6s_rcv_badversion;
+#ifdef MRT6DEBUG
+ log(LOG_ERR,
+ "pim6_input: incorrect version %d, expecting %d\n",
+ pim->pim_ver, PIM_VERSION);
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ if (pim->pim_type == PIM_REGISTER) {
+ /*
+ * since this is a REGISTER, we'll make a copy of the register
+ * headers ip6+pim+u_int32_t+encap_ip6, to be passed up to the
+ * routing daemon.
+ */
+ static struct sockaddr_in6 dst = { sizeof(dst), AF_INET6 };
+
+ struct mbuf *mcp;
+ struct ip6_hdr *eip6;
+ u_int32_t *reghdr;
+ int rc;
+
+ ++pim6stat.pim6s_rcv_registers;
+
+ if ((reg_mif_num >= nummifs) || (reg_mif_num == (mifi_t) -1)) {
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM)
+ log(LOG_DEBUG,
+ "pim6_input: register mif not set: %d\n",
+ reg_mif_num);
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ reghdr = (u_int32_t *)(pim + 1);
+
+ if ((ntohl(*reghdr) & PIM_NULL_REGISTER))
+ goto pim6_input_to_daemon;
+
+ /*
+ * Validate length
+ */
+ if (pimlen < PIM6_REG_MINLEN) {
+ ++pim6stat.pim6s_rcv_tooshort;
+ ++pim6stat.pim6s_rcv_badregisters;
+#ifdef MRT6DEBUG
+ log(LOG_ERR,
+ "pim6_input: register packet size too "
+ "small %d from %s\n",
+ pimlen, ip6_sprintf(&ip6->ip6_src));
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ eip6 = (struct ip6_hdr *) (reghdr + 1);
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM)
+ log(LOG_DEBUG,
+ "pim6_input[register], eip6: %s -> %s, "
+ "eip6 plen %d\n",
+ ip6_sprintf(&eip6->ip6_src),
+ 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)) {
+ ++pim6stat.pim6s_rcv_badregisters;
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM)
+ log(LOG_DEBUG,
+ "pim6_input: inner packet of register "
+ "is not multicast %s\n",
+ ip6_sprintf(&eip6->ip6_dst));
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ /*
+ * make a copy of the whole header to pass to the daemon later.
+ */
+ mcp = m_copy(m, 0, off + PIM6_REG_MINLEN);
+ if (mcp == NULL) {
+#ifdef MRT6DEBUG
+ log(LOG_ERR,
+ "pim6_input: pim register: "
+ "could not copy register head\n");
+#endif
+ m_freem(m);
+ return(IPPROTO_DONE);
+ }
+
+ /*
+ * forward the inner ip6 packet; point m_data at the inner ip6.
+ */
+ m_adj(m, off + PIM_MINLEN);
+#ifdef MRT6DEBUG
+ if (mrt6debug & DEBUG_PIM) {
+ log(LOG_DEBUG,
+ "pim6_input: forwarding decapsulated register: "
+ "src %s, dst %s, mif %d\n",
+ ip6_sprintf(&eip6->ip6_src),
+ ip6_sprintf(&eip6->ip6_dst),
+ reg_mif_num);
+ }
+#endif
+
+ rc = if_simloop(mif6table[reg_mif_num].m6_ifp, m,
+ dst.sin6_family, 0);
+
+ /* prepare the register head to send to the mrouting daemon */
+ m = mcp;
+ }
+
+ /*
+ * Pass the PIM message up to the daemon; if it is a register message
+ * pass the 'head' only up to the daemon. This includes the
+ * encapsulator ip6 header, pim header, register header and the
+ * encapsulated ip6 header.
+ */
+ pim6_input_to_daemon:
+ rip6_input(&m, offp, proto);
+ return(IPPROTO_DONE);
+}
diff --git a/sys/netinet6/ip6_mroute.h b/sys/netinet6/ip6_mroute.h
new file mode 100644
index 0000000..4c75298
--- /dev/null
+++ b/sys/netinet6/ip6_mroute.h
@@ -0,0 +1,277 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_mroute.h,v 1.19 2001/06/14 06:12:55 suz Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp */
+
+/*
+ * Definitions for IP multicast forwarding.
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ * Modified by Ajit Thyagarajan, PARC, August 1993.
+ * Modified by Ajit Thyagarajan, PARC, August 1994.
+ * Modified by Ahmed Helmy, USC, September 1996.
+ *
+ * MROUTING Revision: 1.2
+ */
+
+#ifndef _NETINET6_IP6_MROUTE_H_
+#define _NETINET6_IP6_MROUTE_H_
+
+/*
+ * Multicast Routing set/getsockopt commands.
+ */
+#ifdef _KERNEL
+#define MRT6_OINIT 100 /* initialize forwarder (omrt6msg) */
+#endif
+#define MRT6_DONE 101 /* shut down forwarder */
+#define MRT6_ADD_MIF 102 /* add multicast interface */
+#define MRT6_DEL_MIF 103 /* delete multicast interface */
+#define MRT6_ADD_MFC 104 /* insert forwarding cache entry */
+#define MRT6_DEL_MFC 105 /* delete forwarding cache entry */
+#define MRT6_PIM 107 /* enable pim code */
+#define MRT6_INIT 108 /* initialize forwarder (mrt6msg) */
+
+#if BSD >= 199103
+#define GET_TIME(t) microtime(&t)
+#elif defined(sun)
+#define GET_TIME(t) uniqtime(&t)
+#else
+#define GET_TIME(t) ((t) = time)
+#endif
+
+/*
+ * Types and macros for handling bitmaps with one bit per multicast interface.
+ */
+typedef u_short mifi_t; /* type of a mif index */
+#define MAXMIFS 64
+
+#ifndef IF_SETSIZE
+#define IF_SETSIZE 256
+#endif
+
+typedef u_int32_t if_mask;
+#define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */
+
+#ifndef howmany
+#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+#endif
+
+typedef struct if_set {
+ if_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)];
+} if_set;
+
+#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS)))
+#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS)))
+#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS)))
+#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f)))
+#define IF_ZERO(p) bzero(p, sizeof(*(p)))
+
+/*
+ * Argument structure for MRT6_ADD_IF.
+ */
+struct mif6ctl {
+ mifi_t mif6c_mifi; /* the index of the mif to be added */
+ u_char mif6c_flags; /* MIFF_ flags defined below */
+ u_short mif6c_pifi; /* the index of the physical IF */
+#ifdef notyet
+ u_int mif6c_rate_limit; /* max rate */
+#endif
+};
+
+#define MIFF_REGISTER 0x1 /* mif represents a register end-point */
+
+/*
+ * Argument structure for MRT6_ADD_MFC and MRT6_DEL_MFC
+ */
+struct mf6cctl {
+ struct sockaddr_in6 mf6cc_origin; /* IPv6 origin of mcasts */
+ struct sockaddr_in6 mf6cc_mcastgrp; /* multicast group associated */
+ mifi_t mf6cc_parent; /* incoming ifindex */
+ struct if_set mf6cc_ifset; /* set of forwarding ifs */
+};
+
+/*
+ * The kernel's multicast routing statistics.
+ */
+struct mrt6stat {
+ u_quad_t mrt6s_mfc_lookups; /* # forw. cache hash table hits */
+ u_quad_t mrt6s_mfc_misses; /* # forw. cache hash table misses */
+ u_quad_t mrt6s_upcalls; /* # calls to mrouted */
+ u_quad_t mrt6s_no_route; /* no route for packet's origin */
+ u_quad_t mrt6s_bad_tunnel; /* malformed tunnel options */
+ u_quad_t mrt6s_cant_tunnel; /* no room for tunnel options */
+ u_quad_t mrt6s_wrong_if; /* arrived on wrong interface */
+ u_quad_t mrt6s_upq_ovflw; /* upcall Q overflow */
+ u_quad_t mrt6s_cache_cleanups; /* # entries with no upcalls */
+ u_quad_t mrt6s_drop_sel; /* pkts dropped selectively */
+ u_quad_t mrt6s_q_overflow; /* pkts dropped - Q overflow */
+ u_quad_t mrt6s_pkt2large; /* pkts dropped - size > BKT SIZE */
+ u_quad_t mrt6s_upq_sockfull; /* upcalls dropped - socket full */
+};
+
+#ifdef MRT6_OINIT
+/*
+ * Struct used to communicate from kernel to multicast router
+ * note the convenient similarity to an IPv6 header.
+ * XXX old version, superseded by mrt6msg.
+ */
+struct omrt6msg {
+ u_long unused1;
+ u_char im6_msgtype; /* what type of message */
+#if 0
+#define MRT6MSG_NOCACHE 1
+#define MRT6MSG_WRONGMIF 2
+#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/
+#endif
+ u_char im6_mbz; /* must be zero */
+ u_char im6_mif; /* mif rec'd on */
+ u_char unused2;
+ struct in6_addr im6_src, im6_dst;
+};
+#endif
+
+/*
+ * Structure used to communicate from kernel to multicast router.
+ * We'll overlay the structure onto an MLD header (not an IPv6 header
+ * like igmpmsg{} used for IPv4 implementation). This is because this
+ * structure will be passed via an IPv6 raw socket, on which an application
+ * will only receive the payload i.e. the data after the IPv6 header and all
+ * the extension headers. (see Section 3 of draft-ietf-ipngwg-2292bis-01)
+ */
+struct mrt6msg {
+#define MRT6MSG_NOCACHE 1
+#define MRT6MSG_WRONGMIF 2
+#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/
+ u_char im6_mbz; /* must be zero */
+ u_char im6_msgtype; /* what type of message */
+ u_int16_t im6_mif; /* mif rec'd on */
+ u_int32_t im6_pad; /* padding for 64bit arch */
+ struct in6_addr im6_src, im6_dst;
+};
+
+/*
+ * Argument structure used by multicast routing daemon to get src-grp
+ * packet counts
+ */
+struct sioc_sg_req6 {
+ struct sockaddr_in6 src;
+ struct sockaddr_in6 grp;
+ u_quad_t pktcnt;
+ u_quad_t bytecnt;
+ u_quad_t wrong_if;
+};
+
+/*
+ * Argument structure used by mrouted to get mif pkt counts
+ */
+struct sioc_mif_req6 {
+ mifi_t mifi; /* mif number */
+ u_quad_t icount; /* Input packet count on mif */
+ u_quad_t ocount; /* Output packet count on mif */
+ u_quad_t ibytes; /* Input byte count on mif */
+ u_quad_t obytes; /* Output byte count on mif */
+};
+
+#if defined(_KERNEL) || defined(KERNEL)
+/*
+ * The kernel's multicast-interface structure.
+ */
+struct mif6 {
+ u_char m6_flags; /* MIFF_ flags defined above */
+ u_int m6_rate_limit; /* max rate */
+#ifdef notyet
+ struct tbf *m6_tbf; /* token bucket structure at intf. */
+#endif
+ struct in6_addr m6_lcl_addr; /* local interface address */
+ struct ifnet *m6_ifp; /* pointer to interface */
+ u_quad_t m6_pkt_in; /* # pkts in on interface */
+ u_quad_t m6_pkt_out; /* # pkts out on interface */
+ u_quad_t m6_bytes_in; /* # bytes in on interface */
+ u_quad_t m6_bytes_out; /* # bytes out on interface */
+ struct route_in6 m6_route;/* cached route if this is a tunnel */
+#ifdef notyet
+ u_int m6_rsvp_on; /* RSVP listening on this vif */
+ struct socket *m6_rsvpd; /* RSVP daemon socket */
+#endif
+};
+
+/*
+ * The kernel's multicast forwarding cache entry structure
+ */
+struct mf6c {
+ struct sockaddr_in6 mf6c_origin; /* IPv6 origin of mcasts */
+ struct sockaddr_in6 mf6c_mcastgrp; /* multicast group associated*/
+ mifi_t mf6c_parent; /* incoming IF */
+ struct if_set mf6c_ifset; /* set of outgoing IFs */
+
+ u_quad_t mf6c_pkt_cnt; /* pkt count for src-grp */
+ u_quad_t mf6c_byte_cnt; /* byte count for src-grp */
+ u_quad_t mf6c_wrong_if; /* wrong if for src-grp */
+ int mf6c_expire; /* time to clean entry up */
+ struct timeval mf6c_last_assert; /* last time I sent an assert*/
+ struct rtdetq *mf6c_stall; /* pkts waiting for route */
+ struct mf6c *mf6c_next; /* hash table linkage */
+};
+
+#define MF6C_INCOMPLETE_PARENT ((mifi_t)-1)
+
+/*
+ * Argument structure used for pkt info. while upcall is made
+ */
+#ifndef _NETINET_IP_MROUTE_H_
+struct rtdetq { /* XXX: rtdetq is also defined in ip_mroute.h */
+ struct mbuf *m; /* A copy of the packet */
+ struct ifnet *ifp; /* Interface pkt came in on */
+#ifdef UPCALL_TIMING
+ struct timeval t; /* Timestamp */
+#endif /* UPCALL_TIMING */
+ struct rtdetq *next;
+};
+#endif /* _NETINET_IP_MROUTE_H_ */
+
+#define MF6CTBLSIZ 256
+#if (MF6CTBLSIZ & (MF6CTBLSIZ - 1)) == 0 /* from sys:route.h */
+#define MF6CHASHMOD(h) ((h) & (MF6CTBLSIZ - 1))
+#else
+#define MF6CHASHMOD(h) ((h) % MF6CTBLSIZ)
+#endif
+
+#define MAX_UPQ6 4 /* max. no of pkts in upcall Q */
+
+int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt));
+int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt));
+int ip6_mrouter_done __P((void));
+int mrt6_ioctl __P((int, caddr_t));
+#endif /* _KERNEL */
+
+#endif /* !_NETINET6_IP6_MROUTE_H_ */
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
new file mode 100644
index 0000000..b18f9a7
--- /dev/null
+++ b/sys/netinet6/ip6_output.c
@@ -0,0 +1,2614 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+#include "opt_pfil_hooks.h"
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+#endif
+
+#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>
+#include <netinet/in_pcb.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netkey/key.h>
+#endif /* IPSEC */
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#include <netipsec/key.h>
+#endif /* FAST_IPSEC */
+
+#include <netinet6/ip6_fw.h>
+
+#include <net/net_osdep.h>
+
+#include <netinet6/ip6protosw.h>
+
+static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
+
+struct ip6_exthdrs {
+ struct mbuf *ip6e_ip6;
+ struct mbuf *ip6e_hbh;
+ struct mbuf *ip6e_dest1;
+ struct mbuf *ip6e_rthdr;
+ struct mbuf *ip6e_dest2;
+};
+
+static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
+ struct socket *, struct sockopt *sopt));
+static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *));
+static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
+static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
+static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
+ struct ip6_frag **));
+static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
+static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
+
+/*
+ * IP6 output. The packet in mbuf chain m contains a skeletal IP6
+ * header (with pri, len, nxt, hlim, src, dst).
+ * 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.
+ */
+int
+ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
+ struct mbuf *m0;
+ struct ip6_pktopts *opt;
+ struct route_in6 *ro;
+ int flags;
+ struct ip6_moptions *im6o;
+ struct ifnet **ifpp; /* XXX: just for statistics */
+ struct inpcb *inp;
+{
+ struct ip6_hdr *ip6, *mhip6;
+ struct ifnet *ifp, *origifp;
+ struct mbuf *m = m0;
+ int hlen, tlen, len, off;
+ struct route_in6 ip6route;
+ struct sockaddr_in6 *dst;
+ int error = 0;
+ struct in6_ifaddr *ia = NULL;
+ u_long mtu;
+ u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
+ struct ip6_exthdrs exthdrs;
+ struct in6_addr finaldst;
+ struct route_in6 *ro_pmtu = NULL;
+ int hdrsplit = 0;
+ int needipsec = 0;
+#ifdef PFIL_HOOKS
+ struct packet_filter_hook *pfh;
+ struct mbuf *m1;
+ int rv;
+#endif /* PFIL_HOOKS */
+#ifdef IPSEC
+ int needipsectun = 0;
+ struct secpolicy *sp = NULL;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ int needipsectun = 0;
+ struct secpolicy *sp = NULL;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+#endif /* FAST_IPSEC */
+
+#define MAKE_EXTHDR(hp, mp) \
+ do { \
+ if (hp) { \
+ struct ip6_ext *eh = (struct ip6_ext *)(hp); \
+ error = ip6_copyexthdr((mp), (caddr_t)(hp), \
+ ((eh)->ip6e_len + 1) << 3); \
+ if (error) \
+ goto freehdrs; \
+ } \
+ } while (0)
+
+ bzero(&exthdrs, sizeof(exthdrs));
+
+ if (opt) {
+ /* Hop-by-Hop options header */
+ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
+ /* Destination options header(1st part) */
+ MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
+ /* Routing header */
+ MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
+ /* Destination options header(2nd part) */
+ MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
+ }
+
+#ifdef IPSEC
+ /* get a security policy for this packet */
+ if (inp == NULL)
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ else
+ sp = ipsec6_getpolicybypcb(m, IPSEC_DIR_OUTBOUND, inp, &error);
+
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ goto freehdrs;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ goto freehdrs;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ needipsec = 0;
+ break;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* acquire a policy */
+ error = key_spdacquire(sp);
+ goto freehdrs;
+ }
+ needipsec = 1;
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ printf("ip6_output: Invalid policy found. %d\n", sp->policy);
+ }
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ /* get a security policy for this packet */
+ if (inp == NULL)
+ sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ else
+ sp = ipsec_getpolicybysock(m, IPSEC_DIR_OUTBOUND, inp, &error);
+
+ if (sp == NULL) {
+ newipsecstat.ips_out_inval++;
+ goto freehdrs;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ newipsecstat.ips_out_polvio++;
+ goto freehdrs;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ needipsec = 0;
+ break;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* acquire a policy */
+ error = key_spdacquire(sp);
+ goto freehdrs;
+ }
+ needipsec = 1;
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ printf("ip6_output: Invalid policy found. %d\n", sp->policy);
+ }
+#endif /* FAST_IPSEC */
+
+ /*
+ * Calculate the total length of the extension header chain.
+ * Keep the length of the unfragmentable part for fragmentation.
+ */
+ optlen = 0;
+ if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
+ if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
+ if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
+ unfragpartlen = optlen + sizeof(struct ip6_hdr);
+ /* NOTE: we don't add AH/ESP length here. do that later. */
+ if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
+
+ /*
+ * If we need IPsec, or there is at least one extension header,
+ * separate IP6 header from the payload.
+ */
+ if ((needipsec || optlen) && !hdrsplit) {
+ if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
+ m = NULL;
+ goto freehdrs;
+ }
+ m = exthdrs.ip6e_ip6;
+ hdrsplit++;
+ }
+
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* adjust mbuf packet header length */
+ m->m_pkthdr.len += optlen;
+ plen = m->m_pkthdr.len - sizeof(*ip6);
+
+ /* If this is a jumbo payload, insert a jumbo payload option. */
+ if (plen > IPV6_MAXPACKET) {
+ if (!hdrsplit) {
+ if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
+ m = NULL;
+ goto freehdrs;
+ }
+ m = exthdrs.ip6e_ip6;
+ hdrsplit++;
+ }
+ /* adjust pointer */
+ ip6 = mtod(m, struct ip6_hdr *);
+ if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
+ goto freehdrs;
+ ip6->ip6_plen = 0;
+ } else
+ ip6->ip6_plen = htons(plen);
+
+ /*
+ * Concatenate headers and fill in next header fields.
+ * Here we have, on "m"
+ * IPv6 payload
+ * and we insert headers accordingly. Finally, we should be getting:
+ * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
+ *
+ * during the header composing process, "m" points to IPv6 header.
+ * "mprev" points to an extension header prior to esp.
+ */
+ {
+ u_char *nexthdrp = &ip6->ip6_nxt;
+ struct mbuf *mprev = m;
+
+ /*
+ * we treat dest2 specially. this makes IPsec processing
+ * much easier. the goal here is to make mprev point the
+ * mbuf prior to dest2.
+ *
+ * result: IPv6 dest2 payload
+ * m and mprev will point to IPv6 header.
+ */
+ if (exthdrs.ip6e_dest2) {
+ if (!hdrsplit)
+ panic("assumption failed: hdr not split");
+ exthdrs.ip6e_dest2->m_next = m->m_next;
+ m->m_next = exthdrs.ip6e_dest2;
+ *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
+ ip6->ip6_nxt = IPPROTO_DSTOPTS;
+ }
+
+#define MAKE_CHAIN(m, mp, p, i)\
+ do {\
+ if (m) {\
+ if (!hdrsplit) \
+ panic("assumption failed: hdr not split"); \
+ *mtod((m), u_char *) = *(p);\
+ *(p) = (i);\
+ p = mtod((m), u_char *);\
+ (m)->m_next = (mp)->m_next;\
+ (mp)->m_next = (m);\
+ (mp) = (m);\
+ }\
+ } while (0)
+ /*
+ * result: IPv6 hbh dest1 rthdr dest2 payload
+ * m will point to IPv6 header. mprev will point to the
+ * extension header prior to dest2 (rthdr in the above case).
+ */
+ MAKE_CHAIN(exthdrs.ip6e_hbh, mprev,
+ nexthdrp, IPPROTO_HOPOPTS);
+ MAKE_CHAIN(exthdrs.ip6e_dest1, mprev,
+ nexthdrp, IPPROTO_DSTOPTS);
+ MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
+ nexthdrp, IPPROTO_ROUTING);
+
+#if defined(IPSEC) || defined(FAST_IPSEC)
+ if (!needipsec)
+ goto skip_ipsec2;
+
+ /*
+ * pointers after IPsec headers are not valid any more.
+ * other pointers need a great care too.
+ * (IPsec routines should not mangle mbufs prior to AH/ESP)
+ */
+ exthdrs.ip6e_dest2 = NULL;
+
+ {
+ struct ip6_rthdr *rh = NULL;
+ int segleft_org = 0;
+ struct ipsec_output_state state;
+
+ if (exthdrs.ip6e_rthdr) {
+ rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
+ segleft_org = rh->ip6r_segleft;
+ rh->ip6r_segleft = 0;
+ }
+
+ bzero(&state, sizeof(state));
+ state.m = m;
+ error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
+ &needipsectun);
+ m = state.m;
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_trans. */
+ m = NULL;
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /* fall through */
+ case ENOENT:
+ /* don't show these error codes to the user */
+ error = 0;
+ break;
+ }
+ goto bad;
+ }
+ if (exthdrs.ip6e_rthdr) {
+ /* ah6_output doesn't modify mbuf chain */
+ rh->ip6r_segleft = segleft_org;
+ }
+ }
+skip_ipsec2:;
+#endif
+ }
+
+ /*
+ * If there is a routing header, replace destination address field
+ * with the first hop of the routing header.
+ */
+ if (exthdrs.ip6e_rthdr) {
+ struct ip6_rthdr *rh =
+ (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
+ struct ip6_rthdr *));
+ struct ip6_rthdr0 *rh0;
+
+ finaldst = ip6->ip6_dst;
+ 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)
+ );
+ rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst;
+ break;
+ default: /* is it possible? */
+ error = EINVAL;
+ goto bad;
+ }
+ }
+
+ /* Source address validation */
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) &&
+ (flags & IPV6_DADOUTPUT) == 0) {
+ error = EOPNOTSUPP;
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
+ error = EOPNOTSUPP;
+ ip6stat.ip6s_badscope++;
+ goto bad;
+ }
+
+ ip6stat.ip6s_localout++;
+
+ /*
+ * Route packet.
+ */
+ if (ro == 0) {
+ ro = &ip6route;
+ bzero((caddr_t)ro, sizeof(*ro));
+ }
+ ro_pmtu = ro;
+ if (opt && opt->ip6po_rthdr)
+ ro = &opt->ip6po_route;
+ dst = (struct sockaddr_in6 *)&ro->ro_dst;
+ /*
+ * If there is a cached route,
+ * check that it is to the same destination
+ * and is still up. If not, free it and try again.
+ */
+ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ dst->sin6_family != AF_INET6 ||
+ !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == 0) {
+ bzero(dst, sizeof(*dst));
+ dst->sin6_family = AF_INET6;
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_addr = ip6->ip6_dst;
+#ifdef SCOPEDROUTING
+ /* XXX: sin6_scope_id should already be fixed at this point */
+ if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
+ dst->sin6_scope_id = ntohs(dst->sin6_addr.s6_addr16[1]);
+#endif
+ }
+#if defined(IPSEC) || defined(FAST_IPSEC)
+ if (needipsec && needipsectun) {
+ struct ipsec_output_state state;
+
+ /*
+ * All the extension headers will become inaccessible
+ * (since they can be encrypted).
+ * Don't panic, we need no more updates to extension headers
+ * on inner IPv6 packet (since they are now encapsulated).
+ *
+ * IPv6 [ESP|AH] IPv6 [extension headers] payload
+ */
+ bzero(&exthdrs, sizeof(exthdrs));
+ exthdrs.ip6e_ip6 = m;
+
+ bzero(&state, sizeof(state));
+ state.m = m;
+ state.ro = (struct route *)ro;
+ state.dst = (struct sockaddr *)dst;
+
+ error = ipsec6_output_tunnel(&state, sp, flags);
+
+ m = state.m;
+ ro = (struct route_in6 *)state.ro;
+ dst = (struct sockaddr_in6 *)state.dst;
+ if (error) {
+ /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+ m0 = m = NULL;
+ m = NULL;
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /* fall through */
+ case ENOENT:
+ /* don't show these error codes to the user */
+ error = 0;
+ break;
+ }
+ goto bad;
+ }
+
+ exthdrs.ip6e_ip6 = m;
+ }
+#endif /* IPSEC */
+
+ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ /* Unicast */
+
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+ /* xxx
+ * interface selection comes here
+ * if an interface is specified from an upper layer,
+ * ifp must point it.
+ */
+ if (ro->ro_rt == 0) {
+ /*
+ * non-bsdi always clone routes, if parent is
+ * PRF_CLONING.
+ */
+ rtalloc((struct route *)ro);
+ }
+ if (ro->ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ error = EHOSTUNREACH;
+ /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
+ goto bad;
+ }
+ ia = ifatoia6(ro->ro_rt->rt_ifa);
+ ifp = ro->ro_rt->rt_ifp;
+ ro->ro_rt->rt_use++;
+ if (ro->ro_rt->rt_flags & RTF_GATEWAY)
+ dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
+ m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
+
+ in6_ifstat_inc(ifp, ifs6_out_request);
+
+ /*
+ * Check if the outgoing interface conflicts with
+ * the interface specified by ifi6_ifindex (if specified).
+ * Note that loopback interface is always okay.
+ * (this may happen when we are sending a packet to one of
+ * our own addresses.)
+ */
+ if (opt && opt->ip6po_pktinfo
+ && opt->ip6po_pktinfo->ipi6_ifindex) {
+ if (!(ifp->if_flags & IFF_LOOPBACK)
+ && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+ }
+
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+ } else {
+ /* Multicast */
+ struct in6_multi *in6m;
+
+ m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
+
+ /*
+ * See if the caller provided any multicast options
+ */
+ ifp = NULL;
+ if (im6o != NULL) {
+ ip6->ip6_hlim = im6o->im6o_multicast_hlim;
+ if (im6o->im6o_multicast_ifp != NULL)
+ ifp = im6o->im6o_multicast_ifp;
+ } else
+ ip6->ip6_hlim = ip6_defmcasthlim;
+
+ /*
+ * See if the caller provided the outgoing interface
+ * as an ancillary data.
+ * Boundary check for ifindex is assumed to be already done.
+ */
+ if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex)
+ ifp = ifnet_byindex(opt->ip6po_pktinfo->ipi6_ifindex);
+
+ /*
+ * If the destination is a node-local scope multicast,
+ * the packet should be loop-backed only.
+ */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) {
+ /*
+ * If the outgoing interface is already specified,
+ * it should be a loopback interface.
+ */
+ if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
+ ip6stat.ip6s_badscope++;
+ error = ENETUNREACH; /* XXX: better error? */
+ /* XXX correct ifp? */
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ goto bad;
+ } else {
+ ifp = &loif[0];
+ }
+ }
+
+ if (opt && opt->ip6po_hlim != -1)
+ ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
+
+ /*
+ * If caller did not provide an interface lookup a
+ * default in the routing table. This is either a
+ * default for the speicfied group (i.e. a host
+ * route), or a multicast default (a route for the
+ * ``net'' ff00::/8).
+ */
+ if (ifp == NULL) {
+ if (ro->ro_rt == 0) {
+ ro->ro_rt = rtalloc1((struct sockaddr *)
+ &ro->ro_dst, 0, 0UL);
+ }
+ if (ro->ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ error = EHOSTUNREACH;
+ /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
+ goto bad;
+ }
+ ia = ifatoia6(ro->ro_rt->rt_ifa);
+ ifp = ro->ro_rt->rt_ifp;
+ ro->ro_rt->rt_use++;
+ }
+
+ if ((flags & IPV6_FORWARDING) == 0)
+ in6_ifstat_inc(ifp, ifs6_out_request);
+ in6_ifstat_inc(ifp, ifs6_out_mcast);
+
+ /*
+ * Confirm that the outgoing interface supports multicast.
+ */
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ ip6stat.ip6s_noroute++;
+ in6_ifstat_inc(ifp, ifs6_out_discard);
+ error = ENETUNREACH;
+ goto bad;
+ }
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+ if (in6m != NULL &&
+ (im6o == NULL || im6o->im6o_multicast_loop)) {
+ /*
+ * If we belong to the destination multicast group
+ * on the outgoing interface, and the caller did not
+ * forbid loopback, loop back a copy.
+ */
+ ip6_mloopback(ifp, m, dst);
+ } else {
+ /*
+ * If we are acting as a multicast router, perform
+ * multicast forwarding as if the packet had just
+ * arrived on the interface to which we are about
+ * to send. The multicast forwarding function
+ * recursively calls this function, using the
+ * IPV6_FORWARDING flag to prevent infinite recursion.
+ *
+ * Multicasts that are looped back by ip6_mloopback(),
+ * above, will be forwarded by the ip6_input() routine,
+ * if necessary.
+ */
+ if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
+ if (ip6_mforward(ip6, ifp, m) != 0) {
+ m_freem(m);
+ goto done;
+ }
+ }
+ }
+ /*
+ * Multicasts with a hoplimit of zero may be looped back,
+ * above, but must not be transmitted on a network.
+ * Also, multicasts addressed to the loopback interface
+ * are not sent -- the above call to ip6_mloopback() will
+ * loop back a copy if this host actually belongs to the
+ * destination group on the loopback interface.
+ */
+ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) {
+ m_freem(m);
+ goto done;
+ }
+ }
+
+ /*
+ * Fill the outgoing inteface to tell the upper layer
+ * to increment per-interface statistics.
+ */
+ if (ifpp)
+ *ifpp = ifp;
+
+ /*
+ * Determine path MTU.
+ */
+ if (ro_pmtu != ro) {
+ /* The first hop and the final destination may differ. */
+ struct sockaddr_in6 *sin6_fin =
+ (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
+ if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr,
+ &finaldst))) {
+ RTFREE(ro_pmtu->ro_rt);
+ ro_pmtu->ro_rt = (struct rtentry *)0;
+ }
+ if (ro_pmtu->ro_rt == 0) {
+ bzero(sin6_fin, sizeof(*sin6_fin));
+ sin6_fin->sin6_family = AF_INET6;
+ sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
+ sin6_fin->sin6_addr = finaldst;
+
+ rtalloc((struct route *)ro_pmtu);
+ }
+ }
+ if (ro_pmtu->ro_rt != NULL) {
+ u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu;
+
+ mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
+ if (mtu > ifmtu || mtu == 0) {
+ /*
+ * The MTU on the route is larger than the MTU on
+ * the interface! This shouldn't happen, unless the
+ * MTU of the interface has been changed after the
+ * 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)
+ ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
+ }
+ } else {
+ 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) {
+ /*
+ * If source or destination address is a scoped address, and
+ * the packet is going to be sent to a loopback interface,
+ * we should keep the original interface.
+ */
+
+ /*
+ * XXX: this is a very experimental and temporary solution.
+ * We eventually have sockaddr_in6 and use the sin6_scope_id
+ * field of the structure here.
+ * We rely on the consistency between two scope zone ids
+ * of source and destination, which should already be assured.
+ * Larger scopes than link will be supported in the future.
+ */
+ origifp = NULL;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ origifp = ifnet_byindex(ntohs(ip6->ip6_src.s6_addr16[1]));
+ else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));
+ /*
+ * 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 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
+
+ /*
+ * Check with the firewall...
+ */
+ 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 */
+ if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
+ m_freem(m);
+ goto done;
+ }
+ if (!m) {
+ error = EACCES;
+ goto done;
+ }
+ }
+
+ /*
+ * If the outgoing packet contains a hop-by-hop options header,
+ * it must be examined and processed even by the source node.
+ * (RFC 2460, section 4.)
+ */
+ if (exthdrs.ip6e_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
+ * the IPv6 and the hop-by-hop options header are
+ * continuous unless the flag is set.
+ */
+ m->m_flags |= M_LOOP;
+ m->m_pkthdr.rcvif = ifp;
+ if (ip6_process_hopopts(m,
+ (u_int8_t *)(hbh + 1),
+ ((hbh->ip6h_len + 1) << 3) -
+ sizeof(struct ip6_hbh),
+ &dummy1, &dummy2) < 0) {
+ /* m was already freed at this point */
+ error = EINVAL;/* better error? */
+ goto done;
+ }
+ m->m_flags &= ~M_LOOP; /* XXX */
+ m->m_pkthdr.rcvif = NULL;
+ }
+
+#ifdef PFIL_HOOKS
+ /*
+ * Run through list of hooks for output packets.
+ */
+ m1 = m;
+ pfh = pfil_hook_get(PFIL_OUT, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh);
+ for (; pfh; pfh = pfh->pfil_link.tqe_next)
+ if (pfh->pfil_func) {
+ rv = pfh->pfil_func(ip6, sizeof(*ip6), ifp, 1, &m1);
+ if (rv) {
+ error = EHOSTUNREACH;
+ goto done;
+ }
+ m = m1;
+ if (m == NULL)
+ goto done;
+ ip6 = mtod(m, struct ip6_hdr *);
+ }
+#endif /* PFIL_HOOKS */
+ /*
+ * Send the packet to the outgoing interface.
+ * If necessary, do IPv6 fragmentation before sending.
+ */
+ tlen = m->m_pkthdr.len;
+ if (tlen <= mtu
+#ifdef notyet
+ /*
+ * On any link that cannot convey a 1280-octet packet in one piece,
+ * link-specific fragmentation and reassembly must be provided at
+ * a layer below IPv6. [RFC 2460, sec.5]
+ * Thus if the interface has ability of link-level fragmentation,
+ * we can just send the packet even if the packet size is
+ * larger than the link's MTU.
+ * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
+ */
+
+ || ifp->if_flags & IFF_FRAGMENTABLE
+#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;
+ }
+#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) {
+ /*
+ * note that path MTU is never less than IPV6_MMTU
+ * (see icmp6_input).
+ */
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ } else {
+ struct mbuf **mnext, *m_frgpart;
+ struct ip6_frag *ip6f;
+ u_int32_t id = htonl(ip6_id++);
+ u_char nextproto;
+
+ /*
+ * Too large for the destination or interface;
+ * fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ hlen = unfragpartlen;
+ if (mtu > IPV6_MAXPACKET)
+ mtu = IPV6_MAXPACKET;
+
+ len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
+ if (len < 8) {
+ error = EMSGSIZE;
+ in6_ifstat_inc(ifp, ifs6_out_fragfail);
+ goto bad;
+ }
+
+ mnext = &m->m_nextpkt;
+
+ /*
+ * Change the next header field of the last header in the
+ * unfragmentable part.
+ */
+ if (exthdrs.ip6e_rthdr) {
+ nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
+ *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
+ } else if (exthdrs.ip6e_dest1) {
+ nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
+ *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
+ } else if (exthdrs.ip6e_hbh) {
+ nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
+ *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
+ } else {
+ nextproto = ip6->ip6_nxt;
+ ip6->ip6_nxt = IPPROTO_FRAGMENT;
+ }
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto
+ * chain.
+ */
+ m0 = m;
+ for (off = hlen; off < tlen; off += len) {
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (!m) {
+ error = ENOBUFS;
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ m->m_pkthdr.rcvif = NULL;
+ m->m_flags = m0->m_flags & M_COPYFLAGS;
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ m->m_data += max_linkhdr;
+ mhip6 = mtod(m, struct ip6_hdr *);
+ *mhip6 = *ip6;
+ m->m_len = sizeof(*mhip6);
+ error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
+ if (error) {
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));
+ if (off + len >= tlen)
+ len = tlen - off;
+ else
+ ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
+ mhip6->ip6_plen = htons((u_short)(len + hlen +
+ sizeof(*ip6f) -
+ sizeof(struct ip6_hdr)));
+ if ((m_frgpart = m_copy(m0, off, len)) == 0) {
+ error = ENOBUFS;
+ ip6stat.ip6s_odropped++;
+ goto sendorfree;
+ }
+ m_cat(m, m_frgpart);
+ m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ ip6f->ip6f_reserved = 0;
+ ip6f->ip6f_ident = id;
+ ip6f->ip6f_nxt = nextproto;
+ ip6stat.ip6s_ofragments++;
+ in6_ifstat_inc(ifp, ifs6_out_fragcreat);
+ }
+
+ in6_ifstat_inc(ifp, ifs6_out_fragok);
+ }
+
+ /*
+ * Remove leading garbages.
+ */
+sendorfree:
+ m = m0->m_nextpkt;
+ m0->m_nextpkt = 0;
+ m_freem(m0);
+ for (m0 = m; m; m = m0) {
+ 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;
+ }
+#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);
+ }
+
+ if (error == 0)
+ ip6stat.ip6s_fragmented++;
+
+done:
+ if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
+ RTFREE(ro->ro_rt);
+ } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
+ RTFREE(ro_pmtu->ro_rt);
+ }
+
+#ifdef IPSEC
+ if (sp != NULL)
+ key_freesp(sp);
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ if (sp != NULL)
+ KEY_FREESP(&sp);
+#endif /* FAST_IPSEC */
+
+ return(error);
+
+freehdrs:
+ m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
+ m_freem(exthdrs.ip6e_dest1);
+ m_freem(exthdrs.ip6e_rthdr);
+ m_freem(exthdrs.ip6e_dest2);
+ /* fall through */
+bad:
+ m_freem(m);
+ goto done;
+}
+
+static int
+ip6_copyexthdr(mp, hdr, hlen)
+ struct mbuf **mp;
+ caddr_t hdr;
+ int hlen;
+{
+ struct mbuf *m;
+
+ if (hlen > MCLBYTES)
+ return(ENOBUFS); /* XXX */
+
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ return(ENOBUFS);
+
+ if (hlen > MLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return(ENOBUFS);
+ }
+ }
+ m->m_len = hlen;
+ if (hdr)
+ bcopy(hdr, mtod(m, caddr_t), hlen);
+
+ *mp = m;
+ return(0);
+}
+
+/*
+ * Insert jumbo payload option.
+ */
+static int
+ip6_insert_jumboopt(exthdrs, plen)
+ struct ip6_exthdrs *exthdrs;
+ u_int32_t plen;
+{
+ struct mbuf *mopt;
+ u_char *optbuf;
+ u_int32_t v;
+
+#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
+
+ /*
+ * If there is no hop-by-hop options header, allocate new one.
+ * If there is one but it doesn't have enough space to store the
+ * jumbo payload option, allocate a cluster to store the whole options.
+ * Otherwise, use it to store the options.
+ */
+ if (exthdrs->ip6e_hbh == 0) {
+ MGET(mopt, M_DONTWAIT, MT_DATA);
+ if (mopt == 0)
+ return(ENOBUFS);
+ mopt->m_len = JUMBOOPTLEN;
+ optbuf = mtod(mopt, u_char *);
+ optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
+ exthdrs->ip6e_hbh = mopt;
+ } else {
+ struct ip6_hbh *hbh;
+
+ mopt = exthdrs->ip6e_hbh;
+ if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
+ /*
+ * 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;
+
+ /*
+ * XXX: give up if the whole (new) hbh header does
+ * not fit even in an mbuf cluster.
+ */
+ if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
+ return(ENOBUFS);
+
+ /*
+ * 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;
+ }
+ optbuf[0] = IP6OPT_PADN;
+ optbuf[1] = 1;
+
+ /*
+ * Adjust the header length according to the pad and
+ * the jumbo payload option.
+ */
+ hbh = mtod(mopt, struct ip6_hbh *);
+ hbh->ip6h_len += (JUMBOOPTLEN >> 3);
+ }
+
+ /* fill in the option. */
+ optbuf[2] = IP6OPT_JUMBO;
+ optbuf[3] = 4;
+ 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;
+
+ return(0);
+#undef JUMBOOPTLEN
+}
+
+/*
+ * Insert fragment header and copy unfragmentable header portions.
+ */
+static int
+ip6_insertfraghdr(m0, m, hlen, frghdrp)
+ struct mbuf *m0, *m;
+ int hlen;
+ struct ip6_frag **frghdrp;
+{
+ struct mbuf *n, *mlast;
+
+ if (hlen > sizeof(struct ip6_hdr)) {
+ n = m_copym(m0, sizeof(struct ip6_hdr),
+ hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
+ if (n == 0)
+ return(ENOBUFS);
+ m->m_next = n;
+ } else
+ n = m;
+
+ /* Search for the last mbuf of unfragmentable part. */
+ for (mlast = n; mlast->m_next; mlast = mlast->m_next)
+ ;
+
+ if ((mlast->m_flags & M_EXT) == 0 &&
+ M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {
+ /* use the trailing space of the last mbuf for the fragment hdr */
+ *frghdrp =
+ (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);
+ mlast->m_len += sizeof(struct ip6_frag);
+ m->m_pkthdr.len += sizeof(struct ip6_frag);
+ } else {
+ /* allocate a new mbuf for the fragment header */
+ struct mbuf *mfrg;
+
+ MGET(mfrg, M_DONTWAIT, MT_DATA);
+ if (mfrg == 0)
+ return(ENOBUFS);
+ mfrg->m_len = sizeof(struct ip6_frag);
+ *frghdrp = mtod(mfrg, struct ip6_frag *);
+ mlast->m_next = mfrg;
+ }
+
+ return(0);
+}
+
+/*
+ * IP6 socket option processing.
+ */
+int
+ip6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int privileged;
+ struct inpcb *in6p = sotoinpcb(so);
+ int error, optval;
+ int level, op, optname;
+ int optlen;
+ struct thread *td;
+
+ if (sopt) {
+ level = sopt->sopt_level;
+ op = sopt->sopt_dir;
+ optname = sopt->sopt_name;
+ optlen = sopt->sopt_valsize;
+ td = sopt->sopt_td;
+ } else {
+ panic("ip6_ctloutput: arg soopt is NULL");
+ }
+ error = optval = 0;
+
+ privileged = (td == 0 || suser(td)) ? 0 : 1;
+
+ 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)
+ break;
+ error = soopt_mcopyin(sopt, m); /* XXX */
+ if (error != 0)
+ break;
+ 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_CHECKSUM:
+ case IPV6_FAITH:
+
+ case IPV6_V6ONLY:
+ if (optlen != sizeof(int)) {
+ error = EINVAL;
+ 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); \
+ else \
+ in6p->in6p_flags &= ~(bit); \
+} while (0)
+#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0)
+
+ case IPV6_CHECKSUM:
+ in6p->in6p_cksum = optval;
+ break;
+
+ case IPV6_FAITH:
+ OPTSET(IN6P_FAITH);
+ break;
+
+ case IPV6_V6ONLY:
+ /*
+ * make setsockopt(IPV6_V6ONLY)
+ * available only prior to bind(2).
+ * see ipng mailing list, Jun 22 2001.
+ */
+ if (in6p->in6p_lport ||
+ !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr))
+ {
+ error = EINVAL;
+ break;
+ }
+ OPTSET(IN6P_IPV6_V6ONLY);
+ if (optval)
+ in6p->in6p_vflag &= ~INP_IPV4;
+ else
+ in6p->in6p_vflag |= INP_IPV4;
+ break;
+ }
+ 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
+
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+ {
+ struct mbuf *m;
+ if (sopt->sopt_valsize > MLEN) {
+ error = EMSGSIZE;
+ break;
+ }
+ /* XXX */
+ MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ break;
+ }
+ m->m_len = sopt->sopt_valsize;
+ error = sooptcopyin(sopt, mtod(m, char *),
+ m->m_len, m->m_len);
+ error = ip6_setmoptions(sopt->sopt_name,
+ &in6p->in6p_moptions,
+ m);
+ (void)m_free(m);
+ }
+ break;
+
+ case IPV6_PORTRANGE:
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+
+ switch (optval) {
+ case IPV6_PORTRANGE_DEFAULT:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ break;
+
+ case IPV6_PORTRANGE_HIGH:
+ in6p->in6p_flags &= ~(IN6P_LOWPORT);
+ in6p->in6p_flags |= IN6P_HIGHPORT;
+ break;
+
+ case IPV6_PORTRANGE_LOW:
+ in6p->in6p_flags &= ~(IN6P_HIGHPORT);
+ in6p->in6p_flags |= IN6P_LOWPORT;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+#if defined(IPSEC) || defined(FAST_IPSEC)
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ size_t len = 0;
+ struct mbuf *m;
+
+ if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
+ break;
+ if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
+ break;
+ if (m) {
+ req = mtod(m, caddr_t);
+ len = m->m_len;
+ }
+ error = ipsec6_set_policy(in6p, optname, req,
+ len, privileged);
+ m_freem(m);
+ }
+ break;
+#endif /* KAME IPSEC */
+
+ case IPV6_FW_ADD:
+ case IPV6_FW_DEL:
+ case IPV6_FW_FLUSH:
+ case IPV6_FW_ZERO:
+ {
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+
+ if (ip6_fw_ctl_ptr == NULL)
+ return EINVAL;
+ /* XXX */
+ if ((error = soopt_getm(sopt, &m)) != 0)
+ break;
+ /* XXX */
+ if ((error = soopt_mcopyin(sopt, m)) != 0)
+ break;
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+ m = *mp;
+ }
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+
+ case SOPT_GET:
+ switch (optname) {
+
+ case IPV6_PKTOPTIONS:
+ if (in6p->in6p_options) {
+ struct mbuf *m;
+ m = m_copym(in6p->in6p_options,
+ 0, M_COPYALL, M_TRYWAIT);
+ error = soopt_mcopyout(sopt, m);
+ if (error == 0)
+ m_freem(m);
+ } else
+ sopt->sopt_valsize = 0;
+ break;
+
+ case IPV6_UNICAST_HOPS:
+ case IPV6_CHECKSUM:
+
+ case IPV6_FAITH:
+ case IPV6_V6ONLY:
+ case IPV6_PORTRANGE:
+ switch (optname) {
+
+ case IPV6_UNICAST_HOPS:
+ optval = in6p->in6p_hops;
+ break;
+
+ case IPV6_CHECKSUM:
+ optval = in6p->in6p_cksum;
+ break;
+
+ case IPV6_FAITH:
+ optval = OPTBIT(IN6P_FAITH);
+ break;
+
+ case IPV6_V6ONLY:
+ optval = OPTBIT(IN6P_IPV6_V6ONLY);
+ break;
+
+ case IPV6_PORTRANGE:
+ {
+ int flags;
+ flags = in6p->in6p_flags;
+ if (flags & IN6P_HIGHPORT)
+ optval = IPV6_PORTRANGE_HIGH;
+ else if (flags & IN6P_LOWPORT)
+ optval = IPV6_PORTRANGE_LOW;
+ else
+ optval = 0;
+ break;
+ }
+ }
+ error = sooptcopyout(sopt, &optval,
+ 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:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+ {
+ struct mbuf *m;
+ error = ip6_getmoptions(sopt->sopt_name,
+ in6p->in6p_moptions, &m);
+ if (error == 0)
+ error = sooptcopyout(sopt,
+ mtod(m, char *), m->m_len);
+ m_freem(m);
+ }
+ break;
+
+#if defined(IPSEC) || defined(FAST_IPSEC)
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ size_t len = 0;
+ struct mbuf *m = NULL;
+ struct mbuf **mp = &m;
+
+ error = soopt_getm(sopt, &m); /* XXX */
+ if (error != 0)
+ break;
+ error = soopt_mcopyin(sopt, m); /* XXX */
+ if (error != 0)
+ break;
+ if (m) {
+ req = mtod(m, caddr_t);
+ len = m->m_len;
+ }
+ error = ipsec6_get_policy(in6p, req, len, mp);
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /*XXX*/
+ if (error == 0 && m)
+ m_freem(m);
+ break;
+ }
+#endif /* KAME IPSEC */
+
+ case IPV6_FW_GET:
+ {
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+
+ if (ip6_fw_ctl_ptr == NULL)
+ {
+ return EINVAL;
+ }
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /* XXX */
+ if (error == 0 && m)
+ m_freem(m);
+ }
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ } else {
+ error = EINVAL;
+ }
+ return(error);
+}
+
+/*
+ * 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;
+ struct mbuf *m;
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ struct ip6_pktopts *opt = *pktopt;
+ int error = 0;
+ struct thread *td = sopt->sopt_td;
+ int priv = 0;
+
+ /* turn off any old options. */
+ if (opt) {
+#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 = NULL;
+
+ if (!m || m->m_len == 0) {
+ /*
+ * Only turning off any previous options, regardless of
+ * whether the opt is just created or given.
+ */
+ free(opt, M_IP6OPT);
+ return(0);
+ }
+
+ /* set options specified by user. */
+ if (td && !suser(td))
+ priv = 1;
+ if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
+ ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
+ free(opt, M_IP6OPT);
+ return(error);
+ }
+ *pktopt = opt;
+ return(0);
+}
+
+/*
+ * 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)
+ return (NULL);
+ 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:
+ 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);
+ free(dst, 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
+ip6_setmoptions(optname, im6op, m)
+ int optname;
+ struct ip6_moptions **im6op;
+ struct mbuf *m;
+{
+ int error = 0;
+ u_int loop, ifindex;
+ struct ipv6_mreq *mreq;
+ struct ifnet *ifp;
+ struct ip6_moptions *im6o = *im6op;
+ struct route_in6 ro;
+ struct sockaddr_in6 *dst;
+ struct in6_multi_mship *imm;
+ struct thread *td = curthread; /* XXX */
+
+ if (im6o == NULL) {
+ /*
+ * No multicast option buffer attached to the pcb;
+ * allocate one and initialize to default values.
+ */
+ im6o = (struct ip6_moptions *)
+ malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
+
+ if (im6o == NULL)
+ return(ENOBUFS);
+ *im6op = im6o;
+ im6o->im6o_multicast_ifp = NULL;
+ im6o->im6o_multicast_hlim = ip6_defmcasthlim;
+ im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
+ LIST_INIT(&im6o->im6o_memberships);
+ }
+
+ switch (optname) {
+
+ case IPV6_MULTICAST_IF:
+ /*
+ * Select the interface for outgoing multicast packets.
+ */
+ if (m == NULL || m->m_len != sizeof(u_int)) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex));
+ if (ifindex < 0 || if_index < ifindex) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ ifp = ifnet_byindex(ifindex);
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ im6o->im6o_multicast_ifp = ifp;
+ break;
+
+ case IPV6_MULTICAST_HOPS:
+ {
+ /*
+ * Set the IP6 hoplimit for outgoing multicast packets.
+ */
+ int optval;
+ if (m == NULL || m->m_len != sizeof(int)) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(mtod(m, u_int *), &optval, sizeof(optval));
+ if (optval < -1 || optval >= 256)
+ error = EINVAL;
+ else if (optval == -1)
+ im6o->im6o_multicast_hlim = ip6_defmcasthlim;
+ else
+ im6o->im6o_multicast_hlim = optval;
+ break;
+ }
+
+ case IPV6_MULTICAST_LOOP:
+ /*
+ * Set the loopback flag for outgoing multicast packets.
+ * Must be zero or one.
+ */
+ 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;
+ }
+ im6o->im6o_multicast_loop = loop;
+ break;
+
+ case IPV6_JOIN_GROUP:
+ /*
+ * Add a multicast group membership.
+ * Group must be a valid IP6 multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ipv6_mreq *);
+ if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
+ /*
+ * We use the unspecified address to specify to accept
+ * all multicast addresses. Only super user is allowed
+ * to do this.
+ */
+ if (suser(td))
+ {
+ error = EACCES;
+ break;
+ }
+ } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * If the interface is specified, validate it.
+ */
+ if (mreq->ipv6mr_interface < 0
+ || if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ /*
+ * If no interface was explicitly specified, choose an
+ * appropriate one according to the given multicast address.
+ */
+ if (mreq->ipv6mr_interface == 0) {
+ /*
+ * If the multicast address is in node-local scope,
+ * the interface should be a loopback interface.
+ * Otherwise, look up the routing table for the
+ * address, and choose the outgoing interface.
+ * XXX: is it a good approach?
+ */
+ if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) {
+ ifp = &loif[0];
+ } else {
+ ro.ro_rt = NULL;
+ dst = (struct sockaddr_in6 *)&ro.ro_dst;
+ bzero(dst, sizeof(*dst));
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_family = AF_INET6;
+ dst->sin6_addr = mreq->ipv6mr_multiaddr;
+ rtalloc((struct route *)&ro);
+ if (ro.ro_rt == NULL) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ ifp = ro.ro_rt->rt_ifp;
+ rtfree(ro.ro_rt);
+ }
+ } else
+ ifp = ifnet_byindex(mreq->ipv6mr_interface);
+
+ /*
+ * See if we found an interface, and confirm that it
+ * supports multicast
+ */
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Put interface index into the multicast address,
+ * if the address has link-local scope.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
+ mreq->ipv6mr_multiaddr.s6_addr16[1]
+ = htons(mreq->ipv6mr_interface);
+ }
+ /*
+ * See if the membership already exists.
+ */
+ for (imm = im6o->im6o_memberships.lh_first;
+ imm != NULL; imm = imm->i6mm_chain.le_next)
+ if (imm->i6mm_maddr->in6m_ifp == ifp &&
+ IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
+ &mreq->ipv6mr_multiaddr))
+ break;
+ if (imm != NULL) {
+ error = EADDRINUSE;
+ break;
+ }
+ /*
+ * Everything looks good; add a new record to the multicast
+ * address list for the given interface.
+ */
+ imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK);
+ if (imm == NULL) {
+ error = ENOBUFS;
+ break;
+ }
+ if ((imm->i6mm_maddr =
+ in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
+ free(imm, M_IPMADDR);
+ break;
+ }
+ LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+ break;
+
+ case IPV6_LEAVE_GROUP:
+ /*
+ * Drop a multicast group membership.
+ * Group must be a valid IP6 multicast address.
+ */
+ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
+ error = EINVAL;
+ break;
+ }
+ mreq = mtod(m, struct ipv6_mreq *);
+ if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
+ if (suser(td)) {
+ error = EACCES;
+ break;
+ }
+ } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * If an interface address was specified, get a pointer
+ * to its ifnet structure.
+ */
+ if (mreq->ipv6mr_interface < 0
+ || if_index < mreq->ipv6mr_interface) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ ifp = ifnet_byindex(mreq->ipv6mr_interface);
+ /*
+ * Put interface index into the multicast address,
+ * if the address has link-local scope.
+ */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
+ mreq->ipv6mr_multiaddr.s6_addr16[1]
+ = htons(mreq->ipv6mr_interface);
+ }
+ /*
+ * Find the membership in the membership list.
+ */
+ for (imm = im6o->im6o_memberships.lh_first;
+ imm != NULL; imm = imm->i6mm_chain.le_next) {
+ if ((ifp == NULL ||
+ imm->i6mm_maddr->in6m_ifp == ifp) &&
+ IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
+ &mreq->ipv6mr_multiaddr))
+ break;
+ }
+ if (imm == NULL) {
+ /* Unable to resolve interface */
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ /*
+ * Give up the multicast address record to which the
+ * membership points.
+ */
+ LIST_REMOVE(imm, i6mm_chain);
+ in6_delmulti(imm->i6mm_maddr);
+ free(imm, M_IPMADDR);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * If all options have default values, no need to keep the mbuf.
+ */
+ if (im6o->im6o_multicast_ifp == NULL &&
+ im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
+ im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
+ im6o->im6o_memberships.lh_first == NULL) {
+ free(*im6op, M_IPMOPTS);
+ *im6op = NULL;
+ }
+
+ return(error);
+}
+
+/*
+ * Return the IP6 multicast options in response to user getsockopt().
+ */
+static int
+ip6_getmoptions(optname, im6o, mp)
+ int optname;
+ struct ip6_moptions *im6o;
+ struct mbuf **mp;
+{
+ u_int *hlim, *loop, *ifindex;
+
+ *mp = m_get(M_TRYWAIT, MT_HEADER); /* XXX */
+
+ switch (optname) {
+
+ case IPV6_MULTICAST_IF:
+ ifindex = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL || im6o->im6o_multicast_ifp == NULL)
+ *ifindex = 0;
+ else
+ *ifindex = im6o->im6o_multicast_ifp->if_index;
+ return(0);
+
+ case IPV6_MULTICAST_HOPS:
+ hlim = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL)
+ *hlim = ip6_defmcasthlim;
+ else
+ *hlim = im6o->im6o_multicast_hlim;
+ return(0);
+
+ case IPV6_MULTICAST_LOOP:
+ loop = mtod(*mp, u_int *);
+ (*mp)->m_len = sizeof(u_int);
+ if (im6o == NULL)
+ *loop = ip6_defmcasthlim;
+ else
+ *loop = im6o->im6o_multicast_loop;
+ return(0);
+
+ default:
+ return(EOPNOTSUPP);
+ }
+}
+
+/*
+ * Discard the IP6 multicast options.
+ */
+void
+ip6_freemoptions(im6o)
+ struct ip6_moptions *im6o;
+{
+ struct in6_multi_mship *imm;
+
+ if (im6o == NULL)
+ return;
+
+ while ((imm = im6o->im6o_memberships.lh_first) != NULL) {
+ LIST_REMOVE(imm, i6mm_chain);
+ if (imm->i6mm_maddr)
+ in6_delmulti(imm->i6mm_maddr);
+ free(imm, M_IPMADDR);
+ }
+ free(im6o, M_IPMOPTS);
+}
+
+/*
+ * Set IPv6 outgoing packet options based on advanced API.
+ */
+int
+ip6_setpktoptions(control, opt, priv, needcopy)
+ struct mbuf *control;
+ struct ip6_pktopts *opt;
+ int priv, needcopy;
+{
+ struct cmsghdr *cm = 0;
+
+ if (control == 0 || opt == 0)
+ return(EINVAL);
+
+ init_ip6pktopts(opt);
+
+ /*
+ * XXX: Currently, we assume all the optional information is stored
+ * in a single mbuf.
+ */
+ if (control->m_next)
+ return(EINVAL);
+
+ 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;
+
+ /*
+ * 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);
+ 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] =
+ htons(opt->ip6po_pktinfo->ipi6_ifindex);
+
+ if (opt->ip6po_pktinfo->ipi6_ifindex > if_index
+ || opt->ip6po_pktinfo->ipi6_ifindex < 0) {
+ 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 in6_ifaddr *ia6;
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr =
+ 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;
+
+ case IPV6_HOPLIMIT:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(int)))
+ return(EINVAL);
+
+ opt->ip6po_hlim = *(int *)CMSG_DATA(cm);
+ if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255)
+ return(EINVAL);
+ break;
+
+ 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);
+
+ 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);
+ 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);
+
+ /*
+ * 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)
+ 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);
+ rth = (struct ip6_rthdr *)CMSG_DATA(cm);
+ rthlen = (rth->ip6r_len + 1) << 3;
+ if (cm->cmsg_len != CMSG_LEN(rthlen))
+ return(EINVAL);
+
+ switch (rth->ip6r_type) {
+ case IPV6_RTHDR_TYPE_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); /* 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);
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Routine called from ip6_output() to loop back a copy of an IP6 multicast
+ * packet to the input queue of a specified interface. Note that this
+ * calls the output routine of the loopback "driver", but with an interface
+ * pointer that might NOT be &loif -- easier than replicating that code here.
+ */
+void
+ip6_mloopback(ifp, m, dst)
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct sockaddr_in6 *dst;
+{
+ struct mbuf *copym;
+ struct ip6_hdr *ip6;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym == NULL)
+ return;
+
+ /*
+ * Make sure to deep-copy IPv6 header portion in case the data
+ * is in an mbuf cluster, so that we can safely override the IPv6
+ * header portion later.
+ */
+ if ((copym->m_flags & M_EXT) != 0 ||
+ copym->m_len < sizeof(struct ip6_hdr)) {
+ copym = m_pullup(copym, sizeof(struct ip6_hdr));
+ if (copym == NULL)
+ return;
+ }
+
+#ifdef DIAGNOSTIC
+ if (copym->m_len < sizeof(*ip6)) {
+ m_freem(copym);
+ return;
+ }
+#endif
+
+ 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
+
+ (void)if_simloop(ifp, copym, dst->sin6_family, 0);
+}
+
+/*
+ * Chop IPv6 header off from the payload.
+ */
+static int
+ip6_splithdr(m, exthdrs)
+ struct mbuf *m;
+ struct ip6_exthdrs *exthdrs;
+{
+ struct mbuf *mh;
+ struct ip6_hdr *ip6;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (m->m_len > sizeof(*ip6)) {
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (mh == 0) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ M_MOVE_PKTHDR(mh, m);
+ MH_ALIGN(mh, sizeof(*ip6));
+ m->m_len -= sizeof(*ip6);
+ m->m_data += sizeof(*ip6);
+ mh->m_next = m;
+ m = mh;
+ m->m_len = sizeof(*ip6);
+ bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
+ }
+ exthdrs->ip6e_ip6 = m;
+ return 0;
+}
+
+/*
+ * Compute IPv6 extension header length.
+ */
+int
+ip6_optlen(in6p)
+ struct in6pcb *in6p;
+{
+ int len;
+
+ if (!in6p->in6p_outputopts)
+ return 0;
+
+ len = 0;
+#define elen(x) \
+ (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0)
+
+ len += elen(in6p->in6p_outputopts->ip6po_hbh);
+ 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
new file mode 100644
index 0000000..7ffdf1e
--- /dev/null
+++ b/sys/netinet6/ip6_var.h
@@ -0,0 +1,353 @@
+/* $FreeBSD$ */
+/* $KAME: ip6_var.h,v 1.62 2001/05/03 14:51:48 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IP6_VAR_H_
+#define _NETINET6_IP6_VAR_H_
+
+/*
+ * IP6 reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ */
+struct ip6q {
+ u_int32_t ip6q_head;
+ u_int16_t ip6q_len;
+ u_int8_t ip6q_nxt; /* ip6f_nxt in first fragment */
+ u_int8_t ip6q_hlim;
+ struct ip6asfrag *ip6q_down;
+ struct ip6asfrag *ip6q_up;
+ u_int32_t ip6q_ident;
+ u_int8_t ip6q_arrive;
+ u_int8_t ip6q_ttl;
+ struct in6_addr ip6q_src, ip6q_dst;
+ struct ip6q *ip6q_next;
+ struct ip6q *ip6q_prev;
+ int ip6q_unfrglen; /* len of unfragmentable part */
+#ifdef notyet
+ u_char *ip6q_nxtp;
+#endif
+};
+
+struct ip6asfrag {
+ u_int32_t ip6af_head;
+ u_int16_t ip6af_len;
+ u_int8_t ip6af_nxt;
+ u_int8_t ip6af_hlim;
+ /* must not override the above members during reassembling */
+ struct ip6asfrag *ip6af_down;
+ struct ip6asfrag *ip6af_up;
+ struct mbuf *ip6af_m;
+ int ip6af_offset; /* offset in ip6af_m to next header */
+ int ip6af_frglen; /* fragmentable part length */
+ int ip6af_off; /* fragment offset */
+ u_int16_t ip6af_mff; /* more fragment bit in frag off */
+};
+
+#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m))
+
+struct ip6_moptions {
+ struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */
+ u_char im6o_multicast_loop; /* 1 >= hear sends if a member */
+ LIST_HEAD(, in6_multi_mship) im6o_memberships;
+};
+
+/*
+ * Control options for outgoing packets
+ */
+
+/* Routing header related info */
+struct ip6po_rhinfo {
+ struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */
+ struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */
+};
+#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr
+#define ip6po_route ip6po_rhinfo.ip6po_rhi_route
+
+struct ip6_pktopts {
+ struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */
+ 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 */
+
+ /* 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 */
+ u_quad_t ip6s_toosmall; /* not enough data */
+ u_quad_t ip6s_fragments; /* fragments received */
+ u_quad_t ip6s_fragdropped; /* frags dropped(dups, out of space) */
+ u_quad_t ip6s_fragtimeout; /* fragments timed out */
+ u_quad_t ip6s_fragoverflow; /* fragments that exceeded limit */
+ u_quad_t ip6s_forward; /* packets forwarded */
+ u_quad_t ip6s_cantforward; /* packets rcvd for unreachable dest */
+ u_quad_t ip6s_redirectsent; /* packets forwarded on same net */
+ u_quad_t ip6s_delivered; /* datagrams delivered to upper level*/
+ u_quad_t ip6s_localout; /* total ip packets generated here */
+ u_quad_t ip6s_odropped; /* lost packets due to nobufs, etc. */
+ u_quad_t ip6s_reassembled; /* total packets reassembled ok */
+ u_quad_t ip6s_fragmented; /* datagrams sucessfully fragmented */
+ u_quad_t ip6s_ofragments; /* output fragments created */
+ u_quad_t ip6s_cantfrag; /* don't fragment flag was set, etc. */
+ u_quad_t ip6s_badoptions; /* error in option processing */
+ u_quad_t ip6s_noroute; /* packets discarded due to no route */
+ u_quad_t ip6s_badvers; /* ip6 version != 6 */
+ u_quad_t ip6s_rawout; /* total raw ip packets generated */
+ u_quad_t ip6s_badscope; /* scope error */
+ u_quad_t ip6s_notmember; /* don't join this multicast group */
+ u_quad_t ip6s_nxthist[256]; /* next header history */
+ u_quad_t ip6s_m1; /* one mbuf */
+ u_quad_t ip6s_m2m[32]; /* two or more mbuf */
+ u_quad_t ip6s_mext1; /* one ext mbuf */
+ u_quad_t ip6s_mext2m; /* two or more ext mbuf */
+ u_quad_t ip6s_exthdrtoolong; /* ext hdr are not continuous */
+ u_quad_t ip6s_nogif; /* no match gif found */
+ u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */
+
+ /*
+ * statistics for improvement of the source address selection
+ * algorithm:
+ * XXX: hardcoded 16 = # of ip6 multicast scope types + 1
+ */
+ /* number of times that address selection fails */
+ u_quad_t ip6s_sources_none;
+ /* number of times that an address on the outgoing I/F is chosen */
+ u_quad_t ip6s_sources_sameif[16];
+ /* number of times that an address on a non-outgoing I/F is chosen */
+ u_quad_t ip6s_sources_otherif[16];
+ /*
+ * number of times that an address that has the same scope
+ * from the destination is chosen.
+ */
+ u_quad_t ip6s_sources_samescope[16];
+ /*
+ * number of times that an address that has a different scope
+ * from the destination is chosen.
+ */
+ u_quad_t ip6s_sources_otherscope[16];
+ /* number of times that a deprecated address is chosen */
+ u_quad_t ip6s_sources_deprecated[16];
+
+ u_quad_t ip6s_forward_cachehit;
+ u_quad_t ip6s_forward_cachemiss;
+};
+
+#ifdef _KERNEL
+/*
+ * 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 */
+#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */
+
+extern struct ip6stat ip6stat; /* statistics */
+extern u_int32_t ip6_id; /* fragment identifier */
+extern int ip6_defhlim; /* default hop limit */
+extern int ip6_defmcasthlim; /* default multicast hop limit */
+extern int ip6_forwarding; /* act as router? */
+extern int ip6_forward_srcrt; /* forward src-routed? */
+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_v6only;
+
+extern struct socket *ip6_mrouter; /* multicast routing daemon */
+extern int ip6_sendredirects; /* send IP redirects when forwarding? */
+extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */
+extern int ip6_sourcecheck; /* Verify source interface */
+extern int ip6_sourcecheck_interval; /* Interval between log messages */
+extern int ip6_accept_rtadv; /* Acts as a host not a router */
+extern int ip6_keepfaith; /* Firewall Aided Internet Translator */
+extern int ip6_log_interval;
+extern time_t ip6_log_time;
+extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */
+extern int ip6_dad_count; /* DupAddrDetectionTransmits */
+
+extern u_int32_t ip6_flow_seq;
+extern int ip6_auto_flowlabel;
+extern 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;
+
+struct inpcb;
+
+int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt));
+
+struct in6_ifaddr;
+void ip6_init __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 ip6aux *ip6_addaux __P((struct mbuf *));
+struct ip6aux *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 *));
+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 ip6_moptions *, struct ifnet **,
+ struct inpcb *));
+int ip6_ctloutput __P((struct socket *, struct sockopt *sopt));
+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 *));
+
+int route6_input __P((struct mbuf **, int *, int));
+
+void frag6_init __P((void));
+int frag6_input __P((struct mbuf **, int *, int));
+void frag6_slowtimo __P((void));
+void frag6_drain __P((void));
+
+void rip6_init __P((void));
+int rip6_input __P((struct mbuf **mp, int *offp, int proto));
+void rip6_ctlinput __P((int, struct sockaddr *, void *));
+int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt));
+int rip6_output __P((struct mbuf *, ...));
+int rip6_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *, struct thread *));
+
+int dest6_input __P((struct mbuf **, int *, int));
+int none_input __P((struct mbuf **, int *, int));
+#endif /* _KERNEL */
+
+#endif /* !_NETINET6_IP6_VAR_H_ */
diff --git a/sys/netinet6/ip6protosw.h b/sys/netinet6/ip6protosw.h
new file mode 100644
index 0000000..9730121
--- /dev/null
+++ b/sys/netinet6/ip6protosw.h
@@ -0,0 +1,163 @@
+/* $FreeBSD$ */
+/* $KAME: ip6protosw.h,v 1.25 2001/09/26 06:13:03 keiichi 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.
+ *
+ */
+
+/* BSDI protosw.h,v 2.3 1996/10/11 16:02:40 pjd Exp */
+
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)protosw.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NETINET6_IP6PROTOSW_H_
+#define _NETINET6_IP6PROTOSW_H_
+
+/*
+ * For pfil_head structure.
+ */
+#include <net/pfil.h>
+
+/*
+ * Protocol switch table for IPv6.
+ * All other definitions should refer to sys/protosw.h
+ */
+
+struct mbuf;
+struct sockaddr;
+struct socket;
+struct domain;
+struct thread;
+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 {
+ short pr_type; /* socket type used for */
+ struct domain *pr_domain; /* domain protocol a member of */
+ short pr_protocol; /* protocol number */
+ short pr_flags; /* see below */
+
+/* protocol-protocol hooks */
+ int (*pr_input) /* input to protocol (from below) */
+ __P((struct mbuf **, int *, int));
+ int (*pr_output) /* output to protocol (from above) */
+ __P((struct mbuf *, ...));
+ void (*pr_ctlinput) /* control input (from below) */
+ __P((int, struct sockaddr *, void *));
+ int (*pr_ctloutput) /* control output (from above) */
+ __P((struct socket *, struct sockopt *));
+
+/* user-protocol hook */
+ int (*pr_usrreq) /* user request: see list below */
+ __P((struct socket *, int, struct mbuf *,
+ struct mbuf *, struct mbuf *, struct thread *));
+
+/* utility hooks */
+ void (*pr_init) /* initialization hook */
+ __P((void));
+
+ void (*pr_fasttimo) /* fast timeout (200ms) */
+ __P((void));
+ void (*pr_slowtimo) /* slow timeout (500ms) */
+ __P((void));
+ void (*pr_drain) /* flush any excess space possible */
+ __P((void));
+ struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */
+ struct pfil_head pr_pfh;
+};
+
+#ifdef _KERNEL
+extern struct ip6protosw inet6sw[];
+#endif
+
+#endif /* !_NETINET6_IP6PROTOSW_H_ */
diff --git a/sys/netinet6/ipcomp.h b/sys/netinet6/ipcomp.h
new file mode 100644
index 0000000..ed58768
--- /dev/null
+++ b/sys/netinet6/ipcomp.h
@@ -0,0 +1,71 @@
+/* $FreeBSD$ */
+/* $KAME: ipcomp.h,v 1.11 2001/09/04 08:43:19 itojun Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#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 */
+ u_int16_t comp_cpi; /* Compression parameter index */
+};
+
+/* well-known algorithm number (in CPI), from RFC2409 */
+#define IPCOMP_OUI 1 /* vendor specific */
+#define IPCOMP_DEFLATE 2 /* RFC2394 */
+#define IPCOMP_LZS 3 /* RFC2395 */
+#define IPCOMP_MAX 4
+
+#define IPCOMP_CPI_NEGOTIATE_MIN 256
+
+#ifdef _KERNEL
+struct ipcomp_algorithm {
+ int (*compress) __P((struct mbuf *, struct mbuf *, size_t *));
+ int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *));
+ size_t minplen; /* minimum required length for compression */
+};
+
+struct ipsecrequest;
+extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
+extern void ipcomp4_input __P((struct mbuf *, int));
+extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *));
+#endif /* KERNEL */
+
+#endif /* _NETINET6_IPCOMP_H_ */
diff --git a/sys/netinet6/ipcomp6.h b/sys/netinet6/ipcomp6.h
new file mode 100644
index 0000000..4a1046a
--- /dev/null
+++ b/sys/netinet6/ipcomp6.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#ifndef _NETINET6_IPCOMP6_H_
+#define _NETINET6_IPCOMP6_H_
+
+#ifdef _KERNEL
+extern int ipcomp6_input __P((struct mbuf **, int *, int));
+extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *,
+ struct ipsecrequest *));
+#endif /*KERNEL*/
+
+#endif /*_NETINET6_IPCOMP6_H_*/
diff --git a/sys/netinet6/ipcomp_core.c b/sys/netinet6/ipcomp_core.c
new file mode 100644
index 0000000..a6cdea5
--- /dev/null
+++ b/sys/netinet6/ipcomp_core.c
@@ -0,0 +1,358 @@
+/* $FreeBSD$ */
+/* $KAME: ipcomp_core.c,v 1.25 2001/07/26 06:53:17 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+#include <machine/cpu.h>
+
+#include <netinet6/ipcomp.h>
+#ifdef INET6
+#include <netinet6/ipcomp6.h>
+#endif
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+static void *deflate_alloc __P((void *, u_int, u_int));
+static void deflate_free __P((void *, void *));
+static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int));
+static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *));
+static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *));
+
+/*
+ * We need to use default window size (2^15 = 32Kbytes as of writing) for
+ * inbound case. Otherwise we get interop problem.
+ * Use negative value to avoid Adler32 checksum. This is an undocumented
+ * feature in zlib (see ipsec wg mailing list archive in January 2000).
+ */
+static int deflate_policy = Z_DEFAULT_COMPRESSION;
+static int deflate_window_out = -12;
+static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */
+static int deflate_memlevel = MAX_MEM_LEVEL;
+
+static const struct ipcomp_algorithm ipcomp_algorithms[] = {
+ { deflate_compress, deflate_decompress, 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;
+ u_int items;
+ u_int siz;
+{
+ void *ptr;
+ ptr = malloc(items * siz, M_TEMP, M_NOWAIT);
+ return ptr;
+}
+
+static void
+deflate_free(aux, ptr)
+ void *aux;
+ void *ptr;
+{
+ free(ptr, M_TEMP);
+}
+
+static int
+deflate_common(m, md, lenp, mode)
+ struct mbuf *m;
+ struct mbuf *md;
+ size_t *lenp;
+ int mode; /* 0: compress 1: decompress */
+{
+ struct mbuf *mprev;
+ struct mbuf *p;
+ struct mbuf *n = NULL, *n0 = NULL, **np;
+ z_stream zs;
+ int error = 0;
+ int zerror;
+ size_t offset;
+
+#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)
+ ;
+ if (!mprev)
+ panic("md is not in m in deflate_common");
+
+ bzero(&zs, sizeof(zs));
+ zs.zalloc = deflate_alloc;
+ zs.zfree = deflate_free;
+
+ zerror = mode ? inflateInit2(&zs, deflate_window_in)
+ : deflateInit2(&zs, deflate_policy, Z_DEFLATED,
+ deflate_window_out, deflate_memlevel,
+ Z_DEFAULT_STRATEGY);
+ if (zerror != Z_OK) {
+ error = ENOBUFS;
+ goto fail;
+ }
+
+ n0 = n = NULL;
+ np = &n0;
+ offset = 0;
+ zerror = 0;
+ p = md;
+ 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;
+ while (p && p->m_len == 0) {
+ p = p->m_next;
+ }
+ }
+
+ /* get output buffer */
+ if (zs.next_out == NULL || zs.avail_out == 0) {
+ MOREBLOCK();
+ }
+
+ 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;
+ }
+ }
+
+ if (zerror == Z_STREAM_END)
+ goto terminate;
+
+ /* termination */
+ while (1) {
+ /* get output buffer */
+ if (zs.next_out == NULL || zs.avail_out == 0) {
+ MOREBLOCK();
+ }
+
+ zerror = mode ? inflate(&zs, Z_SYNC_FLUSH)
+ : deflate(&zs, Z_FINISH);
+
+ if (zerror == Z_STREAM_END)
+ break;
+ else if (zerror == Z_OK) {
+ if (mode && zs.avail_out != 0)
+ goto terminate;
+ else
+ ; /* once more. */
+ } else {
+ 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) {
+ 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;
+ }
+ /* keep the final reply buffer into our chain */
+ if (n) {
+ n->m_len = zs.total_out - offset;
+ offset = zs.total_out;
+ *np = n;
+ np = &n->m_next;
+ n = NULL;
+ }
+
+ /* switch the mbuf to the new one */
+ mprev->m_next = n0;
+ m_freem(md);
+ *lenp = zs.total_out;
+
+ return 0;
+
+fail:
+ if (m)
+ m_freem(m);
+ if (n)
+ m_freem(n);
+ if (n0)
+ m_freem(n0);
+ return error;
+#undef MOREBLOCK
+}
+
+static int
+deflate_compress(m, md, lenp)
+ struct mbuf *m;
+ struct mbuf *md;
+ size_t *lenp;
+{
+ if (!m)
+ panic("m == NULL in deflate_compress");
+ if (!md)
+ panic("md == NULL in deflate_compress");
+ if (!lenp)
+ panic("lenp == NULL in deflate_compress");
+
+ return deflate_common(m, md, lenp, 0);
+}
+
+static int
+deflate_decompress(m, md, lenp)
+ struct mbuf *m;
+ struct mbuf *md;
+ size_t *lenp;
+{
+ if (!m)
+ panic("m == NULL in deflate_decompress");
+ if (!md)
+ panic("md == NULL in deflate_decompress");
+ if (!lenp)
+ panic("lenp == NULL in deflate_decompress");
+
+ return deflate_common(m, md, lenp, 1);
+}
diff --git a/sys/netinet6/ipcomp_input.c b/sys/netinet6/ipcomp_input.c
new file mode 100644
index 0000000..65be602
--- /dev/null
+++ b/sys/netinet6/ipcomp_input.c
@@ -0,0 +1,349 @@
+/* $FreeBSD$ */
+/* $KAME: ipcomp_input.c,v 1.25 2001/03/01 09:12:09 itojun Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+#include <machine/cpu.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet6/ipcomp.h>
+#ifdef INET6
+#include <netinet6/ipcomp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+#define IPLEN_FLIPPED
+
+#ifdef INET
+extern struct protosw inetsw[];
+
+void
+ipcomp4_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct mbuf *md;
+ struct ip *ip;
+ struct ipcomp *ipcomp;
+ const struct ipcomp_algorithm *algo;
+ u_int16_t cpi; /* host order */
+ u_int16_t nxt;
+ size_t hlen;
+ int error;
+ size_t newlen, olen;
+ struct secasvar *sav = NULL;
+ int proto;
+
+ if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) {
+ ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
+ "(packet too short)\n"));
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+
+ 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 *);
+ proto = ip->ip_p;
+ nxt = ipcomp->comp_nxt;
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ cpi = ntohs(ipcomp->comp_cpi);
+
+ if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
+ sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src,
+ (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi));
+ if (sav != NULL
+ && (sav->state == SADB_SASTATE_MATURE
+ || sav->state == SADB_SASTATE_DYING)) {
+ cpi = sav->alg_enc; /* XXX */
+ /* other parameters to look at? */
+ }
+ }
+ algo = ipcomp_algorithm_lookup(cpi);
+ if (!algo) {
+ ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n",
+ cpi));
+ ipsecstat.in_nosa++;
+ goto fail;
+ }
+
+ /* chop ipcomp header */
+ ipcomp = NULL;
+ 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);
+#else
+ ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp));
+#endif
+
+ olen = m->m_pkthdr.len;
+ newlen = m->m_pkthdr.len - off;
+ error = (*algo->decompress)(m, m->m_next, &newlen);
+ if (error != 0) {
+ if (error == EINVAL)
+ ipsecstat.in_inval++;
+ else if (error == ENOBUFS)
+ ipsecstat.in_nomem++;
+ m = NULL;
+ goto fail;
+ }
+ ipsecstat.in_comphist[cpi]++;
+
+ /*
+ * returning decompressed packet onto icmp is meaningless.
+ * mark it decrypted to prevent icmp from attaching original packet.
+ */
+ m->m_flags |= M_DECRYPTED;
+
+ m->m_pkthdr.len = off + newlen;
+ ip = mtod(m, struct ip *);
+ {
+ size_t len;
+#ifdef IPLEN_FLIPPED
+ len = ip->ip_len;
+#else
+ len = ntohs(ip->ip_len);
+#endif
+ /*
+ * be careful about underflow. also, do not assign exact value
+ * as ip_len is manipulated differently on *BSDs.
+ */
+ len += m->m_pkthdr.len;
+ len -= olen;
+ if (len & ~0xffff) {
+ /* packet too big after decompress */
+ ipsecstat.in_inval++;
+ goto fail;
+ }
+#ifdef IPLEN_FLIPPED
+ ip->ip_len = len & 0xffff;
+#else
+ ip->ip_len = htons(len & 0xffff);
+#endif
+ ip->ip_p = nxt;
+ }
+
+ if (sav) {
+ key_sa_recordxfer(sav, m);
+ 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 ((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);
+ } else
+ m_freem(m);
+ m = NULL;
+
+ ipsecstat.in_success++;
+ return;
+
+fail:
+ if (sav)
+ key_freesav(sav);
+ if (m)
+ m_freem(m);
+ return;
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+ipcomp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m, *md;
+ int off;
+ struct ip6_hdr *ip6;
+ struct ipcomp *ipcomp;
+ 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;
+
+ 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 *);
+ nxt = ipcomp->comp_nxt;
+
+ cpi = ntohs(ipcomp->comp_cpi);
+
+ if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
+ sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src,
+ (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi));
+ if (sav != NULL
+ && (sav->state == SADB_SASTATE_MATURE
+ || sav->state == SADB_SASTATE_DYING)) {
+ cpi = sav->alg_enc; /* XXX */
+ /* other parameters to look at? */
+ }
+ }
+ algo = ipcomp_algorithm_lookup(cpi);
+ if (!algo) {
+ ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; "
+ "dropping the packet for simplicity\n", cpi));
+ ipsec6stat.in_nosa++;
+ goto fail;
+ }
+
+ /* 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)
+ ipsec6stat.in_inval++;
+ else if (error == ENOBUFS)
+ ipsec6stat.in_nomem++;
+ m = NULL;
+ goto fail;
+ }
+ ipsec6stat.in_comphist[cpi]++;
+ m->m_pkthdr.len = off + newlen;
+
+ /*
+ * returning decompressed packet onto icmp is meaningless.
+ * mark it decrypted to prevent icmp from attaching original packet.
+ */
+ m->m_flags |= M_DECRYPTED;
+
+ /* update next header field */
+ prvnxtp = ip6_get_prevhdr(m, off);
+ *prvnxtp = nxt;
+
+ /*
+ * 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;
+ }
+ *offp = off;
+ *mp = m;
+ ipsec6stat.in_success++;
+ return nxt;
+
+fail:
+ if (m)
+ m_freem(m);
+ if (sav)
+ key_freesav(sav);
+ return IPPROTO_DONE;
+}
+#endif /* INET6 */
diff --git a/sys/netinet6/ipcomp_output.c b/sys/netinet6/ipcomp_output.c
new file mode 100644
index 0000000..f07e06c
--- /dev/null
+++ b/sys/netinet6/ipcomp_output.c
@@ -0,0 +1,384 @@
+/* $FreeBSD$ */
+/* $KAME: ipcomp_output.c,v 1.25 2002/06/09 14:44:00 itojun Exp $ */
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+#include <machine/cpu.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet6/ipcomp.h>
+#ifdef INET6
+#include <netinet6/ipcomp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *,
+ struct ipsecrequest *, int));
+
+/*
+ * Modify the packet so that the payload is compressed.
+ * The mbuf (m) must start with IPv4 or IPv6 header.
+ * On failure, free the given mbuf and return non-zero.
+ *
+ * on invocation:
+ * m nexthdrp md
+ * v v v
+ * IP ......... payload
+ * during the encryption:
+ * m nexthdrp mprev md
+ * v v v v
+ * IP ............... ipcomp payload
+ * <-----><----->
+ * complen plen
+ * <-> hlen
+ * <-----------------> compoff
+ */
+static int
+ipcomp_output(m, nexthdrp, md, isr, af)
+ struct mbuf *m;
+ u_char *nexthdrp;
+ struct mbuf *md;
+ struct ipsecrequest *isr;
+ int af;
+{
+ struct mbuf *n;
+ struct mbuf *md0;
+ struct mbuf *mcopy;
+ struct mbuf *mprev;
+ struct ipcomp *ipcomp;
+ struct secasvar *sav = isr->sav;
+ 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:
+ ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af));
+ return 0; /* no change at all */
+ }
+
+ /* grab parameters */
+ algo = ipcomp_algorithm_lookup(sav->alg_enc);
+ if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) {
+ stat->out_inval++;
+ m_freem(m);
+ return EINVAL;
+ }
+ if ((sav->flags & SADB_X_EXT_RAWCPI) == 0)
+ cpi = sav->alg_enc;
+ else
+ cpi = ntohl(sav->spi) & 0xffff;
+
+ /* compute original payload length */
+ plen = 0;
+ for (n = md; n; n = n->m_next)
+ plen += n->m_len;
+
+ /* if the payload is short enough, we don't need to compress */
+ if (plen < algo->minplen)
+ return 0;
+
+ /*
+ * 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_DONTWAIT);
+ if (mcopy == NULL) {
+ error = ENOBUFS;
+ return 0;
+ }
+ md0 = m_copym(md, 0, M_COPYALL, M_DONTWAIT);
+ if (md0 == NULL) {
+ m_freem(mcopy);
+ error = ENOBUFS;
+ return 0;
+ }
+ plen0 = plen;
+
+ /* make the packet over-writable */
+ for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+ ;
+ if (mprev == NULL || mprev->m_next != md) {
+ ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n",
+ afnumber));
+ 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;
+ }
+ mprev->m_next = md;
+
+ /* compress data part */
+ if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) {
+ ipseclog((LOG_ERR, "packet compression failure\n"));
+ m = NULL;
+ m_freem(md0);
+ m_freem(mcopy);
+ stat->out_inval++;
+ error = EINVAL;
+ goto fail;
+ }
+ stat->out_comphist[sav->alg_enc]++;
+ md = mprev->m_next;
+
+ /*
+ * if the packet became bigger, meaningless to use IPComp.
+ * we've only wasted our cpu time.
+ */
+ if (plen0 < plen) {
+ m_freem(md);
+ m_freem(mcopy);
+ mprev->m_next = md0;
+ return 0;
+ }
+
+ /*
+ * no need to backout change beyond here.
+ */
+ m_freem(md0);
+ md0 = NULL;
+
+ m->m_pkthdr.len -= plen0;
+ m->m_pkthdr.len += plen;
+
+ {
+ /*
+ * insert IPComp header.
+ */
+#ifdef INET
+ struct ip *ip = NULL;
+#endif
+#ifdef INET6
+ struct ip6_hdr *ip6 = NULL;
+#endif
+ size_t hlen = 0; /* ip header len */
+ size_t complen = sizeof(struct ipcomp);
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ hlen = sizeof(*ip6);
+ break;
+#endif
+ }
+
+ compoff = m->m_pkthdr.len - plen;
+
+ /*
+ * grow the mbuf to accomodate ipcomp header.
+ * before: IP ... payload
+ * after: IP ... ipcomp payload
+ */
+ if (M_LEADINGSPACE(md) < complen) {
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ error = ENOBUFS;
+ goto fail;
+ }
+ n->m_len = complen;
+ mprev->m_next = n;
+ n->m_next = md;
+ m->m_pkthdr.len += complen;
+ ipcomp = mtod(n, struct ipcomp *);
+ } else {
+ md->m_len += complen;
+ md->m_data -= complen;
+ m->m_pkthdr.len += complen;
+ ipcomp = mtod(md, struct ipcomp *);
+ }
+
+ bzero(ipcomp, sizeof(*ipcomp));
+ ipcomp->comp_nxt = *nexthdrp;
+ *nexthdrp = IPPROTO_IPCOMP;
+ ipcomp->comp_cpi = htons(cpi);
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if (compoff + complen + plen < IP_MAXPACKET)
+ ip->ip_len = htons(compoff + complen + plen);
+ else {
+ ipseclog((LOG_ERR,
+ "IPv4 ESP output: size exceeds limit\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ error = EMSGSIZE;
+ goto fail;
+ }
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ /* total packet length will be computed in ip6_output() */
+ break;
+#endif
+ }
+ }
+
+ if (!m) {
+ ipseclog((LOG_DEBUG,
+ "NULL mbuf after compression in ipcomp%d_output",
+ afnumber));
+ stat->out_inval++;
+ }
+ stat->out_success++;
+
+ /* compute byte lifetime against original packet */
+ key_sa_recordxfer(sav, mcopy);
+ m_freem(mcopy);
+
+ return 0;
+
+fail:
+#if 1
+ return error;
+#else
+ panic("something bad in ipcomp_output");
+#endif
+}
+
+#ifdef INET
+int
+ipcomp4_output(m, isr)
+ struct mbuf *m;
+ struct ipsecrequest *isr;
+{
+ struct ip *ip;
+ if (m->m_len < sizeof(struct ip)) {
+ ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
+ ipsecstat.out_inval++;
+ m_freem(m);
+ return 0;
+ }
+ ip = mtod(m, struct ip *);
+ /* XXX assumes that m->m_next points to payload */
+ return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+ipcomp6_output(m, nexthdrp, md, isr)
+ struct mbuf *m;
+ u_char *nexthdrp;
+ struct mbuf *md;
+ struct ipsecrequest *isr;
+{
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
+ ipsec6stat.out_inval++;
+ m_freem(m);
+ return 0;
+ }
+ return ipcomp_output(m, nexthdrp, md, isr, AF_INET6);
+}
+#endif /* INET6 */
diff --git a/sys/netinet6/ipsec.c b/sys/netinet6/ipsec.c
new file mode 100644
index 0000000..6ae18f0
--- /dev/null
+++ b/sys/netinet6/ipsec.c
@@ -0,0 +1,3504 @@
+/* $FreeBSD$ */
+/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane 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.
+ */
+
+/*
+ * IPsec controller part.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/ip_ecn.h>
+#ifdef INET6
+#include <netinet6/ip6_ecn.h>
+#endif
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <netinet/ip6.h>
+#ifdef INET6
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet/in_pcb.h>
+#ifdef INET6
+#include <netinet/icmp6.h>
+#endif
+
+#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
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+
+#include <machine/in_cksum.h>
+
+#include <net/net_osdep.h>
+
+#ifdef IPSEC_DEBUG
+int ipsec_debug = 1;
+#else
+int ipsec_debug = 0;
+#endif
+
+struct ipsecstat ipsecstat;
+int ip4_ah_cleartos = 1;
+int ip4_ah_offsetmask = 0; /* maybe IP_DF? */
+int ip4_ipsec_dfbit = 0; /* DF bit on encap. 0: clear 1: set 2: copy */
+int ip4_esp_trans_deflev = IPSEC_LEVEL_USE;
+int ip4_esp_net_deflev = IPSEC_LEVEL_USE;
+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,
+ stats, CTLFLAG_RD, &ipsecstat, ipsecstat, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY,
+ def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
+ CTLFLAG_RW, &ip4_esp_trans_deflev, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,
+ CTLFLAG_RW, &ip4_esp_net_deflev, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,
+ CTLFLAG_RW, &ip4_ah_trans_deflev, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
+ CTLFLAG_RW, &ip4_ah_net_deflev, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS,
+ ah_cleartos, CTLFLAG_RW, &ip4_ah_cleartos, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK,
+ ah_offsetmask, CTLFLAG_RW, &ip4_ah_offsetmask, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT,
+ dfbit, CTLFLAG_RW, &ip4_ipsec_dfbit, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN,
+ ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG,
+ debug, CTLFLAG_RW, &ipsec_debug, 0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD,
+ esp_randpad, CTLFLAG_RW, &ip4_esp_randpad, 0, "");
+
+#ifdef INET6
+struct ipsecstat ipsec6stat;
+int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;
+int ip6_esp_net_deflev = IPSEC_LEVEL_USE;
+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,
+ stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY,
+ def_policy, CTLFLAG_RW, &ip6_def_policy.policy, 0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
+ CTLFLAG_RW, &ip6_esp_trans_deflev, 0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,
+ CTLFLAG_RW, &ip6_esp_net_deflev, 0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,
+ CTLFLAG_RW, &ip6_ah_trans_deflev, 0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
+ CTLFLAG_RW, &ip6_ah_net_deflev, 0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_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 *, 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 *, 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 *));
+static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src));
+static int ipsec_set_policy __P((struct secpolicy **pcb_sp,
+ int optname, caddr_t request, size_t len, int priv));
+static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp));
+static void vshiftl __P((unsigned char *, int, int));
+static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *));
+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
+
+/*
+ * For OUTBOUND packet having a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT: NULL: no apropreate SP found, the following value is set to error.
+ * 0 : bypass
+ * EACCES : discard packet.
+ * ENOENT : ipsec_acquire() in progress, maybe.
+ * others : error occured.
+ * others: a pointer to SP
+ *
+ * NOTE: IPv6 mapped adddress concern is implemented here.
+ */
+struct secpolicy *
+ipsec4_getpolicybypcb(m, dir, inp, error)
+ struct mbuf *m;
+ u_int dir;
+ struct inpcb *inp;
+ int *error;
+{
+ struct inpcbpolicy *pcbsp = NULL;
+ struct secpolicy *currsp = NULL; /* policy on socket */
+ struct secpolicy *kernsp = NULL; /* policy on kernel */
+
+ /* sanity check */
+ if (m == NULL || inp == NULL || error == NULL)
+ panic("ipsec4_getpolicybysock: NULL pointer was passed.");
+
+ /* set spidx in pcb */
+#ifdef INET6
+ if (inp->inp_vflag & INP_IPV6PROTO)
+ *error = ipsec6_setspidx_in6pcb(m, inp);
+ else
+#endif
+ *error = ipsec4_setspidx_inpcb(m, inp);
+ if (*error)
+ return NULL;
+ pcbsp = inp->inp_sp;
+
+ /* sanity check */
+ if (pcbsp == NULL)
+ panic("ipsec4_getpolicybysock: pcbsp is NULL.");
+
+ switch (dir) {
+ case IPSEC_DIR_INBOUND:
+ currsp = pcbsp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ currsp = pcbsp->sp_out;
+ break;
+ default:
+ panic("ipsec4_getpolicybysock: illegal direction.");
+ }
+
+ /* sanity check */
+ if (currsp == NULL)
+ panic("ipsec4_getpolicybysock: currsp is NULL.");
+
+ /* when privilieged socket */
+ if (pcbsp->priv) {
+ switch (currsp->policy) {
+ case IPSEC_POLICY_BYPASS:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ case IPSEC_POLICY_ENTRUST:
+ /* look for a policy in SPD */
+ kernsp = key_allocsp(&currsp->spidx, dir);
+
+ /* SP found */
+ if (kernsp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec4_getpolicybysock called "
+ "to allocate SP:%p\n", kernsp));
+ *error = 0;
+ return kernsp;
+ }
+
+ /* no SP found */
+ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO,
+ "fixed system default policy: %d->%d\n",
+ ip4_def_policy.policy, IPSEC_POLICY_NONE));
+ ip4_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip4_def_policy.refcnt++;
+ *error = 0;
+ return &ip4_def_policy;
+
+ case IPSEC_POLICY_IPSEC:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ default:
+ ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+ "Invalid policy for PCB %d\n", currsp->policy));
+ *error = EINVAL;
+ return NULL;
+ }
+ /* NOTREACHED */
+ }
+
+ /* when non-privilieged socket */
+ /* look for a policy in SPD */
+ kernsp = key_allocsp(&currsp->spidx, dir);
+
+ /* SP found */
+ if (kernsp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec4_getpolicybysock called "
+ "to allocate SP:%p\n", kernsp));
+ *error = 0;
+ return kernsp;
+ }
+
+ /* no SP found */
+ switch (currsp->policy) {
+ case IPSEC_POLICY_BYPASS:
+ ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+ "Illegal policy for non-priviliged defined %d\n",
+ currsp->policy));
+ *error = EINVAL;
+ return NULL;
+
+ case IPSEC_POLICY_ENTRUST:
+ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO,
+ "fixed system default policy: %d->%d\n",
+ ip4_def_policy.policy, IPSEC_POLICY_NONE));
+ ip4_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip4_def_policy.refcnt++;
+ *error = 0;
+ return &ip4_def_policy;
+
+ case IPSEC_POLICY_IPSEC:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ default:
+ ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+ "Invalid policy for PCB %d\n", currsp->policy));
+ *error = EINVAL;
+ return NULL;
+ }
+ /* NOTREACHED */
+}
+
+struct secpolicy *
+ipsec4_getpolicybysock(m, dir, so, error)
+ struct mbuf *m;
+ u_int dir;
+ struct socket *so;
+ int *error;
+{
+
+ if (so == NULL)
+ panic("ipsec4_getpolicybysock: NULL pointer was passed.");
+ return (ipsec4_getpolicybypcb(m, dir, sotoinpcb(so), error));
+}
+
+/*
+ * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT: positive: a pointer to the entry for security policy leaf matched.
+ * NULL: no apropreate SP found, the following value is set to error.
+ * 0 : bypass
+ * EACCES : discard packet.
+ * ENOENT : ipsec_acquire() in progress, maybe.
+ * others : error occured.
+ */
+struct secpolicy *
+ipsec4_getpolicybyaddr(m, dir, flag, error)
+ struct mbuf *m;
+ u_int dir;
+ int flag;
+ int *error;
+{
+ struct secpolicy *sp = NULL;
+
+ /* sanity check */
+ if (m == NULL || error == NULL)
+ panic("ipsec4_getpolicybyaddr: NULL pointer was passed.");
+
+ {
+ struct secpolicyindex spidx;
+
+ bzero(&spidx, sizeof(spidx));
+
+ /* Make an index to look for a policy. */
+ *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m,
+ (flag & IP_FORWARDING) ? 0 : 1);
+
+ if (*error != 0)
+ return NULL;
+
+ sp = key_allocsp(&spidx, dir);
+ }
+
+ /* SP found */
+ if (sp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec4_getpolicybyaddr called "
+ "to allocate SP:%p\n", sp));
+ *error = 0;
+ return sp;
+ }
+
+ /* no SP found */
+ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO, "fixed system default policy:%d->%d\n",
+ ip4_def_policy.policy,
+ IPSEC_POLICY_NONE));
+ ip4_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip4_def_policy.refcnt++;
+ *error = 0;
+ return &ip4_def_policy;
+}
+
+#ifdef INET6
+/*
+ * For OUTBOUND packet having a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT: NULL: no apropreate SP found, the following value is set to error.
+ * 0 : bypass
+ * EACCES : discard packet.
+ * ENOENT : ipsec_acquire() in progress, maybe.
+ * others : error occured.
+ * others: a pointer to SP
+ */
+struct secpolicy *
+ipsec6_getpolicybypcb(m, dir, inp, error)
+ struct mbuf *m;
+ u_int dir;
+ struct inpcb *inp;
+ int *error;
+{
+ struct inpcbpolicy *pcbsp = NULL;
+ struct secpolicy *currsp = NULL; /* policy on socket */
+ struct secpolicy *kernsp = NULL; /* policy on kernel */
+
+ /* sanity check */
+ if (m == NULL || inp == NULL || error == NULL)
+ panic("ipsec6_getpolicybysock: NULL pointer was passed.");
+
+#ifdef DIAGNOSTIC
+ if ((inp->inp_vflag & INP_IPV6PROTO) == 0)
+ panic("ipsec6_getpolicybysock: socket domain != inet6");
+#endif
+
+ /* set spidx in pcb */
+ ipsec6_setspidx_in6pcb(m, inp);
+ pcbsp = inp->in6p_sp;
+
+ /* sanity check */
+ if (pcbsp == NULL)
+ panic("ipsec6_getpolicybysock: pcbsp is NULL.");
+
+ switch (dir) {
+ case IPSEC_DIR_INBOUND:
+ currsp = pcbsp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ currsp = pcbsp->sp_out;
+ break;
+ default:
+ panic("ipsec6_getpolicybysock: illegal direction.");
+ }
+
+ /* sanity check */
+ if (currsp == NULL)
+ panic("ipsec6_getpolicybysock: currsp is NULL.");
+
+ /* when privilieged socket */
+ if (pcbsp->priv) {
+ switch (currsp->policy) {
+ case IPSEC_POLICY_BYPASS:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ case IPSEC_POLICY_ENTRUST:
+ /* look for a policy in SPD */
+ kernsp = key_allocsp(&currsp->spidx, dir);
+
+ /* SP found */
+ if (kernsp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec6_getpolicybysock called "
+ "to allocate SP:%p\n", kernsp));
+ *error = 0;
+ return kernsp;
+ }
+
+ /* no SP found */
+ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO,
+ "fixed system default policy: %d->%d\n",
+ ip6_def_policy.policy, IPSEC_POLICY_NONE));
+ ip6_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip6_def_policy.refcnt++;
+ *error = 0;
+ return &ip6_def_policy;
+
+ case IPSEC_POLICY_IPSEC:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ default:
+ ipseclog((LOG_ERR, "ipsec6_getpolicybysock: "
+ "Invalid policy for PCB %d\n", currsp->policy));
+ *error = EINVAL;
+ return NULL;
+ }
+ /* NOTREACHED */
+ }
+
+ /* when non-privilieged socket */
+ /* look for a policy in SPD */
+ kernsp = key_allocsp(&currsp->spidx, dir);
+
+ /* SP found */
+ if (kernsp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec6_getpolicybysock called "
+ "to allocate SP:%p\n", kernsp));
+ *error = 0;
+ return kernsp;
+ }
+
+ /* no SP found */
+ switch (currsp->policy) {
+ case IPSEC_POLICY_BYPASS:
+ ipseclog((LOG_ERR, "ipsec6_getpolicybysock: "
+ "Illegal policy for non-priviliged defined %d\n",
+ currsp->policy));
+ *error = EINVAL;
+ return NULL;
+
+ case IPSEC_POLICY_ENTRUST:
+ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO,
+ "fixed system default policy: %d->%d\n",
+ ip6_def_policy.policy, IPSEC_POLICY_NONE));
+ ip6_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip6_def_policy.refcnt++;
+ *error = 0;
+ return &ip6_def_policy;
+
+ case IPSEC_POLICY_IPSEC:
+ currsp->refcnt++;
+ *error = 0;
+ return currsp;
+
+ default:
+ ipseclog((LOG_ERR,
+ "ipsec6_policybysock: Invalid policy for PCB %d\n",
+ currsp->policy));
+ *error = EINVAL;
+ return NULL;
+ }
+ /* NOTREACHED */
+}
+
+struct secpolicy *
+ipsec6_getpolicybysock(m, dir, so, error)
+ struct mbuf *m;
+ u_int dir;
+ struct socket *so;
+ int *error;
+{
+
+ if (so == NULL)
+ panic("ipsec6_getpolicybysock: NULL pointer was passed.");
+ return (ipsec6_getpolicybypcb(m, dir, sotoin6pcb(so), error));
+}
+
+/*
+ * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * `flag' means that packet is to be forwarded whether or not.
+ * flag = 1: forwad
+ * OUT: positive: a pointer to the entry for security policy leaf matched.
+ * NULL: no apropreate SP found, the following value is set to error.
+ * 0 : bypass
+ * EACCES : discard packet.
+ * ENOENT : ipsec_acquire() in progress, maybe.
+ * others : error occured.
+ */
+#ifndef IP_FORWARDING
+#define IP_FORWARDING 1
+#endif
+
+struct secpolicy *
+ipsec6_getpolicybyaddr(m, dir, flag, error)
+ struct mbuf *m;
+ u_int dir;
+ int flag;
+ int *error;
+{
+ struct secpolicy *sp = NULL;
+
+ /* sanity check */
+ if (m == NULL || error == NULL)
+ panic("ipsec6_getpolicybyaddr: NULL pointer was passed.");
+
+ {
+ struct secpolicyindex spidx;
+
+ bzero(&spidx, sizeof(spidx));
+
+ /* Make an index to look for a policy. */
+ *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m,
+ (flag & IP_FORWARDING) ? 0 : 1);
+
+ if (*error != 0)
+ return NULL;
+
+ sp = key_allocsp(&spidx, dir);
+ }
+
+ /* SP found */
+ if (sp != NULL) {
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec6_getpolicybyaddr called "
+ "to allocate SP:%p\n", sp));
+ *error = 0;
+ return sp;
+ }
+
+ /* no SP found */
+ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+ && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+ ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n",
+ ip6_def_policy.policy, IPSEC_POLICY_NONE));
+ ip6_def_policy.policy = IPSEC_POLICY_NONE;
+ }
+ ip6_def_policy.refcnt++;
+ *error = 0;
+ return &ip6_def_policy;
+}
+#endif /* INET6 */
+
+/*
+ * set IP address into spidx from mbuf.
+ * When Forwarding packet and ICMP echo reply, this function is used.
+ *
+ * IN: get the followings from mbuf.
+ * protocol family, src, dst, next protocol
+ * OUT:
+ * 0: success.
+ * other: failure, and set errno.
+ */
+int
+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.");
+
+ bzero(spidx, sizeof(*spidx));
+
+ error = ipsec_setspidx(m, spidx, needport);
+ if (error)
+ goto bad;
+ spidx->dir = dir;
+
+ return 0;
+
+ bad:
+ /* XXX initialize */
+ bzero(spidx, sizeof(*spidx));
+ return EINVAL;
+}
+
+static int
+ipsec4_setspidx_inpcb(m, pcb)
+ struct mbuf *m;
+ struct inpcb *pcb;
+{
+ struct secpolicyindex *spidx;
+ int error;
+
+ /* sanity check */
+ if (pcb == NULL)
+ panic("ipsec4_setspidx_inpcb: no PCB found.");
+ if (pcb->inp_sp == NULL)
+ panic("ipsec4_setspidx_inpcb: no inp_sp found.");
+ if (pcb->inp_sp->sp_out == NULL || pcb->inp_sp->sp_in == NULL)
+ panic("ipsec4_setspidx_inpcb: no sp_in/out found.");
+
+ 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;
+ error = ipsec_setspidx(m, spidx, 1);
+ if (error)
+ goto bad;
+ spidx->dir = IPSEC_DIR_INBOUND;
+
+ spidx = &pcb->inp_sp->sp_out->spidx;
+ error = ipsec_setspidx(m, spidx, 1);
+ if (error)
+ goto bad;
+ spidx->dir = IPSEC_DIR_OUTBOUND;
+
+ return 0;
+
+bad:
+ bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx));
+ bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx));
+ return error;
+}
+
+#ifdef INET6
+static int
+ipsec6_setspidx_in6pcb(m, pcb)
+ struct mbuf *m;
+ struct in6pcb *pcb;
+{
+ struct secpolicyindex *spidx;
+ int error;
+
+ /* sanity check */
+ if (pcb == NULL)
+ panic("ipsec6_setspidx_in6pcb: no PCB found.");
+ if (pcb->in6p_sp == NULL)
+ panic("ipsec6_setspidx_in6pcb: no in6p_sp found.");
+ if (pcb->in6p_sp->sp_out == NULL || pcb->in6p_sp->sp_in == NULL)
+ panic("ipsec6_setspidx_in6pcb: no sp_in/out found.");
+
+ bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx));
+ bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx));
+
+ spidx = &pcb->in6p_sp->sp_in->spidx;
+ error = ipsec_setspidx(m, spidx, 1);
+ if (error)
+ goto bad;
+ spidx->dir = IPSEC_DIR_INBOUND;
+
+ spidx = &pcb->in6p_sp->sp_out->spidx;
+ error = ipsec_setspidx(m, spidx, 1);
+ if (error)
+ goto bad;
+ spidx->dir = IPSEC_DIR_OUTBOUND;
+
+ return 0;
+
+bad:
+ bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx));
+ bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx));
+ return error;
+}
+#endif
+
+/*
+ * 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;
+
+ if (m == NULL)
+ panic("ipsec_setspidx: m == 0 passed.");
+
+ /*
+ * 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;
+ }
+
+ 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
+ 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
+ipsec4_get_ulp(m, spidx, needport)
+ struct mbuf *m;
+ struct secpolicyindex *spidx;
+ int needport;
+{
+ 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("ipsec4_get_ulp: NULL pointer was passed.");
+ if (m->m_pkthdr.len < sizeof(ip))
+ panic("ipsec4_get_ulp: too short");
+
+ /* set default */
+ spidx->ul_proto = IPSEC_ULPROTO_ANY;
+ ((struct sockaddr_in *)&spidx->src)->sin_port = IPSEC_PORT_ANY;
+ ((struct sockaddr_in *)&spidx->dst)->sin_port = IPSEC_PORT_ANY;
+
+ 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;
+
+ 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_in *)&spidx->src)->sin_port =
+ th.th_sport;
+ ((struct sockaddr_in *)&spidx->dst)->sin_port =
+ th.th_dport;
+ 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_in *)&spidx->src)->sin_port =
+ uh.uh_sport;
+ ((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;
+ }
+ }
+}
+
+/* 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;
+ struct sockaddr_in *sin;
+
+ if (m->m_len >= sizeof(*ip))
+ ip = mtod(m, struct ip *);
+ else {
+ m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
+ ip = &ipbuf;
+ }
+
+ 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;
+
+ 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_get_ulp(m, spidx, needport)
+ struct mbuf *m;
+ struct secpolicyindex *spidx;
+ int needport;
+{
+ int off, nxt;
+ struct tcphdr th;
+ struct udphdr uh;
+
+ /* sanity check */
+ if (m == NULL)
+ panic("ipsec6_get_ulp: NULL pointer was passed.");
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m));
+
+ /* 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;
+
+ nxt = -1;
+ off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
+ if (off < 0 || m->m_pkthdr.len < off)
+ return;
+
+ switch (nxt) {
+ case IPPROTO_TCP:
+ spidx->ul_proto = nxt;
+ if (!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;
+ }
+}
+
+/* assumes that m is sane */
+static int
+ipsec6_setspidx_ipaddr(m, spidx)
+ struct mbuf *m;
+ struct secpolicyindex *spidx;
+{
+ struct ip6_hdr *ip6 = NULL;
+ struct ip6_hdr ip6buf;
+ struct sockaddr_in6 *sin6;
+
+ if (m->m_len >= sizeof(*ip6))
+ ip6 = mtod(m, struct ip6_hdr *);
+ else {
+ m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
+ ip6 = &ip6buf;
+ }
+
+ 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 0;
+}
+#endif
+
+static struct inpcbpolicy *
+ipsec_newpcbpolicy()
+{
+ struct inpcbpolicy *p;
+
+ p = (struct inpcbpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+ return p;
+}
+
+static void
+ipsec_delpcbpolicy(p)
+ struct inpcbpolicy *p;
+{
+ free(p, M_SECA);
+}
+
+/* initialize policy in PCB */
+int
+ipsec_init_policy(so, pcb_sp)
+ struct socket *so;
+ struct inpcbpolicy **pcb_sp;
+{
+ struct inpcbpolicy *new;
+
+ /* sanity check. */
+ if (so == NULL || pcb_sp == NULL)
+ panic("ipsec_init_policy: NULL pointer was passed.");
+
+ new = ipsec_newpcbpolicy();
+ if (new == NULL) {
+ ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n"));
+ return ENOBUFS;
+ }
+ bzero(new, sizeof(*new));
+
+ if (so->so_cred != 0 && so->so_cred->cr_uid == 0)
+ new->priv = 1;
+ else
+ new->priv = 0;
+
+ if ((new->sp_in = key_newsp()) == NULL) {
+ ipsec_delpcbpolicy(new);
+ return ENOBUFS;
+ }
+ new->sp_in->state = IPSEC_SPSTATE_ALIVE;
+ new->sp_in->policy = IPSEC_POLICY_ENTRUST;
+
+ if ((new->sp_out = key_newsp()) == NULL) {
+ key_freesp(new->sp_in);
+ ipsec_delpcbpolicy(new);
+ return ENOBUFS;
+ }
+ new->sp_out->state = IPSEC_SPSTATE_ALIVE;
+ new->sp_out->policy = IPSEC_POLICY_ENTRUST;
+
+ *pcb_sp = new;
+
+ return 0;
+}
+
+/* copy old ipsec policy into new */
+int
+ipsec_copy_policy(old, new)
+ struct inpcbpolicy *old, *new;
+{
+ struct secpolicy *sp;
+
+ sp = ipsec_deepcopy_policy(old->sp_in);
+ if (sp) {
+ key_freesp(new->sp_in);
+ new->sp_in = sp;
+ } else
+ return ENOBUFS;
+
+ sp = ipsec_deepcopy_policy(old->sp_out);
+ if (sp) {
+ key_freesp(new->sp_out);
+ new->sp_out = sp;
+ } else
+ return ENOBUFS;
+
+ new->priv = old->priv;
+
+ return 0;
+}
+
+/* deep-copy a policy in PCB */
+static struct secpolicy *
+ipsec_deepcopy_policy(src)
+ struct secpolicy *src;
+{
+ struct ipsecrequest *newchain = NULL;
+ struct ipsecrequest *p;
+ struct ipsecrequest **q;
+ struct ipsecrequest *r;
+ struct secpolicy *dst;
+
+ dst = key_newsp();
+ if (src == NULL || dst == NULL)
+ return NULL;
+
+ /*
+ * deep-copy IPsec request chain. This is required since struct
+ * ipsecrequest is not reference counted.
+ */
+ q = &newchain;
+ for (p = src->req; p; p = p->next) {
+ *q = (struct ipsecrequest *)malloc(sizeof(struct ipsecrequest),
+ M_SECA, M_NOWAIT);
+ if (*q == NULL)
+ goto fail;
+ bzero(*q, sizeof(**q));
+ (*q)->next = NULL;
+
+ (*q)->saidx.proto = p->saidx.proto;
+ (*q)->saidx.mode = p->saidx.mode;
+ (*q)->level = p->level;
+ (*q)->saidx.reqid = p->saidx.reqid;
+
+ bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src));
+ bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst));
+
+ (*q)->sav = NULL;
+ (*q)->sp = dst;
+
+ q = &((*q)->next);
+ }
+
+ dst->req = newchain;
+ dst->state = src->state;
+ dst->policy = src->policy;
+ /* do not touch the refcnt fields */
+
+ return dst;
+
+fail:
+ for (p = newchain; p; p = r) {
+ r = p->next;
+ free(p, M_SECA);
+ p = NULL;
+ }
+ return NULL;
+}
+
+/* set policy and ipsec request if present. */
+static int
+ipsec_set_policy(pcb_sp, optname, request, len, priv)
+ struct secpolicy **pcb_sp;
+ int optname;
+ caddr_t request;
+ size_t len;
+ int priv;
+{
+ struct sadb_x_policy *xpl;
+ struct secpolicy *newsp = NULL;
+ int error;
+
+ /* sanity check. */
+ if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
+ return EINVAL;
+ if (len < sizeof(*xpl))
+ return EINVAL;
+ xpl = (struct sadb_x_policy *)request;
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ printf("ipsec_set_policy: passed policy\n");
+ kdebug_sadb_x_policy((struct sadb_ext *)xpl));
+
+ /* check policy type */
+ /* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */
+ if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD
+ || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
+ return EINVAL;
+
+ /* check privileged socket */
+ if (priv == 0 && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS)
+ return EACCES;
+
+ /* allocation new SP entry */
+ if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
+ return error;
+
+ newsp->state = IPSEC_SPSTATE_ALIVE;
+
+ /* clear old SP and set new SP */
+ key_freesp(*pcb_sp);
+ *pcb_sp = newsp;
+ KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ printf("ipsec_set_policy: new policy\n");
+ kdebug_secpolicy(newsp));
+
+ return 0;
+}
+
+static int
+ipsec_get_policy(pcb_sp, mp)
+ struct secpolicy *pcb_sp;
+ struct mbuf **mp;
+{
+
+ /* sanity check. */
+ if (pcb_sp == NULL || mp == NULL)
+ return EINVAL;
+
+ *mp = key_sp2msg(pcb_sp);
+ if (!*mp) {
+ ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n"));
+ return ENOBUFS;
+ }
+
+ (*mp)->m_type = MT_DATA;
+ KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ printf("ipsec_get_policy:\n");
+ kdebug_mbuf(*mp));
+
+ return 0;
+}
+
+int
+ipsec4_set_policy(inp, optname, request, len, priv)
+ struct inpcb *inp;
+ int optname;
+ caddr_t request;
+ size_t len;
+ int priv;
+{
+ struct sadb_x_policy *xpl;
+ struct secpolicy **pcb_sp;
+
+ /* sanity check. */
+ if (inp == NULL || request == NULL)
+ return EINVAL;
+ if (len < sizeof(*xpl))
+ return EINVAL;
+ xpl = (struct sadb_x_policy *)request;
+
+ /* select direction */
+ switch (xpl->sadb_x_policy_dir) {
+ case IPSEC_DIR_INBOUND:
+ pcb_sp = &inp->inp_sp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ pcb_sp = &inp->inp_sp->sp_out;
+ break;
+ default:
+ ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n",
+ xpl->sadb_x_policy_dir));
+ return EINVAL;
+ }
+
+ return ipsec_set_policy(pcb_sp, optname, request, len, priv);
+}
+
+int
+ipsec4_get_policy(inp, request, len, mp)
+ struct inpcb *inp;
+ caddr_t request;
+ size_t len;
+ struct mbuf **mp;
+{
+ struct sadb_x_policy *xpl;
+ struct secpolicy *pcb_sp;
+
+ /* sanity check. */
+ if (inp == NULL || request == NULL || mp == NULL)
+ return EINVAL;
+ if (inp->inp_sp == NULL)
+ panic("policy in PCB is NULL");
+ if (len < sizeof(*xpl))
+ return EINVAL;
+ xpl = (struct sadb_x_policy *)request;
+
+ /* select direction */
+ switch (xpl->sadb_x_policy_dir) {
+ case IPSEC_DIR_INBOUND:
+ pcb_sp = inp->inp_sp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ pcb_sp = inp->inp_sp->sp_out;
+ break;
+ default:
+ ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n",
+ xpl->sadb_x_policy_dir));
+ return EINVAL;
+ }
+
+ return ipsec_get_policy(pcb_sp, mp);
+}
+
+/* delete policy in PCB */
+int
+ipsec4_delete_pcbpolicy(inp)
+ struct inpcb *inp;
+{
+ /* sanity check. */
+ if (inp == NULL)
+ panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.");
+
+ if (inp->inp_sp == NULL)
+ return 0;
+
+ if (inp->inp_sp->sp_in != NULL) {
+ key_freesp(inp->inp_sp->sp_in);
+ inp->inp_sp->sp_in = NULL;
+ }
+
+ if (inp->inp_sp->sp_out != NULL) {
+ key_freesp(inp->inp_sp->sp_out);
+ inp->inp_sp->sp_out = NULL;
+ }
+
+ ipsec_delpcbpolicy(inp->inp_sp);
+ inp->inp_sp = NULL;
+
+ return 0;
+}
+
+#ifdef INET6
+int
+ipsec6_set_policy(in6p, optname, request, len, priv)
+ struct in6pcb *in6p;
+ int optname;
+ caddr_t request;
+ size_t len;
+ int priv;
+{
+ struct sadb_x_policy *xpl;
+ struct secpolicy **pcb_sp;
+
+ /* sanity check. */
+ if (in6p == NULL || request == NULL)
+ return EINVAL;
+ if (len < sizeof(*xpl))
+ return EINVAL;
+ xpl = (struct sadb_x_policy *)request;
+
+ /* select direction */
+ switch (xpl->sadb_x_policy_dir) {
+ case IPSEC_DIR_INBOUND:
+ pcb_sp = &in6p->in6p_sp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ pcb_sp = &in6p->in6p_sp->sp_out;
+ break;
+ default:
+ ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n",
+ xpl->sadb_x_policy_dir));
+ return EINVAL;
+ }
+
+ return ipsec_set_policy(pcb_sp, optname, request, len, priv);
+}
+
+int
+ipsec6_get_policy(in6p, request, len, mp)
+ struct in6pcb *in6p;
+ caddr_t request;
+ size_t len;
+ struct mbuf **mp;
+{
+ struct sadb_x_policy *xpl;
+ struct secpolicy *pcb_sp;
+
+ /* sanity check. */
+ if (in6p == NULL || request == NULL || mp == NULL)
+ return EINVAL;
+ if (in6p->in6p_sp == NULL)
+ panic("policy in PCB is NULL");
+ if (len < sizeof(*xpl))
+ return EINVAL;
+ xpl = (struct sadb_x_policy *)request;
+
+ /* select direction */
+ switch (xpl->sadb_x_policy_dir) {
+ case IPSEC_DIR_INBOUND:
+ pcb_sp = in6p->in6p_sp->sp_in;
+ break;
+ case IPSEC_DIR_OUTBOUND:
+ pcb_sp = in6p->in6p_sp->sp_out;
+ break;
+ default:
+ ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n",
+ xpl->sadb_x_policy_dir));
+ return EINVAL;
+ }
+
+ return ipsec_get_policy(pcb_sp, mp);
+}
+
+int
+ipsec6_delete_pcbpolicy(in6p)
+ struct in6pcb *in6p;
+{
+ /* sanity check. */
+ if (in6p == NULL)
+ panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.");
+
+ if (in6p->in6p_sp == NULL)
+ return 0;
+
+ if (in6p->in6p_sp->sp_in != NULL) {
+ key_freesp(in6p->in6p_sp->sp_in);
+ in6p->in6p_sp->sp_in = NULL;
+ }
+
+ if (in6p->in6p_sp->sp_out != NULL) {
+ key_freesp(in6p->in6p_sp->sp_out);
+ in6p->in6p_sp->sp_out = NULL;
+ }
+
+ ipsec_delpcbpolicy(in6p->in6p_sp);
+ in6p->in6p_sp = NULL;
+
+ return 0;
+}
+#endif
+
+/*
+ * return current level.
+ * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned.
+ */
+u_int
+ipsec_get_reqlevel(isr)
+ struct ipsecrequest *isr;
+{
+ u_int level = 0;
+ u_int esp_trans_deflev, esp_net_deflev, ah_trans_deflev, ah_net_deflev;
+
+ /* sanity check */
+ if (isr == NULL || isr->sp == NULL)
+ panic("ipsec_get_reqlevel: NULL pointer is passed.");
+ if (((struct sockaddr *)&isr->sp->spidx.src)->sa_family
+ != ((struct sockaddr *)&isr->sp->spidx.dst)->sa_family)
+ panic("ipsec_get_reqlevel: family mismatched.");
+
+/* XXX note that we have ipseclog() expanded here - code sync issue */
+#define IPSEC_CHECK_DEFAULT(lev) \
+ (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \
+ && (lev) != IPSEC_LEVEL_UNIQUE) \
+ ? (ipsec_debug \
+ ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
+ (lev), IPSEC_LEVEL_REQUIRE) \
+ : 0), \
+ (lev) = IPSEC_LEVEL_REQUIRE, \
+ (lev) \
+ : (lev))
+
+ /* set default level */
+ switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
+#ifdef INET
+ case AF_INET:
+ esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev);
+ esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev);
+ ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev);
+ ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev);
+ esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev);
+ ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev);
+ ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev);
+ break;
+#endif /* INET6 */
+ default:
+ panic("key_get_reqlevel: Unknown family. %d",
+ ((struct sockaddr *)&isr->sp->spidx.src)->sa_family);
+ }
+
+#undef IPSEC_CHECK_DEFAULT
+
+ /* set level */
+ switch (isr->level) {
+ case IPSEC_LEVEL_DEFAULT:
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+ level = esp_net_deflev;
+ else
+ level = esp_trans_deflev;
+ break;
+ case IPPROTO_AH:
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+ level = ah_net_deflev;
+ else
+ level = ah_trans_deflev;
+ case IPPROTO_IPCOMP:
+ /*
+ * we don't really care, as IPcomp document says that
+ * we shouldn't compress small packets
+ */
+ level = IPSEC_LEVEL_USE;
+ break;
+ default:
+ panic("ipsec_get_reqlevel: "
+ "Illegal protocol defined %u",
+ isr->saidx.proto);
+ }
+ break;
+
+ case IPSEC_LEVEL_USE:
+ case IPSEC_LEVEL_REQUIRE:
+ level = isr->level;
+ break;
+ case IPSEC_LEVEL_UNIQUE:
+ level = IPSEC_LEVEL_REQUIRE;
+ break;
+
+ default:
+ panic("ipsec_get_reqlevel: Illegal IPsec level %u",
+ isr->level);
+ }
+
+ return level;
+}
+
+/*
+ * Check AH/ESP integrity.
+ * OUT:
+ * 0: valid
+ * 1: invalid
+ */
+static int
+ipsec_in_reject(sp, m)
+ struct secpolicy *sp;
+ struct mbuf *m;
+{
+ struct ipsecrequest *isr;
+ u_int level;
+ int need_auth, need_conf, need_icv;
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec_in_reject: using SP\n");
+ kdebug_secpolicy(sp));
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ return 1;
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ return 0;
+
+ case IPSEC_POLICY_IPSEC:
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ panic("ipsec_hdrsiz: Invalid policy found. %d", sp->policy);
+ }
+
+ need_auth = 0;
+ 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 */
+ level = ipsec_get_reqlevel(isr);
+
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+ if (level == IPSEC_LEVEL_REQUIRE) {
+ need_conf++;
+
+ if (isr->sav != NULL
+ && isr->sav->flags == SADB_X_EXT_NONE
+ && isr->sav->alg_auth != SADB_AALG_NONE)
+ need_icv++;
+ }
+ break;
+ case IPPROTO_AH:
+ if (level == IPSEC_LEVEL_REQUIRE) {
+ need_auth++;
+ need_icv++;
+ }
+ break;
+ case IPPROTO_IPCOMP:
+ /*
+ * we don't really care, as IPcomp document says that
+ * we shouldn't compress small packets, IPComp policy
+ * should always be treated as being in "use" level.
+ */
+ break;
+ }
+ }
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ printf("ipsec_in_reject: auth:%d conf:%d icv:%d m_flags:%x\n",
+ need_auth, need_conf, need_icv, m->m_flags));
+
+ if ((need_conf && !(m->m_flags & M_DECRYPTED))
+ || (!need_auth && need_icv && !(m->m_flags & M_AUTHIPDGM))
+ || (need_auth && !(m->m_flags & M_AUTHIPHDR)))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Check AH/ESP integrity.
+ * This function is called from tcp_input(), udp_input(),
+ * and {ah,esp}4_input for tunnel mode
+ */
+int
+ipsec4_in_reject(m, inp)
+ struct mbuf *m;
+ struct inpcb *inp;
+{
+ struct secpolicy *sp = NULL;
+ int error;
+ int result;
+
+ /* sanity check */
+ if (m == NULL)
+ return 0; /* XXX should be panic ? */
+
+ /* get SP for this packet.
+ * When we are called from ip_forward(), we call
+ * ipsec4_getpolicybyaddr() with IP_FORWARDING flag.
+ */
+ if (inp == NULL)
+ sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error);
+ else
+ sp = ipsec4_getpolicybypcb(m, IPSEC_DIR_INBOUND, inp, &error);
+
+ if (sp == NULL)
+ return 0; /* XXX should be panic ?
+ * -> No, there may be error. */
+
+ result = ipsec_in_reject(sp, m);
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec4_in_reject_so call free SP:%p\n", sp));
+ key_freesp(sp);
+
+ return result;
+}
+
+int
+ipsec4_in_reject_so(m, so)
+ struct mbuf *m;
+ struct socket *so;
+{
+ if (so == NULL)
+ return ipsec4_in_reject(m, NULL);
+ return ipsec4_in_reject(m, sotoinpcb(so));
+}
+
+
+#ifdef INET6
+/*
+ * Check AH/ESP integrity.
+ * This function is called from tcp6_input(), udp6_input(),
+ * and {ah,esp}6_input for tunnel mode
+ */
+int
+ipsec6_in_reject(m, in6p)
+ struct mbuf *m;
+ struct in6pcb *in6p;
+{
+ struct secpolicy *sp = NULL;
+ int error;
+ int result;
+
+ /* sanity check */
+ if (m == NULL)
+ return 0; /* XXX should be panic ? */
+
+ /* get SP for this packet.
+ * When we are called from ip_forward(), we call
+ * ipsec6_getpolicybyaddr() with IP_FORWARDING flag.
+ */
+ if (in6p == NULL)
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error);
+ else
+ sp = ipsec6_getpolicybypcb(m, IPSEC_DIR_INBOUND, in6p, &error);
+
+ if (sp == NULL)
+ return 0; /* XXX should be panic ? */
+
+ result = ipsec_in_reject(sp, m);
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec6_in_reject call free SP:%p\n", sp));
+ key_freesp(sp);
+
+ return result;
+}
+
+int
+ipsec6_in_reject_so(m, so)
+ struct mbuf *m;
+ struct socket *so;
+{
+ if (so == NULL)
+ return ipsec6_in_reject(m, NULL);
+ return ipsec6_in_reject(m, sotoin6pcb(so));
+}
+#endif
+
+/*
+ * compute the byte size to be occupied by IPsec header.
+ * in case it is tunneled, it includes the size of outer IP header.
+ * NOTE: SP passed is free in this function.
+ */
+static size_t
+ipsec_hdrsiz(sp)
+ struct secpolicy *sp;
+{
+ struct ipsecrequest *isr;
+ size_t siz, clen;
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec_hdrsiz: using SP\n");
+ kdebug_secpolicy(sp));
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ return 0;
+
+ case IPSEC_POLICY_IPSEC:
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ panic("ipsec_hdrsiz: Invalid policy found. %d", sp->policy);
+ }
+
+ siz = 0;
+
+ for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+ clen = 0;
+
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+ clen = esp_hdrsiz(isr);
+#else
+ clen = 0; /* XXX */
+#endif
+ break;
+ case IPPROTO_AH:
+ clen = ah_hdrsiz(isr);
+ break;
+ case IPPROTO_IPCOMP:
+ clen = sizeof(struct ipcomp);
+ break;
+ }
+
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ switch (((struct sockaddr *)&isr->saidx.dst)->sa_family) {
+ case AF_INET:
+ clen += sizeof(struct ip);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ clen += sizeof(struct ip6_hdr);
+ break;
+#endif
+ default:
+ ipseclog((LOG_ERR, "ipsec_hdrsiz: "
+ "unknown AF %d in IPsec tunnel SA\n",
+ ((struct sockaddr *)&isr->saidx.dst)->sa_family));
+ break;
+ }
+ }
+ siz += clen;
+ }
+
+ return siz;
+}
+
+/* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */
+size_t
+ipsec4_hdrsiz(m, dir, inp)
+ struct mbuf *m;
+ u_int dir;
+ struct inpcb *inp;
+{
+ struct secpolicy *sp = NULL;
+ int error;
+ size_t size;
+
+ /* sanity check */
+ if (m == NULL)
+ return 0; /* XXX should be panic ? */
+#if 0
+ /* this is possible in TIME_WAIT state */
+ if (inp != NULL && inp->inp_socket == NULL)
+ panic("ipsec4_hdrsize: why is socket NULL but there is PCB.");
+#endif
+
+ /* get SP for this packet.
+ * When we are called from ip_forward(), we call
+ * ipsec4_getpolicybyaddr() with IP_FORWARDING flag.
+ */
+ if (inp == NULL)
+ sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
+ else
+ sp = ipsec4_getpolicybypcb(m, dir, inp, &error);
+
+ if (sp == NULL)
+ return 0; /* XXX should be panic ? */
+
+ size = ipsec_hdrsiz(sp);
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec4_hdrsiz call free SP:%p\n", sp));
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec4_hdrsiz: size:%lu.\n", (unsigned long)size));
+ key_freesp(sp);
+
+ return size;
+}
+
+#ifdef INET6
+/* This function is called from ipsec6_hdrsize_tcp(),
+ * and maybe from ip6_forward.()
+ */
+size_t
+ipsec6_hdrsiz(m, dir, in6p)
+ struct mbuf *m;
+ u_int dir;
+ struct in6pcb *in6p;
+{
+ struct secpolicy *sp = NULL;
+ int error;
+ size_t size;
+
+ /* sanity check */
+ if (m == NULL)
+ return 0; /* XXX shoud be panic ? */
+#if 0
+ /* this is possible in TIME_WAIT state */
+ if (in6p != NULL && in6p->in6p_socket == NULL)
+ panic("ipsec6_hdrsize: why is socket NULL but there is PCB.");
+#endif
+
+ /* get SP for this packet */
+ /* XXX Is it right to call with IP_FORWARDING. */
+ if (in6p == NULL)
+ sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
+ else
+ sp = ipsec6_getpolicybypcb(m, dir, in6p, &error);
+
+ if (sp == NULL)
+ return 0;
+ size = ipsec_hdrsiz(sp);
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP ipsec6_hdrsiz call free SP:%p\n", sp));
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size));
+ key_freesp(sp);
+
+ return size;
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * encapsulate for ipsec tunnel.
+ * ip->ip_src must be fixed later on.
+ */
+static int
+ipsec4_encapsulate(m, sav)
+ struct mbuf *m;
+ struct secasvar *sav;
+{
+ struct ip *oip;
+ struct ip *ip;
+ size_t hlen;
+ size_t plen;
+
+ /* can't tunnel between different AFs */
+ if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+ != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
+ || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
+ m_freem(m);
+ return EINVAL;
+ }
+#if 0
+ /* XXX if the dst is myself, perform nothing. */
+ if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
+ m_freem(m);
+ return EINVAL;
+ }
+#endif
+
+ if (m->m_len < sizeof(*ip))
+ panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
+
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+
+ if (m->m_len != hlen)
+ panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
+
+ /* generate header checksum */
+ ip->ip_sum = 0;
+#ifdef _IP_VHL
+ if (ip->ip_vhl == IP_VHL_BORING)
+ ip->ip_sum = in_cksum_hdr(ip);
+ else
+ ip->ip_sum = in_cksum(m, hlen);
+#else
+ ip->ip_sum = in_cksum(m, hlen);
+#endif
+
+ plen = m->m_pkthdr.len;
+
+ /*
+ * grow the mbuf to accomodate the new IPv4 header.
+ * NOTE: IPv4 options will never be copied.
+ */
+ if (M_LEADINGSPACE(m->m_next) < hlen) {
+ struct mbuf *n;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ n->m_len = hlen;
+ n->m_next = m->m_next;
+ m->m_next = n;
+ m->m_pkthdr.len += hlen;
+ oip = mtod(n, struct ip *);
+ } else {
+ m->m_next->m_len += hlen;
+ m->m_next->m_data -= hlen;
+ m->m_pkthdr.len += hlen;
+ oip = mtod(m->m_next, struct ip *);
+ }
+ ip = mtod(m, struct ip *);
+ ovbcopy((caddr_t)ip, (caddr_t)oip, hlen);
+ m->m_len = sizeof(struct ip);
+ m->m_pkthdr.len -= (hlen - sizeof(struct ip));
+
+ /* construct new IPv4 header. see RFC 2401 5.1.2.1 */
+ /* ECN consideration. */
+ ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos);
+#ifdef _IP_VHL
+ ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
+#else
+ ip->ip_hl = sizeof(struct ip) >> 2;
+#endif
+ ip->ip_off &= htons(~IP_OFFMASK);
+ ip->ip_off &= htons(~IP_MF);
+ switch (ip4_ipsec_dfbit) {
+ case 0: /* clear DF bit */
+ ip->ip_off &= htons(~IP_DF);
+ break;
+ case 1: /* set DF bit */
+ ip->ip_off |= htons(IP_DF);
+ break;
+ default: /* copy DF bit */
+ break;
+ }
+ ip->ip_p = IPPROTO_IPIP;
+ if (plen + sizeof(struct ip) < IP_MAXPACKET)
+ ip->ip_len = htons(plen + sizeof(struct ip));
+ else {
+ ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: "
+ "leave ip_len as is (invalid packet)\n"));
+ }
+#ifdef RANDOM_IP_ID
+ ip->ip_id = ip_randomid();
+#else
+ ip->ip_id = htons(ip_id++);
+#endif
+ bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr,
+ &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 ? */
+
+ return 0;
+}
+#endif /* INET */
+
+#ifdef INET6
+static int
+ipsec6_encapsulate(m, sav)
+ struct mbuf *m;
+ struct secasvar *sav;
+{
+ struct ip6_hdr *oip6;
+ struct ip6_hdr *ip6;
+ size_t plen;
+
+ /* can't tunnel between different AFs */
+ if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+ != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
+ || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET6) {
+ m_freem(m);
+ return EINVAL;
+ }
+#if 0
+ /* XXX if the dst is myself, perform nothing. */
+ if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
+ m_freem(m);
+ return EINVAL;
+ }
+#endif
+
+ plen = m->m_pkthdr.len;
+
+ /*
+ * grow the mbuf to accomodate the new IPv6 header.
+ */
+ if (m->m_len != sizeof(struct ip6_hdr))
+ panic("ipsec6_encapsulate: assumption failed (first mbuf length)");
+ if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
+ struct mbuf *n;
+ MGET(n, M_DONTWAIT, MT_DATA);
+ if (!n) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+ n->m_len = sizeof(struct ip6_hdr);
+ n->m_next = m->m_next;
+ m->m_next = n;
+ m->m_pkthdr.len += sizeof(struct ip6_hdr);
+ oip6 = mtod(n, struct ip6_hdr *);
+ } else {
+ m->m_next->m_len += sizeof(struct ip6_hdr);
+ m->m_next->m_data -= sizeof(struct ip6_hdr);
+ m->m_pkthdr.len += sizeof(struct ip6_hdr);
+ oip6 = mtod(m->m_next, struct ip6_hdr *);
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
+
+ /* Fake link-local scope-class addresses */
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
+ oip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
+ oip6->ip6_dst.s6_addr16[1] = 0;
+
+ /* construct new IPv6 header. see RFC 2401 5.1.2.2 */
+ /* ECN consideration. */
+ ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
+ if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
+ ip6->ip6_plen = htons(plen);
+ else {
+ /* ip6->ip6_plen will be updated in ip6_output() */
+ }
+ ip6->ip6_nxt = IPPROTO_IPV6;
+ bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr,
+ &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 ? */
+
+ return 0;
+}
+#endif /* INET6 */
+
+/*
+ * Check the variable replay window.
+ * ipsec_chkreplay() performs replay check before ICV verification.
+ * ipsec_updatereplay() updates replay bitmap. This must be called after
+ * ICV verification (it also performs replay check, which is usually done
+ * beforehand).
+ * 0 (zero) is returned if packet disallowed, 1 if packet permitted.
+ *
+ * based on RFC 2401.
+ */
+int
+ipsec_chkreplay(seq, sav)
+ u_int32_t seq;
+ struct secasvar *sav;
+{
+ const struct secreplay *replay;
+ u_int32_t diff;
+ int fr;
+ u_int32_t wsizeb; /* constant: bits of window size */
+ int frlast; /* constant: last frame */
+
+ /* sanity check */
+ if (sav == NULL)
+ panic("ipsec_chkreplay: NULL pointer was passed.");
+
+ replay = sav->replay;
+
+ if (replay->wsize == 0)
+ return 1; /* no need to check replay. */
+
+ /* constant */
+ frlast = replay->wsize - 1;
+ wsizeb = replay->wsize << 3;
+
+ /* sequence number of 0 is invalid */
+ if (seq == 0)
+ return 0;
+
+ /* first time is always okay */
+ if (replay->count == 0)
+ return 1;
+
+ if (seq > replay->lastseq) {
+ /* larger sequences are okay */
+ return 1;
+ } else {
+ /* seq is equal or less than lastseq. */
+ diff = replay->lastseq - seq;
+
+ /* over range to check, i.e. too old or wrapped */
+ if (diff >= wsizeb)
+ return 0;
+
+ fr = frlast - diff / 8;
+
+ /* this packet already seen ? */
+ if ((replay->bitmap)[fr] & (1 << (diff % 8)))
+ return 0;
+
+ /* out of order but good */
+ return 1;
+ }
+}
+
+/*
+ * check replay counter whether to update or not.
+ * OUT: 0: OK
+ * 1: NG
+ */
+int
+ipsec_updatereplay(seq, sav)
+ u_int32_t seq;
+ struct secasvar *sav;
+{
+ struct secreplay *replay;
+ u_int32_t diff;
+ int fr;
+ u_int32_t wsizeb; /* constant: bits of window size */
+ int frlast; /* constant: last frame */
+
+ /* sanity check */
+ if (sav == NULL)
+ panic("ipsec_chkreplay: NULL pointer was passed.");
+
+ replay = sav->replay;
+
+ if (replay->wsize == 0)
+ goto ok; /* no need to check replay. */
+
+ /* constant */
+ frlast = replay->wsize - 1;
+ wsizeb = replay->wsize << 3;
+
+ /* sequence number of 0 is invalid */
+ if (seq == 0)
+ return 1;
+
+ /* first time */
+ if (replay->count == 0) {
+ replay->lastseq = seq;
+ bzero(replay->bitmap, replay->wsize);
+ (replay->bitmap)[frlast] = 1;
+ goto ok;
+ }
+
+ if (seq > replay->lastseq) {
+ /* seq is larger than lastseq. */
+ diff = seq - replay->lastseq;
+
+ /* new larger sequence number */
+ if (diff < wsizeb) {
+ /* In window */
+ /* set bit for this packet */
+ vshiftl(replay->bitmap, diff, replay->wsize);
+ (replay->bitmap)[frlast] |= 1;
+ } else {
+ /* this packet has a "way larger" */
+ bzero(replay->bitmap, replay->wsize);
+ (replay->bitmap)[frlast] = 1;
+ }
+ replay->lastseq = seq;
+
+ /* larger is good */
+ } else {
+ /* seq is equal or less than lastseq. */
+ diff = replay->lastseq - seq;
+
+ /* over range to check, i.e. too old or wrapped */
+ if (diff >= wsizeb)
+ return 1;
+
+ fr = frlast - diff / 8;
+
+ /* this packet already seen ? */
+ if ((replay->bitmap)[fr] & (1 << (diff % 8)))
+ return 1;
+
+ /* mark as seen */
+ (replay->bitmap)[fr] |= (1 << (diff % 8));
+
+ /* out of order but good */
+ }
+
+ok:
+ if (replay->count == ~0) {
+
+ /* set overflow flag */
+ replay->overflow++;
+
+ /* don't increment, no more packets accepted */
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
+ return 1;
+
+ ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n",
+ replay->overflow, ipsec_logsastr(sav)));
+ }
+
+ replay->count++;
+
+ return 0;
+}
+
+/*
+ * shift variable length buffer to left.
+ * IN: bitmap: pointer to the buffer
+ * nbit: the number of to shift.
+ * wsize: buffer size (bytes).
+ */
+static void
+vshiftl(bitmap, nbit, wsize)
+ unsigned char *bitmap;
+ int nbit, wsize;
+{
+ int s, j, i;
+ unsigned char over;
+
+ for (j = 0; j < nbit; j += 8) {
+ s = (nbit - j < 8) ? (nbit - j): 8;
+ bitmap[0] <<= s;
+ for (i = 1; i < wsize; i++) {
+ over = (bitmap[i] >> (8 - s));
+ bitmap[i] <<= s;
+ bitmap[i-1] |= over;
+ }
+ }
+
+ return;
+}
+
+const char *
+ipsec4_logpacketstr(ip, spi)
+ struct ip *ip;
+ u_int32_t spi;
+{
+ static char buf[256];
+ char *p;
+ u_int8_t *s, *d;
+
+ s = (u_int8_t *)(&ip->ip_src);
+ d = (u_int8_t *)(&ip->ip_dst);
+
+ p = buf;
+ snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi));
+ while (p && *p)
+ p++;
+ 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=%u.%u.%u.%u",
+ d[0], d[1], d[2], d[3]);
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf), ")");
+
+ return buf;
+}
+
+#ifdef INET6
+const char *
+ipsec6_logpacketstr(ip6, spi)
+ struct ip6_hdr *ip6;
+ u_int32_t spi;
+{
+ static char buf[256];
+ char *p;
+
+ p = buf;
+ snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi));
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf), "src=%s",
+ ip6_sprintf(&ip6->ip6_src));
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf), " dst=%s",
+ ip6_sprintf(&ip6->ip6_dst));
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf), ")");
+
+ return buf;
+}
+#endif /* INET6 */
+
+const char *
+ipsec_logsastr(sav)
+ struct secasvar *sav;
+{
+ static char buf[256];
+ char *p;
+ struct secasindex *saidx = &sav->sah->saidx;
+
+ /* validity check */
+ if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+ != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family)
+ panic("ipsec_logsastr: family mismatched.");
+
+ p = buf;
+ snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi));
+ while (p && *p)
+ p++;
+ if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET) {
+ u_int8_t *s, *d;
+ s = (u_int8_t *)&((struct sockaddr_in *)&saidx->src)->sin_addr;
+ d = (u_int8_t *)&((struct sockaddr_in *)&saidx->dst)->sin_addr;
+ snprintf(p, sizeof(buf) - (p - buf),
+ "src=%d.%d.%d.%d dst=%d.%d.%d.%d",
+ s[0], s[1], s[2], s[3], d[0], d[1], d[2], d[3]);
+ }
+#ifdef INET6
+ else if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET6) {
+ snprintf(p, sizeof(buf) - (p - buf),
+ "src=%s",
+ ip6_sprintf(&((struct sockaddr_in6 *)&saidx->src)->sin6_addr));
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf),
+ " dst=%s",
+ ip6_sprintf(&((struct sockaddr_in6 *)&saidx->dst)->sin6_addr));
+ }
+#endif
+ while (p && *p)
+ p++;
+ snprintf(p, sizeof(buf) - (p - buf), ")");
+
+ return buf;
+}
+
+void
+ipsec_dumpmbuf(m)
+ struct mbuf *m;
+{
+ int totlen;
+ int i;
+ u_char *p;
+
+ totlen = 0;
+ printf("---\n");
+ while (m) {
+ p = mtod(m, u_char *);
+ for (i = 0; i < m->m_len; i++) {
+ printf("%02x ", p[i]);
+ totlen++;
+ if (totlen % 16 == 0)
+ printf("\n");
+ }
+ m = m->m_next;
+ }
+ if (totlen % 16 != 0)
+ printf("\n");
+ printf("---\n");
+}
+
+#ifdef INET
+/*
+ * IPsec output logic for IPv4.
+ */
+int
+ipsec4_output(state, sp, flags)
+ struct ipsec_output_state *state;
+ struct secpolicy *sp;
+ int flags;
+{
+ struct ip *ip = NULL;
+ struct ipsecrequest *isr = NULL;
+ struct secasindex saidx;
+ int s;
+ int error;
+ struct sockaddr_in *dst4;
+ struct sockaddr_in *sin;
+
+ if (!state)
+ panic("state == NULL in ipsec4_output");
+ if (!state->m)
+ panic("state->m == NULL in ipsec4_output");
+ if (!state->ro)
+ panic("state->ro == NULL in ipsec4_output");
+ if (!state->dst)
+ panic("state->dst == NULL in ipsec4_output");
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec4_output: applyed SP\n");
+ kdebug_secpolicy(sp));
+
+ for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+#if 0 /* give up to check restriction of transport mode */
+ /* XXX but should be checked somewhere */
+ /*
+ * some of the IPsec operation must be performed only in
+ * originating case.
+ */
+ if (isr->saidx.mode == IPSEC_MODE_TRANSPORT
+ && (flags & IP_FORWARDING))
+ continue;
+#endif
+
+ /* make SA index for search proper SA */
+ ip = mtod(state->m, struct ip *);
+ bcopy(&isr->saidx, &saidx, sizeof(saidx));
+ 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);
+ sin->sin_family = AF_INET;
+ sin->sin_port = IPSEC_PORT_ANY;
+ bcopy(&ip->ip_src, &sin->sin_addr,
+ sizeof(sin->sin_addr));
+ }
+ sin = (struct sockaddr_in *)&saidx.dst;
+ if (sin->sin_len == 0) {
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = IPSEC_PORT_ANY;
+ bcopy(&ip->ip_dst, &sin->sin_addr,
+ sizeof(sin->sin_addr));
+ }
+
+ if ((error = key_checkrequest(isr, &saidx)) != 0) {
+ /*
+ * IPsec processing is required, but no SA found.
+ * I assume that key_acquire() had been called
+ * to get/establish the SA. Here I discard
+ * this packet because it is responsibility for
+ * upper layer to retransmit the packet.
+ */
+ ipsecstat.out_nosa++;
+ goto bad;
+ }
+
+ /* validity check */
+ if (isr->sav == NULL) {
+ switch (ipsec_get_reqlevel(isr)) {
+ case IPSEC_LEVEL_USE:
+ continue;
+ case IPSEC_LEVEL_REQUIRE:
+ /* must be not reached here. */
+ panic("ipsec4_output: no SA found, but required.");
+ }
+ }
+
+ /*
+ * If there is no valid SA, we give up to process any
+ * more. In such a case, the SA's status is changed
+ * from DYING to DEAD after allocating. If a packet
+ * send to the receiver by dead SA, the receiver can
+ * not decode a packet because SA has been dead.
+ */
+ if (isr->sav->state != SADB_SASTATE_MATURE
+ && isr->sav->state != SADB_SASTATE_DYING) {
+ ipsecstat.out_nosa++;
+ error = EINVAL;
+ goto bad;
+ }
+
+ /*
+ * There may be the case that SA status will be changed when
+ * we are refering to one. So calling splsoftnet().
+ */
+ s = splnet();
+
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /*
+ * build IPsec tunnel.
+ */
+ /* XXX should be processed with other familiy */
+ if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET) {
+ ipseclog((LOG_ERR, "ipsec4_output: "
+ "family mismatched between inner and outer spi=%u\n",
+ (u_int32_t)ntohl(isr->sav->spi)));
+ splx(s);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+ state->m = ipsec4_splithdr(state->m);
+ if (!state->m) {
+ splx(s);
+ error = ENOMEM;
+ goto bad;
+ }
+ error = ipsec4_encapsulate(state->m, isr->sav);
+ splx(s);
+ if (error) {
+ state->m = NULL;
+ goto bad;
+ }
+ ip = mtod(state->m, struct ip *);
+
+ state->ro = &isr->sav->sah->sa_route;
+ state->dst = (struct sockaddr *)&state->ro->ro_dst;
+ dst4 = (struct sockaddr_in *)state->dst;
+ if (state->ro->ro_rt
+ && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
+ || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) {
+ RTFREE(state->ro->ro_rt);
+ state->ro->ro_rt = NULL;
+ }
+ if (state->ro->ro_rt == 0) {
+ dst4->sin_family = AF_INET;
+ dst4->sin_len = sizeof(*dst4);
+ dst4->sin_addr = ip->ip_dst;
+ rtalloc(state->ro);
+ }
+ if (state->ro->ro_rt == 0) {
+ ipstat.ips_noroute++;
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+
+ /* adjust state->dst if tunnel endpoint is offlink */
+ if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
+ state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
+ dst4 = (struct sockaddr_in *)state->dst;
+ }
+ } else
+ splx(s);
+
+ state->m = ipsec4_splithdr(state->m);
+ if (!state->m) {
+ error = ENOMEM;
+ goto bad;
+ }
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+ if ((error = esp4_output(state->m, isr)) != 0) {
+ state->m = NULL;
+ goto bad;
+ }
+ break;
+#else
+ m_freem(state->m);
+ state->m = NULL;
+ error = EINVAL;
+ goto bad;
+#endif
+ case IPPROTO_AH:
+ if ((error = ah4_output(state->m, isr)) != 0) {
+ state->m = NULL;
+ goto bad;
+ }
+ break;
+ case IPPROTO_IPCOMP:
+ if ((error = ipcomp4_output(state->m, isr)) != 0) {
+ state->m = NULL;
+ goto bad;
+ }
+ break;
+ default:
+ ipseclog((LOG_ERR,
+ "ipsec4_output: unknown ipsec protocol %d\n",
+ isr->saidx.proto));
+ m_freem(state->m);
+ state->m = NULL;
+ error = EINVAL;
+ goto bad;
+ }
+
+ if (state->m == 0) {
+ error = ENOMEM;
+ goto bad;
+ }
+ ip = mtod(state->m, struct ip *);
+ }
+
+ return 0;
+
+bad:
+ m_freem(state->m);
+ state->m = NULL;
+ return error;
+}
+#endif
+
+#ifdef INET6
+/*
+ * IPsec output logic for IPv6, transport mode.
+ */
+int
+ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun)
+ struct ipsec_output_state *state;
+ u_char *nexthdrp;
+ struct mbuf *mprev;
+ struct secpolicy *sp;
+ int flags;
+ int *tun;
+{
+ struct ip6_hdr *ip6;
+ struct ipsecrequest *isr = NULL;
+ struct secasindex saidx;
+ int error = 0;
+ int plen;
+ struct sockaddr_in6 *sin6;
+
+ if (!state)
+ panic("state == NULL in ipsec6_output_trans");
+ if (!state->m)
+ panic("state->m == NULL in ipsec6_output_trans");
+ if (!nexthdrp)
+ panic("nexthdrp == NULL in ipsec6_output_trans");
+ if (!mprev)
+ panic("mprev == NULL in ipsec6_output_trans");
+ if (!sp)
+ panic("sp == NULL in ipsec6_output_trans");
+ if (!tun)
+ panic("tun == NULL in ipsec6_output_trans");
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec6_output_trans: applyed SP\n");
+ kdebug_secpolicy(sp));
+
+ *tun = 0;
+ for (isr = sp->req; isr; isr = isr->next) {
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /* the rest will be handled by ipsec6_output_tunnel() */
+ break;
+ }
+
+ /* 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);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ bcopy(&ip6->ip6_src, &sin6->sin6_addr,
+ sizeof(ip6->ip6_src));
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+ }
+ }
+ sin6 = (struct sockaddr_in6 *)&saidx.dst;
+ if (sin6->sin6_len == 0) {
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ bcopy(&ip6->ip6_dst, &sin6->sin6_addr,
+ sizeof(ip6->ip6_dst));
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+ }
+ }
+
+ if (key_checkrequest(isr, &saidx) == ENOENT) {
+ /*
+ * IPsec processing is required, but no SA found.
+ * I assume that key_acquire() had been called
+ * to get/establish the SA. Here I discard
+ * this packet because it is responsibility for
+ * upper layer to retransmit the packet.
+ */
+ 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;
+ }
+
+ /* validity check */
+ if (isr->sav == NULL) {
+ switch (ipsec_get_reqlevel(isr)) {
+ case IPSEC_LEVEL_USE:
+ continue;
+ case IPSEC_LEVEL_REQUIRE:
+ /* must be not reached here. */
+ panic("ipsec6_output_trans: no SA found, but required.");
+ }
+ }
+
+ /*
+ * If there is no valid SA, we give up to process.
+ * see same place at ipsec4_output().
+ */
+ if (isr->sav->state != SADB_SASTATE_MATURE
+ && isr->sav->state != SADB_SASTATE_DYING) {
+ ipsec6stat.out_nosa++;
+ error = EINVAL;
+ goto bad;
+ }
+
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+ error = esp6_output(state->m, nexthdrp, mprev->m_next, isr);
+#else
+ m_freem(state->m);
+ error = EINVAL;
+#endif
+ break;
+ case IPPROTO_AH:
+ error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
+ break;
+ case IPPROTO_IPCOMP:
+ error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr);
+ break;
+ default:
+ ipseclog((LOG_ERR, "ipsec6_output_trans: "
+ "unknown ipsec protocol %d\n", isr->saidx.proto));
+ m_freem(state->m);
+ ipsec6stat.out_inval++;
+ error = EINVAL;
+ break;
+ }
+ if (error) {
+ state->m = NULL;
+ goto bad;
+ }
+ plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr);
+ if (plen > IPV6_MAXPACKET) {
+ ipseclog((LOG_ERR, "ipsec6_output_trans: "
+ "IPsec with IPv6 jumbogram is not supported\n"));
+ ipsec6stat.out_inval++;
+ error = EINVAL; /* XXX */
+ goto bad;
+ }
+ ip6 = mtod(state->m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(plen);
+ }
+
+ /* if we have more to go, we need a tunnel mode processing */
+ if (isr != NULL)
+ *tun = 1;
+
+ return 0;
+
+bad:
+ m_freem(state->m);
+ state->m = NULL;
+ return error;
+}
+
+/*
+ * IPsec output logic for IPv6, tunnel mode.
+ */
+int
+ipsec6_output_tunnel(state, sp, flags)
+ struct ipsec_output_state *state;
+ struct secpolicy *sp;
+ int flags;
+{
+ struct ip6_hdr *ip6;
+ struct ipsecrequest *isr = NULL;
+ struct secasindex saidx;
+ int error = 0;
+ int plen;
+ struct sockaddr_in6* dst6;
+ int s;
+
+ if (!state)
+ panic("state == NULL in ipsec6_output_tunnel");
+ if (!state->m)
+ panic("state->m == NULL in ipsec6_output_tunnel");
+ if (!sp)
+ panic("sp == NULL in ipsec6_output_tunnel");
+
+ KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+ printf("ipsec6_output_tunnel: applyed SP\n");
+ kdebug_secpolicy(sp));
+
+ /*
+ * transport mode ipsec (before the 1st tunnel mode) is already
+ * processed by ipsec6_output_trans().
+ */
+ for (isr = sp->req; isr; isr = isr->next) {
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+ break;
+ }
+
+ for (/* already initialized */; isr; isr = isr->next) {
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /* When tunnel mode, SA peers must be specified. */
+ bcopy(&isr->saidx, &saidx, sizeof(saidx));
+ } else {
+ /* make SA index to look for a proper SA */
+ struct sockaddr_in6 *sin6;
+
+ bzero(&saidx, sizeof(saidx));
+ saidx.proto = isr->saidx.proto;
+ saidx.mode = isr->saidx.mode;
+ saidx.reqid = isr->saidx.reqid;
+
+ ip6 = mtod(state->m, struct ip6_hdr *);
+ sin6 = (struct sockaddr_in6 *)&saidx.src;
+ if (sin6->sin6_len == 0) {
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ bcopy(&ip6->ip6_src, &sin6->sin6_addr,
+ sizeof(ip6->ip6_src));
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+ }
+ }
+ sin6 = (struct sockaddr_in6 *)&saidx.dst;
+ if (sin6->sin6_len == 0) {
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ bcopy(&ip6->ip6_dst, &sin6->sin6_addr,
+ sizeof(ip6->ip6_dst));
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+ }
+ }
+ }
+
+ if (key_checkrequest(isr, &saidx) == ENOENT) {
+ /*
+ * IPsec processing is required, but no SA found.
+ * I assume that key_acquire() had been called
+ * to get/establish the SA. Here I discard
+ * this packet because it is responsibility for
+ * upper layer to retransmit the packet.
+ */
+ ipsec6stat.out_nosa++;
+ error = ENOENT;
+ goto bad;
+ }
+
+ /* validity check */
+ if (isr->sav == NULL) {
+ switch (ipsec_get_reqlevel(isr)) {
+ case IPSEC_LEVEL_USE:
+ continue;
+ case IPSEC_LEVEL_REQUIRE:
+ /* must be not reached here. */
+ panic("ipsec6_output_tunnel: no SA found, but required.");
+ }
+ }
+
+ /*
+ * If there is no valid SA, we give up to process.
+ * see same place at ipsec4_output().
+ */
+ if (isr->sav->state != SADB_SASTATE_MATURE
+ && isr->sav->state != SADB_SASTATE_DYING) {
+ ipsec6stat.out_nosa++;
+ error = EINVAL;
+ goto bad;
+ }
+
+ /*
+ * There may be the case that SA status will be changed when
+ * we are refering to one. So calling splsoftnet().
+ */
+ s = splnet();
+
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ /*
+ * build IPsec tunnel.
+ */
+ /* XXX should be processed with other familiy */
+ if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) {
+ ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+ "family mismatched between inner and outer, spi=%u\n",
+ (u_int32_t)ntohl(isr->sav->spi)));
+ splx(s);
+ ipsec6stat.out_inval++;
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+ state->m = ipsec6_splithdr(state->m);
+ if (!state->m) {
+ splx(s);
+ ipsec6stat.out_nomem++;
+ error = ENOMEM;
+ goto bad;
+ }
+ error = ipsec6_encapsulate(state->m, isr->sav);
+ splx(s);
+ if (error) {
+ state->m = 0;
+ goto bad;
+ }
+ ip6 = mtod(state->m, struct ip6_hdr *);
+
+ state->ro = &isr->sav->sah->sa_route;
+ state->dst = (struct sockaddr *)&state->ro->ro_dst;
+ dst6 = (struct sockaddr_in6 *)state->dst;
+ if (state->ro->ro_rt
+ && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
+ || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) {
+ RTFREE(state->ro->ro_rt);
+ state->ro->ro_rt = NULL;
+ }
+ if (state->ro->ro_rt == 0) {
+ bzero(dst6, sizeof(*dst6));
+ dst6->sin6_family = AF_INET6;
+ dst6->sin6_len = sizeof(*dst6);
+ dst6->sin6_addr = ip6->ip6_dst;
+ rtalloc(state->ro);
+ }
+ if (state->ro->ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ ipsec6stat.out_noroute++;
+ error = EHOSTUNREACH;
+ goto bad;
+ }
+
+ /* adjust state->dst if tunnel endpoint is offlink */
+ if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
+ state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
+ dst6 = (struct sockaddr_in6 *)state->dst;
+ }
+ } else
+ splx(s);
+
+ state->m = ipsec6_splithdr(state->m);
+ if (!state->m) {
+ ipsec6stat.out_nomem++;
+ error = ENOMEM;
+ goto bad;
+ }
+ ip6 = mtod(state->m, struct ip6_hdr *);
+ switch (isr->saidx.proto) {
+ case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+ error = esp6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr);
+#else
+ m_freem(state->m);
+ error = EINVAL;
+#endif
+ break;
+ case IPPROTO_AH:
+ error = ah6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr);
+ break;
+ case IPPROTO_IPCOMP:
+ /* XXX code should be here */
+ /* FALLTHROUGH */
+ default:
+ ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+ "unknown ipsec protocol %d\n", isr->saidx.proto));
+ m_freem(state->m);
+ ipsec6stat.out_inval++;
+ error = EINVAL;
+ break;
+ }
+ if (error) {
+ state->m = NULL;
+ goto bad;
+ }
+ plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr);
+ if (plen > IPV6_MAXPACKET) {
+ ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+ "IPsec with IPv6 jumbogram is not supported\n"));
+ ipsec6stat.out_inval++;
+ error = EINVAL; /* XXX */
+ goto bad;
+ }
+ ip6 = mtod(state->m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(plen);
+ }
+
+ return 0;
+
+bad:
+ m_freem(state->m);
+ state->m = NULL;
+ return error;
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * Chop IP header and option off from the payload.
+ */
+static struct mbuf *
+ipsec4_splithdr(m)
+ struct mbuf *m;
+{
+ struct mbuf *mh;
+ struct ip *ip;
+ int hlen;
+
+ if (m->m_len < sizeof(struct ip))
+ panic("ipsec4_splithdr: first mbuf too short");
+ ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+ hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+ hlen = ip->ip_hl << 2;
+#endif
+ if (m->m_len > hlen) {
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (!mh) {
+ m_freem(m);
+ return NULL;
+ }
+ M_MOVE_PKTHDR(mh, m);
+ MH_ALIGN(mh, hlen);
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ mh->m_next = m;
+ m = mh;
+ m->m_len = hlen;
+ bcopy((caddr_t)ip, mtod(m, caddr_t), hlen);
+ } else if (m->m_len < hlen) {
+ m = m_pullup(m, hlen);
+ if (!m)
+ return NULL;
+ }
+ return m;
+}
+#endif
+
+#ifdef INET6
+static struct mbuf *
+ipsec6_splithdr(m)
+ struct mbuf *m;
+{
+ struct mbuf *mh;
+ struct ip6_hdr *ip6;
+ int hlen;
+
+ if (m->m_len < sizeof(struct ip6_hdr))
+ panic("ipsec6_splithdr: first mbuf too short");
+ ip6 = mtod(m, struct ip6_hdr *);
+ hlen = sizeof(struct ip6_hdr);
+ if (m->m_len > hlen) {
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (!mh) {
+ m_freem(m);
+ return NULL;
+ }
+ M_MOVE_PKTHDR(mh, m);
+ MH_ALIGN(mh, hlen);
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ mh->m_next = m;
+ m = mh;
+ m->m_len = hlen;
+ bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen);
+ } else if (m->m_len < hlen) {
+ m = m_pullup(m, hlen);
+ if (!m)
+ return NULL;
+ }
+ return m;
+}
+#endif
+
+/* validate inbound IPsec tunnel packet. */
+int
+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(oip->ip_vhl) << 2;
+#else
+ hlen = oip->ip_hl << 2;
+#endif
+ if (hlen != sizeof(struct ip))
+ return 0;
+
+ /* 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;
+}
+
+#ifdef INET6
+/* validate inbound IPsec tunnel packet. */
+int
+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;
+ 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);
+ /*
+ * when there is no suitable inbound policy for the packet of the ipsec
+ * tunnel mode, the kernel never decapsulate the tunneled packet
+ * as the ipsec tunnel mode even when the system wide policy is "none".
+ * then the kernel leaves the generic tunnel module to process this
+ * packet. if there is no rule of the generic tunnel, the packet
+ * is rejected and the statistics will be counted up.
+ */
+ if (!sp)
+ return 0;
+ key_freesp(sp);
+
+ return 1;
+}
+#endif
+
+/*
+ * Make a mbuf chain for encryption.
+ * If the original mbuf chain contains a mbuf with a cluster,
+ * allocate a new cluster and copy the data to the new cluster.
+ * XXX: this hack is inefficient, but is necessary to handle cases
+ * of TCP retransmission...
+ */
+struct mbuf *
+ipsec_copypkt(m)
+ struct mbuf *m;
+{
+ struct mbuf *n, **mpp, *mnew;
+
+ for (n = m, mpp = &m; n; n = n->m_next) {
+ if (n->m_flags & M_EXT) {
+ /*
+ * Make a copy only if there are more than one
+ * references to the cluster.
+ * XXX: is this approach effective?
+ */
+ if (n->m_ext.ext_type != EXT_CLUSTER || MEXT_IS_REF(n))
+ {
+ int remain, copied;
+ struct mbuf *mm;
+
+ if (n->m_flags & M_PKTHDR) {
+ MGETHDR(mnew, M_DONTWAIT, MT_HEADER);
+ if (mnew == NULL)
+ goto fail;
+ if (!m_dup_pkthdr(mnew, n, M_DONTWAIT)) {
+ m_free(mnew);
+ goto fail;
+ }
+ }
+ else {
+ MGET(mnew, M_DONTWAIT, MT_DATA);
+ if (mnew == NULL)
+ goto fail;
+ }
+ mnew->m_len = 0;
+ mm = mnew;
+
+ /*
+ * Copy data. If we don't have enough space to
+ * store the whole data, allocate a cluster
+ * or additional mbufs.
+ * XXX: we don't use m_copyback(), since the
+ * function does not use clusters and thus is
+ * inefficient.
+ */
+ remain = n->m_len;
+ copied = 0;
+ while (1) {
+ int len;
+ struct mbuf *mn;
+
+ if (remain <= (mm->m_flags & M_PKTHDR ? MHLEN : MLEN))
+ len = remain;
+ else { /* allocate a cluster */
+ MCLGET(mm, M_DONTWAIT);
+ if (!(mm->m_flags & M_EXT)) {
+ m_free(mm);
+ goto fail;
+ }
+ len = remain < MCLBYTES ?
+ remain : MCLBYTES;
+ }
+
+ bcopy(n->m_data + copied, mm->m_data,
+ len);
+
+ copied += len;
+ remain -= len;
+ mm->m_len = len;
+
+ if (remain <= 0) /* completed? */
+ break;
+
+ /* need another mbuf */
+ MGETHDR(mn, M_DONTWAIT, MT_HEADER);
+ if (mn == NULL)
+ goto fail;
+ mn->m_pkthdr.rcvif = NULL;
+ mm->m_next = mn;
+ mm = mn;
+ }
+
+ /* adjust chain */
+ mm->m_next = m_free(n);
+ n = mm;
+ *mpp = mnew;
+ mpp = &n->m_next;
+
+ continue;
+ }
+ }
+ *mpp = n;
+ mpp = &n->m_next;
+ }
+
+ return(m);
+ fail:
+ m_freem(m);
+ return(NULL);
+}
+
+void
+ipsec_delaux(m)
+ struct mbuf *m;
+{
+ struct m_tag *tag;
+
+ while ((tag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL)) != NULL)
+ m_tag_delete(m, tag);
+}
+
+int
+ipsec_addhist(m, proto, spi)
+ struct mbuf *m;
+ int proto;
+ u_int32_t spi;
+{
+ struct m_tag *tag;
+ struct ipsec_history *p;
+
+ tag = m_tag_get(PACKET_TAG_IPSEC_HISTORY,
+ sizeof (struct ipsec_history), M_NOWAIT);
+ if (tag == NULL)
+ return ENOBUFS;
+ p = (struct ipsec_history *)(tag+1);
+ bzero(p, sizeof(*p));
+ p->ih_proto = proto;
+ p->ih_spi = spi;
+ m_tag_prepend(m, tag);
+ return 0;
+}
+
+struct ipsec_history *
+ipsec_gethist(m, lenp)
+ struct mbuf *m;
+ int *lenp;
+{
+ struct m_tag *tag;
+
+ tag = m_tag_find(m, PACKET_TAG_IPSEC_HISTORY, NULL);
+ if (tag == NULL)
+ return NULL;
+ /* XXX NB: noone uses this so fake it */
+ if (lenp)
+ *lenp = sizeof (struct ipsec_history);
+ return ((struct ipsec_history *)(tag+1));
+}
diff --git a/sys/netinet6/ipsec.h b/sys/netinet6/ipsec.h
new file mode 100644
index 0000000..40f5f56
--- /dev/null
+++ b/sys/netinet6/ipsec.h
@@ -0,0 +1,353 @@
+/* $FreeBSD$ */
+/* $KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 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.
+ */
+
+/*
+ * IPsec controller part.
+ */
+
+#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>
+
+#ifdef _KERNEL
+
+/*
+ * Security Policy Index
+ * Ensure that both address families in the "src" and "dst" are same.
+ * When the value of the ul_proto is ICMPv6, the port field in "src"
+ * specifies ICMPv6 type, and the port field in "dst" specifies ICMPv6 code.
+ */
+struct secpolicyindex {
+ u_int8_t dir; /* direction of packet flow, see blow */
+ struct sockaddr_storage src; /* IP src address for SP */
+ struct sockaddr_storage dst; /* IP dst address for SP */
+ u_int8_t prefs; /* prefix length in bits for src */
+ u_int8_t prefd; /* prefix length in bits for dst */
+ u_int16_t ul_proto; /* upper layer Protocol */
+#ifdef notyet
+ uid_t uids;
+ uid_t uidd;
+ gid_t gids;
+ gid_t gidd;
+#endif
+};
+
+/* Security Policy Data Base */
+struct secpolicy {
+ LIST_ENTRY(secpolicy) chain;
+
+ int refcnt; /* reference count */
+ struct secpolicyindex spidx; /* selector */
+ u_int32_t id; /* It's unique number on the system. */
+ u_int state; /* 0: dead, others: alive */
+#define IPSEC_SPSTATE_DEAD 0
+#define IPSEC_SPSTATE_ALIVE 1
+
+ u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */
+ struct ipsecrequest *req;
+ /* 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 */
+struct ipsecrequest {
+ struct ipsecrequest *next;
+ /* pointer to next structure */
+ /* If NULL, it means the end of chain. */
+ struct secasindex saidx;/* hint for search proper SA */
+ /* if __ss_len == 0 then no address specified.*/
+ u_int level; /* IPsec level defined below. */
+
+ struct secasvar *sav; /* place holder of SA for use */
+ struct secpolicy *sp; /* back pointer to SP */
+};
+
+/* security policy in PCB */
+struct inpcbpolicy {
+ struct secpolicy *sp_in;
+ struct secpolicy *sp_out;
+ int priv; /* privileged socket ? */
+};
+
+/* SP acquiring list table. */
+struct secspacq {
+ LIST_ENTRY(secspacq) chain;
+
+ struct secpolicyindex spidx;
+
+ long created; /* for lifetime */
+ int count; /* for lifetime */
+ /* XXX: here is mbuf place holder to be sent ? */
+};
+#endif /* _KERNEL */
+
+/* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */
+#define IPSEC_PORT_ANY 0
+#define IPSEC_ULPROTO_ANY 255
+#define IPSEC_PROTO_ANY 255
+
+/* mode of security protocol */
+/* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */
+#define IPSEC_MODE_ANY 0 /* i.e. wildcard. */
+#define IPSEC_MODE_TRANSPORT 1
+#define IPSEC_MODE_TUNNEL 2
+
+/*
+ * Direction of security policy.
+ * NOTE: Since INVALID is used just as flag.
+ * The other are used for loop counter too.
+ */
+#define IPSEC_DIR_ANY 0
+#define IPSEC_DIR_INBOUND 1
+#define IPSEC_DIR_OUTBOUND 2
+#define IPSEC_DIR_MAX 3
+#define IPSEC_DIR_INVALID 4
+
+/* Policy level */
+/*
+ * 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 */
+#define IPSEC_POLICY_IPSEC 2 /* do IPsec */
+#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */
+#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */
+
+/* Security protocol level */
+#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */
+#define IPSEC_LEVEL_USE 1 /* use SA if present. */
+#define IPSEC_LEVEL_REQUIRE 2 /* require SA. */
+#define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */
+
+#define IPSEC_MANUAL_REQID_MAX 0x3fff
+ /*
+ * if security policy level == unique, this id
+ * indicate to a relative SA for use, else is
+ * zero.
+ * 1 - 0x3fff are reserved for manual keying.
+ * 0 are reserved for above reason. Others is
+ * for kernel use.
+ * Note that this id doesn't identify SA
+ * by only itself.
+ */
+#define IPSEC_REPLAYWSIZE 32
+
+/* statistics for ipsec processing */
+struct ipsecstat {
+ u_quad_t in_success; /* succeeded inbound process */
+ u_quad_t in_polvio;
+ /* security policy violation for inbound process */
+ u_quad_t in_nosa; /* inbound SA is unavailable */
+ u_quad_t in_inval; /* inbound processing failed due to EINVAL */
+ u_quad_t in_nomem; /* inbound processing failed due to ENOBUFS */
+ u_quad_t in_badspi; /* failed getting a SPI */
+ u_quad_t in_ahreplay; /* AH replay check failed */
+ u_quad_t in_espreplay; /* ESP replay check failed */
+ u_quad_t in_ahauthsucc; /* AH authentication success */
+ u_quad_t in_ahauthfail; /* AH authentication failure */
+ u_quad_t in_espauthsucc; /* ESP authentication success */
+ u_quad_t in_espauthfail; /* ESP authentication failure */
+ u_quad_t in_esphist[256];
+ u_quad_t in_ahhist[256];
+ u_quad_t in_comphist[256];
+ u_quad_t out_success; /* succeeded outbound process */
+ u_quad_t out_polvio;
+ /* security policy violation for outbound process */
+ u_quad_t out_nosa; /* outbound SA is unavailable */
+ u_quad_t out_inval; /* outbound process failed due to EINVAL */
+ u_quad_t out_nomem; /* inbound processing failed due to ENOBUFS */
+ u_quad_t out_noroute; /* there is no route */
+ u_quad_t out_esphist[256];
+ u_quad_t out_ahhist[256];
+ u_quad_t out_comphist[256];
+};
+
+/*
+ * Definitions for IPsec & Key sysctl operations.
+ */
+/*
+ * Names for IPsec & Key sysctl objects
+ */
+#define IPSECCTL_STATS 1 /* stats */
+#define IPSECCTL_DEF_POLICY 2
+#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */
+#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */
+#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */
+#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */
+#if 0 /* obsolete, do not reuse */
+#define IPSECCTL_INBOUND_CALL_IKE 7
+#endif
+#define IPSECCTL_AH_CLEARTOS 8
+#define IPSECCTL_AH_OFFSETMASK 9
+#define IPSECCTL_DFBIT 10
+#define IPSECCTL_ECN 11
+#define IPSECCTL_DEBUG 12
+#define IPSECCTL_ESP_RANDPAD 13
+#define IPSECCTL_MAXID 14
+
+#define IPSECCTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "def_policy", CTLTYPE_INT }, \
+ { "esp_trans_deflev", CTLTYPE_INT }, \
+ { "esp_net_deflev", CTLTYPE_INT }, \
+ { "ah_trans_deflev", CTLTYPE_INT }, \
+ { "ah_net_deflev", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { "ah_cleartos", CTLTYPE_INT }, \
+ { "ah_offsetmask", CTLTYPE_INT }, \
+ { "dfbit", CTLTYPE_INT }, \
+ { "ecn", CTLTYPE_INT }, \
+ { "debug", CTLTYPE_INT }, \
+ { "esp_randpad", CTLTYPE_INT }, \
+}
+
+#define IPSEC6CTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "def_policy", CTLTYPE_INT }, \
+ { "esp_trans_deflev", CTLTYPE_INT }, \
+ { "esp_net_deflev", CTLTYPE_INT }, \
+ { "ah_trans_deflev", CTLTYPE_INT }, \
+ { "ah_net_deflev", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "ecn", CTLTYPE_INT }, \
+ { "debug", CTLTYPE_INT }, \
+ { "esp_randpad", CTLTYPE_INT }, \
+}
+
+#ifdef _KERNEL
+struct ipsec_output_state {
+ struct mbuf *m;
+ struct route *ro;
+ struct sockaddr *dst;
+};
+
+struct ipsec_history {
+ int ih_proto;
+ u_int32_t ih_spi;
+};
+
+extern int ipsec_debug;
+
+extern struct ipsecstat ipsecstat;
+extern struct secpolicy ip4_def_policy;
+extern int ip4_esp_trans_deflev;
+extern int ip4_esp_net_deflev;
+extern int ip4_ah_trans_deflev;
+extern int ip4_ah_net_deflev;
+extern int ip4_ah_cleartos;
+extern int ip4_ah_offsetmask;
+extern int ip4_ipsec_dfbit;
+extern int ip4_ipsec_ecn;
+extern int ip4_esp_randpad;
+
+#define ipseclog(x) do { if (ipsec_debug) log x; } while (0)
+
+struct inpcb;
+extern struct secpolicy *ipsec4_getpolicybypcb
+ __P((struct mbuf *, u_int, struct inpcb *, int *));
+extern struct secpolicy *ipsec4_getpolicybysock
+ __P((struct mbuf *, u_int, struct socket *, int *));
+extern struct secpolicy *ipsec4_getpolicybyaddr
+ __P((struct mbuf *, u_int, int, int *));
+
+extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **));
+extern int ipsec_copy_policy
+ __P((struct inpcbpolicy *, struct inpcbpolicy *));
+extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *));
+
+extern int ipsec4_set_policy __P((struct inpcb *inp, int optname,
+ caddr_t request, size_t len, int priv));
+extern int ipsec4_get_policy __P((struct inpcb *inpcb, caddr_t request,
+ size_t len, struct mbuf **mp));
+extern int ipsec4_delete_pcbpolicy __P((struct inpcb *));
+extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *));
+extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *));
+
+struct secas;
+struct tcpcb;
+extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *));
+extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *));
+
+extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
+extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *));
+
+struct ip;
+extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t));
+extern const char *ipsec_logsastr __P((struct secasvar *));
+
+extern void ipsec_dumpmbuf __P((struct mbuf *));
+
+extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *,
+ int));
+extern int ipsec4_tunnel_validate __P((struct mbuf *, int, u_int,
+ struct secasvar *));
+extern struct mbuf *ipsec_copypkt __P((struct mbuf *));
+extern void ipsec_delaux __P((struct mbuf *));
+extern int ipsec_addhist __P((struct mbuf *, int, u_int32_t));
+extern struct ipsec_history *ipsec_gethist __P((struct mbuf *, int *));
+#endif /* _KERNEL */
+
+#ifndef _KERNEL
+extern caddr_t ipsec_set_policy __P((char *, int));
+extern int ipsec_get_policylen __P((caddr_t));
+extern char *ipsec_dump_policy __P((caddr_t, char *));
+
+extern const char *ipsec_strerror __P((void));
+#endif /* !_KERNEL */
+
+#endif /* _NETINET6_IPSEC_H_ */
diff --git a/sys/netinet6/ipsec6.h b/sys/netinet6/ipsec6.h
new file mode 100644
index 0000000..1811088
--- /dev/null
+++ b/sys/netinet6/ipsec6.h
@@ -0,0 +1,84 @@
+/* $FreeBSD$ */
+/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 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.
+ */
+
+/*
+ * IPsec controller part.
+ */
+
+#ifndef _NETINET6_IPSEC6_H_
+#define _NETINET6_IPSEC6_H_
+
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+
+#ifdef _KERNEL
+extern struct ipsecstat ipsec6stat;
+extern struct secpolicy ip6_def_policy;
+extern int ip6_esp_trans_deflev;
+extern int ip6_esp_net_deflev;
+extern int ip6_ah_trans_deflev;
+extern int ip6_ah_net_deflev;
+extern int ip6_ipsec_ecn;
+extern int ip6_esp_randpad;
+
+struct inpcb;
+extern struct secpolicy *ipsec6_getpolicybypcb
+ __P((struct mbuf *, u_int, struct inpcb *, int *));
+extern struct secpolicy *ipsec6_getpolicybysock
+ __P((struct mbuf *, u_int, struct socket *, int *));
+extern struct secpolicy *ipsec6_getpolicybyaddr
+ __P((struct mbuf *, u_int, int, int *));
+
+extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *));
+extern int ipsec6_delete_pcbpolicy __P((struct inpcb *));
+extern int ipsec6_set_policy __P((struct inpcb *inp, int optname,
+ caddr_t request, size_t len, int priv));
+extern int ipsec6_get_policy
+ __P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp));
+extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *));
+
+struct tcp6cb;
+
+extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
+
+struct ip6_hdr;
+extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t));
+
+extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *,
+ struct mbuf *, struct secpolicy *, int, int *));
+extern int ipsec6_output_tunnel __P((struct ipsec_output_state *,
+ struct secpolicy *, int));
+extern int ipsec6_tunnel_validate __P((struct mbuf *, int, u_int,
+ struct secasvar *));
+#endif /*_KERNEL*/
+
+#endif /*_NETINET6_IPSEC6_H_*/
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
new file mode 100644
index 0000000..61c0e0c
--- /dev/null
+++ b/sys/netinet6/mld6.c
@@ -0,0 +1,474 @@
+/* $FreeBSD$ */
+/* $KAME: mld6.c,v 1.27 2001/04/04 05:17:30 itojun Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)igmp.c 8.1 (Berkeley) 7/19/93
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/mld6_var.h>
+
+#include <net/net_osdep.h>
+
+/*
+ * Protocol constants
+ */
+
+/* denotes that the MLD max response delay field specifies time in milliseconds */
+#define MLD6_TIMER_SCALE 1000
+/*
+ * time between repetitions of a node's initial report of interest in a
+ * multicast address(in seconds)
+ */
+#define MLD6_UNSOLICITED_REPORT_INTERVAL 10
+
+static struct ip6_pktopts ip6_opts;
+static int mld6_timers_are_running;
+/* XXX: These are necessary for KAME's link-local hack */
+static struct in6_addr mld6_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+static struct in6_addr mld6_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+
+static void mld6_sendpkt __P((struct in6_multi *, int, const struct in6_addr *));
+
+void
+mld6_init()
+{
+ static u_int8_t hbh_buf[8];
+ struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
+ u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
+
+ mld6_timers_are_running = 0;
+
+ /* ip6h_nxt will be fill in later */
+ hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
+
+ /* XXX: grotty hard coding... */
+ hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
+ hbh_buf[3] = 0;
+ hbh_buf[4] = IP6OPT_RTALERT;
+ 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;
+}
+
+void
+mld6_start_listening(in6m)
+ struct in6_multi *in6m;
+{
+ int s = splnet();
+
+ /*
+ * RFC2710 page 10:
+ * The node never sends a Report or Done for the link-scope all-nodes
+ * address.
+ * MLD messages are never sent for multicast addresses whose scope is 0
+ * (reserved) or 1 (node-local).
+ */
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX */
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
+ in6m->in6m_timer = 0;
+ in6m->in6m_state = MLD6_OTHERLISTENER;
+ } else {
+ mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
+ in6m->in6m_timer = MLD6_RANDOM_DELAY(
+ MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ);
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ mld6_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+mld6_stop_listening(in6m)
+ struct in6_multi *in6m;
+{
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX */
+ mld6_all_routers_linklocal.s6_addr16[1] =
+ htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
+
+ if (in6m->in6m_state == MLD6_IREPORTEDLAST &&
+ (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) &&
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL)
+ mld6_sendpkt(in6m, MLD_LISTENER_DONE,
+ &mld6_all_routers_linklocal);
+}
+
+void
+mld6_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct mld_hdr *mldh;
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct in6_multi *in6m;
+ struct in6_ifaddr *ia;
+ struct ifmultiaddr *ifma;
+ int timer; /* timer value in the MLD query header */
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),);
+ mldh = (struct mld_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(mldh, struct mld_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 (grp=%s)\n",
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&mldh->mld_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;
+ }
+
+ /*
+ * In the MLD6 specification, there are 3 states and a flag.
+ *
+ * In Non-Listener state, we simply don't have a membership record.
+ * In Delaying Listener state, our timer is running (in6m->in6m_timer)
+ * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
+ *
+ * The flag is in6m->in6m_state, it is set to MLD6_OTHERLISTENER if
+ * we have heard a report from another member, or MLD6_IREPORTEDLAST
+ * if we sent the last report.
+ */
+ switch(mldh->mld_type) {
+ case MLD_LISTENER_QUERY:
+ if (ifp->if_flags & IFF_LOOPBACK)
+ break;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
+ !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
+ break; /* print error or log stat? */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
+ mldh->mld_addr.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+
+ /*
+ * - Start the timers in all of our membership records
+ * that the query applies to for the interface on
+ * which the query arrived excl. those that belong
+ * to the "all-nodes" group (ff02::1).
+ * - Restart any timer that is already running but has
+ * A value longer than the requested timeout.
+ * - Use the value specified in the query message as
+ * the maximum timeout.
+ */
+ IFP_TO_IA6(ifp, ia);
+ if (ia == NULL)
+ break;
+
+ /*
+ * XXX: System timer resolution is too low to handle Max
+ * Response Delay, so set 1 to the internal timer even if
+ * the calculated value equals to zero when Max Response
+ * Delay is positive.
+ */
+ timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE;
+ if (timer == 0 && mldh->mld_maxdelay)
+ timer = 1;
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_INET6)
+ continue;
+ in6m = (struct in6_multi *)ifma->ifma_protospec;
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
+ &mld6_all_nodes_linklocal) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
+ IPV6_ADDR_SCOPE_LINKLOCAL)
+ continue;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
+ IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
+ &in6m->in6m_addr))
+ {
+ if (timer == 0) {
+ /* send a report immediately */
+ mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
+ NULL);
+ in6m->in6m_timer = 0; /* reset timer */
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ }
+ else if (in6m->in6m_timer == 0 || /*idle state*/
+ in6m->in6m_timer > timer) {
+ in6m->in6m_timer =
+ MLD6_RANDOM_DELAY(timer);
+ mld6_timers_are_running = 1;
+ }
+ }
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
+ mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ case MLD_LISTENER_REPORT:
+ /*
+ * For fast leave to work, we have to know that we are the
+ * last person to send a report for this group. Reports
+ * can potentially get looped back if we are a multicast
+ * router, so discard reports sourced by me.
+ * Note that it is impossible to check IFF_LOOPBACK flag of
+ * ifp for this purpose, since ip6_mloopback pass the physical
+ * interface to looutput.
+ */
+ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
+ break;
+
+ if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
+ break;
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
+ mldh->mld_addr.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
+ if (in6m) {
+ in6m->in6m_timer = 0; /* transit to idle state */
+ in6m->in6m_state = MLD6_OTHERLISTENER; /* clear flag */
+ }
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
+ mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ default: /* this is impossible */
+ log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type);
+ break;
+ }
+
+ m_freem(m);
+}
+
+void
+mld6_fasttimeo()
+{
+ struct in6_multi *in6m;
+ struct in6_multistep step;
+ int s;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!mld6_timers_are_running)
+ return;
+
+ s = splnet();
+ mld6_timers_are_running = 0;
+ IN6_FIRST_MULTI(step, in6m);
+ while (in6m != NULL) {
+ if (in6m->in6m_timer == 0) {
+ /* do nothing */
+ } else if (--in6m->in6m_timer == 0) {
+ mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
+ in6m->in6m_state = MLD6_IREPORTEDLAST;
+ } else {
+ mld6_timers_are_running = 1;
+ }
+ IN6_NEXT_MULTI(step, in6m);
+ }
+ splx(s);
+}
+
+static void
+mld6_sendpkt(in6m, type, dst)
+ struct in6_multi *in6m;
+ int type;
+ const struct in6_addr *dst;
+{
+ struct mbuf *mh, *md;
+ struct mld_hdr *mldh;
+ struct ip6_hdr *ip6;
+ struct ip6_moptions im6o;
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp = in6m->in6m_ifp;
+ struct ifnet *outif = NULL;
+
+ /*
+ * At first, find a link local address on the outgoing interface
+ * to use as the source address of the MLD packet.
+ */
+ if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
+ == NULL)
+ return;
+
+ /*
+ * Allocate mbufs to store ip6 header and MLD header.
+ * We allocate 2 mbufs and make chain in advance because
+ * it is more convenient when inserting the hop-by-hop option later.
+ */
+ MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+ if (mh == NULL)
+ return;
+ MGET(md, M_DONTWAIT, MT_DATA);
+ if (md == NULL) {
+ m_free(mh);
+ return;
+ }
+ mh->m_next = md;
+
+ mh->m_pkthdr.rcvif = NULL;
+ mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
+ mh->m_len = sizeof(struct ip6_hdr);
+ MH_ALIGN(mh, sizeof(struct ip6_hdr));
+
+ /* fill in the ip6 header */
+ ip6 = mtod(mh, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ /* ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
+
+ /* fill in the MLD header */
+ md->m_len = sizeof(struct mld_hdr);
+ mldh = mtod(md, struct mld_hdr *);
+ mldh->mld_type = type;
+ mldh->mld_code = 0;
+ mldh->mld_cksum = 0;
+ /* XXX: we assume the function will not be called for query messages */
+ mldh->mld_maxdelay = 0;
+ mldh->mld_reserved = 0;
+ mldh->mld_addr = in6m->in6m_addr;
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
+ mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
+ mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
+ sizeof(struct ip6_hdr),
+ sizeof(struct mld_hdr));
+
+ /* construct multicast option */
+ bzero(&im6o, sizeof(im6o));
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 1;
+
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing daemon can hear it.
+ */
+ im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
+
+ /* increment output statictics */
+ icmp6stat.icp6s_outhist[type]++;
+
+ ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif, NULL);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ switch (type) {
+ case MLD_LISTENER_QUERY:
+ icmp6_ifstat_inc(outif, ifs6_out_mldquery);
+ break;
+ case MLD_LISTENER_REPORT:
+ icmp6_ifstat_inc(outif, ifs6_out_mldreport);
+ break;
+ case MLD_LISTENER_DONE:
+ icmp6_ifstat_inc(outif, ifs6_out_mlddone);
+ break;
+ }
+ }
+}
diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h
new file mode 100644
index 0000000..e58560e
--- /dev/null
+++ b/sys/netinet6/mld6_var.h
@@ -0,0 +1,53 @@
+/* $FreeBSD$ */
+/* $KAME: mld6_var.h,v 1.4 2000/03/25 07:23:54 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_MLD6_VAR_H_
+#define _NETINET6_MLD6_VAR_H_
+
+#ifdef _KERNEL
+
+#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1)
+
+/*
+ * States for MLD stop-listening processing
+ */
+#define MLD6_OTHERLISTENER 0
+#define MLD6_IREPORTEDLAST 1
+
+void mld6_init __P((void));
+void mld6_input __P((struct mbuf *, int));
+void mld6_start_listening __P((struct in6_multi *));
+void mld6_stop_listening __P((struct in6_multi *));
+void mld6_fasttimeo __P((void));
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_MLD6_VAR_H_ */
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
new file mode 100644
index 0000000..f2f09f4
--- /dev/null
+++ b/sys/netinet6/nd6.c
@@ -0,0 +1,2251 @@
+/* $FreeBSD$ */
+/* $KAME: nd6.c,v 1.144 2001/05/24 07:44:00 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.
+ */
+
+/*
+ * XXX
+ * KAME 970409 note:
+ * BSD/OS version heavily modifies this code, related to llinfo.
+ * Since we don't have BSD/OS version of net/route.c in our hand,
+ * I left the code mostly as it was in 970310. -- itojun
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_mac.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+#include <sys/mac.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_atm.h>
+#include <net/fddi.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet/icmp6.h>
+
+#include <net/net_osdep.h>
+
+#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
+#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
+
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+/* timer values */
+int nd6_prune = 1; /* walk list every 1 seconds */
+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;
+
+struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6};
+static size_t nd_ifinfo_indexlim = 8;
+struct nd_ifinfo *nd_ifinfo = NULL;
+struct nd_drhead nd_defrouter;
+struct nd_prhead nd_prefix = { 0 };
+
+int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
+static struct sockaddr_in6 all1_sa;
+
+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()
+{
+ static int nd6_init_done = 0;
+ int i;
+
+ if (nd6_init_done) {
+ log(LOG_NOTICE, "nd6_init called more than once(ignored)\n");
+ return;
+ }
+
+ all1_sa.sin6_family = AF_INET6;
+ all1_sa.sin6_len = sizeof(struct sockaddr_in6);
+ for (i = 0; i < sizeof(all1_sa.sin6_addr); i++)
+ all1_sa.sin6_addr.s6_addr[i] = 0xff;
+
+ /* initialization of the default router list */
+ TAILQ_INIT(&nd_defrouter);
+
+ nd6_init_done = 1;
+
+ /* start timer */
+ callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
+ nd6_slowtimo, NULL);
+}
+
+void
+nd6_ifattach(ifp)
+ struct ifnet *ifp;
+{
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ */
+ if (nd_ifinfo == NULL || if_index >= nd_ifinfo_indexlim) {
+ size_t n;
+ caddr_t q;
+
+ while (if_index >= nd_ifinfo_indexlim)
+ nd_ifinfo_indexlim <<= 1;
+
+ /* grow nd_ifinfo */
+ n = nd_ifinfo_indexlim * sizeof(struct nd_ifinfo);
+ q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK);
+ bzero(q, n);
+ if (nd_ifinfo) {
+ bcopy((caddr_t)nd_ifinfo, q, n/2);
+ free((caddr_t)nd_ifinfo, M_IP6NDP);
+ }
+ nd_ifinfo = (struct nd_ifinfo *)q;
+ }
+
+#define ND nd_ifinfo[ifp->if_index]
+
+ /*
+ * Don't initialize if called twice.
+ * XXX: to detect this, we should choose a member that is never set
+ * before initialization of the ND structure itself. We formaly used
+ * the linkmtu member, which was not suitable because it could be
+ * initialized via "ifconfig mtu".
+ */
+ if (ND.basereachable)
+ return;
+
+ ND.linkmtu = ifnet_byindex(ifp->if_index)->if_mtu;
+ ND.chlim = IPV6_DEFHLIM;
+ ND.basereachable = REACHABLE_TIME;
+ ND.reachable = ND_COMPUTE_RTIME(ND.basereachable);
+ ND.retrans = RETRANS_TIMER;
+ ND.receivedra = 0;
+ ND.flags = ND6_IFF_PERFORMNUD;
+ nd6_setmtu(ifp);
+#undef ND
+}
+
+/*
+ * Reset ND level link MTU. This function is called when the physical MTU
+ * changes, which means we might have to adjust the ND level MTU.
+ */
+void
+nd6_setmtu(ifp)
+ struct ifnet *ifp;
+{
+ struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
+ 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;
+ 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) {
+ /*
+ * If the ND level MTU is not set yet, or if the maxmtu
+ * is reset to a smaller value than the ND level MTU,
+ * also reset the ND level MTU.
+ */
+ if (ndi->linkmtu == 0 ||
+ ndi->maxmtu < ndi->linkmtu) {
+ ndi->linkmtu = ndi->maxmtu;
+ /* also adjust in6_maxmtu if necessary. */
+ if (oldlinkmtu == 0) {
+ /*
+ * XXX: the case analysis is grotty, but
+ * it is not efficient to call in6_setmaxmtu()
+ * here when we are during the initialization
+ * procedure.
+ */
+ if (in6_maxmtu < ndi->linkmtu)
+ in6_maxmtu = ndi->linkmtu;
+ } else
+ in6_setmaxmtu();
+ }
+ }
+#undef MIN
+}
+
+void
+nd6_option_init(opt, icmp6len, ndopts)
+ void *opt;
+ int icmp6len;
+ union nd_opts *ndopts;
+{
+ bzero(ndopts, sizeof(*ndopts));
+ ndopts->nd_opts_search = (struct nd_opt_hdr *)opt;
+ ndopts->nd_opts_last
+ = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len);
+
+ if (icmp6len == 0) {
+ ndopts->nd_opts_done = 1;
+ ndopts->nd_opts_search = NULL;
+ }
+}
+
+/*
+ * Take one ND option.
+ */
+struct nd_opt_hdr *
+nd6_option(ndopts)
+ union nd_opts *ndopts;
+{
+ struct nd_opt_hdr *nd_opt;
+ int olen;
+
+ if (!ndopts)
+ panic("ndopts == NULL in nd6_option");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_option");
+ if (!ndopts->nd_opts_search)
+ return NULL;
+ if (ndopts->nd_opts_done)
+ return NULL;
+
+ nd_opt = ndopts->nd_opts_search;
+
+ /* make sure nd_opt_len is inside the buffer */
+ if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) {
+ bzero(ndopts, sizeof(*ndopts));
+ return NULL;
+ }
+
+ olen = nd_opt->nd_opt_len << 3;
+ if (olen == 0) {
+ /*
+ * Message validation requires that all included
+ * options have a length that is greater than zero.
+ */
+ bzero(ndopts, sizeof(*ndopts));
+ return NULL;
+ }
+
+ ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen);
+ if (ndopts->nd_opts_search > ndopts->nd_opts_last) {
+ /* option overruns the end of buffer, invalid */
+ bzero(ndopts, sizeof(*ndopts));
+ return NULL;
+ } else if (ndopts->nd_opts_search == ndopts->nd_opts_last) {
+ /* reached the end of options chain */
+ ndopts->nd_opts_done = 1;
+ ndopts->nd_opts_search = NULL;
+ }
+ return nd_opt;
+}
+
+/*
+ * Parse multiple ND options.
+ * This function is much easier to use, for ND routines that do not need
+ * multiple options of the same type.
+ */
+int
+nd6_options(ndopts)
+ union nd_opts *ndopts;
+{
+ struct nd_opt_hdr *nd_opt;
+ int i = 0;
+
+ if (!ndopts)
+ panic("ndopts == NULL in nd6_options");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_options");
+ if (!ndopts->nd_opts_search)
+ return 0;
+
+ while (1) {
+ nd_opt = nd6_option(ndopts);
+ if (!nd_opt && !ndopts->nd_opts_last) {
+ /*
+ * 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;
+ }
+
+ if (!nd_opt)
+ goto skip1;
+
+ switch (nd_opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_MTU:
+ case ND_OPT_REDIRECTED_HEADER:
+ if (ndopts->nd_opt_array[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]
+ = nd_opt;
+ }
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
+ ndopts->nd_opt_array[nd_opt->nd_opt_type]
+ = nd_opt;
+ }
+ ndopts->nd_opts_pi_end =
+ (struct nd_opt_prefix_info *)nd_opt;
+ break;
+ default:
+ /*
+ * Unknown options must be silently ignored,
+ * to accomodate future extension to the protocol.
+ */
+ nd6log((LOG_DEBUG,
+ "nd6_options: unsupported option %d - "
+ "option ignored\n", nd_opt->nd_opt_type));
+ }
+
+skip1:
+ i++;
+ if (i > nd6_maxndopt) {
+ icmp6stat.icp6s_nd_toomanyopt++;
+ nd6log((LOG_INFO, "too many loop in nd opt\n"));
+ break;
+ }
+
+ if (ndopts->nd_opts_done)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * ND6 timer routine to expire default route list and prefix list
+ */
+void
+nd6_timer(ignored_arg)
+ void *ignored_arg;
+{
+ int s;
+ 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();
+ callout_reset(&nd6_timer_ch, nd6_prune * hz,
+ nd6_timer, NULL);
+
+ ln = llinfo_nd6.ln_next;
+ while (ln && ln != &llinfo_nd6) {
+ struct rtentry *rt;
+ struct sockaddr_in6 *dst;
+ struct llinfo_nd6 *next = ln->ln_next;
+ /* XXX: used for the DELAY case only: */
+ struct nd_ifinfo *ndi = NULL;
+
+ if ((rt = ln->ln_rt) == NULL) {
+ ln = next;
+ continue;
+ }
+ if ((ifp = rt->rt_ifp) == NULL) {
+ ln = next;
+ continue;
+ }
+ ndi = &nd_ifinfo[ifp->if_index];
+ dst = (struct sockaddr_in6 *)rt_key(rt);
+
+ if (ln->ln_expire > time_second) {
+ ln = next;
+ continue;
+ }
+
+ /* sanity check */
+ if (!rt)
+ panic("rt=0 in nd6_timer(ln=%p)", ln);
+ if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
+ panic("rt_llinfo(%p) is not equal to ln(%p)",
+ rt->rt_llinfo, ln);
+ if (!dst)
+ panic("dst=0 in nd6_timer(ln=%p)", ln);
+
+ switch (ln->ln_state) {
+ case ND6_LLINFO_INCOMPLETE:
+ if (ln->ln_asked < nd6_mmaxtries) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr,
+ ln, 0);
+ } else {
+ struct mbuf *m = ln->ln_hold;
+ if (m) {
+ if (rt->rt_ifp) {
+ /*
+ * Fake rcvif to make ICMP error
+ * more helpful in diagnosing
+ * for the receiver.
+ * XXX: should we consider
+ * older rcvif?
+ */
+ m->m_pkthdr.rcvif = rt->rt_ifp;
+ }
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR, 0);
+ ln->ln_hold = NULL;
+ }
+ next = nd6_free(rt);
+ }
+ break;
+ case ND6_LLINFO_REACHABLE:
+ if (ln->ln_expire) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+ break;
+
+ 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 */
+ ln->ln_asked = 1;
+ ln->ln_state = ND6_LLINFO_PROBE;
+ ln->ln_expire = time_second +
+ ndi->retrans / 1000;
+ nd6_ns_output(ifp, &dst->sin6_addr,
+ &dst->sin6_addr,
+ ln, 0);
+ } else {
+ ln->ln_state = ND6_LLINFO_STALE; /* XXX */
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+ break;
+ case ND6_LLINFO_PROBE:
+ if (ln->ln_asked < nd6_umaxtries) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, &dst->sin6_addr,
+ &dst->sin6_addr, ln, 0);
+ } else {
+ next = nd6_free(rt);
+ }
+ break;
+ }
+ ln = next;
+ }
+
+ /* expire default router list */
+ dr = TAILQ_FIRST(&nd_defrouter);
+ while (dr) {
+ if (dr->expire && dr->expire < time_second) {
+ struct nd_defrouter *t;
+ t = TAILQ_NEXT(dr, dr_entry);
+ defrtrlist_del(dr);
+ dr = t;
+ } else {
+ dr = TAILQ_NEXT(dr, dr_entry);
+ }
+ }
+
+ /*
+ * 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 it 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 */
+ }
+ 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 a deletion, but an
+ * addition,) we'd rather restart the
+ * loop just for safety. Or does this
+ * significantly reduce performance??
+ */
+ goto addrloop;
+ }
+ }
+ } else {
+ /*
+ * 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
+ * prefix is not necessary.
+ */
+ 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_purgeaddr here.
+ */
+
+ prelist_remove(pr);
+ pr = t;
+ } else
+ pr = pr->ndpr_next;
+ }
+ 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.
+ */
+void
+nd6_purge(ifp)
+ struct ifnet *ifp;
+{
+ struct llinfo_nd6 *ln, *nln;
+ struct nd_defrouter *dr, *ndr, drany;
+ struct nd_prefix *pr, *npr;
+
+ /* Nuke default router list entries toward ifp */
+ if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
+ /*
+ * The first entry of the list may be stored in
+ * the routing table, so we'll delete it later.
+ */
+ for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
+ if (dr->ifp == ifp)
+ defrtrlist_del(dr);
+ }
+ dr = TAILQ_FIRST(&nd_defrouter);
+ if (dr->ifp == ifp)
+ defrtrlist_del(dr);
+ }
+
+ /* Nuke prefix list entries toward ifp */
+ for (pr = nd_prefix.lh_first; pr; pr = npr) {
+ npr = pr->ndpr_next;
+ if (pr->ndpr_ifp == ifp) {
+ /*
+ * 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);
+ }
+ }
+
+ /* cancel default outgoing interface setting */
+ if (nd6_defifindex == ifp->if_index)
+ nd6_setdefaultiface(0);
+
+ if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
+ /* refresh default router list */
+ bzero(&drany, sizeof(drany));
+ defrouter_delreq(&drany, 0);
+ defrouter_select();
+ }
+
+ /*
+ * Nuke neighbor cache entries for the ifp.
+ * Note that rt->rt_ifp may not be the same as ifp,
+ * due to KAME goto ours hack. See RTM_RESOLVE case in
+ * nd6_rtrequest(), and ip6_input().
+ */
+ ln = llinfo_nd6.ln_next;
+ while (ln && ln != &llinfo_nd6) {
+ struct rtentry *rt;
+ struct sockaddr_dl *sdl;
+
+ nln = ln->ln_next;
+ rt = ln->ln_rt;
+ if (rt && rt->rt_gateway &&
+ rt->rt_gateway->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)rt->rt_gateway;
+ if (sdl->sdl_index == ifp->if_index)
+ nln = nd6_free(rt);
+ }
+ ln = nln;
+ }
+}
+
+struct rtentry *
+nd6_lookup(addr6, create, ifp)
+ struct in6_addr *addr6;
+ int create;
+ struct ifnet *ifp;
+{
+ struct rtentry *rt;
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = *addr6;
+#ifdef SCOPEDROUTING
+ sin6.sin6_scope_id = in6_addr2scopeid(ifp, addr6);
+#endif
+ rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL);
+ if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
+ /*
+ * This is the case for the default route.
+ * If we want to create a neighbor cache for the address, we
+ * should free the route for the destination and allocate an
+ * interface route.
+ */
+ if (create) {
+ RTFREE(rt);
+ rt = 0;
+ }
+ }
+ if (!rt) {
+ if (create && ifp) {
+ int e;
+
+ /*
+ * If no route is available and create is set,
+ * we allocate a host route for the destination
+ * and treat it like an interface route.
+ * This hack is necessary for a neighbor which can't
+ * be covered by our own prefix.
+ */
+ struct ifaddr *ifa =
+ ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp);
+ if (ifa == NULL)
+ return(NULL);
+
+ /*
+ * Create a new route. RTF_LLINFO is necessary
+ * to create a Neighbor Cache entry for the
+ * destination in nd6_rtrequest which will be
+ * called in rtrequest via ifa->ifa_rtrequest.
+ */
+ if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
+ ifa->ifa_addr,
+ (struct sockaddr *)&all1_sa,
+ (ifa->ifa_flags |
+ RTF_HOST | RTF_LLINFO) &
+ ~RTF_CLONING,
+ &rt)) != 0)
+ log(LOG_ERR,
+ "nd6_lookup: failed to add route for a "
+ "neighbor(%s), errno=%d\n",
+ ip6_sprintf(addr6), e);
+ if (rt == NULL)
+ return(NULL);
+ if (rt->rt_llinfo) {
+ struct llinfo_nd6 *ln =
+ (struct llinfo_nd6 *)rt->rt_llinfo;
+ ln->ln_state = ND6_LLINFO_NOSTATE;
+ }
+ } else
+ return(NULL);
+ }
+ rt->rt_refcnt--;
+ /*
+ * Validation for the entry.
+ * Note that the check for rt_llinfo is necessary because a cloned
+ * route from a parent route that has the L flag (e.g. the default
+ * route to a p2p interface) may have the flag, too, while the
+ * destination is not actually a neighbor.
+ * XXX: we can't use rt->rt_ifp to check for the interface, since
+ * it might be the loopback interface if the entry is for our
+ * own address on a non-loopback interface. Instead, we should
+ * use rt->rt_ifa->ifa_ifp, which would specify the REAL
+ * interface.
+ */
+ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
+ rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
+ (ifp && rt->rt_ifa->ifa_ifp != ifp)) {
+ if (create) {
+ log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n",
+ ip6_sprintf(addr6), ifp ? if_name(ifp) : "unspec");
+ /* xxx more logs... kazu */
+ }
+ return(NULL);
+ }
+ return(rt);
+}
+
+/*
+ * Detect if a given IPv6 address identifies a neighbor on a given link.
+ * XXX: should take care of the destination of a p2p link?
+ */
+int
+nd6_is_addr_neighbor(addr, ifp)
+ struct sockaddr_in6 *addr;
+ struct ifnet *ifp;
+{
+ struct ifaddr *ifa;
+ int i;
+
+#define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr)
+#define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr)
+
+ /*
+ * A link-local address is always a neighbor.
+ * XXX: we should use the sin6_scope_id field rather than the embedded
+ * interface index.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) &&
+ ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index)
+ return(1);
+
+ /*
+ * If the address matches one of our addresses,
+ * it should be a neighbor.
+ */
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ next: continue;
+
+ for (i = 0; i < 4; i++) {
+ if ((IFADDR6(ifa).s6_addr32[i] ^
+ addr->sin6_addr.s6_addr32[i]) &
+ IFMASK6(ifa).s6_addr32[i])
+ goto next;
+ }
+ return(1);
+ }
+
+ /*
+ * Even if the address matches none of our addresses, it might be
+ * in the neighbor cache.
+ */
+ if (nd6_lookup(&addr->sin6_addr, 0, ifp) != NULL)
+ return(1);
+
+ return(0);
+#undef IFADDR6
+#undef IFMASK6
+}
+
+/*
+ * Free an nd6 llinfo entry.
+ */
+struct llinfo_nd6 *
+nd6_free(rt)
+ struct rtentry *rt;
+{
+ 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;
+
+ /*
+ * we used to have pfctlinput(PRC_HOSTDEAD) here.
+ * even though it is not harmful, it was not really necessary.
+ */
+
+ 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
+ * is in the Default Router List.
+ * See a corresponding comment in nd6_na_input().
+ */
+ rt6_flush(&in6, rt->rt_ifp);
+ }
+
+ if (dr) {
+ /*
+ * Unreachablity of a router might affect the default
+ * router selection and on-link detection of advertised
+ * prefixes.
+ */
+
+ /*
+ * Temporarily fake the state to choose a new default
+ * router and to perform on-link determination of
+ * prefixes correctly.
+ * Below the state will be set correctly,
+ * or the entry itself will be deleted.
+ */
+ 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,
+ * so we have to move it to the end of the
+ * list and choose a new one.
+ * XXX: it is not very efficient if this is
+ * the only router.
+ */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry);
+
+ defrouter_select();
+ }
+ }
+ splx(s);
+ }
+
+ /*
+ * 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);
+}
+
+/*
+ * Upper-layer reachability hint for Neighbor Unreachability Detection.
+ *
+ * XXX cost-effective metods?
+ */
+void
+nd6_nud_hint(rt, dst6, force)
+ struct rtentry *rt;
+ struct in6_addr *dst6;
+ int force;
+{
+ struct llinfo_nd6 *ln;
+
+ /*
+ * If the caller specified "rt", use that. Otherwise, resolve the
+ * routing table by supplied "dst6".
+ */
+ if (!rt) {
+ if (!dst6)
+ return;
+ if (!(rt = nd6_lookup(dst6, 0, NULL)))
+ return;
+ }
+
+ if ((rt->rt_flags & RTF_GATEWAY) != 0 ||
+ (rt->rt_flags & RTF_LLINFO) == 0 ||
+ !rt->rt_llinfo || !rt->rt_gateway ||
+ rt->rt_gateway->sa_family != AF_LINK) {
+ /* This is not a host route. */
+ return;
+ }
+
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ if (ln->ln_state < ND6_LLINFO_REACHABLE)
+ return;
+
+ /*
+ * if we get upper-layer reachability confirmation many times,
+ * it is possible we have false information.
+ */
+ if (!force) {
+ ln->ln_byhint++;
+ if (ln->ln_byhint > nd6_maxnudhint)
+ return;
+ }
+
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (ln->ln_expire)
+ ln->ln_expire = time_second +
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+}
+
+void
+nd6_rtrequest(req, rt, info)
+ int req;
+ struct rtentry *rt;
+ struct rt_addrinfo *info; /* xxx unused */
+{
+ struct sockaddr *gate = rt->rt_gateway;
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ 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;
+
+ 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;
+ }
+
+ if (req == RTM_RESOLVE &&
+ (nd6_need_cache(ifp) == 0 || /* stf case */
+ !nd6_is_addr_neighbor((struct sockaddr_in6 *)rt_key(rt), ifp))) {
+ /*
+ * FreeBSD and BSD/OS often make a cloned host route based
+ * on a less-specific route (e.g. the default route).
+ * If the less specific route does not have a "gateway"
+ * (this is the case when the route just goes to a p2p or an
+ * stf interface), we'll mistakenly make a neighbor cache for
+ * the host route, and will see strange neighbor solicitation
+ * for the corresponding destination. In order to avoid the
+ * confusion, we check if the destination of the route is
+ * a neighbor in terms of neighbor discovery, and stop the
+ * process if not. Additionally, we remove the LLINFO flag
+ * so that ndp(8) will not try to get the neighbor information
+ * of the destination.
+ */
+ rt->rt_flags &= ~RTF_LLINFO;
+ 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 | RTF_LLINFO)) {
+ /*
+ * Case 1: This route should come from
+ * a route to interface. RTF_LLINFO flag is set
+ * for a host route whose destination should be
+ * treated as on-link.
+ */
+ 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;
+ if (ln)
+ ln->ln_expire = time_second;
+#if 1
+ if (ln && ln->ln_expire == 0) {
+ /* kludge for desktops */
+#if 0
+ printf("nd6_rtequest: time.tv_sec is zero; "
+ "treat it as 1\n");
+#endif
+ ln->ln_expire = 1;
+ }
+#endif
+ if ((rt->rt_flags & RTF_CLONING))
+ break;
+ }
+ /*
+ * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here.
+ * We don't do that here since llinfo is not ready yet.
+ *
+ * There are also couple of other things to be discussed:
+ * - unsolicited NA code needs improvement beforehand
+ * - RFC2461 says we MAY send multicast unsolicited NA
+ * (7.2.6 paragraph 4), however, it also says that we
+ * SHOULD provide a mechanism to prevent multicast NA storm.
+ * we don't have anything like it right now.
+ * note that the mechanism needs a mutual agreement
+ * between proxies, which means that we need to implement
+ * 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)
+ */
+#if 0
+ /* XXX it does not work */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ nd6_na_output(ifp,
+ &SIN6(rt_key(rt))->sin6_addr,
+ &SIN6(rt_key(rt))->sin6_addr,
+ ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
+ 1, NULL);
+#endif
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ 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.
+ */
+ if (gate->sa_family != AF_LINK ||
+ gate->sa_len < sizeof(null_sdl)) {
+ log(LOG_DEBUG,
+ "nd6_rtrequest: bad gateway value: %s\n",
+ if_name(ifp));
+ break;
+ }
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ }
+ if (ln != NULL)
+ break; /* This happens on a route change */
+ /*
+ * Case 2: This route may come from cloning, or a manual route
+ * add with a LL address.
+ */
+ R_Malloc(ln, struct llinfo_nd6 *, sizeof(*ln));
+ rt->rt_llinfo = (caddr_t)ln;
+ if (!ln) {
+ log(LOG_DEBUG, "nd6_rtrequest: malloc failed\n");
+ break;
+ }
+ nd6_inuse++;
+ nd6_allocated++;
+ Bzero(ln, sizeof(*ln));
+ ln->ln_rt = rt;
+ /* this is required for "ndp" command. - shin */
+ if (req == RTM_ADD) {
+ /*
+ * gate should have some valid AF_LINK entry,
+ * and ln->ln_expire should have some lifetime
+ * which is specified by ndp command.
+ */
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_byhint = 0;
+ } else {
+ /*
+ * When req == RTM_RESOLVE, rt is created and
+ * initialized in rtrequest(), so rt_expire is 0.
+ */
+ ln->ln_state = ND6_LLINFO_NOSTATE;
+ ln->ln_expire = time_second;
+ }
+ rt->rt_flags |= RTF_LLINFO;
+ ln->ln_next = llinfo_nd6.ln_next;
+ llinfo_nd6.ln_next = ln;
+ ln->ln_prev = &llinfo_nd6;
+ ln->ln_next->ln_prev = ln;
+
+ /*
+ * 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) {
+ caddr_t macp = nd6_ifptomac(ifp);
+ ln->ln_expire = 0;
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_byhint = 0;
+ if (macp) {
+ Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen);
+ SDL(gate)->sdl_alen = ifp->if_addrlen;
+ }
+ if (nd6_useloopback) {
+ rt->rt_ifp = &loif[0]; /* XXX */
+ /*
+ * Make sure rt_ifa be equal to the ifaddr
+ * corresponding to the address.
+ * 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.
+ */
+ if (ifa != rt->rt_ifa) {
+ IFAFREE(rt->rt_ifa);
+ IFAREF(ifa);
+ rt->rt_ifa = ifa;
+ }
+ }
+ } else if (rt->rt_flags & RTF_ANNOUNCE) {
+ ln->ln_expire = 0;
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_byhint = 0;
+
+ /* join solicited node multicast for proxy ND */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ struct in6_addr llsol;
+ int error;
+
+ llsol = SIN6(rt_key(rt))->sin6_addr;
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr8[12] = 0xff;
+
+ 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;
+
+ case RTM_DELETE:
+ if (!ln)
+ break;
+ /* leave from solicited node multicast for proxy ND */
+ if ((rt->rt_flags & RTF_ANNOUNCE) != 0 &&
+ (ifp->if_flags & IFF_MULTICAST) != 0) {
+ struct in6_addr llsol;
+ struct in6_multi *in6m;
+
+ llsol = SIN6(rt_key(rt))->sin6_addr;
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr8[12] = 0xff;
+
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ nd6_inuse--;
+ ln->ln_next->ln_prev = ln->ln_prev;
+ ln->ln_prev->ln_next = ln->ln_next;
+ ln->ln_prev = NULL;
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ Free((caddr_t)ln);
+ }
+}
+
+int
+nd6_ioctl(cmd, data, ifp)
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+{
+ struct in6_drlist *drl = (struct in6_drlist *)data;
+ struct in6_prlist *prl = (struct in6_prlist *)data;
+ struct in6_ndireq *ndi = (struct in6_ndireq *)data;
+ struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
+ struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
+ struct nd_defrouter *dr, any;
+ struct nd_prefix *pr;
+ struct rtentry *rt;
+ int i = 0, error = 0;
+ int s;
+
+ switch (cmd) {
+ case SIOCGDRLST_IN6:
+ /*
+ * obsolete API, use sysctl under net.inet6.icmp6
+ */
+ bzero(drl, sizeof(*drl));
+ s = splnet();
+ dr = TAILQ_FIRST(&nd_defrouter);
+ while (dr && i < DRLSTSIZ) {
+ drl->defrouter[i].rtaddr = dr->rtaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) {
+ /* XXX: need to this hack for KAME stack */
+ drl->defrouter[i].rtaddr.s6_addr16[1] = 0;
+ } else
+ log(LOG_ERR,
+ "default router list contains a "
+ "non-linklocal address(%s)\n",
+ ip6_sprintf(&drl->defrouter[i].rtaddr));
+
+ drl->defrouter[i].flags = dr->flags;
+ drl->defrouter[i].rtlifetime = dr->rtlifetime;
+ drl->defrouter[i].expire = dr->expire;
+ drl->defrouter[i].if_index = dr->ifp->if_index;
+ i++;
+ dr = TAILQ_NEXT(dr, dr_entry);
+ }
+ splx(s);
+ 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?
+ */
+ bzero(prl, sizeof(*prl));
+ s = splnet();
+ pr = nd_prefix.lh_first;
+ while (pr && i < PRLSTSIZ) {
+ struct nd_pfxrouter *pfr;
+ int j;
+
+ (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;
+ prl->prefix[i].pltime = pr->ndpr_pltime;
+ prl->prefix[i].if_index = pr->ndpr_ifp->if_index;
+ prl->prefix[i].expire = pr->ndpr_expire;
+
+ pfr = pr->ndpr_advrtrs.lh_first;
+ j = 0;
+ while (pfr) {
+ if (j < DRLSTSIZ) {
+#define RTRADDR prl->prefix[i].advrtr[j]
+ RTRADDR = pfr->router->rtaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) {
+ /* XXX: hack for KAME */
+ RTRADDR.s6_addr16[1] = 0;
+ } else
+ log(LOG_ERR,
+ "a router(%s) advertises "
+ "a prefix with "
+ "non-link local address\n",
+ ip6_sprintf(&RTRADDR));
+#undef RTRADDR
+ }
+ j++;
+ pfr = pfr->pfr_next;
+ }
+ prl->prefix[i].advrtrs = j;
+ prl->prefix[i].origin = PR_ORIG_RA;
+
+ i++;
+ pr = pr->ndpr_next;
+ }
+ {
+ struct rr_prefix *rpp;
+
+ for (rpp = LIST_FIRST(&rr_prefix); rpp;
+ rpp = LIST_NEXT(rpp, rp_entry)) {
+ if (i >= PRLSTSIZ)
+ break;
+ (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;
+ prl->prefix[i].pltime = rpp->rp_pltime;
+ prl->prefix[i].if_index = rpp->rp_ifp->if_index;
+ prl->prefix[i].expire = rpp->rp_expire;
+ prl->prefix[i].advrtrs = 0;
+ prl->prefix[i].origin = rpp->rp_origin;
+ i++;
+ }
+ }
+ splx(s);
+
+ break;
+ case 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;
+ break;
+ }
+ ndi->ndi = nd_ifinfo[ifp->if_index];
+ break;
+ case SIOCSIFINFO_FLAGS:
+ /* XXX: almost all other fields of ndi->ndi is unused */
+ if (!nd_ifinfo || i >= nd_ifinfo_indexlim) {
+ error = EINVAL;
+ break;
+ }
+ nd_ifinfo[ifp->if_index].flags = ndi->ndi.flags;
+ break;
+ case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
+ /* flush default router list */
+ /*
+ * xxx sumikawa: should not delete route if default
+ * route equals to the top of default router list
+ */
+ bzero(&any, sizeof(any));
+ defrouter_delreq(&any, 0);
+ defrouter_select();
+ /* xxx sumikawa: flush prefix list */
+ break;
+ case SIOCSPFXFLUSH_IN6:
+ {
+ /* flush all the prefix advertised by routers */
+ struct nd_prefix *pr, *next;
+
+ 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_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);
+ break;
+ }
+ case SIOCSRTRFLUSH_IN6:
+ {
+ /* flush all the default routers */
+ struct nd_defrouter *dr, *next;
+
+ s = splnet();
+ if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
+ /*
+ * The first entry of the list may be stored in
+ * the routing table, so we'll delete it later.
+ */
+ for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) {
+ next = TAILQ_NEXT(dr, dr_entry);
+ defrtrlist_del(dr);
+ }
+ defrtrlist_del(TAILQ_FIRST(&nd_defrouter));
+ }
+ splx(s);
+ break;
+ }
+ case SIOCGNBRINFO_IN6:
+ {
+ struct llinfo_nd6 *ln;
+ struct in6_addr nb_addr = nbi->addr; /* make local for safety */
+
+ /*
+ * XXX: KAME specific hack for scoped addresses
+ * XXXX: for other scopes than link-local?
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) {
+ u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2];
+
+ if (*idp == 0)
+ *idp = htons(ifp->if_index);
+ }
+
+ s = splnet();
+ if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
+ error = EINVAL;
+ splx(s);
+ break;
+ }
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ nbi->state = ln->ln_state;
+ nbi->asked = ln->ln_asked;
+ nbi->isrouter = ln->ln_router;
+ nbi->expire = ln->ln_expire;
+ splx(s);
+
+ break;
+ }
+ case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */
+ ndif->ifindex = nd6_defifindex;
+ break;
+ case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */
+ return(nd6_setdefaultiface(ndif->ifindex));
+ break;
+ }
+ return(error);
+}
+
+/*
+ * Create neighbor cache entry and cache link-layer address,
+ * on reception of inbound ND6 packets. (RS/RA/NS/redirect)
+ */
+struct rtentry *
+nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
+ struct ifnet *ifp;
+ struct in6_addr *from;
+ char *lladdr;
+ int lladdrlen;
+ int type; /* ICMP6 type */
+ int code; /* type dependent information */
+{
+ struct rtentry *rt = NULL;
+ struct llinfo_nd6 *ln = NULL;
+ int is_newentry;
+ struct sockaddr_dl *sdl = NULL;
+ int do_update;
+ int olladdr;
+ int llchange;
+ int newstate = 0;
+
+ if (!ifp)
+ panic("ifp == NULL in nd6_cache_lladdr");
+ if (!from)
+ panic("from == NULL in nd6_cache_lladdr");
+
+ /* nothing must be updated for unspecified address */
+ if (IN6_IS_ADDR_UNSPECIFIED(from))
+ return NULL;
+
+ /*
+ * Validation about ifp->if_addrlen and lladdrlen must be done in
+ * the caller.
+ *
+ * XXX If the link does not have link-layer adderss, what should
+ * we do? (ifp->if_addrlen == 0)
+ * Spec says nothing in sections for RA, RS and NA. There's small
+ * description on it in NS section (RFC 2461 7.2.3).
+ */
+
+ rt = nd6_lookup(from, 0, ifp);
+ if (!rt) {
+#if 0
+ /* nothing must be done if there's no lladdr */
+ if (!lladdr || !lladdrlen)
+ return NULL;
+#endif
+
+ rt = nd6_lookup(from, 1, ifp);
+ is_newentry = 1;
+ } else {
+ /* 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:
+ (void)nd6_free(rt);
+ return NULL;
+ }
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ if (!ln)
+ goto fail;
+ if (!rt->rt_gateway)
+ goto fail;
+ if (rt->rt_gateway->sa_family != AF_LINK)
+ goto fail;
+ sdl = SDL(rt->rt_gateway);
+
+ olladdr = (sdl->sdl_alen) ? 1 : 0;
+ if (olladdr && lladdr) {
+ if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
+ llchange = 1;
+ else
+ llchange = 0;
+ } else
+ llchange = 0;
+
+ /*
+ * newentry olladdr lladdr llchange (*=record)
+ * 0 n n -- (1)
+ * 0 y n -- (2)
+ * 0 n y -- (3) * STALE
+ * 0 y y n (4) *
+ * 0 y y y (5) * STALE
+ * 1 -- n -- (6) NOSTATE(= PASSIVE)
+ * 1 -- y -- (7) * STALE
+ */
+
+ if (lladdr) { /* (3-5) and (7) */
+ /*
+ * Record source link-layer address
+ * XXX is it dependent to ifp->if_type?
+ */
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ }
+
+ if (!is_newentry) {
+ if ((!olladdr && lladdr) /* (3) */
+ || (olladdr && lladdr && llchange)) { /* (5) */
+ do_update = 1;
+ newstate = ND6_LLINFO_STALE;
+ } else /* (1-2,4) */
+ do_update = 0;
+ } else {
+ do_update = 1;
+ if (!lladdr) /* (6) */
+ newstate = ND6_LLINFO_NOSTATE;
+ else /* (7) */
+ newstate = ND6_LLINFO_STALE;
+ }
+
+ if (do_update) {
+ /*
+ * Update the state of the neighbor cache.
+ */
+ ln->ln_state = newstate;
+
+ if (ln->ln_state == ND6_LLINFO_STALE) {
+ /*
+ * 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) {
+ /*
+ * 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);
+ ln->ln_hold = NULL;
+ }
+ } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
+ /* probe right away */
+ ln->ln_expire = time_second;
+ }
+ }
+
+ /*
+ * ICMP6 type dependent behavior.
+ *
+ * NS: clear IsRouter if new entry
+ * RS: clear IsRouter
+ * RA: set IsRouter if there's lladdr
+ * redir: clear IsRouter if new entry
+ *
+ * RA case, (1):
+ * The spec says that we must set IsRouter in the following cases:
+ * - If lladdr exist, set IsRouter. This means (1-5).
+ * - If it is old entry (!newentry), set IsRouter. This means (7).
+ * So, based on the spec, in (1-5) and (7) cases we must set IsRouter.
+ * A quetion arises for (1) case. (1) case has no lladdr in the
+ * neighbor cache, this is similar to (6).
+ * This case is rare but we figured that we MUST NOT set IsRouter.
+ *
+ * newentry olladdr lladdr llchange NS RS RA redir
+ * D R
+ * 0 n n -- (1) c ? s
+ * 0 y n -- (2) c s s
+ * 0 n y -- (3) c s s
+ * 0 y y n (4) c s s
+ * 0 y y y (5) c s s
+ * 1 -- n -- (6) c c c s
+ * 1 -- y -- (7) c c s c s
+ *
+ * (c=clear s=set)
+ */
+ switch (type & 0xff) {
+ case ND_NEIGHBOR_SOLICIT:
+ /*
+ * New entry must have is_router flag cleared.
+ */
+ if (is_newentry) /* (6-7) */
+ ln->ln_router = 0;
+ break;
+ case ND_REDIRECT:
+ /*
+ * If the icmp is a redirect to a better router, always set the
+ * is_router flag. Otherwise, if the entry is newly created,
+ * clear the flag. [RFC 2461, sec 8.3]
+ */
+ if (code == ND_REDIRECT_ROUTER)
+ ln->ln_router = 1;
+ else if (is_newentry) /* (6-7) */
+ ln->ln_router = 0;
+ break;
+ case ND_ROUTER_SOLICIT:
+ /*
+ * is_router flag must always be cleared.
+ */
+ ln->ln_router = 0;
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Mark an entry with lladdr as a router.
+ */
+ if ((!is_newentry && (olladdr || lladdr)) /* (2-5) */
+ || (is_newentry && lladdr)) { /* (7) */
+ ln->ln_router = 1;
+ }
+ break;
+ }
+
+ /*
+ * When the link-layer address of a router changes, select the
+ * best router again. In particular, when the neighbor entry is newly
+ * created, it might affect the selection policy.
+ * Question: can we restrict the first condition to the "is_newentry"
+ * case?
+ * XXX: when we hear an RA from a new router with the link-layer
+ * address option, defrouter_select() is called twice, since
+ * defrtrlist_update called the function as well. However, I believe
+ * we can compromise the overhead, since it only happens the first
+ * time.
+ * XXX: although defrouter_select() should not have a bad effect
+ * for those are not autoconfigured hosts, we explicitly avoid such
+ * cases for safety.
+ */
+ if (do_update && ln->ln_router && !ip6_forwarding && ip6_accept_rtadv)
+ defrouter_select();
+
+ return rt;
+}
+
+static void
+nd6_slowtimo(ignored_arg)
+ void *ignored_arg;
+{
+ int s = splnet();
+ int i;
+ struct nd_ifinfo *nd6if;
+
+ 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;
+ nd6if = &nd_ifinfo[i];
+ if (nd6if->basereachable && /* already initialized */
+ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
+ /*
+ * Since reachable time rarely changes by router
+ * advertisements, we SHOULD insure that a new random
+ * value gets recomputed at least once every few hours.
+ * (RFC 2461, 6.3.4)
+ */
+ nd6if->recalctm = nd6_recalc_reachtm_interval;
+ nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable);
+ }
+ }
+ splx(s);
+}
+
+#define senderr(e) { error = (e); goto bad;}
+int
+nd6_output(ifp, origifp, m0, dst, rt0)
+ struct ifnet *ifp;
+ struct ifnet *origifp;
+ struct mbuf *m0;
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt0;
+{
+ struct mbuf *m = m0;
+ struct rtentry *rt = rt0;
+ struct sockaddr_in6 *gw6 = NULL;
+ struct llinfo_nd6 *ln = NULL;
+ int error = 0;
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ goto sendpkt;
+
+ if (nd6_need_cache(ifp) == 0)
+ goto sendpkt;
+
+ /*
+ * next hop determination. This routine is derived from ether_outpout.
+ */
+ if (rt) {
+ if ((rt->rt_flags & RTF_UP) == 0) {
+ if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) !=
+ NULL)
+ {
+ rt->rt_refcnt--;
+ if (rt->rt_ifp != ifp) {
+ /* XXX: loop care? */
+ return nd6_output(ifp, origifp, m0,
+ dst, rt);
+ }
+ } else
+ senderr(EHOSTUNREACH);
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY) {
+ gw6 = (struct sockaddr_in6 *)rt->rt_gateway;
+
+ /*
+ * We skip link-layer address resolution and NUD
+ * if the gateway is not a neighbor from ND point
+ * of view, regardless of the value of nd_ifinfo.flags.
+ * The second condition is a bit tricky; we skip
+ * if the gateway is our own address, which is
+ * sometimes used to install a route to a p2p link.
+ */
+ if (!nd6_is_addr_neighbor(gw6, ifp) ||
+ in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) {
+ /*
+ * We allow this kind of tricky route only
+ * when the outgoing interface is p2p.
+ * XXX: we may need a more generic rule here.
+ */
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ senderr(EHOSTUNREACH);
+
+ goto sendpkt;
+ }
+
+ if (rt->rt_gwroute == 0)
+ goto lookup;
+ if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
+ rtfree(rt); rt = rt0;
+ lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);
+ if ((rt = rt->rt_gwroute) == 0)
+ senderr(EHOSTUNREACH);
+ }
+ }
+ }
+
+ /*
+ * Address resolution or Neighbor Unreachability Detection
+ * for the next hop.
+ * At this point, the destination of the packet must be a unicast
+ * or an anycast address(i.e. not a multicast).
+ */
+
+ /* Look up the neighbor cache for the nexthop */
+ if (rt && (rt->rt_flags & RTF_LLINFO) != 0)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ else {
+ /*
+ * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
+ * the condition below is not very efficient. But we believe
+ * it is tolerable, because this should be a rare case.
+ */
+ if (nd6_is_addr_neighbor(dst, ifp) &&
+ (rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ }
+ if (!ln || !rt) {
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0 &&
+ !(nd_ifinfo[ifp->if_index].flags & ND6_IFF_PERFORMNUD)) {
+ log(LOG_DEBUG,
+ "nd6_output: can't allocate llinfo for %s "
+ "(ln=%p, rt=%p)\n",
+ ip6_sprintf(&dst->sin6_addr), ln, rt);
+ senderr(EIO); /* XXX: good error? */
+ }
+
+ goto sendpkt; /* send anyway */
+ }
+
+ /* We don't have to do link-layer address resolution on a p2p link. */
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
+ ln->ln_state < ND6_LLINFO_REACHABLE) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+
+ /*
+ * The first time we send a packet to a neighbor whose entry is
+ * STALE, we have to change the state to DELAY and a sets a timer to
+ * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do
+ * neighbor unreachability detection on expiration.
+ * (RFC 2461 7.3.3)
+ */
+ if (ln->ln_state == ND6_LLINFO_STALE) {
+ ln->ln_asked = 0;
+ ln->ln_state = ND6_LLINFO_DELAY;
+ ln->ln_expire = time_second + nd6_delay;
+ }
+
+ /*
+ * If the neighbor cache entry has a state other than INCOMPLETE
+ * (i.e. its link-layer address is already resolved), just
+ * send the packet.
+ */
+ if (ln->ln_state > ND6_LLINFO_INCOMPLETE)
+ goto sendpkt;
+
+ /*
+ * There is a neighbor cache entry, but no ethernet address
+ * response yet. Replace the held mbuf (if any) with this
+ * latest one.
+ *
+ * This code conforms to the rate-limiting rule described in Section
+ * 7.2.2 of RFC 2461, because the timer is set correctly after sending
+ * an NS below.
+ */
+ 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) {
+ 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, &dst->sin6_addr, ln, 0);
+ }
+ }
+ return(0);
+
+ sendpkt:
+
+#ifdef MAC
+ mac_create_mbuf_linklayer(ifp, m);
+#endif
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst,
+ rt));
+ }
+ return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
+
+ bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+#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_L2VLAN
+ case IFT_L2VLAN:
+#endif
+#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;
+ struct mbuf *m;
+ 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:
+#ifdef IFT_L2VLAN
+ case IFT_L2VLAN:
+#endif
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
+ desten);
+ return(1);
+ case IFT_IEEE1394:
+ /*
+ * netbsd can use if_broadcastaddr, but we don't do so
+ * to reduce # of ifdef.
+ */
+ for (i = 0; i < ifp->if_addrlen; i++)
+ desten[i] = ~0;
+ return(1);
+ case IFT_ARCNET:
+ *desten = 0;
+ return(1);
+ default:
+ m_freem(m);
+ return(0);
+ }
+ }
+
+ if (rt == NULL) {
+ /* 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 happens\n");
+ m_freem(m);
+ return(0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ if (sdl->sdl_alen == 0) {
+ /* this should be impossible, but we bark here for debugging */
+ printf("nd6_storelladdr: sdl_alen == 0\n");
+ m_freem(m);
+ return(0);
+ }
+
+ 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
new file mode 100644
index 0000000..c8b531a
--- /dev/null
+++ b/sys/netinet6/nd6.h
@@ -0,0 +1,404 @@
+/* $FreeBSD$ */
+/* $KAME: nd6.h,v 1.76 2001/12/18 02:10:31 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.
+ */
+
+#ifndef _NETINET6_ND6_H_
+#define _NETINET6_ND6_H_
+
+/* see net/route.h, or net/if_inarp.h */
+#ifndef RTF_ANNOUNCE
+#define RTF_ANNOUNCE RTF_PROTO2
+#endif
+
+#include <sys/queue.h>
+#include <sys/callout.h>
+
+struct llinfo_nd6 {
+ struct llinfo_nd6 *ln_next;
+ struct llinfo_nd6 *ln_prev;
+ struct rtentry *ln_rt;
+ struct mbuf *ln_hold; /* last packet until resolved/timeout */
+ long ln_asked; /* number of queries already sent for this addr */
+ u_long ln_expire; /* lifetime for NDP state transition */
+ short ln_state; /* reachability state */
+ short ln_router; /* 2^0: ND6 router bit */
+ int ln_byhint; /* # of times we made it reachable by UL hint */
+};
+
+#define ND6_LLINFO_NOSTATE -2
+/*
+ * 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
+#define ND6_LLINFO_DELAY 3
+#define ND6_LLINFO_PROBE 4
+
+#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE)
+
+struct nd_ifinfo {
+ u_int32_t linkmtu; /* LinkMTU */
+ u_int32_t maxmtu; /* Upper bound of LinkMTU */
+ u_int32_t basereachable; /* BaseReachableTime */
+ u_int32_t reachable; /* Reachable Time */
+ u_int32_t retrans; /* Retrans Timer */
+ u_int32_t flags; /* Flags */
+ int recalctm; /* BaseReacable re-calculation timer */
+ u_int8_t chlim; /* CurHopLimit */
+ u_int8_t receivedra;
+ /* the following 3 members 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
+
+struct in6_nbrinfo {
+ char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct in6_addr addr; /* IPv6 address of the neighbor */
+ long asked; /* number of queries already sent for this addr */
+ int isrouter; /* if it acts as a router */
+ int state; /* reachability state */
+ int expire; /* lifetime for NDP state transition */
+};
+
+#define DRLSTSIZ 10
+#define PRLSTSIZ 10
+struct in6_drlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ u_short if_index;
+ } defrouter[DRLSTSIZ];
+};
+
+struct in6_defrouter {
+ struct sockaddr_in6 rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ u_short if_index;
+};
+
+struct in6_prlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_char origin;
+ u_int32_t vltime;
+ u_int32_t pltime;
+ time_t expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } 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[] */
+};
+
+#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;
+};
+
+struct in6_ndifreq {
+ char ifname[IFNAMSIZ];
+ u_long ifindex;
+};
+
+/* Prefix status */
+#define NDPRF_ONLINK 0x1
+#define NDPRF_DETACHED 0x2
+
+/* protocol constants */
+#define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */
+#define RTR_SOLICITATION_INTERVAL 4 /* 4sec */
+#define MAX_RTR_SOLICITATIONS 3
+
+#define ND6_INFINITE_LIFETIME 0xffffffff
+
+#ifdef _KERNEL
+/* node constants */
+#define MAX_REACHABLE_TIME 3600000 /* msec */
+#define REACHABLE_TIME 30000 /* msec */
+#define RETRANS_TIMER 1000 /* msec */
+#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */
+#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */
+#define 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)
+
+TAILQ_HEAD(nd_drhead, nd_defrouter);
+struct nd_defrouter {
+ TAILQ_ENTRY(nd_defrouter) dr_entry;
+ struct in6_addr rtaddr;
+ u_char flags; /* flags on RA message */
+ u_short rtlifetime;
+ u_long expire;
+ u_long advint; /* Mobile IPv6 addition (milliseconds) */
+ u_long advint_expire; /* Mobile IPv6 addition */
+ int advints_lost; /* Mobile IPv6 addition */
+ struct ifnet *ifp;
+};
+
+struct nd_prefix {
+ struct ifnet *ndpr_ifp;
+ LIST_ENTRY(nd_prefix) ndpr_entry;
+ struct sockaddr_in6 ndpr_prefix; /* prefix */
+ struct in6_addr ndpr_mask; /* netmask derived from the prefix */
+ struct in6_addr ndpr_addr; /* address that is derived from the prefix */
+ u_int32_t ndpr_vltime; /* advertised valid lifetime */
+ u_int32_t ndpr_pltime; /* advertised preferred lifetime */
+ time_t ndpr_expire; /* expiration time of the prefix */
+ time_t ndpr_preferred; /* preferred time of the prefix */
+ struct prf_ra ndpr_flags;
+ 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;
+ int ndpr_refcnt; /* reference couter from addresses */
+};
+
+#define ndpr_next ndpr_entry.le_next
+
+#define ndpr_raf ndpr_flags
+#define ndpr_raf_onlink ndpr_flags.onlink
+#define ndpr_raf_auto ndpr_flags.autonomous
+
+/*
+ * We keep expired prefix for certain amount of time, for validation purposes.
+ * 1800s = MaxRtrAdvInterval
+ */
+#define NDPR_KEEP_EXPIRED (1800 * 2)
+
+/*
+ * Message format for use in obtaining information about prefixes
+ * from inet6 sysctl function
+ */
+struct inet6_ndpr_msghdr {
+ u_short inpm_msglen; /* to skip over non-understood messages */
+ u_char inpm_version; /* future binary compatibility */
+ u_char inpm_type; /* message type */
+ struct in6_addr inpm_prefix;
+ u_long prm_vltim;
+ u_long prm_pltime;
+ u_long prm_expire;
+ u_long prm_preferred;
+ struct in6_prflags prm_flags;
+ u_short prm_index; /* index for associated ifp */
+ u_char prm_plen; /* length of prefix in bits */
+};
+
+#define prm_raf_onlink prm_flags.prf_ra.onlink
+#define prm_raf_auto prm_flags.prf_ra.autonomous
+
+#define prm_statef_onlink prm_flags.prf_state.onlink
+
+#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid
+#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd
+
+#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr))
+#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr))
+
+struct nd_pfxrouter {
+ LIST_ENTRY(nd_pfxrouter) pfr_entry;
+#define pfr_next pfr_entry.le_next
+ struct nd_defrouter *router;
+};
+
+LIST_HEAD(nd_prhead, nd_prefix);
+
+/* nd6.c */
+extern int nd6_prune;
+extern int nd6_delay;
+extern int nd6_umaxtries;
+extern int nd6_mmaxtries;
+extern int nd6_useloopback;
+extern int nd6_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 */
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi_beg; /* multiple opts, start */
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ struct nd_opt_hdr *six;
+ struct nd_opt_advint *adv;
+ struct nd_opt_hai *hai;
+ struct nd_opt_hdr *search; /* multiple opts */
+ struct nd_opt_hdr *last; /* multiple opts */
+ int done;
+ struct nd_opt_prefix_info *pi_end;/* multiple opts, end */
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi_beg
+#define nd_opts_pi_end nd_opt_each.pi_end
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_adv nd_opt_each.adv
+#define nd_opts_hai nd_opt_each.hai
+#define nd_opts_search nd_opt_each.search
+#define nd_opts_last nd_opt_each.last
+#define nd_opts_done nd_opt_each.done
+
+/* XXX: need nd6_var.h?? */
+/* nd6.c */
+void nd6_init __P((void));
+void nd6_ifattach __P((struct ifnet *));
+int nd6_is_addr_neighbor __P((struct sockaddr_in6 *, struct ifnet *));
+void nd6_option_init __P((void *, int, union nd_opts *));
+struct nd_opt_hdr *nd6_option __P((union nd_opts *));
+int nd6_options __P((union nd_opts *));
+struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *));
+void nd6_setmtu __P((struct ifnet *));
+void nd6_timer __P((void *));
+void nd6_purge __P((struct ifnet *));
+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 rt_addrinfo *));
+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));
+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 *, 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 *, 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 */
+void nd6_rs_input __P((struct mbuf *, int, int));
+void nd6_ra_input __P((struct mbuf *, int, int));
+void prelist_del __P((struct nd_prefix *));
+void defrouter_addreq __P((struct nd_defrouter *));
+void defrouter_delreq __P((struct nd_defrouter *, int));
+void defrouter_select __P((void));
+void defrtrlist_del __P((struct nd_defrouter *));
+void prelist_remove __P((struct nd_prefix *));
+int prelist_update __P((struct nd_prefix *, struct nd_defrouter *,
+ struct mbuf *));
+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 *));
+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 */
+
+#endif /* _NETINET6_ND6_H_ */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
new file mode 100644
index 0000000..16bfbb4
--- /dev/null
+++ b/sys/netinet6/nd6_nbr.c
@@ -0,0 +1,1396 @@
+/* $FreeBSD$ */
+/* $KAME: nd6_nbr.c,v 1.86 2002/01/21 02:33:04 jinmei 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 "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#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 <sys/callout.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet/icmp6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#endif
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+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 *));
+static void nd6_dad_na_input __P((struct ifaddr *));
+
+static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/
+static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
+
+/*
+ * Input a Neighbor Solicitation Message.
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ */
+void
+nd6_ns_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_neighbor_solicit *nd_ns;
+ struct in6_addr saddr6 = ip6->ip6_src;
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6;
+ struct in6_addr myaddr6;
+ char *lladdr = NULL;
+ struct ifaddr *ifa;
+ int lladdrlen = 0;
+ int anycast = 0, proxy = 0, tentative = 0;
+ int tlladdr;
+ 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) {
+ 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)) {
+ /* dst has to be solicited node multicast address. */
+ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL
+ /* don't check ifindex portion */
+ && daddr6.s6_addr32[1] == 0
+ && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE
+ && daddr6.s6_addr8[12] == 0xff) {
+ ; /* good */
+ } else {
+ nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(wrong ip6 dst)\n"));
+ goto bad;
+ }
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"));
+ goto bad;
+ }
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
+ taddr6.s6_addr16[1] = htons(ifp->if_index);
+
+ icmp6len -= sizeof(*nd_ns);
+ nd6_option_init(nd_ns + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ nd6log((LOG_INFO,
+ "nd6_ns_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
+ goto freeit;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) {
+ nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(link-layer address option)\n"));
+ goto bad;
+ }
+
+ /*
+ * Attaching target link-layer address to the NA?
+ * (RFC 2461 7.2.4)
+ *
+ * NS IP dst is unicast/anycast MUST NOT add
+ * NS IP dst is solicited-node multicast MUST add
+ *
+ * In implementation, we add target link-layer address by default.
+ * We do not add one in MUST NOT cases.
+ */
+#if 0 /* too much! */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6);
+ if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST))
+ tlladdr = 0;
+ else
+#endif
+ if (!IN6_IS_ADDR_MULTICAST(&daddr6))
+ tlladdr = 0;
+ else
+ tlladdr = 1;
+
+ /*
+ * Target address (taddr6) must be either:
+ * (1) Valid unicast/anycast address for my receiving interface,
+ * (2) Unicast address for which I'm offering proxy service, or
+ * (3) "tentative" address on which DAD is being performed.
+ */
+ /* (1) and (3) check. */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
+
+ /* (2) check. */
+ if (!ifa) {
+ struct rtentry *rt;
+ struct sockaddr_in6 tsin6;
+
+ bzero(&tsin6, sizeof tsin6);
+ tsin6.sin6_len = sizeof(struct sockaddr_in6);
+ tsin6.sin6_family = AF_INET6;
+ tsin6.sin6_addr = taddr6;
+
+ rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0);
+ if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 &&
+ rt->rt_gateway->sa_family == AF_LINK) {
+ /*
+ * proxy NDP for single entry
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
+ IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
+ if (ifa) {
+ proxy = 1;
+ proxydl = SDL(rt->rt_gateway);
+ }
+ }
+ if (rt)
+ rtfree(rt);
+ }
+ if (!ifa) {
+ /*
+ * 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.
+ */
+ goto freeit;
+ }
+ myaddr6 = *IFA_IN6(ifa);
+ anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
+ tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
+ goto freeit;
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ nd6log((LOG_INFO,
+ "nd6_ns_input: lladdrlen mismatch for %s "
+ "(if %d, NS packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ goto bad;
+ }
+
+ if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
+ nd6log((LOG_INFO,
+ "nd6_ns_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&saddr6)));
+ goto freeit;
+ }
+
+ /*
+ * We have neighbor solicitation packet, with target address equals to
+ * one of my tentative address.
+ *
+ * src addr how to process?
+ * --- ---
+ * multicast of course, invalid (rejected in ip6_input)
+ * unicast somebody is doing address resolution -> ignore
+ * unspec dup address detection
+ *
+ * The processing is defined in RFC 2462.
+ */
+ if (tentative) {
+ /*
+ * If source address is unspecified address, it is for
+ * duplicated address detection.
+ *
+ * If not, the packet is for addess resolution;
+ * silently ignore it.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
+ nd6_dad_ns_input(ifa);
+
+ goto freeit;
+ }
+
+ /*
+ * If the source address is unspecified address, entries must not
+ * be created or updated.
+ * It looks that sender is performing DAD. Output NA toward
+ * all-node multicast address, to tell the sender that I'm using
+ * the address.
+ * S bit ("solicited") must be zero.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
+ saddr6 = in6addr_linklocal_allnodes;
+ saddr6.s6_addr16[1] = htons(ifp->if_index);
+ nd6_na_output(ifp, &saddr6, &taddr6,
+ ((anycast || proxy || !tlladdr)
+ ? 0 : ND_NA_FLAG_OVERRIDE)
+ | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
+ tlladdr, (struct sockaddr *)proxydl);
+ goto freeit;
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
+
+ nd6_na_output(ifp, &saddr6, &taddr6,
+ ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE)
+ | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0)
+ | ND_NA_FLAG_SOLICITED,
+ tlladdr, (struct sockaddr *)proxydl);
+ freeit:
+ m_freem(m);
+ return;
+
+ bad:
+ 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);
+}
+
+/*
+ * Output a Neighbor Solicitation Message. Caller specifies:
+ * - ICMP6 header source IP6 address
+ * - ND6 header target IP6 address
+ * - ND6 header source datalink address
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ */
+void
+nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
+ struct ifnet *ifp;
+ const struct in6_addr *daddr6, *taddr6;
+ struct llinfo_nd6 *ln; /* for source address determination */
+ int dad; /* duplicated address detection */
+{
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct nd_neighbor_solicit *nd_ns;
+ struct in6_ifaddr *ia = NULL;
+ struct ip6_moptions im6o;
+ int icmp6len;
+ int maxlen;
+ caddr_t mac;
+ struct ifnet *outif = NULL;
+
+ if (IN6_IS_ADDR_MULTICAST(taddr6))
+ return;
+
+ /* estimate the size of message */
+ maxlen = sizeof(*ip6) + sizeof(*nd_ns);
+ maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
+ if (max_linkhdr + maxlen >= MCLBYTES) {
+#ifdef DIAGNOSTIC
+ printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES "
+ "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
+#endif
+ return;
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m && max_linkhdr + maxlen >= MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ m = NULL;
+ }
+ }
+ if (m == NULL)
+ return;
+ m->m_pkthdr.rcvif = NULL;
+
+ if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
+
+ icmp6len = sizeof(*nd_ns);
+ m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
+ m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
+
+ /* fill neighbor solicitation packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ /* ip6->ip6_plen will be set later */
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ if (daddr6)
+ ip6->ip6_dst = *daddr6;
+ else {
+ ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr32[1] = 0;
+ ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
+ ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
+ ip6->ip6_dst.s6_addr8[12] = 0xff;
+ }
+ if (!dad) {
+#if 0 /* KAME way, exact address scope match */
+ /*
+ * Select a source whose scope is the same as that of the dest.
+ * Typically, the dest is link-local solicitation multicast
+ * (i.e. neighbor discovery) or link-local/global unicast
+ * (i.e. neighbor un-reachability detection).
+ */
+ ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
+ if (ia == NULL) {
+ m_freem(m);
+ return;
+ }
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+#else /* spec-wise correct */
+ /*
+ * RFC2461 7.2.2:
+ * "If the source address of the packet prompting the
+ * solicitation is the same as one of the addresses assigned
+ * to the outgoing interface, that address SHOULD be placed
+ * in the IP Source Address of the outgoing solicitation.
+ * Otherwise, any one of the addresses assigned to the
+ * interface should be used."
+ *
+ * We use the source address for the prompting packet
+ * (saddr6), if:
+ * - saddr6 is given from the caller (by giving "ln"), and
+ * - saddr6 belongs to the outgoing interface.
+ * Otherwise, we perform a scope-wise match.
+ */
+ struct ip6_hdr *hip6; /* hold ip6 */
+ struct in6_addr *saddr6;
+
+ if (ln && ln->ln_hold) {
+ hip6 = mtod(ln->ln_hold, struct ip6_hdr *);
+ /* XXX pullup? */
+ if (sizeof(*hip6) < ln->ln_hold->m_len)
+ saddr6 = &hip6->ip6_src;
+ else
+ saddr6 = NULL;
+ } else
+ saddr6 = NULL;
+ if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
+ bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6));
+ else {
+ ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
+ if (ia == NULL) {
+ m_freem(m);
+ return;
+ }
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ }
+#endif
+ } else {
+ /*
+ * Source address for DAD packet must always be IPv6
+ * unspecified address. (0::0)
+ */
+ bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
+ }
+ nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
+ nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
+ nd_ns->nd_ns_code = 0;
+ nd_ns->nd_ns_reserved = 0;
+ nd_ns->nd_ns_target = *taddr6;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target))
+ nd_ns->nd_ns_target.s6_addr16[1] = 0;
+
+ /*
+ * Add source link-layer address option.
+ *
+ * spec implementation
+ * --- ---
+ * DAD packet MUST NOT do not add the option
+ * there's no link layer address:
+ * impossible do not add the option
+ * there's link layer address:
+ * Multicast NS MUST add one add the option
+ * Unicast NS SHOULD add one add the option
+ */
+ if (!dad && (mac = nd6_ifptomac(ifp))) {
+ int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
+ /* 8 byte alignments... */
+ optlen = (optlen + 7) & ~7;
+
+ m->m_pkthdr.len += optlen;
+ m->m_len += optlen;
+ icmp6len += optlen;
+ bzero((caddr_t)nd_opt, optlen);
+ nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+ nd_opt->nd_opt_len = optlen >> 3;
+ bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
+ }
+
+ ip6->ip6_plen = htons((u_short)icmp6len);
+ nd_ns->nd_ns_cksum = 0;
+ nd_ns->nd_ns_cksum
+ = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
+
+ ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif, NULL);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit);
+ }
+ icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
+}
+
+/*
+ * Neighbor advertisement input handling.
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ *
+ * the following items are not implemented yet:
+ * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
+ * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
+ */
+void
+nd6_na_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_neighbor_advert *nd_na;
+#if 0
+ struct in6_addr saddr6 = ip6->ip6_src;
+#endif
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6;
+ int flags;
+ int is_router;
+ int is_solicited;
+ int is_override;
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+ struct ifaddr *ifa;
+ struct llinfo_nd6 *ln;
+ struct rtentry *rt;
+ struct sockaddr_dl *sdl;
+ union nd_opts ndopts;
+
+ if (ip6->ip6_hlim != 255) {
+ 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
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);
+ if (nd_na == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+ taddr6 = nd_na->nd_na_target;
+ flags = nd_na->nd_na_flags_reserved;
+ is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
+ is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
+ is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
+ taddr6.s6_addr16[1] = htons(ifp->if_index);
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ nd6log((LOG_ERR,
+ "nd6_na_input: invalid target address %s\n",
+ ip6_sprintf(&taddr6)));
+ goto bad;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&daddr6))
+ if (is_solicited) {
+ 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) {
+ nd6log((LOG_INFO,
+ "nd6_na_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
+ goto freeit;
+ }
+
+ if (ndopts.nd_opts_tgt_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+ }
+
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
+
+ /*
+ * Target address matches one of my interface address.
+ *
+ * If my address is tentative, this means that there's somebody
+ * already using the same address as mine. This indicates DAD failure.
+ * This is defined in RFC 2462.
+ *
+ * Otherwise, process as defined in RFC 2461.
+ */
+ if (ifa
+ && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {
+ nd6_dad_na_input(ifa);
+ goto freeit;
+ }
+
+ /* Just for safety, maybe unnecessary. */
+ if (ifa) {
+ log(LOG_ERR,
+ "nd6_na_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&taddr6));
+ goto freeit;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ nd6log((LOG_INFO,
+ "nd6_na_input: lladdrlen mismatch for %s "
+ "(if %d, NA packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ goto bad;
+ }
+
+ /*
+ * If no neighbor cache entry is found, NA SHOULD silently be discarded.
+ */
+ rt = nd6_lookup(&taddr6, 0, ifp);
+ if ((rt == NULL) ||
+ ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||
+ ((sdl = SDL(rt->rt_gateway)) == NULL))
+ goto freeit;
+
+ if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
+ /*
+ * If the link-layer has address, and no lladdr option came,
+ * discard the packet.
+ */
+ if (ifp->if_addrlen && !lladdr)
+ goto freeit;
+
+ /*
+ * Record link-layer address, and update the state.
+ */
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ if (is_solicited) {
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_byhint = 0;
+ if (ln->ln_expire)
+ ln->ln_expire = time_second +
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+ } else {
+ ln->ln_state = ND6_LLINFO_STALE;
+ 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;
+
+ /*
+ * Check if the link-layer address has changed or not.
+ */
+ if (!lladdr)
+ llchange = 0;
+ else {
+ if (sdl->sdl_alen) {
+ if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))
+ llchange = 1;
+ else
+ llchange = 0;
+ } else
+ llchange = 1;
+ }
+
+ /*
+ * This is VERY complex. Look at it with care.
+ *
+ * override solicit lladdr llchange action
+ * (L: record lladdr)
+ *
+ * 0 0 n -- (2c)
+ * 0 0 y n (2b) L
+ * 0 0 y y (1) REACHABLE->STALE
+ * 0 1 n -- (2c) *->REACHABLE
+ * 0 1 y n (2b) L *->REACHABLE
+ * 0 1 y y (1) REACHABLE->STALE
+ * 1 0 n -- (2a)
+ * 1 0 y n (2a) L
+ * 1 0 y y (2a) L *->STALE
+ * 1 1 n -- (2a) *->REACHABLE
+ * 1 1 y n (2a) L *->REACHABLE
+ * 1 1 y y (2a) L *->REACHABLE
+ */
+ if (!is_override && (lladdr && llchange)) { /* (1) */
+ /*
+ * If state is REACHABLE, make it STALE.
+ * no other updates should be done.
+ */
+ 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) */
+ || !lladdr) { /* (2c) */
+ /*
+ * Update link-local address, if any.
+ */
+ if (lladdr) {
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+ }
+
+ /*
+ * If solicited, make the state REACHABLE.
+ * If not solicited and the link-layer address was
+ * changed, make it STALE.
+ */
+ if (is_solicited) {
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_byhint = 0;
+ if (ln->ln_expire) {
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].reachable;
+ }
+ } else {
+ if (lladdr && llchange) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+ }
+ }
+
+ if (ln->ln_router && !is_router) {
+ /*
+ * The peer dropped the router flag.
+ * Remove the sender from the Default Router List and
+ * update the Destination Cache entries.
+ */
+ struct nd_defrouter *dr;
+ struct in6_addr *in6;
+ int s;
+
+ in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+
+ /*
+ * Lock to protect the default router list.
+ * XXX: this might be unnecessary, since this function
+ * is only called under the network software interrupt
+ * context. However, we keep it just for safety.
+ */
+ s = splnet();
+ dr = defrouter_lookup(in6, rt->rt_ifp);
+ if (dr)
+ defrtrlist_del(dr);
+ else if (!ip6_forwarding && ip6_accept_rtadv) {
+ /*
+ * Even if the neighbor is not in the default
+ * router list, the neighbor may be used
+ * as a next hop for some destinations
+ * (e.g. redirect case). So we must
+ * call rt6_flush explicitly.
+ */
+ rt6_flush(&ip6->ip6_src, rt->rt_ifp);
+ }
+ splx(s);
+ }
+ ln->ln_router = is_router;
+ }
+ rt->rt_flags &= ~RTF_REJECT;
+ ln->ln_asked = 0;
+ if (ln->ln_hold) {
+ /*
+ * we assume ifp is not a loopback 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);
+ ln->ln_hold = 0;
+ }
+
+ freeit:
+ m_freem(m);
+ return;
+
+ bad:
+ icmp6stat.icp6s_badna++;
+ m_freem(m);
+}
+
+/*
+ * Neighbor advertisement output handling.
+ *
+ * Based on RFC 2461
+ *
+ * the following items are not implemented yet:
+ * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
+ * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
+ */
+void
+nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
+ struct ifnet *ifp;
+ 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 */
+{
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct nd_neighbor_advert *nd_na;
+ struct in6_ifaddr *ia = NULL;
+ struct ip6_moptions im6o;
+ int icmp6len;
+ int maxlen;
+ caddr_t mac = NULL;
+ struct ifnet *outif = NULL;
+
+ /* estimate the size of message */
+ maxlen = sizeof(*ip6) + sizeof(*nd_na);
+ maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
+ if (max_linkhdr + maxlen >= MCLBYTES) {
+#ifdef DIAGNOSTIC
+ printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "
+ "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
+#endif
+ return;
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m && max_linkhdr + maxlen >= MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ m = NULL;
+ }
+ }
+ if (m == NULL)
+ return;
+ m->m_pkthdr.rcvif = NULL;
+
+ if (IN6_IS_ADDR_MULTICAST(daddr6)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
+
+ icmp6len = sizeof(*nd_na);
+ m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
+ m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
+
+ /* fill neighbor advertisement packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+ ip6->ip6_nxt = IPPROTO_ICMPV6;
+ ip6->ip6_hlim = 255;
+ if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
+ /* reply to DAD */
+ ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
+ ip6->ip6_dst.s6_addr32[1] = 0;
+ ip6->ip6_dst.s6_addr32[2] = 0;
+ ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+ flags &= ~ND_NA_FLAG_SOLICITED;
+ } else
+ ip6->ip6_dst = *daddr6;
+
+ /*
+ * Select a source whose scope is the same as that of the dest.
+ */
+ ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
+ if (ia == NULL) {
+ m_freem(m);
+ return;
+ }
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
+ nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
+ nd_na->nd_na_code = 0;
+ nd_na->nd_na_target = *taddr6;
+ if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target))
+ nd_na->nd_na_target.s6_addr16[1] = 0;
+
+ /*
+ * "tlladdr" indicates NS's condition for adding tlladdr or not.
+ * see nd6_ns_input() for details.
+ * Basically, if NS packet is sent to unicast/anycast addr,
+ * target lladdr option SHOULD NOT be included.
+ */
+ if (tlladdr) {
+ /*
+ * sdl0 != NULL indicates proxy NA. If we do proxy, use
+ * lladdr in sdl0. If we are not proxying (sending NA for
+ * my address) use lladdr configured for the interface.
+ */
+ if (sdl0 == NULL)
+ mac = nd6_ifptomac(ifp);
+ else if (sdl0->sa_family == AF_LINK) {
+ struct sockaddr_dl *sdl;
+ sdl = (struct sockaddr_dl *)sdl0;
+ if (sdl->sdl_alen == ifp->if_addrlen)
+ mac = LLADDR(sdl);
+ }
+ }
+ if (tlladdr && mac) {
+ int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
+
+ /* roundup to 8 bytes alignment! */
+ optlen = (optlen + 7) & ~7;
+
+ m->m_pkthdr.len += optlen;
+ m->m_len += optlen;
+ icmp6len += optlen;
+ bzero((caddr_t)nd_opt, optlen);
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ nd_opt->nd_opt_len = optlen >> 3;
+ bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
+ } else
+ flags &= ~ND_NA_FLAG_OVERRIDE;
+
+ ip6->ip6_plen = htons((u_short)icmp6len);
+ nd_na->nd_na_flags_reserved = flags;
+ nd_na->nd_na_cksum = 0;
+ nd_na->nd_na_cksum =
+ in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
+
+ ip6_output(m, NULL, NULL, 0, &im6o, &outif, NULL);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ icmp6_ifstat_inc(outif, ifs6_out_neighboradvert);
+ }
+ icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
+}
+
+caddr_t
+nd6_ifptomac(ifp)
+ struct ifnet *ifp;
+{
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_IEEE1394:
+#ifdef IFT_L2VLAN
+ case IFT_L2VLAN:
+#endif
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ return ((caddr_t)(ifp + 1));
+ break;
+ default:
+ return NULL;
+ }
+}
+
+TAILQ_HEAD(dadq_head, dadq);
+struct dadq {
+ TAILQ_ENTRY(dadq) dad_list;
+ struct ifaddr *dad_ifa;
+ int dad_count; /* max NS to send */
+ int dad_ns_tcount; /* # of trials to send NS */
+ int dad_ns_ocount; /* NS sent so far */
+ int dad_ns_icount;
+ int dad_na_icount;
+ struct callout dad_timer_ch;
+};
+
+static struct dadq_head dadq;
+static int dad_init = 0;
+
+static struct dadq *
+nd6_dad_find(ifa)
+ struct ifaddr *ifa;
+{
+ struct dadq *dp;
+
+ for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) {
+ if (dp->dad_ifa == ifa)
+ return dp;
+ }
+ 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.
+ */
+void
+nd6_dad_start(ifa, tick)
+ struct ifaddr *ifa;
+ int *tick; /* minimum delay ticks for IFF_UP event */
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+ if (!dad_init) {
+ TAILQ_INIT(&dadq);
+ dad_init++;
+ }
+
+ /*
+ * If we don't need DAD, don't do it.
+ * There are several cases:
+ * - DAD is disabled (ip6_dad_count == 0)
+ * - the interface address is anycast
+ */
+ if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) {
+ log(LOG_DEBUG,
+ "nd6_dad_start: called with non-tentative address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ return;
+ }
+ if (ia->ia6_flags & IN6_IFF_ANYCAST) {
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ return;
+ }
+ if (!ip6_dad_count) {
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ return;
+ }
+ if (!ifa->ifa_ifp)
+ panic("nd6_dad_start: ifa->ifa_ifp == NULL");
+ if (!(ifa->ifa_ifp->if_flags & IFF_UP))
+ return;
+ if (nd6_dad_find(ifa) != NULL) {
+ /* DAD already in progress */
+ return;
+ }
+
+ dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT);
+ if (dp == NULL) {
+ log(LOG_ERR, "nd6_dad_start: memory allocation failed for "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ return;
+ }
+ bzero(dp, sizeof(*dp));
+ callout_init(&dp->dad_timer_ch, 0);
+ TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
+
+ 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.
+ * Note that we must delay the first transmission, if this is the
+ * first packet to be sent from the interface after interface
+ * (re)initialization.
+ */
+ dp->dad_ifa = ifa;
+ 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 == NULL) {
+ nd6_dad_ns_output(dp, ifa);
+ nd6_dad_starttimer(dp,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ } else {
+ int ntick;
+
+ if (*tick == 0)
+ ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz);
+ else
+ ntick = *tick + random() % (hz / 2);
+ *tick = 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;
+{
+ int s;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+ s = splnet(); /* XXX */
+
+ /* Sanity check */
+ if (ia == NULL) {
+ log(LOG_ERR, "nd6_dad_timer: called with null parameter\n");
+ goto done;
+ }
+ dp = nd6_dad_find(ifa);
+ if (dp == NULL) {
+ log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n");
+ goto done;
+ }
+ if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
+ log(LOG_ERR, "nd6_dad_timer: called with duplicated address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ goto done;
+ }
+ if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
+ log(LOG_ERR, "nd6_dad_timer: called with non-tentative address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ goto done;
+ }
+
+ /* timeouted with IFF_{RUNNING,UP} check */
+ if (dp->dad_ns_tcount > dad_maxtry) {
+ 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);
+ dp = NULL;
+ IFAFREE(ifa);
+ goto done;
+ }
+
+ /* Need more checks? */
+ if (dp->dad_ns_ocount < dp->dad_count) {
+ /*
+ * We have more NS to go. Send NS packet for DAD.
+ */
+ nd6_dad_ns_output(dp, ifa);
+ nd6_dad_starttimer(dp,
+ nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
+ } else {
+ /*
+ * We have transmitted sufficient number of DAD packets.
+ * See what we've got.
+ */
+ int duplicate;
+
+ duplicate = 0;
+
+ if (dp->dad_na_icount) {
+ /*
+ * the check is in nd6_dad_na_input(),
+ * but just in case
+ */
+ duplicate++;
+ }
+
+ if (dp->dad_ns_icount) {
+#if 0 /* heuristics */
+ /*
+ * if
+ * - we have sent many(?) DAD NS, and
+ * - the number of NS we sent equals to the
+ * number of NS we've got, and
+ * - we've got no NA
+ * we may have a faulty network card/driver which
+ * loops back multicasts to myself.
+ */
+ if (3 < dp->dad_count
+ && dp->dad_ns_icount == dp->dad_count
+ && dp->dad_na_icount == 0) {
+ log(LOG_INFO, "DAD questionable for %s(%s): "
+ "network card loops back multicast?\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ if_name(ifa->ifa_ifp));
+ /* XXX consider it a duplicate or not? */
+ /* duplicate++; */
+ } else {
+ /* We've seen NS, means DAD has failed. */
+ duplicate++;
+ }
+#else
+ /* We've seen NS, means DAD has failed. */
+ duplicate++;
+#endif
+ }
+
+ if (duplicate) {
+ /* (*dp) will be freed in nd6_dad_duplicated() */
+ dp = NULL;
+ nd6_dad_duplicated(ifa);
+ } else {
+ /*
+ * We are done with DAD. No NA came, no NS came.
+ * duplicated address found.
+ */
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+
+ nd6log((LOG_DEBUG,
+ "%s: DAD complete for %s - no duplicates found\n",
+ if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr)));
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ IFAFREE(ifa);
+ }
+ }
+
+done:
+ splx(s);
+}
+
+void
+nd6_dad_duplicated(ifa)
+ struct ifaddr *ifa;
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+ dp = nd6_dad_find(ifa);
+ if (dp == NULL) {
+ log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n");
+ return;
+ }
+
+ 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) */
+ 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));
+ log(LOG_ERR, "%s: manual intervention required\n",
+ if_name(ifa->ifa_ifp));
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ IFAFREE(ifa);
+}
+
+static void
+nd6_dad_ns_output(dp, ifa)
+ struct dadq *dp;
+ struct ifaddr *ifa;
+{
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct ifnet *ifp = ifa->ifa_ifp;
+
+ dp->dad_ns_tcount++;
+ if ((ifp->if_flags & IFF_UP) == 0) {
+#if 0
+ printf("%s: interface down?\n", if_name(ifp));
+#endif
+ return;
+ }
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+#if 0
+ printf("%s: interface not running?\n", if_name(ifp));
+#endif
+ return;
+ }
+
+ dp->dad_ns_ocount++;
+ nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
+}
+
+static void
+nd6_dad_ns_input(ifa)
+ struct ifaddr *ifa;
+{
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp;
+ const struct in6_addr *taddr6;
+ struct dadq *dp;
+ int duplicate;
+
+ if (!ifa)
+ panic("ifa == NULL in nd6_dad_ns_input");
+
+ ia = (struct in6_ifaddr *)ifa;
+ ifp = ifa->ifa_ifp;
+ taddr6 = &ia->ia_addr.sin6_addr;
+ duplicate = 0;
+ dp = nd6_dad_find(ifa);
+
+ /* Quickhack - completely ignore DAD NS packets */
+ if (dad_ignore_ns) {
+ nd6log((LOG_INFO,
+ "nd6_dad_ns_input: ignoring DAD NS packet for "
+ "address %s(%s)\n", ip6_sprintf(taddr6),
+ if_name(ifa->ifa_ifp)));
+ return;
+ }
+
+ /*
+ * if I'm yet to start DAD, someone else started using this address
+ * first. I have a duplicate and you win.
+ */
+ if (!dp || dp->dad_ns_ocount == 0)
+ duplicate++;
+
+ /* XXX more checks for loopback situation - see nd6_dad_timer too */
+
+ if (duplicate) {
+ dp = NULL; /* will be freed in nd6_dad_duplicated() */
+ nd6_dad_duplicated(ifa);
+ } else {
+ /*
+ * not sure if I got a duplicate.
+ * increment ns count and see what happens.
+ */
+ if (dp)
+ dp->dad_ns_icount++;
+ }
+}
+
+static void
+nd6_dad_na_input(ifa)
+ struct ifaddr *ifa;
+{
+ struct dadq *dp;
+
+ if (!ifa)
+ panic("ifa == NULL in nd6_dad_na_input");
+
+ dp = nd6_dad_find(ifa);
+ if (dp)
+ dp->dad_na_icount++;
+
+ /* remove the address. */
+ nd6_dad_duplicated(ifa);
+}
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
new file mode 100644
index 0000000..651a14d
--- /dev/null
+++ b/sys/netinet6/nd6_rtr.c
@@ -0,0 +1,1982 @@
+/* $FreeBSD$ */
+/* $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei 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/malloc.h>
+#include <sys/mbuf.h>
+#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>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/radix.h>
+
+#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>
+#include <netinet/icmp6.h>
+#include <netinet6/scope6_var.h>
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
+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 *));
+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 defrouter_addifreq __P((struct ifnet *));
+static void nd6_rtmsg __P((int, struct rtentry *));
+
+static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
+ struct in6_addrlifetime *lt6));
+
+static int rt6_deleteroute __P((struct radix_node *, void *));
+
+extern int nd6_recalc_reachtm_interval;
+
+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
+ * (rtadvd) so here we have no function like nd6_ra_output().
+ *
+ * Based on RFC 2461
+ */
+void
+nd6_rs_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_router_solicit *nd_rs;
+ struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+#endif
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+#if 0
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL;
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
+ struct rtentry *rt = NULL;
+ int is_newentry;
+#endif
+ union nd_opts ndopts;
+
+ /* If I'm not a router, ignore it. */
+ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
+ goto freeit;
+
+ /* Sanity checks */
+ if (ip6->ip6_hlim != 255) {
+ 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;
+ }
+
+ /*
+ * Don't update the neighbor cache, if src = ::.
+ * This indicates that the src has no IP address assigned yet.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
+ goto freeit;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
+ if (nd_rs == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+
+ icmp6len -= sizeof(*nd_rs);
+ nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ nd6log((LOG_INFO,
+ "nd6_rs_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
+ goto freeit;
+ }
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ nd6log((LOG_INFO,
+ "nd6_rs_input: lladdrlen mismatch for %s "
+ "(if %d, RS packet %d)\n",
+ 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);
+}
+
+/*
+ * Receive Router Advertisement Message.
+ *
+ * Based on RFC 2461
+ * TODO: on-link bit on prefix information
+ * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
+ */
+void
+nd6_ra_input(m, off, icmp6len)
+ struct mbuf *m;
+ int off, icmp6len;
+{
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ struct nd_router_advert *nd_ra;
+ struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ int flags; /* = nd_ra->nd_ra_flags_reserved; */
+ int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
+ int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
+#endif
+ union nd_opts ndopts;
+ struct nd_defrouter *dr;
+
+ if (ip6_accept_rtadv == 0)
+ goto freeit;
+
+ if (ip6->ip6_hlim != 255) {
+ 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)) {
+ nd6log((LOG_ERR,
+ "nd6_ra_input: src %s is not link-local\n",
+ ip6_sprintf(&saddr6)));
+ goto bad;
+ }
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
+ if (nd_ra == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return;
+ }
+#endif
+
+ icmp6len -= sizeof(*nd_ra);
+ nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ nd6log((LOG_INFO,
+ "nd6_ra_input: invalid ND option, ignored\n"));
+ /* nd6_options have incremented stats */
+ goto freeit;
+ }
+
+ {
+ struct nd_defrouter dr0;
+ u_int32_t advreachable = nd_ra->nd_ra_reachable;
+
+ dr0.rtaddr = saddr6;
+ dr0.flags = nd_ra->nd_ra_flags_reserved;
+ dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
+ dr0.expire = time_second + dr0.rtlifetime;
+ dr0.ifp = ifp;
+ dr0.advint = 0; /* Mobile IPv6 */
+ dr0.advint_expire = 0; /* Mobile IPv6 */
+ dr0.advints_lost = 0; /* Mobile IPv6 */
+ /* unspecified or not? (RFC 2461 6.3.4) */
+ if (advreachable) {
+ advreachable = ntohl(advreachable);
+ if (advreachable <= MAX_REACHABLE_TIME &&
+ ndi->basereachable != advreachable) {
+ ndi->basereachable = advreachable;
+ ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
+ ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */
+ }
+ }
+ if (nd_ra->nd_ra_retransmit)
+ ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
+ if (nd_ra->nd_ra_curhoplimit)
+ ndi->chlim = nd_ra->nd_ra_curhoplimit;
+ dr = defrtrlist_update(&dr0);
+ }
+
+ /*
+ * prefix
+ */
+ if (ndopts.nd_opts_pi) {
+ struct nd_opt_hdr *pt;
+ struct nd_opt_prefix_info *pi = NULL;
+ struct nd_prefix pr;
+
+ for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
+ pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
+ pt = (struct nd_opt_hdr *)((caddr_t)pt +
+ (pt->nd_opt_len << 3))) {
+ if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
+ continue;
+ pi = (struct nd_opt_prefix_info *)pt;
+
+ if (pi->nd_opt_pi_len != 4) {
+ 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) {
+ 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)) {
+ 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) {
+ 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;
+ }
+
+ bzero(&pr, sizeof(pr));
+ pr.ndpr_prefix.sin6_family = AF_INET6;
+ pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
+ pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
+ pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
+
+ pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
+ pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
+ pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
+ pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
+ pr.ndpr_pltime =
+ ntohl(pi->nd_opt_pi_preferred_time);
+
+ if (in6_init_prefix_ltimes(&pr))
+ continue; /* prefix lifetime init failed */
+
+ (void)prelist_update(&pr, dr, m);
+ }
+ }
+
+ /*
+ * MTU
+ */
+ if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
+ u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+
+ /* lower bound */
+ if (mtu < IPV6_MMTU) {
+ nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option "
+ "mtu=%d sent from %s, ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src)));
+ goto skip;
+ }
+
+ /* upper bound */
+ if (ndi->maxmtu) {
+ if (mtu <= ndi->maxmtu) {
+ int change = (ndi->linkmtu != mtu);
+
+ ndi->linkmtu = mtu;
+ if (change) /* in6_maxmtu may change */
+ in6_setmaxmtu();
+ } else {
+ 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));
+ }
+ } else {
+ nd6log((LOG_INFO, "nd6_ra_input: mtu option "
+ "mtu=%d sent from %s; maxmtu unknown, "
+ "ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src)));
+ }
+ }
+
+ skip:
+
+ /*
+ * Source link layer address
+ */
+ {
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+
+ if (ndopts.nd_opts_src_lladdr) {
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
+ lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ nd6log((LOG_INFO,
+ "nd6_ra_input: lladdrlen mismatch for %s "
+ "(if %d, RA packet %d)\n",
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2));
+ goto bad;
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
+
+ /*
+ * Installing a link-layer address might change the state of the
+ * router's neighbor cache, which might also affect our on-link
+ * detection of adveritsed prefixes.
+ */
+ pfxlist_onlink_check();
+ }
+
+ freeit:
+ m_freem(m);
+ return;
+
+ bad:
+ icmp6stat.icp6s_badra++;
+ m_freem(m);
+}
+
+/*
+ * default router list proccessing sub routines
+ */
+
+/* tell the change to user processes watching the routing socket. */
+static void
+nd6_rtmsg(cmd, rt)
+ int cmd;
+ struct rtentry *rt;
+{
+ struct rt_addrinfo info;
+
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] = rt_key(rt);
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ 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);
+}
+
+void
+defrouter_addreq(new)
+ struct nd_defrouter *new;
+{
+ struct sockaddr_in6 def, mask, gate;
+ struct rtentry *newrt = NULL;
+ int s;
+
+ Bzero(&def, sizeof(def));
+ Bzero(&mask, sizeof(mask));
+ Bzero(&gate, sizeof(gate));
+
+ def.sin6_len = mask.sin6_len = gate.sin6_len
+ = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
+ gate.sin6_addr = new->rtaddr;
+
+ s = splnet();
+ (void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate, (struct sockaddr *)&mask,
+ RTF_GATEWAY, &newrt);
+ if (newrt) {
+ nd6_rtmsg(RTM_ADD, newrt); /* tell user process */
+ newrt->rt_refcnt--;
+ }
+ splx(s);
+ return;
+}
+
+/* Add a route to a given interface as default */
+void
+defrouter_addifreq(ifp)
+ struct ifnet *ifp;
+{
+ struct sockaddr_in6 def, mask;
+ struct ifaddr *ifa;
+ struct rtentry *newrt = NULL;
+ int error, flags;
+
+ bzero(&def, sizeof(def));
+ bzero(&mask, sizeof(mask));
+
+ def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = AF_INET6;
+
+ /*
+ * Search for an ifaddr beloging to the specified interface.
+ * XXX: An IPv6 address are required to be assigned on the interface.
+ */
+ if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) {
+ nd6log((LOG_ERR, /* better error? */
+ "defrouter_addifreq: failed to find an ifaddr "
+ "to install a route to interface %s\n",
+ if_name(ifp)));
+ return;
+ }
+
+ flags = ifa->ifa_flags;
+ 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 (newrt) /* maybe unnecessary, but do it for safety */
+ newrt->rt_refcnt--;
+ } else {
+ if (newrt) {
+ nd6_rtmsg(RTM_ADD, newrt);
+ newrt->rt_refcnt--;
+ }
+ }
+}
+
+struct nd_defrouter *
+defrouter_lookup(addr, ifp)
+ struct in6_addr *addr;
+ struct ifnet *ifp;
+{
+ struct nd_defrouter *dr;
+
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
+ return(dr);
+ }
+
+ return(NULL); /* search failed */
+}
+
+void
+defrouter_delreq(dr, dofree)
+ struct nd_defrouter *dr;
+ int dofree;
+{
+ struct sockaddr_in6 def, mask, gate;
+ struct rtentry *oldrt = NULL;
+
+ Bzero(&def, sizeof(def));
+ Bzero(&mask, sizeof(mask));
+ Bzero(&gate, sizeof(gate));
+
+ def.sin6_len = mask.sin6_len = gate.sin6_len
+ = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
+ gate.sin6_addr = dr->rtaddr;
+
+ rtrequest(RTM_DELETE, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate,
+ (struct sockaddr *)&mask,
+ RTF_GATEWAY, &oldrt);
+ if (oldrt) {
+ nd6_rtmsg(RTM_DELETE, oldrt);
+ RTFREE(oldrt);
+ }
+
+ if (dofree) /* XXX: necessary? */
+ free(dr, M_IP6NDP);
+}
+
+void
+defrtrlist_del(dr)
+ struct nd_defrouter *dr;
+{
+ struct nd_defrouter *deldr = NULL;
+ struct nd_prefix *pr;
+
+ /*
+ * Flush all the routing table entries that use the router
+ * as a next hop.
+ */
+ if (!ip6_forwarding && ip6_accept_rtadv) {
+ /* above is a good condition? */
+ rt6_flush(&dr->rtaddr, dr->ifp);
+ }
+
+ if (dr == TAILQ_FIRST(&nd_defrouter))
+ deldr = dr; /* The router is primary. */
+
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+
+ /*
+ * Also delete all the pointers to the router in each prefix lists.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ struct nd_pfxrouter *pfxrtr;
+ if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
+ pfxrtr_del(pfxrtr);
+ }
+ pfxlist_onlink_check();
+
+ /*
+ * If the router is the primary one, choose a new one.
+ * Note that defrouter_select() will remove the current gateway
+ * from the routing table.
+ */
+ if (deldr)
+ defrouter_select();
+
+ free(dr, M_IP6NDP);
+}
+
+/*
+ * Default Router Selection according to Section 6.3.6 of RFC 2461:
+ * 1) Routers that are reachable or probably reachable should be
+ * preferred.
+ * 2) When no routers on the list are known to be reachable or
+ * probably reachable, routers SHOULD be selected in a round-robin
+ * fashion.
+ * 3) If the Default Router List is empty, assume that all
+ * destinations are on-link.
+ */
+void
+defrouter_select()
+{
+ int s = splnet();
+ struct nd_defrouter *dr, anydr;
+ struct rtentry *rt = NULL;
+ struct llinfo_nd6 *ln = NULL;
+
+ /*
+ * Search for a (probably) reachable router from the list.
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ /* Got it, and move it to the head */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry);
+ break;
+ }
+ }
+
+ if ((dr = TAILQ_FIRST(&nd_defrouter))) {
+ /*
+ * De-install the previous default gateway and install
+ * a new one.
+ * Note that if there is no reachable router in the list,
+ * the head entry will be used anyway.
+ * XXX: do we have to check the current routing table entry?
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ defrouter_addreq(dr);
+ }
+ else {
+ /*
+ * The Default Router List is empty, so install the default
+ * route to an inteface.
+ * XXX: The specification does not say this mechanism should
+ * be restricted to hosts, but this would be not useful
+ * (even harmful) for routers.
+ */
+ if (!ip6_forwarding) {
+ /*
+ * De-install the current default route
+ * in advance.
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ if (nd6_defifp) {
+ /*
+ * Install a route to the default interface
+ * as default route.
+ * 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);
+ } else {
+ nd6log((LOG_INFO, "defrouter_select: "
+ "there's no default router and no default"
+ " interface\n"));
+ }
+ }
+ }
+
+ splx(s);
+ return;
+}
+
+static struct nd_defrouter *
+defrtrlist_update(new)
+ struct nd_defrouter *new;
+{
+ struct nd_defrouter *dr, *n;
+ int s = splnet();
+
+ if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) {
+ /* entry exists */
+ if (new->rtlifetime == 0) {
+ defrtrlist_del(dr);
+ dr = NULL;
+ } else {
+ /* override */
+ dr->flags = new->flags; /* xxx flag check */
+ dr->rtlifetime = new->rtlifetime;
+ dr->expire = new->expire;
+ }
+ splx(s);
+ return(dr);
+ }
+
+ /* entry does not exist */
+ if (new->rtlifetime == 0) {
+ splx(s);
+ return(NULL);
+ }
+
+ n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT);
+ if (n == NULL) {
+ splx(s);
+ return(NULL);
+ }
+ bzero(n, sizeof(*n));
+ *n = *new;
+
+ /*
+ * Insert the new router at the end of the Default Router List.
+ * If there is no other router, install it anyway. Otherwise,
+ * just continue to use the current default router.
+ */
+ TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
+ if (TAILQ_FIRST(&nd_defrouter) == n)
+ defrouter_select();
+ splx(s);
+
+ return(n);
+}
+
+static struct nd_pfxrouter *
+pfxrtr_lookup(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_pfxrouter *search;
+
+ for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
+ if (search->router == dr)
+ break;
+ }
+
+ return(search);
+}
+
+static void
+pfxrtr_add(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_pfxrouter *new;
+
+ new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
+ if (new == NULL)
+ return;
+ bzero(new, sizeof(*new));
+ new->router = dr;
+
+ LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry);
+
+ pfxlist_onlink_check();
+}
+
+static void
+pfxrtr_del(pfr)
+ struct nd_pfxrouter *pfr;
+{
+ LIST_REMOVE(pfr, pfr_entry);
+ free(pfr, M_IP6NDP);
+}
+
+struct nd_prefix *
+nd6_prefix_lookup(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_prefix *search;
+
+ for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
+ if (pr->ndpr_ifp == search->ndpr_ifp &&
+ pr->ndpr_plen == search->ndpr_plen &&
+ in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
+ &search->ndpr_prefix.sin6_addr,
+ pr->ndpr_plen)
+ ) {
+ break;
+ }
+ }
+
+ return(search);
+}
+
+int
+nd6_prelist_add(pr, dr, newp)
+ struct nd_prefix *pr, **newp;
+ struct nd_defrouter *dr;
+{
+ struct nd_prefix *new = NULL;
+ int i, s;
+
+ new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
+ if (new == NULL)
+ return ENOMEM;
+ bzero(new, sizeof(*new));
+ *new = *pr;
+ if (newp != NULL)
+ *newp = new;
+
+ /* initilization */
+ LIST_INIT(&new->ndpr_advrtrs);
+ in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
+ /* make prefix in the canonical form */
+ for (i = 0; i < 4; i++)
+ new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ new->ndpr_mask.s6_addr32[i];
+
+ 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);
+ }
+
+ return 0;
+}
+
+void
+prelist_remove(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfr, *next;
+ 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);
+
+ /* free list of routers that adversed the prefix */
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
+ next = pfr->pfr_next;
+
+ free(pfr, M_IP6NDP);
+ }
+ splx(s);
+
+ free(pr, M_IP6NDP);
+
+ pfxlist_onlink_check();
+}
+
+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, *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_tmp;
+
+ auth = 0;
+ if (m) {
+ /*
+ * Authenticity for NA consists authentication for
+ * both IP header and IP datagrams, doesn't it ?
+ */
+#if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM)
+ auth = (m->m_flags & M_AUTHIPHDR
+ && m->m_flags & M_AUTHIPDGM) ? 1 : 0;
+#endif
+ }
+
+
+ if ((pr = nd6_prefix_lookup(new)) != NULL) {
+ /*
+ * nd6_prefix_lookup() ensures that pr and new have the same
+ * prefix on a same interface.
+ */
+
+ /*
+ * 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 (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? */
+ }
+ }
+
+ if (dr && pfxrtr_lookup(pr, dr) == NULL)
+ pfxrtr_add(pr, dr);
+ } else {
+ struct nd_prefix *newpr = NULL;
+
+ newprefix = 1;
+
+ if (new->ndpr_vltime == 0)
+ goto end;
+ if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0)
+ goto end;
+
+ 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. */
+ }
+
+ /*
+ * 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);
+ }
+
+ pr = newpr;
+ }
+
+ /*
+ * Address autoconfiguration based on Section 5.5.3 of RFC 2462.
+ * Note that pr must be non NULL at this point.
+ */
+
+ /* 5.5.3 (a). Ignore the prefix without the A bit set. */
+ if (!new->ndpr_raf_auto)
+ goto afteraddrconf;
+
+ /*
+ * 5.5.3 (b). the link-local prefix should have been ignored in
+ * nd6_ra_input.
+ */
+
+ /*
+ * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime.
+ * This should have been done in nd6_ra_input.
+ */
+
+ /*
+ * 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;
+
+ 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;
+
+ /*
+ * 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.
+ */
+#define TWOHOUR (120*60)
+ lt6_tmp = ifa6->ia6_lifetime;
+
+ if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME)
+ storedlifetime = ND6_INFINITE_LIFETIME;
+ else if (IFA6_IS_INVALID(ifa6))
+ storedlifetime = 0;
+ else
+ storedlifetime = lt6_tmp.ia6t_expire - time_second;
+
+ /* when not updating, keep the current stored lifetime. */
+ lt6_tmp.ia6t_vltime = storedlifetime;
+
+ 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;
+ }
+
+ /* 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
+
+ /*
+ * 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));
+ }
+ }
+
+ /*
+ * 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;
+}
+
+/*
+ * A supplement function used in the on-link detection below;
+ * detect if a given prefix has a (probably) reachable advertising router.
+ * XXX: lengthy function name...
+ */
+static struct nd_pfxrouter *
+find_pfxlist_reachable_router(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfxrtr;
+ struct rtentry *rt;
+ struct llinfo_nd6 *ln;
+
+ for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
+ pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
+ if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
+ pfxrtr->router->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln))
+ break; /* found */
+ }
+
+ return(pfxrtr);
+
+}
+
+/*
+ * Check if each prefix in the prefix list has at least one available router
+ * that advertised the prefix (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
+ * 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.
+ */
+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 (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
+ break;
+ }
+
+ if (pr) {
+ /*
+ * There is at least one prefix that has a reachable router.
+ * Detach prefixes which have no reachable advertising
+ * router, and attach other prefixes.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ /* 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 (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 {
+ for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
+ continue;
+
+ ifa->ia6_flags &= ~IN6_IFF_DETACHED;
+ }
+ }
+}
+
+int
+nd6_prefix_onlink(pr)
+ struct nd_prefix *pr;
+{
+ 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));
+ }
+
+ /*
+ * 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);
+ bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
+ sizeof(struct in6_addr));
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_family = AF_INET6;
+ mask6.sin6_len = sizeof(sa6);
+ bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
+ error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
+ (struct sockaddr *)&mask6, 0, &rt);
+ if (error == 0) {
+ pr->ndpr_stateflags &= ~NDPRF_ONLINK;
+
+ /* report the route deletion to the routing socket. */
+ if (rt != NULL)
+ nd6_rtmsg(RTM_DELETE, rt);
+
+ /*
+ * 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;
+
+ if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0)
+ continue;
+
+ /*
+ * 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 {
+ /* 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));
+ }
+
+ if (rt != NULL)
+ RTFREE(rt);
+
+ return(error);
+}
+
+static struct in6_ifaddr *
+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_aliasreq ifra;
+ struct in6_ifaddr *ia, *ib;
+ int error, plen0;
+ struct in6_addr mask;
+ int prefixlen = pr->ndpr_plen;
+
+ in6_len2mask(&mask, prefixlen);
+
+ /*
+ * 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 an 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;
+ else
+ return NULL;
+
+#if 0 /* don't care link local addr state, and always do DAD */
+ /* if link-local address is not eligible, do not autoconfigure. */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) {
+ printf("in6_ifadd: link-local address not ready\n");
+ return NULL;
+ }
+#endif
+
+ /* prefixlen + ifidlen must be equal to 128 */
+ 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 */
+
+ 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(&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];
+
+ /* 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));
+
+ /*
+ * 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;
+
+ /* XXX: scope zone ID? */
+
+ 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;
+
+ /*
+ * keep the new address, regardless of the result of in6_update_ifa.
+ * XXX: this address is now meaningless.
+ * We should reconsider its role.
+ */
+ 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. */
+ }
+
+ ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
+
+ return(ia); /* this must NOT be NULL. */
+}
+
+int
+in6_tmpifadd(ia0, forcegen)
+ const struct in6_ifaddr *ia0; /* corresponding public address */
+ int forcegen;
+{
+ 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];
+ }
+
+ 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]));
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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++;
+
+ /*
+ * A newly added address might affect the status of other addresses.
+ * XXX: when the temporary address is generated with a new public
+ * address, the onlink check is redundant. However, it would be safe
+ * to do the check explicitly everywhere a new address is generated,
+ * and, in fact, we surely need the check when we create a new
+ * temporary address due to deprecation of an old temporary address.
+ */
+ pfxlist_onlink_check();
+
+ return(0);
+}
+
+int
+in6_init_prefix_ltimes(struct nd_prefix *ndpr)
+{
+ /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */
+ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
+ 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));
+ return (EINVAL);
+ }
+ if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME)
+ ndpr->ndpr_preferred = 0;
+ else
+ ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime;
+ if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+ ndpr->ndpr_expire = 0;
+ else
+ ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime;
+
+ return 0;
+}
+
+static void
+in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
+{
+ /* 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 */
+ if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_preferred = 0;
+ else {
+ lt6->ia6t_preferred = time_second;
+ lt6->ia6t_preferred += lt6->ia6t_pltime;
+ }
+}
+
+/*
+ * Delete all the routing table entries that use the specified gateway.
+ * XXX: this function causes search through all entries of routing table, so
+ * it shouldn't be called when acting as a router.
+ */
+void
+rt6_flush(gateway, ifp)
+ struct in6_addr *gateway;
+ struct ifnet *ifp;
+{
+ struct radix_node_head *rnh = rt_tables[AF_INET6];
+ int s = splnet();
+
+ /* We'll care only link-local addresses */
+ if (!IN6_IS_ADDR_LINKLOCAL(gateway)) {
+ splx(s);
+ return;
+ }
+ /* XXX: hack for KAME's link-local address kludge */
+ gateway->s6_addr16[1] = htons(ifp->if_index);
+
+ RADIX_NODE_HEAD_LOCK(rnh);
+ rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
+ RADIX_NODE_HEAD_UNLOCK(rnh);
+ splx(s);
+}
+
+static int
+rt6_deleteroute(rn, arg)
+ struct radix_node *rn;
+ void *arg;
+{
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+ struct rtentry *rt = (struct rtentry *)rn;
+ struct in6_addr *gate = (struct in6_addr *)arg;
+
+ if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6)
+ return(0);
+
+ if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr))
+ 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.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ return(0);
+
+ return(rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
+#undef SIN6
+}
+
+int
+nd6_setdefaultiface(ifindex)
+ int ifindex;
+{
+ int error = 0;
+
+ if (ifindex < 0 || if_index < ifindex)
+ return(EINVAL);
+
+ if (nd6_defifindex != ifindex) {
+ nd6_defifindex = ifindex;
+ if (nd6_defifindex > 0)
+ nd6_defifp = ifnet_byindex(nd6_defifindex);
+ else
+ nd6_defifp = NULL;
+
+ /*
+ * If the Default Router List is empty, install a route
+ * to the specified interface as default or remove the default
+ * route when the default interface becomes canceled.
+ * The check for the queue is actually redundant, but
+ * we do this here to avoid re-install the default route
+ * if the list is NOT empty.
+ */
+ if (TAILQ_FIRST(&nd_defrouter) == NULL)
+ defrouter_select();
+
+ /*
+ * Our current implementation assumes one-to-one maping between
+ * interfaces and links, so it would be natural to use the
+ * default interface as the default link.
+ */
+ scope6_setdefault(nd6_defifp);
+ }
+
+ return(error);
+}
diff --git a/sys/netinet6/pim6.h b/sys/netinet6/pim6.h
new file mode 100644
index 0000000..d420db9
--- /dev/null
+++ b/sys/netinet6/pim6.h
@@ -0,0 +1,69 @@
+/* $FreeBSD$ */
+/* $KAME: pim6.h,v 1.3 2000/03/25 07:23:58 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Protocol Independent Multicast (PIM) definitions
+ *
+ * Written by Ahmed Helmy, SGI, July 1996
+ *
+ * MULTICAST
+ */
+
+/*
+ * PIM packet header
+ */
+#define PIM_VERSION 2
+struct pim {
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ u_char pim_type:4, /* the PIM message type, currently they are:
+ * Hello, Register, Register-Stop, Join/Prune,
+ * Bootstrap, Assert, Graft (PIM-DM only),
+ * Graft-Ack (PIM-DM only), C-RP-Adv
+ */
+ pim_ver:4; /* PIM version number; 2 for PIMv2 */
+#else
+ u_char pim_ver:4, /* PIM version */
+ pim_type:4; /* PIM type */
+#endif
+ u_char pim_rsv; /* Reserved */
+ u_short pim_cksum; /* IP style check sum */
+};
+
+#define PIM_MINLEN 8 /* The header min. length is 8 */
+#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */
+
+/*
+ * Message types
+ */
+#define PIM_REGISTER 1 /* PIM Register type is 1 */
+
+/* second bit in reg_head is the null bit */
+#define PIM_NULL_REGISTER 0x40000000
diff --git a/sys/netinet6/pim6_var.h b/sys/netinet6/pim6_var.h
new file mode 100644
index 0000000..358a195d
--- /dev/null
+++ b/sys/netinet6/pim6_var.h
@@ -0,0 +1,70 @@
+/* $FreeBSD$ */
+/* $KAME: pim6_var.h,v 1.8 2000/06/06 08:07:43 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_PIM6_VAR_H_
+#define _NETINET6_PIM6_VAR_H_
+
+/*
+ * Protocol Independent Multicast (PIM),
+ * implementation-specific definitions.
+ *
+ * Written by George Edmond Eddy (Rusty), ISI, February 1998
+ * Modified by Pavlin Ivanov Radoslavov, USC/ISI, May 1998
+ */
+
+struct pim6stat {
+ u_quad_t pim6s_rcv_total; /* total PIM messages received */
+ u_quad_t pim6s_rcv_tooshort; /* received with too few bytes */
+ u_quad_t pim6s_rcv_badsum; /* received with bad checksum */
+ u_quad_t pim6s_rcv_badversion; /* received bad PIM version */
+ u_quad_t pim6s_rcv_registers; /* received registers */
+ u_quad_t pim6s_rcv_badregisters; /* received invalid registers */
+ u_quad_t pim6s_snd_registers; /* sent registers */
+};
+
+#if (defined(KERNEL)) || (defined(_KERNEL))
+extern struct pim6stat pim6stat;
+
+int pim6_input __P((struct mbuf **, int*, int));
+#endif /* KERNEL */
+
+/*
+ * Names for PIM sysctl objects
+ */
+#define PIM6CTL_STATS 1 /* statistics (read-only) */
+#define PIM6CTL_MAXID 2
+
+#define PIM6CTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+}
+#endif /* _NETINET6_PIM6_VAR_H_ */
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
new file mode 100644
index 0000000..728da31
--- /dev/null
+++ b/sys/netinet6/raw_ip6.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
+ */
+
+#include "opt_ipsec.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/signalvar.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/icmp6.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/raw_ip6.h>
+#ifdef ENABLE_DEFAULT_SCOPE
+#include <netinet6/scope6_var.h>
+#endif
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netinet6/ipsec6.h>
+#endif /*IPSEC*/
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#endif /* FAST_IPSEC */
+
+#include <machine/stdarg.h>
+
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+
+/*
+ * Raw interface to IP6 protocol.
+ */
+
+extern struct inpcbhead ripcb;
+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
+ * mbuf chain.
+ */
+int
+rip6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct inpcb *in6p;
+ struct inpcb *last = 0;
+ struct mbuf *opts = NULL;
+ struct sockaddr_in6 rip6src;
+
+ rip6stat.rip6s_ipackets++;
+
+ if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
+ /* XXX send icmp6 host/port unreach? */
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+
+ init_sin6(&rip6src, m); /* general init */
+
+ LIST_FOREACH(in6p, &ripcb, inp_list) {
+ if ((in6p->in6p_vflag & INP_IPV6) == 0)
+ continue;
+ if (in6p->in6p_ip6_nxt &&
+ in6p->in6p_ip6_nxt != proto)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
+ 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(n, last)) {
+ m_freem(n);
+ ipsec6stat.in_polvio++;
+ /* do not inject data into pcb */
+ } else
+#endif /*IPSEC*/
+#ifdef FAST_IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (n && ipsec6_in_reject(n, last)) {
+ m_freem(n);
+ /* do not inject data into pcb */
+ } else
+#endif /*FAST_IPSEC*/
+ if (n) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS ||
+ last->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(last, &opts, ip6, n);
+ /* strip intermediate headers */
+ m_adj(n, *offp);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src,
+ n, opts) == 0) {
+ m_freem(n);
+ if (opts)
+ m_freem(opts);
+ rip6stat.rip6s_fullsock++;
+ } else
+ sorwakeup(last->in6p_socket);
+ opts = NULL;
+ }
+ }
+ last = in6p;
+ }
+#ifdef IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (last && ipsec6_in_reject(m, last)) {
+ m_freem(m);
+ ipsec6stat.in_polvio++;
+ ip6stat.ip6s_delivered--;
+ /* do not inject data into pcb */
+ } else
+#endif /*IPSEC*/
+#ifdef FAST_IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (last && ipsec6_in_reject(m, last)) {
+ m_freem(m);
+ ip6stat.ip6s_delivered--;
+ /* do not inject data into pcb */
+ } else
+#endif /*FAST_IPSEC*/
+ if (last) {
+ if (last->in6p_flags & IN6P_CONTROLOPTS ||
+ last->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(last, &opts, ip6, m);
+ /* strip intermediate headers */
+ m_adj(m, *offp);
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&rip6src, m, opts) == 0) {
+ 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 {
+ char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_NEXTHEADER,
+ prvnxtp - mtod(m, char *));
+ }
+ ip6stat.ip6s_delivered--;
+ }
+ return IPPROTO_DONE;
+}
+
+void
+rip6_ctlinput(cmd, sa, d)
+ int cmd;
+ struct sockaddr *sa;
+ void *d;
+{
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
+ int off = 0;
+ struct ip6ctlparam *ip6cp = NULL;
+ const struct sockaddr_in6 *sa6_src = NULL;
+ struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange;
+
+ if (sa->sa_family != AF_INET6 ||
+ sa->sa_len != sizeof(struct sockaddr_in6))
+ return;
+
+ if ((unsigned)cmd >= PRC_NCMDS)
+ return;
+ if (PRC_IS_REDIRECT(cmd))
+ notify = in6_rtchange, d = NULL;
+ else if (cmd == PRC_HOSTDEAD)
+ d = NULL;
+ else if (inet6ctlerrmap[cmd] == 0)
+ return;
+
+ /* if the parameter is from icmp6, decode it. */
+ if (d != NULL) {
+ 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;
+ }
+
+ (void) in6_pcbnotify(&ripcb, sa, 0, (const struct sockaddr *)sa6_src,
+ 0, cmd, notify);
+}
+
+/*
+ * Generate IPv6 header and pass packet to ip6_output.
+ * Tack on options user may have setup with control call.
+ */
+int
+#if __STDC__
+rip6_output(struct mbuf *m, ...)
+#else
+rip6_output(m, va_alist)
+ struct mbuf *m;
+ va_dcl
+#endif
+{
+ struct socket *so;
+ struct sockaddr_in6 *dstsock;
+ struct mbuf *control;
+ struct in6_addr *dst;
+ struct ip6_hdr *ip6;
+ struct inpcb *in6p;
+ u_int plen = m->m_pkthdr.len;
+ int error = 0;
+ struct ip6_pktopts opt, *optp = 0;
+ struct ifnet *oifp = NULL;
+ int type = 0, code = 0; /* for ICMPv6 output statistics only */
+ int priv = 0;
+ va_list ap;
+
+ va_start(ap, m);
+ so = va_arg(ap, struct socket *);
+ dstsock = va_arg(ap, struct sockaddr_in6 *);
+ control = va_arg(ap, struct mbuf *);
+ va_end(ap);
+
+ in6p = sotoin6pcb(so);
+
+ priv = 0;
+ if (so->so_cred->cr_uid == 0)
+ priv = 1;
+ dst = &dstsock->sin6_addr;
+ if (control) {
+ if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ goto bad;
+ optp = &opt;
+ } else
+ optp = in6p->in6p_outputopts;
+
+ /*
+ * For an ICMPv6 packet, we should know its type and code
+ * to update statistics.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+ struct icmp6_hdr *icmp6;
+ if (m->m_len < sizeof(struct icmp6_hdr) &&
+ (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ icmp6 = mtod(m, struct icmp6_hdr *);
+ type = icmp6->icmp6_type;
+ code = icmp6->icmp6_code;
+ }
+
+ M_PREPEND(m, sizeof(*ip6), M_TRYWAIT);
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /*
+ * Next header might not be ICMP6 but use its pseudo header anyway.
+ */
+ ip6->ip6_dst = *dst;
+
+ /*
+ * If the scope of the destination is link-local, embed the interface
+ * index in the address.
+ *
+ * XXX advanced-api value overrides sin6_scope_id
+ */
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ struct in6_pktinfo *pi;
+
+ /*
+ * XXX Boundary check is assumed to be already done in
+ * ip6_setpktoptions().
+ */
+ if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) {
+ ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex);
+ oifp = ifnet_byindex(pi->ipi6_ifindex);
+ } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
+ in6p->in6p_moptions &&
+ in6p->in6p_moptions->im6o_multicast_ifp) {
+ oifp = in6p->in6p_moptions->im6o_multicast_ifp;
+ ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
+ } else if (dstsock->sin6_scope_id) {
+ /* boundary check */
+ if (dstsock->sin6_scope_id < 0
+ || if_index < dstsock->sin6_scope_id) {
+ error = ENXIO; /* XXX EINVAL? */
+ goto bad;
+ }
+ ip6->ip6_dst.s6_addr16[1]
+ = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/
+ }
+ }
+
+ /*
+ * Source address selection.
+ */
+ {
+ struct in6_addr *in6a;
+
+ if ((in6a = in6_selectsrc(dstsock, optp,
+ in6p->in6p_moptions,
+ &in6p->in6p_route,
+ &in6p->in6p_laddr,
+ &error)) == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ ip6->ip6_src = *in6a;
+ if (in6p->in6p_route.ro_rt)
+ oifp = ifnet_byindex(in6p->in6p_route.ro_rt->rt_ifp->if_index);
+ }
+ ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
+ (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK);
+ ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
+ (IPV6_VERSION & IPV6_VERSION_MASK);
+ /* ip6_plen will be filled in ip6_output, so not fill it here. */
+ ip6->ip6_nxt = in6p->in6p_ip6_nxt;
+ ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
+
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
+ in6p->in6p_cksum != -1) {
+ struct mbuf *n;
+ int off;
+ u_int16_t *p;
+
+ /* compute checksum */
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
+ off = offsetof(struct icmp6_hdr, icmp6_cksum);
+ else
+ off = in6p->in6p_cksum;
+ if (plen < off + 1) {
+ error = EINVAL;
+ goto bad;
+ }
+ off += sizeof(struct ip6_hdr);
+
+ n = m;
+ while (n && n->m_len <= off) {
+ off -= n->m_len;
+ n = n->m_next;
+ }
+ if (!n)
+ goto bad;
+ p = (u_int16_t *)(mtod(n, caddr_t) + off);
+ *p = 0;
+ *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
+ }
+
+ error = ip6_output(m, optp, &in6p->in6p_route, 0,
+ in6p->in6p_moptions, &oifp, in6p);
+ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
+ if (oifp)
+ icmp6_ifoutstat_inc(oifp, type, code);
+ icmp6stat.icp6s_outhist[type]++;
+ } else
+ rip6stat.rip6s_opackets++;
+
+ goto freectl;
+
+ bad:
+ if (m)
+ m_freem(m);
+
+ freectl:
+ if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt)
+ RTFREE(optp->ip6po_route.ro_rt);
+ if (control) {
+ if (optp == &opt)
+ ip6_clearpktopts(optp, 0, -1);
+ m_freem(control);
+ }
+ return(error);
+}
+
+/*
+ * Raw IPv6 socket option processing.
+ */
+int
+rip6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ int error;
+
+ if (sopt->sopt_level == IPPROTO_ICMPV6)
+ /*
+ * XXX: is it better to call icmp6_ctloutput() directly
+ * from protosw?
+ */
+ return(icmp6_ctloutput(so, sopt));
+ else if (sopt->sopt_level != IPPROTO_IPV6)
+ return (EINVAL);
+
+ error = 0;
+
+ switch (sopt->sopt_dir) {
+ case SOPT_GET:
+ switch (sopt->sopt_name) {
+ case MRT6_INIT:
+ case MRT6_DONE:
+ case MRT6_ADD_MIF:
+ case MRT6_DEL_MIF:
+ case MRT6_ADD_MFC:
+ case MRT6_DEL_MFC:
+ case MRT6_PIM:
+ error = ip6_mrouter_get(so, sopt);
+ break;
+ default:
+ error = ip6_ctloutput(so, sopt);
+ break;
+ }
+ break;
+
+ case SOPT_SET:
+ switch (sopt->sopt_name) {
+ case MRT6_INIT:
+ case MRT6_DONE:
+ case MRT6_ADD_MIF:
+ case MRT6_DEL_MIF:
+ case MRT6_ADD_MFC:
+ case MRT6_DEL_MFC:
+ case MRT6_PIM:
+ error = ip6_mrouter_set(so, sopt);
+ break;
+ default:
+ error = ip6_ctloutput(so, sopt);
+ break;
+ }
+ break;
+ }
+
+ return (error);
+}
+
+static int
+rip6_attach(struct socket *so, int proto, struct thread *td)
+{
+ struct inpcb *inp;
+ int error, s;
+
+ inp = sotoinpcb(so);
+ if (inp)
+ panic("rip6_attach");
+ if (td && (error = suser(td)) != 0)
+ return error;
+
+ error = soreserve(so, rip_sendspace, rip_recvspace);
+ if (error)
+ return error;
+ s = splnet();
+ error = in_pcballoc(so, &ripcbinfo, td);
+ splx(s);
+ if (error)
+ return error;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_vflag |= INP_IPV6;
+ inp->in6p_ip6_nxt = (long)proto;
+ inp->in6p_hops = -1; /* use kernel default */
+ inp->in6p_cksum = -1;
+ MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *,
+ sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
+ ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
+ return 0;
+}
+
+static int
+rip6_detach(struct socket *so)
+{
+ struct inpcb *inp;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ panic("rip6_detach");
+ /* xxx: RSVP */
+ if (so == ip6_mrouter)
+ ip6_mrouter_done();
+ if (inp->in6p_icmp6filt) {
+ FREE(inp->in6p_icmp6filt, M_PCB);
+ inp->in6p_icmp6filt = NULL;
+ }
+ in6_pcbdetach(inp);
+ return 0;
+}
+
+static int
+rip6_abort(struct socket *so)
+{
+ soisdisconnected(so);
+ return rip6_detach(so);
+}
+
+static int
+rip6_disconnect(struct socket *so)
+{
+ struct inpcb *inp = sotoinpcb(so);
+
+ if ((so->so_state & SS_ISCONNECTED) == 0)
+ return ENOTCONN;
+ inp->in6p_faddr = in6addr_any;
+ return rip6_abort(so);
+}
+
+static int
+rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
+ struct ifaddr *ia = NULL;
+
+ if (nam->sa_len != sizeof(*addr))
+ return EINVAL;
+
+ if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6)
+ return EADDRNOTAVAIL;
+#ifdef ENABLE_DEFAULT_SCOPE
+ if (addr->sin6_scope_id == 0) { /* not change if specified */
+ addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
+ }
+#endif
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
+ (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
+ return EADDRNOTAVAIL;
+ if (ia &&
+ ((struct in6_ifaddr *)ia)->ia6_flags &
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
+ IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
+ return(EADDRNOTAVAIL);
+ }
+ inp->in6p_laddr = addr->sin6_addr;
+ return 0;
+}
+
+static int
+rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
+ struct in6_addr *in6a = NULL;
+ int error = 0;
+#ifdef ENABLE_DEFAULT_SCOPE
+ struct sockaddr_in6 tmp;
+#endif
+
+ if (nam->sa_len != sizeof(*addr))
+ return EINVAL;
+ if (TAILQ_EMPTY(&ifnet))
+ return EADDRNOTAVAIL;
+ if (addr->sin6_family != AF_INET6)
+ return EAFNOSUPPORT;
+#ifdef ENABLE_DEFAULT_SCOPE
+ if (addr->sin6_scope_id == 0) { /* not change if specified */
+ /* avoid overwrites */
+ tmp = *addr;
+ addr = &tmp;
+ addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr);
+ }
+#endif
+ /* Source address selection. XXX: need pcblookup? */
+ in6a = in6_selectsrc(addr, inp->in6p_outputopts,
+ inp->in6p_moptions, &inp->in6p_route,
+ &inp->in6p_laddr, &error);
+ if (in6a == NULL)
+ return (error ? error : EADDRNOTAVAIL);
+ inp->in6p_laddr = *in6a;
+ inp->in6p_faddr = addr->sin6_addr;
+ soisconnected(so);
+ return 0;
+}
+
+static int
+rip6_shutdown(struct socket *so)
+{
+ socantsendmore(so);
+ return 0;
+}
+
+static int
+rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
+ struct mbuf *control, struct thread *td)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ struct sockaddr_in6 tmp;
+ struct sockaddr_in6 *dst;
+
+ /* always copy sockaddr to avoid overwrites */
+ if (so->so_state & SS_ISCONNECTED) {
+ if (nam) {
+ m_freem(m);
+ return EISCONN;
+ }
+ /* XXX */
+ bzero(&tmp, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_len = sizeof(struct sockaddr_in6);
+ bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
+ sizeof(struct in6_addr));
+ dst = &tmp;
+ } else {
+ if (nam == NULL) {
+ m_freem(m);
+ return ENOTCONN;
+ }
+ tmp = *(struct sockaddr_in6 *)nam;
+ dst = &tmp;
+ }
+#ifdef ENABLE_DEFAULT_SCOPE
+ if (dst->sin6_scope_id == 0) { /* not change if specified */
+ dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr);
+ }
+#endif
+ return rip6_output(m, so, dst, control);
+}
+
+struct pr_usrreqs rip6_usrreqs = {
+ rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect,
+ pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect,
+ pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown,
+ in6_setsockaddr, sosend, soreceive, sopoll
+};
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
new file mode 100644
index 0000000..f2575bc
--- /dev/null
+++ b/sys/netinet6/route6.c
@@ -0,0 +1,221 @@
+/* $FreeBSD$ */
+/* $KAME: route6.c,v 1.24 2001/03/14 03:07: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/mbuf.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+
+#include <netinet/icmp6.h>
+
+static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *,
+ struct ip6_rthdr0 *));
+
+int
+route6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto; /* proto is unused */
+{
+ struct ip6_hdr *ip6;
+ struct mbuf *m = *mp;
+ struct ip6_rthdr *rh;
+ int off = *offp, rhlen;
+ struct ip6aux *ip6a;
+
+ ip6a = ip6_findaux(m);
+ if (ip6a) {
+ /* 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);
+ ip6 = mtod(m, struct ip6_hdr *);
+ rh = (struct ip6_rthdr *)((caddr_t)ip6 + off);
+#else
+ ip6 = mtod(m, struct ip6_hdr *);
+ IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh));
+ if (rh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+
+ switch (rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rhlen = (rh->ip6r_len + 1) << 3;
+#ifndef PULLDOWN_TEST
+ /*
+ * note on option length:
+ * due to IP6_EXTHDR_CHECK assumption, we cannot handle
+ * very big routing header (max rhlen == 2048).
+ */
+ IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE);
+#else
+ /*
+ * note on option length:
+ * maximum rhlen: 2048
+ * max mbuf m_pulldown can handle: MCLBYTES == usually 2048
+ * so, here we are assuming that m_pulldown can handle
+ * rhlen == 2048 case. this may not be a good thing to
+ * assume - we may want to avoid pulling it up altogether.
+ */
+ IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen);
+ if (rh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh))
+ return(IPPROTO_DONE);
+ break;
+ default:
+ /* unknown routing type */
+ if (rh->ip6r_segleft == 0) {
+ rhlen = (rh->ip6r_len + 1) << 3;
+ break; /* Final dst. Just ignore the header. */
+ }
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh->ip6r_type - (caddr_t)ip6);
+ return(IPPROTO_DONE);
+ }
+
+ *offp += rhlen;
+ return(rh->ip6r_nxt);
+}
+
+/*
+ * 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)
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct ip6_rthdr0 *rh0;
+{
+ int addrs, index;
+ struct in6_addr *nextaddr, tmpaddr;
+
+ if (rh0->ip6r0_segleft == 0)
+ return(0);
+
+ if (rh0->ip6r0_len % 2
+#ifdef COMPAT_RFC1883
+ || rh0->ip6r0_len > 46
+#endif
+ ) {
+ /*
+ * Type 0 routing header can't contain more than 23 addresses.
+ * RFC 2462: this limitation was removed since strict/loose
+ * bitmap field was deleted.
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6);
+ return(-1);
+ }
+
+ if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) {
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6);
+ return(-1);
+ }
+
+ index = addrs - rh0->ip6r0_segleft;
+ rh0->ip6r0_segleft--;
+ /* note that ip6r0_addr does not exist in RFC2292bis */
+ nextaddr = rh0->ip6r0_addr + index;
+
+ /*
+ * reject invalid addresses. be proactive about malicious use of
+ * IPv4 mapped/compat address.
+ * XXX need more checks?
+ */
+ if (IN6_IS_ADDR_MULTICAST(nextaddr) ||
+ IN6_IS_ADDR_UNSPECIFIED(nextaddr) ||
+ IN6_IS_ADDR_V4MAPPED(nextaddr) ||
+ IN6_IS_ADDR_V4COMPAT(nextaddr)) {
+ ip6stat.ip6s_badoptions++;
+ m_freem(m);
+ return(-1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
+ IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) ||
+ IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) ||
+ IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
+ ip6stat.ip6s_badoptions++;
+ m_freem(m);
+ return(-1);
+ }
+
+ /*
+ * Swap the IPv6 destination address and nextaddr. Forward the packet.
+ */
+ tmpaddr = *nextaddr;
+ *nextaddr = ip6->ip6_dst;
+ if (IN6_IS_ADDR_LINKLOCAL(nextaddr))
+ nextaddr->s6_addr16[1] = 0;
+ ip6->ip6_dst = tmpaddr;
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+
+#ifdef COMPAT_RFC1883
+ if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
+ ip6_forward(m, IPV6_SRCRT_NEIGHBOR);
+ else
+ ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR);
+#else
+ ip6_forward(m, 1);
+#endif
+
+ return(-1); /* m would be freed in ip6_forward() */
+}
diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c
new file mode 100644
index 0000000..0c32da8
--- /dev/null
+++ b/sys/netinet6/scope6.c
@@ -0,0 +1,303 @@
+/* $FreeBSD$ */
+/* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+
+#include <netinet6/in6_var.h>
+#include <netinet6/scope6_var.h>
+
+struct scope6_id {
+ /*
+ * 16 is correspondent to 4bit multicast scope field.
+ * i.e. from node-local to global with some reserved/unassigned types.
+ */
+ u_int32_t s6id_list[16];
+};
+static size_t if_indexlim = 8;
+struct scope6_id *scope6_ids = NULL;
+
+void
+scope6_ifattach(ifp)
+ struct ifnet *ifp;
+{
+ int s = splnet();
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ */
+ if (scope6_ids == NULL || if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow scope index array */
+ n = if_indexlim * sizeof(struct scope6_id);
+ /* XXX: need new malloc type? */
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (scope6_ids) {
+ bcopy((caddr_t)scope6_ids, q, n/2);
+ free((caddr_t)scope6_ids, M_IFADDR);
+ }
+ scope6_ids = (struct scope6_id *)q;
+ }
+
+#define SID scope6_ids[ifp->if_index]
+
+ /* don't initialize if called twice */
+ if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) {
+ splx(s);
+ return;
+ }
+
+ /*
+ * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
+ * Should we rather hardcode here?
+ */
+ SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
+#ifdef MULTI_SCOPE
+ /* by default, we don't care about scope boundary for these scopes. */
+ SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
+ SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
+#endif
+#undef SID
+
+ splx(s);
+}
+
+int
+scope6_set(ifp, idlist)
+ struct ifnet *ifp;
+ u_int32_t *idlist;
+{
+ int i, s;
+ int error = 0;
+
+ if (scope6_ids == NULL) /* paranoid? */
+ return(EINVAL);
+
+ /*
+ * XXX: We need more consistency checks of the relationship among
+ * scopes (e.g. an organization should be larger than a site).
+ */
+
+ /*
+ * TODO(XXX): after setting, we should reflect the changes to
+ * interface addresses, routing table entries, PCB entries...
+ */
+
+ s = splnet();
+
+ for (i = 0; i < 16; i++) {
+ if (idlist[i] &&
+ idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) {
+ if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
+ idlist[i] > if_index) {
+ /*
+ * XXX: theoretically, there should be no
+ * relationship between link IDs and interface
+ * IDs, but we check the consistency for
+ * safety in later use.
+ */
+ splx(s);
+ return(EINVAL);
+ }
+
+ /*
+ * XXX: we must need lots of work in this case,
+ * but we simply set the new value in this initial
+ * implementation.
+ */
+ scope6_ids[ifp->if_index].s6id_list[i] = idlist[i];
+ }
+ }
+ splx(s);
+
+ return(error);
+}
+
+int
+scope6_get(ifp, idlist)
+ struct ifnet *ifp;
+ u_int32_t *idlist;
+{
+ if (scope6_ids == NULL) /* paranoid? */
+ return(EINVAL);
+
+ bcopy(scope6_ids[ifp->if_index].s6id_list, idlist,
+ sizeof(scope6_ids[ifp->if_index].s6id_list));
+
+ return(0);
+}
+
+
+/*
+ * Get a scope of the address. Node-local, link-local, site-local or global.
+ */
+int
+in6_addrscope(addr)
+struct in6_addr *addr;
+{
+ int scope;
+
+ if (addr->s6_addr8[0] == 0xfe) {
+ scope = addr->s6_addr8[1] & 0xc0;
+
+ switch (scope) {
+ case 0x80:
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ break;
+ case 0xc0:
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ break;
+ default:
+ return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
+ break;
+ }
+ }
+
+
+ if (addr->s6_addr8[0] == 0xff) {
+ scope = addr->s6_addr8[1] & 0x0f;
+
+ /*
+ * due to other scope such as reserved,
+ * return scope doesn't work.
+ */
+ switch (scope) {
+ case IPV6_ADDR_SCOPE_NODELOCAL:
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ break;
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ break;
+ default:
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ break;
+ }
+ }
+
+ if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
+ if (addr->s6_addr8[15] == 1) /* loopback */
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ if (addr->s6_addr8[15] == 0) /* unspecified */
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ }
+
+ return IPV6_ADDR_SCOPE_GLOBAL;
+}
+
+int
+in6_addr2scopeid(ifp, addr)
+ struct ifnet *ifp; /* must not be NULL */
+ struct in6_addr *addr; /* must not be NULL */
+{
+ int scope = in6_addrscope(addr);
+
+ if (scope6_ids == NULL) /* paranoid? */
+ return(0); /* XXX */
+ if (ifp->if_index >= if_indexlim)
+ return(0); /* XXX */
+
+#define SID scope6_ids[ifp->if_index]
+ switch(scope) {
+ case IPV6_ADDR_SCOPE_NODELOCAL:
+ return(-1); /* XXX: is this an appropriate value? */
+
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]);
+
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]);
+
+ case IPV6_ADDR_SCOPE_ORGLOCAL:
+ return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]);
+
+ default:
+ return(0); /* XXX: treat as global. */
+ }
+#undef SID
+}
+
+void
+scope6_setdefault(ifp)
+ struct ifnet *ifp; /* note that this might be NULL */
+{
+ /*
+ * Currently, this function just set the default "link" according to
+ * the given interface.
+ * We might eventually have to separate the notion of "link" from
+ * "interface" and provide a user interface to set the default.
+ */
+ if (ifp) {
+ scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
+ ifp->if_index;
+ }
+ else
+ scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
+}
+
+int
+scope6_get_default(idlist)
+ u_int32_t *idlist;
+{
+ if (scope6_ids == NULL) /* paranoid? */
+ return(EINVAL);
+
+ bcopy(scope6_ids[0].s6id_list, idlist,
+ sizeof(scope6_ids[0].s6id_list));
+
+ return(0);
+}
+
+u_int32_t
+scope6_addr2default(addr)
+ struct in6_addr *addr;
+{
+ return(scope6_ids[0].s6id_list[in6_addrscope(addr)]);
+}
diff --git a/sys/netinet6/scope6_var.h b/sys/netinet6/scope6_var.h
new file mode 100644
index 0000000..6e107d7
--- /dev/null
+++ b/sys/netinet6/scope6_var.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $KAME: scope6_var.h,v 1.4 2000/05/18 15:03:27 jinmei Exp $ */
+
+/*
+ * Copyright (C) 2000 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_SCOPE6_VAR_H_
+#define _NETINET6_SCOPE6_VAR_H_
+
+#ifdef _KERNEL
+void scope6_ifattach __P((struct ifnet *));
+int scope6_set __P((struct ifnet *, u_int32_t *));
+int scope6_get __P((struct ifnet *, u_int32_t *));
+void scope6_setdefault __P((struct ifnet *));
+int scope6_get_default __P((u_int32_t *));
+u_int32_t scope6_in6_addrscope __P((struct in6_addr *));
+u_int32_t scope6_addr2default __P((struct in6_addr *));
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_SCOPE6_VAR_H_ */
diff --git a/sys/netinet6/tcp6_var.h b/sys/netinet6/tcp6_var.h
new file mode 100644
index 0000000..d911167
--- /dev/null
+++ b/sys/netinet6/tcp6_var.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_TCP6_VAR_H_
+#define _NETINET_TCP6_VAR_H_
+
+#ifdef _KERNEL
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_inet6_tcp6);
+#endif
+
+extern int tcp_v6mssdflt; /* XXX */
+
+struct ip6_hdr;
+void tcp6_ctlinput __P((int, struct sockaddr *, void *));
+void tcp6_init __P((void));
+int tcp6_input __P((struct mbuf **, int *, int));
+struct rtentry *tcp_rtlookup6(struct in_conninfo *);
+
+extern struct pr_usrreqs tcp6_usrreqs;
+
+#endif /* _KERNEL */
+
+#endif /* _NETINET_TCP6_VAR_H_ */
diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c
new file mode 100644
index 0000000..1de45f0
--- /dev/null
+++ b/sys/netinet6/udp6_output.c
@@ -0,0 +1,312 @@
+/* $FreeBSD$ */
+/* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#include "opt_ipsec.h"
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/udp6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6protosw.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#ifdef INET6
+#include <netinet6/ipsec6.h>
+#endif
+#endif /* IPSEC */
+
+#include <net/net_osdep.h>
+
+/*
+ * UDP protocol inplementation.
+ * Per RFC 768, August, 1980.
+ */
+
+#define in6pcb inpcb
+#define udp6stat udpstat
+#define udp6s_opackets udps_opackets
+
+int
+udp6_output(in6p, m, addr6, control, td)
+ struct in6pcb *in6p;
+ struct mbuf *m;
+ struct mbuf *control;
+ struct sockaddr *addr6;
+ struct thread *td;
+{
+ u_int32_t ulen = m->m_pkthdr.len;
+ u_int32_t plen = sizeof(struct udphdr) + ulen;
+ struct ip6_hdr *ip6;
+ struct udphdr *udp6;
+ struct in6_addr *laddr, *faddr;
+ u_short fport;
+ int error = 0;
+ struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts;
+ int priv;
+ int af = AF_INET6, hlen = sizeof(struct ip6_hdr);
+ int flags;
+ struct sockaddr_in6 tmp;
+
+ priv = 0;
+ if (td && !suser(td))
+ priv = 1;
+ if (control) {
+ if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0)
+ goto release;
+ in6p->in6p_outputopts = &opt;
+ }
+
+ if (addr6) {
+ /*
+ * IPv4 version of udp_output calls in_pcbconnect in this case,
+ * which needs splnet and affects performance.
+ * Since we saw no essential reason for calling in_pcbconnect,
+ * we get rid of such kind of logic, and call in6_selectsrc
+ * and in6_pcbsetport in order to fill in the local address
+ * and the local port.
+ */
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6;
+ if (sin6->sin6_port == 0) {
+ error = EADDRNOTAVAIL;
+ goto release;
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
+ /* how about ::ffff:0.0.0.0 case? */
+ error = EISCONN;
+ goto release;
+ }
+
+ /* protect *sin6 from overwrites */
+ tmp = *sin6;
+ sin6 = &tmp;
+
+ 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;
+ goto release;
+ }
+
+ if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
+ laddr = in6_selectsrc(sin6, in6p->in6p_outputopts,
+ in6p->in6p_moptions,
+ &in6p->in6p_route,
+ &in6p->in6p_laddr, &error);
+ } else
+ laddr = &in6p->in6p_laddr; /* XXX */
+ if (laddr == NULL) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto release;
+ }
+ if (in6p->in6p_lport == 0 &&
+ (error = in6_pcbsetport(laddr, in6p, td)) != 0)
+ goto release;
+ } else {
+ if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
+ 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 (af == AF_INET)
+ hlen = sizeof(struct ip);
+
+ /*
+ * Calculate data length and get a mbuf
+ * for UDP and IP6 headers.
+ */
+ M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto release;
+ }
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen);
+ udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */
+ udp6->uh_dport = fport;
+ if (plen <= 0xffff)
+ udp6->uh_ulen = htons((u_short)plen);
+ else
+ udp6->uh_ulen = 0;
+ udp6->uh_sum = 0;
+
+ switch (af) {
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
+ ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
+ ip6->ip6_vfc |= IPV6_VERSION;
+#if 0 /* ip6_plen will be filled in ip6_output. */
+ ip6->ip6_plen = htons((u_short)plen);
+#endif
+ ip6->ip6_nxt = IPPROTO_UDP;
+ ip6->ip6_hlim = in6_selecthlim(in6p,
+ in6p->in6p_route.ro_rt ?
+ in6p->in6p_route.ro_rt->rt_ifp : NULL);
+ ip6->ip6_src = *laddr;
+ ip6->ip6_dst = *faddr;
+
+ if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
+ sizeof(struct ip6_hdr), plen)) == 0) {
+ udp6->uh_sum = 0xffff;
+ }
+
+ flags = 0;
+
+ udp6stat.udp6s_opackets++;
+ error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route,
+ flags, in6p->in6p_moptions, NULL, in6p);
+ break;
+ case AF_INET:
+ error = EAFNOSUPPORT;
+ goto release;
+ }
+ goto releaseopt;
+
+release:
+ m_freem(m);
+
+releaseopt:
+ if (control) {
+ ip6_clearpktopts(in6p->in6p_outputopts, 0, -1);
+ in6p->in6p_outputopts = stickyopt;
+ m_freem(control);
+ }
+ return(error);
+}
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
new file mode 100644
index 0000000..fd642cb
--- /dev/null
+++ b/sys/netinet6/udp6_usrreq.c
@@ -0,0 +1,772 @@
+/* $FreeBSD$ */
+/* $KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipsec.h"
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/signalvar.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/udp6_var.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netinet6/ipsec6.h>
+#endif /* IPSEC */
+
+#ifdef FAST_IPSEC
+#include <netipsec/ipsec.h>
+#include <netipsec/ipsec6.h>
+#endif /* FAST_IPSEC */
+
+/*
+ * UDP protocol inplementation.
+ * Per RFC 768, August, 1980.
+ */
+
+extern struct protosw inetsw[];
+static int in6_mcmatch __P((struct inpcb *, struct in6_addr *, struct ifnet *));
+static int udp6_detach __P((struct socket *so));
+
+static int
+in6_mcmatch(in6p, ia6, ifp)
+ struct inpcb *in6p;
+ register struct in6_addr *ia6;
+ struct ifnet *ifp;
+{
+ struct ip6_moptions *im6o = in6p->in6p_moptions;
+ struct in6_multi_mship *imm;
+
+ if (im6o == NULL)
+ return 0;
+
+ for (imm = im6o->im6o_memberships.lh_first; imm != NULL;
+ imm = imm->i6mm_chain.le_next) {
+ if ((ifp == NULL ||
+ imm->i6mm_maddr->in6m_ifp == ifp) &&
+ IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
+ ia6))
+ return 1;
+ }
+ return 0;
+}
+
+int
+udp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6;
+ register struct udphdr *uh;
+ register struct inpcb *in6p;
+ 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 (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
+ /* XXX send icmp6 host/port unreach? */
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+
+ udpstat.udps_ipackets++;
+
+ plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
+ uh = (struct udphdr *)((caddr_t)ip6 + off);
+ ulen = ntohs((u_short)uh->uh_ulen);
+
+ if (plen != ulen) {
+ udpstat.udps_badlen++;
+ goto bad;
+ }
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (uh->uh_sum == 0)
+ udpstat.udps_nosum++;
+ else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
+ udpstat.udps_badsum++;
+ goto bad;
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ struct inpcb *last;
+
+ /*
+ * Deliver a multicast datagram to all sockets
+ * for which the local and remote addresses and ports match
+ * those of the incoming datagram. This allows more than
+ * one process to receive multicasts on the same port.
+ * (This really ought to be done for unicast datagrams as
+ * well, but that would cause problems with existing
+ * applications that open both address-specific sockets and
+ * a wildcard socket listening to the same port -- they would
+ * end up receiving duplicates of every unicast datagram.
+ * Those applications open the multiple sockets to overcome an
+ * inadequacy of the UDP socket interface, but for backwards
+ * compatibility we avoid the problem here rather than
+ * fixing the interface. Maybe 4.5BSD will remedy this?)
+ */
+
+ /*
+ * In a case that laddr should be set to the link-local
+ * address (this happens in RIPng), the multicast address
+ * specified in the received packet does not match with
+ * laddr. To cure this situation, the matching is relaxed
+ * if the receiving interface is the same as one specified
+ * in the socket and if the destination multicast address
+ * matches one of the multicast groups specified in the socket.
+ */
+
+ /*
+ * Construct sockaddr format source address.
+ */
+ init_sin6(&udp_in6, m); /* general init */
+ udp_in6.sin6_port = uh->uh_sport;
+ /*
+ * KAME note: traditionally we dropped udpiphdr from mbuf here.
+ * We need udphdr for IPsec processing so we do that later.
+ */
+
+ /*
+ * Locate pcb(s) for datagram.
+ * (Algorithm copied from raw_intr().)
+ */
+ last = NULL;
+ LIST_FOREACH(in6p, &udb, inp_list) {
+ if ((in6p->inp_vflag & INP_IPV6) == 0)
+ continue;
+ if (in6p->in6p_lport != uh->uh_dport)
+ continue;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
+ if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr,
+ &ip6->ip6_dst) &&
+ !in6_mcmatch(in6p, &ip6->ip6_dst,
+ m->m_pkthdr.rcvif))
+ continue;
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
+ if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr,
+ &ip6->ip6_src) ||
+ in6p->in6p_fport != uh->uh_sport)
+ continue;
+ }
+
+ if (last != NULL) {
+ struct mbuf *n;
+
+#ifdef IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, last))
+ ipsec6stat.in_polvio++;
+ /* do not inject data into pcb */
+ else
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, last))
+ ;
+ else
+#endif /* FAST_IPSEC */
+ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+ /*
+ * KAME NOTE: do not
+ * m_copy(m, offset, ...) above.
+ * sbappendaddr() expects M_PKTHDR,
+ * and m_copy() will copy M_PKTHDR
+ * only if offset is 0.
+ */
+ if (last->in6p_flags & IN6P_CONTROLOPTS
+ || 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,
+ n, opts) == 0) {
+ m_freem(n);
+ if (opts)
+ m_freem(opts);
+ udpstat.udps_fullsock++;
+ } else
+ sorwakeup(last->in6p_socket);
+ opts = NULL;
+ }
+ }
+ last = in6p;
+ /*
+ * Don't look for additional matches if this one does
+ * not have either the SO_REUSEPORT or SO_REUSEADDR
+ * socket options set. This heuristic avoids searching
+ * through all pcbs in the common case of a non-shared
+ * port. It assumes that an application will never
+ * clear these options after setting them.
+ */
+ if ((last->in6p_socket->so_options &
+ (SO_REUSEPORT|SO_REUSEADDR)) == 0)
+ break;
+ }
+
+ if (last == NULL) {
+ /*
+ * No matching pcb found; discard datagram.
+ * (No need to send an ICMP Port Unreachable
+ * for a broadcast or multicast datgram.)
+ */
+ udpstat.udps_noport++;
+ udpstat.udps_noportmcast++;
+ goto bad;
+ }
+#ifdef IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, last)) {
+ ipsec6stat.in_polvio++;
+ goto bad;
+ }
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, last)) {
+ goto bad;
+ }
+#endif /* FAST_IPSEC */
+ if (last->in6p_flags & IN6P_CONTROLOPTS
+ || last->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(last, &opts, ip6, m);
+
+ m_adj(m, off + sizeof(struct udphdr));
+ if (sbappendaddr(&last->in6p_socket->so_rcv,
+ (struct sockaddr *)&udp_in6,
+ m, opts) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(last->in6p_socket);
+ return IPPROTO_DONE;
+ }
+ /*
+ * Locate pcb for datagram.
+ */
+ in6p = in6_pcblookup_hash(&udbinfo, &ip6->ip6_src, uh->uh_sport,
+ &ip6->ip6_dst, uh->uh_dport, 1,
+ m->m_pkthdr.rcvif);
+ if (in6p == 0) {
+ if (log_in_vain) {
+ char buf[INET6_ADDRSTRLEN];
+
+ strcpy(buf, ip6_sprintf(&ip6->ip6_dst));
+ log(LOG_INFO,
+ "Connection attempt to UDP [%s]:%d from [%s]:%d\n",
+ buf, ntohs(uh->uh_dport),
+ ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport));
+ }
+ udpstat.udps_noport++;
+ if (m->m_flags & M_MCAST) {
+ printf("UDP6: M_MCAST is set in a unicast packet.\n");
+ udpstat.udps_noportmcast++;
+ goto bad;
+ }
+ icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
+ return IPPROTO_DONE;
+ }
+#ifdef IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, in6p)) {
+ ipsec6stat.in_polvio++;
+ goto bad;
+ }
+#endif /* IPSEC */
+#ifdef FAST_IPSEC
+ /*
+ * Check AH/ESP integrity.
+ */
+ if (ipsec6_in_reject(m, in6p)) {
+ goto bad;
+ }
+#endif /* FAST_IPSEC */
+
+ /*
+ * Construct sockaddr format source address.
+ * Stuff source address and datagram in user buffer.
+ */
+ init_sin6(&udp_in6, m); /* general init */
+ udp_in6.sin6_port = uh->uh_sport;
+ if (in6p->in6p_flags & IN6P_CONTROLOPTS
+ || in6p->in6p_socket->so_options & SO_TIMESTAMP)
+ ip6_savecontrol(in6p, &opts, ip6, m);
+ m_adj(m, off + sizeof(struct udphdr));
+ if (sbappendaddr(&in6p->in6p_socket->so_rcv,
+ (struct sockaddr *)&udp_in6,
+ m, opts) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(in6p->in6p_socket);
+ return IPPROTO_DONE;
+bad:
+ if (m)
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+ return IPPROTO_DONE;
+}
+
+void
+udp6_ctlinput(cmd, sa, d)
+ int cmd;
+ struct sockaddr *sa;
+ void *d;
+{
+ struct udphdr uh;
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
+ int off = 0;
+ struct ip6ctlparam *ip6cp = NULL;
+ const struct sockaddr_in6 *sa6_src = NULL;
+ struct inpcb *(*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))
+ return;
+
+ if ((unsigned)cmd >= PRC_NCMDS)
+ return;
+ if (PRC_IS_REDIRECT(cmd))
+ notify = in6_rtchange, d = NULL;
+ else if (cmd == PRC_HOSTDEAD)
+ d = NULL;
+ else if (inet6ctlerrmap[cmd] == 0)
+ return;
+
+ /* if the parameter is from icmp6, decode it. */
+ if (d != NULL) {
+ 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;
+ }
+
+ if (ip6) {
+ /*
+ * XXX: We assume that when IPV6 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(*uhp))
+ return;
+
+ bzero(&uh, sizeof(uh));
+ m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh);
+
+ (void) in6_pcbnotify(&udb, sa, uh.uh_dport,
+ (struct sockaddr *)ip6cp->ip6c_src,
+ uh.uh_sport, cmd, notify);
+ } else
+ (void) in6_pcbnotify(&udb, sa, 0,
+ (const struct sockaddr *)sa6_src,
+ 0, cmd, notify);
+}
+
+static int
+udp6_getcred(SYSCTL_HANDLER_ARGS)
+{
+ struct xucred xuc;
+ struct sockaddr_in6 addrs[2];
+ struct inpcb *inp;
+ int error, s;
+
+ error = suser(req->td);
+ if (error)
+ return (error);
+
+ if (req->newlen != sizeof(addrs))
+ return (EINVAL);
+ if (req->oldlen != sizeof(struct xucred))
+ return (EINVAL);
+ error = SYSCTL_IN(req, addrs, sizeof(addrs));
+ if (error)
+ return (error);
+ s = splnet();
+ inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr,
+ addrs[1].sin6_port,
+ &addrs[0].sin6_addr, addrs[0].sin6_port,
+ 1, NULL);
+ if (!inp || !inp->inp_socket) {
+ error = ENOENT;
+ goto out;
+ }
+ cru2x(inp->inp_socket->so_cred, &xuc);
+ error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
+out:
+ splx(s);
+ return (error);
+}
+
+SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
+ 0, 0,
+ udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection");
+
+static int
+udp6_abort(struct socket *so)
+{
+ struct inpcb *inp;
+ int s;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ return EINVAL; /* ??? possible? panic instead? */
+ soisdisconnected(so);
+ s = splnet();
+ in6_pcbdetach(inp);
+ splx(s);
+ return 0;
+}
+
+static int
+udp6_attach(struct socket *so, int proto, struct thread *td)
+{
+ struct inpcb *inp;
+ int s, error;
+
+ inp = sotoinpcb(so);
+ if (inp != 0)
+ return EINVAL;
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ error = soreserve(so, udp_sendspace, udp_recvspace);
+ if (error)
+ return error;
+ }
+ s = splnet();
+ error = in_pcballoc(so, &udbinfo, td);
+ splx(s);
+ if (error)
+ return error;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_vflag |= INP_IPV6;
+ if (!ip6_v6only)
+ inp->inp_vflag |= INP_IPV4;
+ inp->in6p_hops = -1; /* use kernel default */
+ inp->in6p_cksum = -1; /* just to be sure */
+ /*
+ * XXX: ugly!!
+ * IPv4 TTL initialization is necessary for an IPv6 socket as well,
+ * because the socket may be bound to an IPv6 wildcard address,
+ * which may match an IPv4-mapped IPv6 address.
+ */
+ inp->inp_ip_ttl = ip_defttl;
+ return 0;
+}
+
+static int
+udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
+{
+ struct inpcb *inp;
+ int s, error;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ return EINVAL;
+
+ inp->inp_vflag &= ~INP_IPV4;
+ inp->inp_vflag |= INP_IPV6;
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
+ struct sockaddr_in6 *sin6_p;
+
+ sin6_p = (struct sockaddr_in6 *)nam;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr))
+ inp->inp_vflag |= INP_IPV4;
+ else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
+ struct sockaddr_in sin;
+
+ in6_sin6_2_sin(&sin, sin6_p);
+ inp->inp_vflag |= INP_IPV4;
+ inp->inp_vflag &= ~INP_IPV6;
+ s = splnet();
+ error = in_pcbbind(inp, (struct sockaddr *)&sin, td);
+ splx(s);
+ return error;
+ }
+ }
+
+ s = splnet();
+ error = in6_pcbbind(inp, nam, td);
+ splx(s);
+ return error;
+}
+
+static int
+udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
+{
+ struct inpcb *inp;
+ int s, error;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ return EINVAL;
+
+ if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
+ struct sockaddr_in6 *sin6_p;
+
+ sin6_p = (struct sockaddr_in6 *)nam;
+ if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
+ struct sockaddr_in sin;
+
+ if (inp->inp_faddr.s_addr != INADDR_ANY)
+ return EISCONN;
+ in6_sin6_2_sin(&sin, sin6_p);
+ s = splnet();
+ error = in_pcbconnect(inp, (struct sockaddr *)&sin, td);
+ splx(s);
+ if (error == 0) {
+ inp->inp_vflag |= INP_IPV4;
+ inp->inp_vflag &= ~INP_IPV6;
+ soisconnected(so);
+ }
+ return error;
+ }
+ }
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return EISCONN;
+ s = splnet();
+ error = in6_pcbconnect(inp, nam, td);
+ splx(s);
+ if (error == 0) {
+ if (!ip6_v6only) { /* should be non mapped addr */
+ inp->inp_vflag &= ~INP_IPV4;
+ inp->inp_vflag |= INP_IPV6;
+ }
+ soisconnected(so);
+ }
+ return error;
+}
+
+static int
+udp6_detach(struct socket *so)
+{
+ struct inpcb *inp;
+ int s;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ return EINVAL;
+ s = splnet();
+ in6_pcbdetach(inp);
+ splx(s);
+ return 0;
+}
+
+static int
+udp6_disconnect(struct socket *so)
+{
+ struct inpcb *inp;
+ int s;
+
+ inp = sotoinpcb(so);
+ if (inp == 0)
+ return EINVAL;
+
+ if (inp->inp_vflag & INP_IPV4) {
+ struct pr_usrreqs *pru;
+
+ pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
+ return ((*pru->pru_disconnect)(so));
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return ENOTCONN;
+
+ s = splnet();
+ in6_pcbdisconnect(inp);
+ inp->in6p_laddr = in6addr_any;
+ splx(s);
+ so->so_state &= ~SS_ISCONNECTED; /* XXX */
+ return 0;
+}
+
+static int
+udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+ struct mbuf *control, struct thread *td)
+{
+ struct inpcb *inp;
+ int error = 0;
+
+ inp = sotoinpcb(so);
+ if (inp == 0) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ if (addr) {
+ if (addr->sa_len != sizeof(struct sockaddr_in6)) {
+ error = EINVAL;
+ goto bad;
+ }
+ if (addr->sa_family != AF_INET6) {
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+ }
+
+ if (!ip6_v6only) {
+ int hasv4addr;
+ struct sockaddr_in6 *sin6 = 0;
+
+ if (addr == 0)
+ hasv4addr = (inp->inp_vflag & INP_IPV4);
+ else {
+ sin6 = (struct sockaddr_in6 *)addr;
+ hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)
+ ? 1 : 0;
+ }
+ if (hasv4addr) {
+ struct pr_usrreqs *pru;
+
+ if (sin6)
+ in6_sin6_2_sin_in_sock(addr);
+ pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
+ error = ((*pru->pru_send)(so, flags, m, addr, control,
+ td));
+ /* addr will just be freed in sendit(). */
+ return error;
+ }
+ }
+
+ return udp6_output(inp, m, addr, control, td);
+
+ bad:
+ m_freem(m);
+ return(error);
+}
+
+struct pr_usrreqs udp6_usrreqs = {
+ udp6_abort, pru_accept_notsupp, udp6_attach, udp6_bind, udp6_connect,
+ pru_connect2_notsupp, in6_control, udp6_detach, udp6_disconnect,
+ pru_listen_notsupp, in6_mapped_peeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, udp6_send, pru_sense_null, udp_shutdown,
+ in6_mapped_sockaddr, sosend, soreceive, sopoll
+};
diff --git a/sys/netinet6/udp6_var.h b/sys/netinet6/udp6_var.h
new file mode 100644
index 0000000..2da5057
--- /dev/null
+++ b/sys/netinet6/udp6_var.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_var.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_UDP6_VAR_H_
+#define _NETINET6_UDP6_VAR_H_
+
+#ifdef _KERNEL
+SYSCTL_DECL(_net_inet6_udp6);
+
+extern struct pr_usrreqs udp6_usrreqs;
+
+void udp6_ctlinput __P((int, struct sockaddr *, void *));
+int udp6_input __P((struct mbuf **, int *, int));
+int udp6_output __P((struct inpcb *inp, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control,
+ struct thread *td));
+#endif
+
+#endif /*_NETINET6_UDP6_VAR_H_*/
OpenPOWER on IntegriCloud