summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/alpha/alpha/machdep.c13
-rw-r--r--sys/arch/amiga/amiga/machdep.c5
-rw-r--r--sys/arch/arc/arc/trap.c4
-rw-r--r--sys/arch/arm32/arm32/irqhandler.c3
-rw-r--r--sys/arch/atari/atari/machdep.c6
-rw-r--r--sys/arch/hp300/hp300/intr.c4
-rw-r--r--sys/arch/i386/isa/icu.s4
-rw-r--r--sys/arch/kbus/kbus/intr.c3
-rw-r--r--sys/arch/mac68k/mac68k/machdep.c4
-rw-r--r--sys/arch/mvme68k/mvme68k/machdep.c4
-rw-r--r--sys/arch/mvme88k/mvme88k/machdep.c4
-rw-r--r--sys/arch/pc532/pc532/intr.c5
-rw-r--r--sys/arch/sparc/sparc/intr.c11
-rw-r--r--sys/arch/sun3/sun3/isr.c7
-rw-r--r--sys/arch/vax/vax/machdep.c8
-rw-r--r--sys/conf/files59
-rw-r--r--sys/kern/uipc_domain.c41
-rw-r--r--sys/kern/uipc_mbuf2.c277
-rw-r--r--sys/kern/uipc_socket2.c39
-rw-r--r--sys/kern/uipc_syscalls.c36
-rw-r--r--sys/net/if.c149
-rw-r--r--sys/net/if.h43
-rw-r--r--sys/net/if_ethersubr.c80
-rw-r--r--sys/net/if_faith.c323
-rw-r--r--sys/net/if_fddisubr.c26
-rw-r--r--sys/net/if_gif.c552
-rw-r--r--sys/net/if_gif.h86
-rw-r--r--sys/net/if_loop.c106
-rw-r--r--sys/net/if_ppp.c8
-rw-r--r--sys/net/if_tun.c14
-rw-r--r--sys/net/if_types.h8
-rw-r--r--sys/net/net_osdep.c87
-rw-r--r--sys/net/net_osdep.h149
-rw-r--r--sys/net/ppp_defs.h3
-rw-r--r--sys/net/route.c248
-rw-r--r--sys/net/route.h38
-rw-r--r--sys/net/rtsock.c126
-rw-r--r--sys/netccitt/if_x25subr.c6
-rw-r--r--sys/netinet/fil.c11
-rw-r--r--sys/netinet/icmp6.h37
-rw-r--r--sys/netinet/if_atm.c4
-rw-r--r--sys/netinet/if_ether.h19
-rw-r--r--sys/netinet/igmp.c39
-rw-r--r--sys/netinet/in.c259
-rw-r--r--sys/netinet/in.h35
-rw-r--r--sys/netinet/in4_cksum.c217
-rw-r--r--sys/netinet/in_gif.c357
-rw-r--r--sys/netinet/in_gif.h42
-rw-r--r--sys/netinet/in_pcb.c146
-rw-r--r--sys/netinet/in_pcb.h86
-rw-r--r--sys/netinet/in_proto.c73
-rw-r--r--sys/netinet/in_var.h7
-rw-r--r--sys/netinet/ip.h7
-rw-r--r--sys/netinet/ip6.h3
-rw-r--r--sys/netinet/ip_auth.c9
-rw-r--r--sys/netinet/ip_ecn.c148
-rw-r--r--sys/netinet/ip_ecn.h55
-rw-r--r--sys/netinet/ip_fil.c9
-rw-r--r--sys/netinet/ip_frag.c9
-rw-r--r--sys/netinet/ip_icmp.c37
-rw-r--r--sys/netinet/ip_input.c61
-rw-r--r--sys/netinet/ip_mroute.h6
-rw-r--r--sys/netinet/ip_output.c56
-rw-r--r--sys/netinet/ip_var.h3
-rw-r--r--sys/netinet/tcp_debug.c8
-rw-r--r--sys/netinet/tcp_input.c173
-rw-r--r--sys/netinet/tcp_output.c89
-rw-r--r--sys/netinet/tcp_subr.c109
-rw-r--r--sys/netinet/tcp_usrreq.c84
-rw-r--r--sys/netinet/tcp_var.h14
-rw-r--r--sys/netinet/udp_usrreq.c326
-rw-r--r--sys/netinet/udp_var.h8
-rw-r--r--sys/netinet6/dest6.c123
-rw-r--r--sys/netinet6/frag6.c668
-rw-r--r--sys/netinet6/icmp6.c2320
-rw-r--r--sys/netinet6/icmp6.h657
-rw-r--r--sys/netinet6/in6.c3009
-rw-r--r--sys/netinet6/in6.h706
-rw-r--r--sys/netinet6/in6_cksum.c444
-rw-r--r--sys/netinet6/in6_gif.c307
-rw-r--r--sys/netinet6/in6_gif.h40
-rw-r--r--sys/netinet6/in6_ifattach.c745
-rw-r--r--sys/netinet6/in6_ifattach.h50
-rw-r--r--sys/netinet6/in6_pcb.c207
-rw-r--r--sys/netinet6/in6_pcb.h178
-rw-r--r--sys/netinet6/in6_prefix.c1149
-rw-r--r--sys/netinet6/in6_prefix.h88
-rw-r--r--sys/netinet6/in6_proto.c1012
-rw-r--r--sys/netinet6/in6_src.c346
-rw-r--r--sys/netinet6/in6_var.h783
-rw-r--r--sys/netinet6/ip6.h287
-rw-r--r--sys/netinet6/ip6_forward.c450
-rw-r--r--sys/netinet6/ip6_input.c1320
-rw-r--r--sys/netinet6/ip6_mroute.c1865
-rw-r--r--sys/netinet6/ip6_mroute.h254
-rw-r--r--sys/netinet6/ip6_output.c2534
-rw-r--r--sys/netinet6/ip6_var.h289
-rw-r--r--sys/netinet6/ip6protosw.h151
-rw-r--r--sys/netinet6/ipv6.h123
-rw-r--r--sys/netinet6/ipv6_addrconf.c248
-rw-r--r--sys/netinet6/ipv6_addrconf.h22
-rw-r--r--sys/netinet6/ipv6_discovery.c3295
-rw-r--r--sys/netinet6/ipv6_icmp.c1086
-rw-r--r--sys/netinet6/ipv6_icmp.h257
-rw-r--r--sys/netinet6/ipv6_input.c1449
-rw-r--r--sys/netinet6/ipv6_output.c2107
-rw-r--r--sys/netinet6/ipv6_rtrequest.c1017
-rw-r--r--sys/netinet6/ipv6_trans.c692
-rw-r--r--sys/netinet6/ipv6_trans.h26
-rw-r--r--sys/netinet6/ipv6_var.h356
-rw-r--r--sys/netinet6/mld6.c486
-rw-r--r--sys/netinet6/mld6_var.h52
-rw-r--r--sys/netinet6/nd6.c1801
-rw-r--r--sys/netinet6/nd6.h307
-rw-r--r--sys/netinet6/nd6_nbr.c1261
-rw-r--r--sys/netinet6/nd6_rtr.c1444
-rw-r--r--sys/netinet6/pim6.h68
-rw-r--r--sys/netinet6/pim6_var.h71
-rw-r--r--sys/netinet6/raw_ipv6.c445
-rw-r--r--sys/netinet6/route6.c172
-rw-r--r--sys/netinet6/tcpipv6.h4
-rw-r--r--sys/netiso/if_eon.c10
-rw-r--r--sys/sys/malloc.h17
-rw-r--r--sys/sys/mbuf.h20
-rw-r--r--sys/sys/socket.h15
-rw-r--r--sys/sys/socketvar.h4
-rw-r--r--sys/sys/sockio.h12
127 files changed, 29040 insertions, 12944 deletions
diff --git a/sys/arch/alpha/alpha/machdep.c b/sys/arch/alpha/alpha/machdep.c
index 4281760f068..f5a4d7ffc6e 100644
--- a/sys/arch/alpha/alpha/machdep.c
+++ b/sys/arch/alpha/alpha/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.28 1999/05/24 23:08:55 jason Exp $ */
+/* $OpenBSD: machdep.c,v 1.29 1999/12/08 06:50:14 itojun Exp $ */
/* $NetBSD: machdep.c,v 1.61 1996/12/07 01:54:49 cgd Exp $ */
/*
@@ -95,6 +95,15 @@
#include <netinet/if_ether.h>
#include <netinet/ip_var.h>
#endif
+
+#ifdef INET6
+# ifndef INET
+# include <netinet/in.h>
+# endif
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+
#include "ppp.h"
#include "bridge.h"
@@ -1474,7 +1483,7 @@ netintr()
DONETISR(NETISR_IP, ipintr());
#endif
#ifdef INET6
- DONETISR(NETISR_IPV6, ipv6intr());
+ DONETISR(NETISR_IPV6, ip6intr());
#endif
#ifdef NETATALK
DONETISR(NETISR_ATALK, atintr());
diff --git a/sys/arch/amiga/amiga/machdep.c b/sys/arch/amiga/amiga/machdep.c
index 7ce67d58b9c..458526fb3e6 100644
--- a/sys/arch/amiga/amiga/machdep.c
+++ b/sys/arch/amiga/amiga/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.34 1999/09/03 18:00:28 art Exp $ */
+/* $OpenBSD: machdep.c,v 1.35 1999/12/08 06:50:14 itojun Exp $ */
/* $NetBSD: machdep.c,v 1.95 1997/08/27 18:31:17 is Exp $ */
/*
@@ -108,7 +108,6 @@
#include "ser.h"
#include "ether.h"
#include "ppp.h"
-#include "bridge.h"
#include <net/if.h>
@@ -1205,7 +1204,7 @@ netintr()
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/arc/arc/trap.c b/sys/arch/arc/arc/trap.c
index edb0e8c0fea..6deb34facc8 100644
--- a/sys/arch/arc/arc/trap.c
+++ b/sys/arch/arc/arc/trap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: trap.c,v 1.22 1999/05/24 23:08:59 jason Exp $ */
+/* $OpenBSD: trap.c,v 1.23 1999/12/08 06:50:15 itojun Exp $ */
/*
* Copyright (c) 1988 University of Utah.
@@ -893,7 +893,7 @@ interrupt(statusReg, causeReg, what, pc, args)
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/arm32/arm32/irqhandler.c b/sys/arch/arm32/arm32/irqhandler.c
index 6f75277f577..7ee69cd3e46 100644
--- a/sys/arch/arm32/arm32/irqhandler.c
+++ b/sys/arch/arm32/arm32/irqhandler.c
@@ -444,6 +444,9 @@ dosoftints()
#endif
ipintr();
#endif
+#ifdef INET6
+ ip6intr();
+#endif
#ifdef IMP
impintr();
#endif
diff --git a/sys/arch/atari/atari/machdep.c b/sys/arch/atari/atari/machdep.c
index 01e18c172ac..2ead98665bd 100644
--- a/sys/arch/atari/atari/machdep.c
+++ b/sys/arch/atari/atari/machdep.c
@@ -1116,6 +1116,12 @@ netintr()
ipintr();
}
#endif
+#ifdef INET6
+ if (netisr & (1 << NETISR_IPV6)) {
+ netisr &= ~(1 << NETISR_IPV6);
+ ip6intr();
+ }
+#endif
#ifdef NETATALK
if (netisr & (1 << NETISR_ATALK)) {
netisr &= ~(1 << NETISR_ATALK);
diff --git a/sys/arch/hp300/hp300/intr.c b/sys/arch/hp300/hp300/intr.c
index a73ea086e65..7cd9efa2fcf 100644
--- a/sys/arch/hp300/hp300/intr.c
+++ b/sys/arch/hp300/hp300/intr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: intr.c,v 1.5 1999/05/24 23:09:00 jason Exp $ */
+/* $OpenBSD: intr.c,v 1.6 1999/12/08 06:50:15 itojun Exp $ */
/* $NetBSD: intr.c,v 1.2 1997/05/01 16:24:26 thorpej Exp $ */
/*-
@@ -297,7 +297,7 @@ netintr()
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/i386/isa/icu.s b/sys/arch/i386/isa/icu.s
index bb65eb051df..90d52454b93 100644
--- a/sys/arch/i386/isa/icu.s
+++ b/sys/arch/i386/isa/icu.s
@@ -1,4 +1,4 @@
-/* $OpenBSD: icu.s,v 1.11 1999/05/24 23:09:01 jason Exp $ */
+/* $OpenBSD: icu.s,v 1.12 1999/12/08 06:50:15 itojun Exp $ */
/* $NetBSD: icu.s,v 1.45 1996/01/07 03:59:34 mycroft Exp $ */
/*-
@@ -158,7 +158,7 @@ IDTVEC(softnet)
DONET(NETISR_IP, _ipintr)
#endif
#ifdef INET6
- DONET(NETISR_IPV6, _ipv6intr)
+ DONET(NETISR_IPV6, _ip6intr)
#endif /* INET6 */
#ifdef NETATALK
DONET(NETISR_ATALK, _atintr)
diff --git a/sys/arch/kbus/kbus/intr.c b/sys/arch/kbus/kbus/intr.c
index bbdef71399e..0770a8215dc 100644
--- a/sys/arch/kbus/kbus/intr.c
+++ b/sys/arch/kbus/kbus/intr.c
@@ -149,6 +149,9 @@ netintr()
DONETISR(NETISR_ARP, arpintr());
DONETISR(NETISR_IP, ipintr());
#endif
+#ifdef INET6
+ DONETISR(NETISR_IPV6, ip6intr());
+#endif
#ifdef NS
DONETISR(NETISR_NS, nsintr());
#endif
diff --git a/sys/arch/mac68k/mac68k/machdep.c b/sys/arch/mac68k/mac68k/machdep.c
index 98a37234efb..d27edd00388 100644
--- a/sys/arch/mac68k/mac68k/machdep.c
+++ b/sys/arch/mac68k/mac68k/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.63 1999/09/03 18:01:12 art Exp $ */
+/* $OpenBSD: machdep.c,v 1.64 1999/12/08 06:50:16 itojun Exp $ */
/* $NetBSD: machdep.c,v 1.134 1997/02/14 06:15:30 scottr Exp $ */
/*
@@ -1038,7 +1038,7 @@ netintr()
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/mvme68k/mvme68k/machdep.c b/sys/arch/mvme68k/mvme68k/machdep.c
index 9287013541f..fff1f90c012 100644
--- a/sys/arch/mvme68k/mvme68k/machdep.c
+++ b/sys/arch/mvme68k/mvme68k/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.29 1999/09/27 20:30:32 smurph Exp $ */
+/* $OpenBSD: machdep.c,v 1.30 1999/12/08 06:50:16 itojun Exp $ */
/*
* Copyright (c) 1995 Theo de Raadt
@@ -872,7 +872,7 @@ netintr()
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/mvme88k/mvme88k/machdep.c b/sys/arch/mvme88k/mvme88k/machdep.c
index 247f163b31c..50cf804b303 100644
--- a/sys/arch/mvme88k/mvme88k/machdep.c
+++ b/sys/arch/mvme88k/mvme88k/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.17 1999/09/27 19:13:23 smurph Exp $ */
+/* $OpenBSD: machdep.c,v 1.18 1999/12/08 06:50:16 itojun Exp $ */
/*
* Copyright (c) 1998, 1999 Steve Murphree, Jr.
* Copyright (c) 1996 Nivas Madhur
@@ -1827,7 +1827,7 @@ void netintr()
#ifdef INET6
if (netisr & (1 << NETISR_IPV6)) {
netisr &= ~(1 << NETISR_IPV6);
- ipv6intr();
+ ip6intr();
}
#endif
#ifdef NETATALK
diff --git a/sys/arch/pc532/pc532/intr.c b/sys/arch/pc532/pc532/intr.c
index afe3d85ea4b..5960b18becd 100644
--- a/sys/arch/pc532/pc532/intr.c
+++ b/sys/arch/pc532/pc532/intr.c
@@ -29,7 +29,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: intr.c,v 1.4 1999/05/24 23:09:06 jason Exp $
+ * $Id: intr.c,v 1.5 1999/12/08 06:50:16 itojun Exp $
*/
#define DEFINE_SPLX
@@ -177,6 +177,9 @@ softnet()
#endif
if (isr & (1 << NETISR_IP)) ipintr();
#endif
+#ifdef INET6
+ if (isr & (1 << NETISR_IPV6)) ip6intr();
+#endif
#ifdef NETATALK
if (isr & (1 << NETISR_ATALK)) atintr();
#endif
diff --git a/sys/arch/sparc/sparc/intr.c b/sys/arch/sparc/sparc/intr.c
index 477f54e4ab8..2ba1aab69c0 100644
--- a/sys/arch/sparc/sparc/intr.c
+++ b/sys/arch/sparc/sparc/intr.c
@@ -66,6 +66,15 @@
#include <netinet/if_ether.h>
#include <netinet/ip_var.h>
#endif
+
+#ifdef INET6
+# ifndef INET
+# include <netinet/in.h>
+# endif
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+
#include "ppp.h"
#include "bridge.h"
@@ -135,7 +144,7 @@ soft01intr(fp)
#endif
#ifdef INET6
if (n & (1 << NETISR_IPV6))
- ipv6intr();
+ ip6intr();
#endif
#ifdef NETATALK
if (n & (1 << NETISR_ATALK))
diff --git a/sys/arch/sun3/sun3/isr.c b/sys/arch/sun3/sun3/isr.c
index 10ad3d52620..c052f1697f3 100644
--- a/sys/arch/sun3/sun3/isr.c
+++ b/sys/arch/sun3/sun3/isr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: isr.c,v 1.9 1999/05/24 23:09:08 jason Exp $ */
+/* $OpenBSD: isr.c,v 1.10 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: isr.c,v 1.25 1996/11/20 18:57:32 gwr Exp $ */
/*-
@@ -101,6 +101,7 @@ isr_add_custom(level, handler)
*/
void arpintr __P((void));
void ipintr __P((void));
+void ip6intr __P((void));
void atintr __P((void));
void nsintr __P((void));
void clnlintr __P((void));
@@ -126,6 +127,10 @@ netintr()
if (n & (1 << NETISR_IP))
ipintr();
#endif
+#ifdef INET6
+ if (n & (1 << NETISR_IPV6))
+ ip6intr();
+#endif
#ifdef NETATALK
if (n & (1 << NETISR_ATALK))
atintr();
diff --git a/sys/arch/vax/vax/machdep.c b/sys/arch/vax/vax/machdep.c
index f63eaec3d57..c3e616a680f 100644
--- a/sys/arch/vax/vax/machdep.c
+++ b/sys/arch/vax/vax/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.19 1999/05/24 23:09:09 jason Exp $ */
+/* $OpenBSD: machdep.c,v 1.20 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: machdep.c,v 1.45 1997/07/26 10:12:49 ragge Exp $ */
/*
@@ -694,6 +694,12 @@ netintr()
ipintr();
}
#endif
+#ifdef INET6
+ if (netisr & (1 << NETISR_IPV6)) {
+ netisr &= ~(1 << NETISR_IPV6);
+ ip6intr();
+ }
+#endif
#ifdef NETATALK
if (netisr & (1 << NETISR_ATALK)) {
netisr &= ~(1 << NETISR_ATALK);
diff --git a/sys/conf/files b/sys/conf/files
index cb167e90925..e3586c9074a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.137 1999/11/30 07:55:55 cmetz Exp $
+# $OpenBSD: files,v 1.138 1999/12/08 06:50:17 itojun Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -189,6 +189,8 @@ pseudo-device random
pseudo-device enc: ifnet
pseudo-device bridge: ifnet, ether
pseudo-device sppp: ifnet
+pseudo-device faith: ifnet
+pseudo-device gif: ifnet
pseudo-device sequencer
@@ -666,19 +668,54 @@ file uvm/uvm_vnode.c uvm
# XXX
file kern/subr_pool.c uvm
-# ... IPv6
-file netinet6/in6.c inet6
-file netinet6/ipv6_icmp.c inet6
-file netinet6/in6_cksum.c inet6
+# ... NRL IPv6
+#file netinet6/in6.c inet6
+#file netinet6/ipv6_icmp.c inet6
+#file netinet6/in6_cksum.c inet6
file netinet6/in6_pcb.c inet6
file netinet6/raw_ipv6.c inet6
-file netinet6/ipv6_input.c inet6
-file netinet6/ipv6_discovery.c inet6
-file netinet6/ipv6_addrconf.c inet6
-file netinet6/ipv6_rtrequest.c inet6
-file netinet6/ipv6_output.c inet6
+#file netinet6/ipv6_input.c inet6
+#file netinet6/ipv6_discovery.c inet6
+#file netinet6/ipv6_addrconf.c inet6
+#file netinet6/ipv6_rtrequest.c inet6
+#file netinet6/ipv6_output.c inet6
+#file netinet6/in6_proto.c inet6
+#file netinet6/ipv6_trans.c inet6
+
+# ... KAME IPv6
+file net/if_faith.c faith needs-count
+file net/if_gif.c gif needs-count
+file net/net_osdep.c
+file netinet/ip_ecn.c inet | inet6
+file netinet/in_gif.c gif & inet
+file netinet6/in6_gif.c gif & inet6
+file netinet6/in6.c inet6
+file netinet6/in6_ifattach.c inet6
+file netinet6/in6_cksum.c inet6
+#file netinet6/in6_pcb.c inet6
+file netinet6/in6_src.c inet6
+file netinet6/in6_prefix.c inet6
file netinet6/in6_proto.c inet6
-file netinet6/ipv6_trans.c inet6
+file netinet6/dest6.c inet6
+file netinet6/frag6.c inet6
+file netinet6/icmp6.c inet6
+file netinet6/ip6_input.c inet6
+file netinet6/ip6_forward.c inet6
+file netinet6/ip6_mroute.c inet6
+file netinet6/ip6_output.c inet6
+file netinet6/route6.c inet6
+file netinet6/mld6.c inet6
+file netinet6/nd6.c inet6
+file netinet6/nd6_nbr.c inet6
+file netinet6/nd6_rtr.c inet6
+#file netinet6/raw_ip6.c inet6
+#file netinet6/tcp6_debug.c inet6 & tcp6
+#file netinet6/tcp6_input.c inet6 & tcp6
+#file netinet6/tcp6_output.c inet6 & tcp6
+#file netinet6/tcp6_subr.c inet6 & tcp6
+#file netinet6/tcp6_timer.c inet6 & tcp6
+#file netinet6/tcp6_usrreq.c inet6 & tcp6
+#file netinet6/udp6_usrreq.c inet6
# ... PF_KEY
file net/pfkey.c key | ipsec | tcp_signature
diff --git a/sys/kern/uipc_domain.c b/sys/kern/uipc_domain.c
index 9d5a23865f8..3695d21f72d 100644
--- a/sys/kern/uipc_domain.c
+++ b/sys/kern/uipc_domain.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_domain.c,v 1.8 1999/03/30 00:19:05 niklas Exp $ */
+/* $OpenBSD: uipc_domain.c,v 1.9 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_domain.c,v 1.14 1996/02/09 19:00:44 christos Exp $ */
/*
@@ -67,9 +67,12 @@ domaininit()
register struct protosw *pr;
#undef unix
+ /*
+ * KAME NOTE: ADDDOMAIN(route) is moved to the last part so that
+ * it will be initialized as the *first* element. confusing!
+ */
#ifndef lint
ADDDOMAIN(unix);
- ADDDOMAIN(route);
#ifdef INET
ADDDOMAIN(inet);
#endif
@@ -100,6 +103,12 @@ domaininit()
ADDDOMAIN(imp);
#endif
#endif
+#ifdef IPSEC
+#ifdef __KAME__
+ ADDDOMAIN(key);
+#endif
+#endif
+ ADDDOMAIN(route);
#endif
for (dp = domains; dp; dp = dp->dom_next) {
@@ -177,14 +186,15 @@ net_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
int family, protocol;
/*
- * All sysctl names at this level are nonterminal;
- * next two components are protocol family and protocol number,
- * then at least one addition component.
+ * All sysctl names at this level are nonterminal.
+ * PF_KEY: next component is protocol family, and then at least one
+ * additional component.
+ * usually: next two components are protocol family and protocol
+ * number, then at least one addition component.
*/
- if (namelen < 3)
+ if (namelen < 2)
return (EISDIR); /* overloaded */
family = name[0];
- protocol = name[1];
if (family == 0)
return (0);
@@ -193,6 +203,23 @@ net_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
goto found;
return (ENOPROTOOPT);
found:
+ switch (family) {
+#ifdef IPSEC
+#ifdef __KAME__
+ case PF_KEY:
+ pr = dp->dom_protosw;
+ if (pr->pr_sysctl)
+ return ((*pr->pr_sysctl)(name + 1, namelen - 1,
+ oldp, oldlenp, newp, newlen));
+ return (ENOPROTOOPT);
+#endif
+#endif
+ default:
+ break;
+ }
+ if (namelen < 3)
+ return (EISDIR); /* overloaded */
+ protocol = name[1];
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_protocol == protocol && pr->pr_sysctl)
return ((*pr->pr_sysctl)(name + 2, namelen - 2,
diff --git a/sys/kern/uipc_mbuf2.c b/sys/kern/uipc_mbuf2.c
new file mode 100644
index 00000000000..6b9816e1f40
--- /dev/null
+++ b/sys/kern/uipc_mbuf2.c
@@ -0,0 +1,277 @@
+/* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej 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.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1988, 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.
+ *
+ * @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95
+ */
+
+#define PULLDOWN_STAT
+/*#define PULLDOWN_DEBUG*/
+
+#ifdef PULLDOWN_STAT
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include "opt_inet.h"
+#endif
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#if defined(PULLDOWN_STAT) && defined(INET6)
+#include <netinet/in.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+
+/*
+ * ensure that [off, off + len) is contiguous on the mbuf chain "m".
+ * packet chain before "off" is kept untouched.
+ * if offp == NULL, the target will start at <retval, 0> on resulting chain.
+ * if offp != NULL, the target will start at <retval, *offp> on resulting chain.
+ *
+ * on error return (NULL return value), original "m" will be freed.
+ *
+ * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster)
+ */
+struct mbuf *
+m_pulldown(m, off, len, offp)
+ struct mbuf *m;
+ int off, len;
+ int *offp;
+{
+ struct mbuf *n, *o;
+ int hlen, tlen, olen;
+ int sharedcluster;
+
+ /* check invalid arguments. */
+ if (m == NULL)
+ panic("m == NULL in m_pulldown()");
+ if (len > MCLBYTES) {
+ m_freem(m);
+ return NULL; /* impossible */
+ }
+
+#if defined(PULLDOWN_STAT) && defined(INET6)
+ ip6stat.ip6s_pulldown++;
+#endif
+
+#ifdef PULLDOWN_DEBUG
+ {
+ struct mbuf *t;
+ printf("before:");
+ for (t = m; t; t = t->m_next)
+ printf(" %d", t->m_len);
+ printf("\n");
+ }
+#endif
+ n = m;
+ while (n != NULL && off > 0) {
+ if (n->m_len > off)
+ break;
+ off -= n->m_len;
+ n = n->m_next;
+ }
+ if (!n) {
+ m_freem(m);
+ return NULL; /* mbuf chain too short */
+ }
+
+ /*
+ * the target data is on <n, off>.
+ * if we got enough data on the mbuf "n", we're done.
+ */
+ if ((off == 0 || offp) && len <= n->m_len - off)
+ goto ok;
+
+#if defined(PULLDOWN_STAT) && defined(INET6)
+ ip6stat.ip6s_pulldown_copy++;
+#endif
+
+ /*
+ * when len < n->m_len - off and off != 0, it is a special case.
+ * len bytes from <n, off> sits in single mbuf, but the caller does
+ * not like the starting position (off).
+ * chop the current mbuf into two pieces, set off to 0.
+ */
+ if (len < n->m_len - off) {
+ o = m_copym(n, off, n->m_len - off, M_DONTWAIT);
+ if (o == NULL) {
+ m_freem(m);
+ return NULL; /* ENOBUFS */
+ }
+ n->m_len = off;
+ o->m_next = n->m_next;
+ n->m_next = o;
+ n = n->m_next;
+ off = 0;
+ goto ok;
+ }
+
+ /*
+ * we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
+ * and construct contiguous mbuf with m_len == len.
+ * note that hlen + tlen == len, and tlen > 0.
+ */
+ hlen = n->m_len - off;
+ tlen = len - hlen;
+
+ /*
+ * ensure that we have enough trailing data on mbuf chain.
+ * if not, we can do nothing about the chain.
+ */
+ olen = 0;
+ for (o = n->m_next; o != NULL; o = o->m_next)
+ olen += o->m_len;
+ if (hlen + olen < len) {
+ m_freem(m);
+ return NULL; /* mbuf chain too short */
+ }
+
+ /*
+ * easy cases first.
+ * we need to use m_copydata() to get data from <n->m_next, 0>.
+ */
+ if ((n->m_flags & M_EXT) == 0)
+ sharedcluster = 0;
+ else {
+#ifdef __bsdi__
+ if (n->m_ext.ext_func)
+#else
+ if (n->m_ext.ext_free)
+#endif
+ sharedcluster = 1;
+#ifdef __NetBSD__
+ else if (MCLISREFERENCED(n))
+#else
+ else if (mclrefcnt[mtocl(n->m_ext.ext_buf)] > 1)
+#endif
+ sharedcluster = 1;
+ else
+ sharedcluster = 0;
+ }
+ if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen
+ && !sharedcluster) {
+ m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
+ n->m_len += tlen;
+ m_adj(n->m_next, tlen);
+ goto ok;
+ }
+ if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen
+ && !sharedcluster) {
+ n->m_next->m_data -= hlen;
+ n->m_next->m_len += hlen;
+ bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen);
+ n->m_len -= hlen;
+ n = n->m_next;
+ off = 0;
+ goto ok;
+ }
+
+ /*
+ * now, we need to do the hard way. don't m_copy as there's no room
+ * on both end.
+ */
+#if defined(PULLDOWN_STAT) && defined(INET6)
+ ip6stat.ip6s_pulldown_alloc++;
+#endif
+ MGET(o, M_DONTWAIT, m->m_type);
+ if (o == NULL) {
+ m_freem(m);
+ return NULL; /* ENOBUFS */
+ }
+ if (len > MHLEN) { /* use MHLEN just for safety */
+ MCLGET(o, M_DONTWAIT);
+ if ((o->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ m_free(o);
+ return NULL; /* ENOBUFS */
+ }
+ }
+ /* get hlen from <n, off> into <o, 0> */
+ o->m_len = hlen;
+ bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen);
+ n->m_len -= hlen;
+ /* get tlen from <n->m_next, 0> into <o, hlen> */
+ m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
+ o->m_len += tlen;
+ m_adj(n->m_next, tlen);
+ o->m_next = n->m_next;
+ n->m_next = o;
+ n = o;
+ off = 0;
+
+ok:
+#ifdef PULLDOWN_DEBUG
+ {
+ struct mbuf *t;
+ printf("after:");
+ for (t = m; t; t = t->m_next)
+ printf("%c%d", t == n ? '*' : ' ', t->m_len);
+ printf(" (off=%d)\n", off);
+ }
+#endif
+ if (offp)
+ *offp = off;
+ return n;
+}
diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c
index e1ea890769f..d7f6702fedd 100644
--- a/sys/kern/uipc_socket2.c
+++ b/sys/kern/uipc_socket2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_socket2.c,v 1.10 1999/02/19 15:06:52 millert Exp $ */
+/* $OpenBSD: uipc_socket2.c,v 1.11 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */
/*
@@ -778,3 +778,40 @@ sbdroprecord(sb)
} while ((m = mn) != NULL);
}
}
+
+/*
+ * Create a "control" mbuf containing the specified data
+ * with the specified type for presentation on a socket buffer.
+ */
+struct mbuf *
+sbcreatecontrol(p, size, type, level)
+ caddr_t p;
+ register int size;
+ int type, level;
+{
+ register struct cmsghdr *cp;
+ struct mbuf *m;
+
+ if (size + sizeof(*cp) > MCLBYTES) {
+ printf("sbcreatecontrol: message too large %d\n", size);
+ return NULL;
+ }
+
+ if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
+ return ((struct mbuf *) NULL);
+ if (size + sizeof(*cp) > MLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return NULL;
+ }
+ }
+ cp = mtod(m, struct cmsghdr *);
+ bcopy(p, CMSG_DATA(cp), size);
+ size += sizeof(*cp);
+ m->m_len = size;
+ cp->cmsg_len = size;
+ cp->cmsg_level = level;
+ cp->cmsg_type = type;
+ return (m);
+}
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index 555cddb2b52..f5103797f94 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_syscalls.c,v 1.28 1999/07/13 15:17:51 provos Exp $ */
+/* $OpenBSD: uipc_syscalls.c,v 1.29 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
/*
@@ -718,12 +718,25 @@ recvit(p, s, mp, namelenp, retsize)
if (len <= 0 || control == 0)
len = 0;
else {
- if (len >= control->m_len)
- len = control->m_len;
- else
- mp->msg_flags |= MSG_CTRUNC;
- error = copyout((caddr_t)mtod(control, caddr_t),
- (caddr_t)mp->msg_control, (unsigned)len);
+ struct mbuf *m = control;
+ caddr_t p = (caddr_t)mp->msg_control;
+
+ do {
+ i = m->m_len;
+ if (len < i) {
+ mp->msg_flags |= MSG_CTRUNC;
+ i = len;
+ }
+ error = copyout(mtod(m, caddr_t), p,
+ (unsigned)i);
+ if (m->m_next)
+ i = ALIGN(i);
+ p += i;
+ len -= i;
+ if (error != 0 || len <= 0)
+ break;
+ } while ((m = m->m_next) != NULL);
+ len = p - (caddr_t)mp->msg_control;
}
mp->msg_controllen = len;
}
@@ -774,10 +787,17 @@ sys_setsockopt(p, v, retval)
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
- if (SCARG(uap, valsize) > MLEN)
+ if (SCARG(uap, valsize) > MCLBYTES)
return (EINVAL);
if (SCARG(uap, val)) {
m = m_get(M_WAIT, MT_SOOPTS);
+ if (SCARG(uap, valsize) > MLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ }
if (m == NULL)
return (ENOBUFS);
error = copyin(SCARG(uap, val), mtod(m, caddr_t),
diff --git a/sys/net/if.c b/sys/net/if.c
index 2670eba4e5d..22ad590d968 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: if.c,v 1.24 1999/11/20 18:51:58 espie Exp $ */
+/* $OpenBSD: if.c,v 1.25 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej 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) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -53,6 +82,7 @@
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/radix.h>
+
#include <net/route.h>
#ifdef INET
@@ -65,6 +95,12 @@
#endif
#endif
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#endif
+
#ifdef IPFILTER
#include <netinet/ip_fil_compat.h>
#include <netinet/ip_fil.h>
@@ -85,6 +121,14 @@ int if_detach_rtdelete __P((struct radix_node *, void *));
int ifqmaxlen = IFQ_MAXLEN;
void if_slowtimo __P((void *arg));
+#ifdef INET6
+/*
+ * XXX: declare here to avoid to include many inet6 related files..
+ * should be more generalized?
+ */
+extern void nd6_setmtu __P((struct ifnet *));
+#endif
+
/*
* Network interface utility routines.
*
@@ -103,7 +147,8 @@ ifinit()
}
int if_index = 0;
-struct ifaddr **ifnet_addrs;
+struct ifaddr **ifnet_addrs = NULL;
+struct ifnet **ifindex2ifnet = NULL;
/*
* Attach an interface to the
@@ -120,16 +165,43 @@ if_attachsetup(ifp)
static int if_indexlim = 8;
ifp->if_index = ++if_index;
- if (ifnet_addrs == 0 || if_index >= if_indexlim) {
- unsigned int n = (if_indexlim <<= 1) * sizeof(ifa);
- struct ifaddr **q = (struct ifaddr **)
- malloc(n, M_IFADDR, M_WAITOK);
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ * struct ifadd **ifnet_addrs
+ * struct ifnet **ifindex2ifnet
+ */
+ if (ifnet_addrs == 0 || ifindex2ifnet == 0 || if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow ifnet_addrs */
+ n = if_indexlim * sizeof(ifa);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
if (ifnet_addrs) {
- bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
+ bcopy((caddr_t)ifnet_addrs, q, n/2);
free((caddr_t)ifnet_addrs, M_IFADDR);
}
- ifnet_addrs = q;
+ ifnet_addrs = (struct ifaddr **)q;
+
+ /* grow ifindex2ifnet */
+ n = if_indexlim * sizeof(struct ifnet *);
+ q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK);
+ bzero(q, n);
+ if (ifindex2ifnet) {
+ bcopy((caddr_t)ifindex2ifnet, q, n/2);
+ free((caddr_t)ifindex2ifnet, M_IFADDR);
+ }
+ ifindex2ifnet = (struct ifnet **)q;
}
+
+ ifindex2ifnet[if_index] = ifp;
+
/*
* create a Link Level name for this device
*/
@@ -151,7 +223,7 @@ if_attachsetup(ifp)
sdl->sdl_nlen = namelen;
sdl->sdl_index = ifp->if_index;
sdl->sdl_type = ifp->if_type;
- ifnet_addrs[if_index - 1] = ifa;
+ ifnet_addrs[if_index] = ifa;
ifa->ifa_ifp = ifp;
ifa->ifa_rtrequest = link_rtrequest;
TAILQ_INSERT_HEAD(&ifp->if_addrlist, ifa, ifa_list);
@@ -301,6 +373,8 @@ ifa_ifwithaddr(addr)
if (equal(addr, ifa->ifa_addr))
return (ifa);
if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
+ /* IP6 doesn't have broadcast */
+ ifa->ifa_broadaddr->sa_len != 0 &&
equal(ifa->ifa_broadaddr, addr))
return (ifa);
}
@@ -346,7 +420,7 @@ ifa_ifwithnet(addr)
if (af == AF_LINK) {
register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
if (sdl->sdl_index && sdl->sdl_index <= if_index)
- return (ifnet_addrs[sdl->sdl_index - 1]);
+ return (ifnet_addrs[sdl->sdl_index]);
}
for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
@@ -495,6 +569,9 @@ if_up(ifp)
pfctlinput(PRC_IFUP, ifa->ifa_addr);
#endif
rt_ifmsg(ifp);
+#ifdef INET6
+ in6_if_up(ifp);
+#endif
}
/*
@@ -555,6 +632,35 @@ ifunit(name)
return (NULL);
}
+
+/*
+ * Map interface name in a sockaddr_dl to
+ * interface structure pointer.
+ */
+struct ifnet *
+if_withname(sa)
+ struct sockaddr *sa;
+{
+ char ifname[IFNAMSIZ+1];
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+
+ if ( (sa->sa_family != AF_LINK) || (sdl->sdl_nlen == 0) ||
+ (sdl->sdl_nlen > IFNAMSIZ) )
+ return NULL;
+
+ /*
+ * ifunit wants a null-terminated name. It may not be null-terminated
+ * in the sockaddr. We don't want to change the caller's sockaddr,
+ * and there might not be room to put the trailing null anyway, so we
+ * make a local copy that we know we can null terminate safely.
+ */
+
+ bcopy(sdl->sdl_data, ifname, sdl->sdl_nlen);
+ ifname[sdl->sdl_nlen] = '\0';
+ return ifunit(ifname);
+}
+
+
/*
* Interface ioctls.
*/
@@ -567,7 +673,8 @@ ifioctl(so, cmd, data, p)
{
register struct ifnet *ifp;
register struct ifreq *ifr;
- int error;
+ int error = 0;
+ short oif_flags;
switch (cmd) {
@@ -579,6 +686,7 @@ ifioctl(so, cmd, data, p)
ifp = ifunit(ifr->ifr_name);
if (ifp == 0)
return (ENXIO);
+ oif_flags = ifp->if_flags;
switch (cmd) {
case SIOCGIFFLAGS:
@@ -628,13 +736,14 @@ ifioctl(so, cmd, data, p)
case SIOCGIFMEDIA:
if (ifp->if_ioctl == 0)
return (EOPNOTSUPP);
- return ((*ifp->if_ioctl)(ifp, cmd, data));
+ error = (*ifp->if_ioctl)(ifp, cmd, data);
+ break;
default:
if (so->so_proto == 0)
return (EOPNOTSUPP);
#if !defined(COMPAT_43) && !defined(COMPAT_LINUX) && !defined(COMPAT_SVR4)
- return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
+ error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
(struct mbuf *) cmd, (struct mbuf *) data,
(struct mbuf *) ifp));
#else
@@ -686,12 +795,22 @@ ifioctl(so, cmd, data, p)
case OSIOCGIFNETMASK:
*(u_int16_t *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
}
- return (error);
}
#endif
+ break;
}
- return (0);
+
+ if (((oif_flags ^ ifp->if_flags) & IFF_UP) != 0) {
+#ifdef INET6
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ int s = splimp();
+ in6_if_up(ifp);
+ splx(s);
+ }
+#endif
+ }
+ return (error);
}
/*
diff --git a/sys/net/if.h b/sys/net/if.h
index 2fed82f3649..9c4c29acb00 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.h,v 1.13 1999/08/08 00:43:00 niklas Exp $ */
+/* $OpenBSD: if.h,v 1.14 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $ */
/*
@@ -111,6 +111,7 @@ TAILQ_HEAD(ifnet_head, ifnet); /* the actual queue head */
* Note: this is the same size as a generic device's external name.
*/
#define IFNAMSIZ 16
+#define IF_NAMESIZE IFNAMSIZ
struct ifnet { /* and the entries */
void *if_softc; /* lower-level data for this if */
@@ -143,6 +144,7 @@ struct ifnet { /* and the entries */
int ifq_maxlen;
int ifq_drops;
} if_snd; /* output queue */
+ struct ifprefix *if_prefixlist; /* linked list of prefixes per if */
};
#define if_mtu if_data.ifi_mtu
#define if_type if_data.ifi_type
@@ -244,6 +246,20 @@ struct ifaddr {
#define IFA_ROUTE RTF_UP /* route installed */
/*
+ * The prefix structure contains information about one prefix
+ * of an interface. They are maintained by the different address families,
+ * are allocated and attached when an prefix or an address is set,
+ * and are linked together so all prfefixes for an interface can be located.
+ */
+struct ifprefix {
+ struct sockaddr *ifpr_prefix; /* prefix of interface */
+ struct ifnet *ifpr_ifp; /* back-pointer to interface */
+ struct ifprefix *ifpr_next;
+ u_char ifpr_plen; /* prefix length in bits */
+ u_char ifpr_type; /* protocol dependent prefix type */
+};
+
+/*
* Message format for use in obtaining information about interfaces
* from sysctl and the routing socket.
*/
@@ -331,16 +347,31 @@ struct ifconf {
#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */
};
+/*
+ * Structure for SIOC[AGD]LIFADDR
+ */
+struct if_laddrreq {
+ char iflr_name[IFNAMSIZ];
+ unsigned int flags;
+#define IFLR_PREFIX 0x8000 /* in: prefix given out: kernel fills id */
+ unsigned int prefixlen; /* in/out */
+ struct sockaddr_storage addr; /* in/out */
+ struct sockaddr_storage dstaddr; /* out */
+};
+
struct if_nameindex {
unsigned int if_index;
char *if_name;
};
+#ifndef _KERNEL
+__BEGIN_DECLS
unsigned int if_nametoindex __P((const char *));
char *if_indextoname __P((unsigned int, char *));
struct if_nameindex *if_nameindex __P((void));
-
-#define if_freenameindex(x) free(x)
+__END_DECLS
+#define if_freenameindex(x) free(x)
+#endif
#include <net/if_arp.h>
@@ -353,6 +384,11 @@ struct if_nameindex *if_nameindex __P((void));
}
struct ifnet_head ifnet;
+struct ifnet **ifindex2ifnet;
+#if 0
+struct ifnet loif[];
+#endif
+int if_index;
void ether_ifattach __P((struct ifnet *));
void ether_ifdetach __P((struct ifnet *));
@@ -375,6 +411,7 @@ void ifinit __P((void));
int ifioctl __P((struct socket *, u_long, caddr_t, struct proc *));
int ifpromisc __P((struct ifnet *, int));
struct ifnet *ifunit __P((char *));
+struct ifnet *if_withname __P((struct sockaddr *));
struct ifaddr *ifa_ifwithaddr __P((struct sockaddr *));
struct ifaddr *ifa_ifwithaf __P((int));
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index c22ba6d04ea..3513c322bf5 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: if_ethersubr.c,v 1.31 1999/09/01 21:38:48 jason Exp $ */
+/* $OpenBSD: if_ethersubr.c,v 1.32 1999/12/08 06:50:17 itojun Exp $ */
/* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej 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, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -79,6 +108,14 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <net/if_bridge.h>
#endif
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#endif
+
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
@@ -118,7 +155,7 @@ extern u_char aarp_org_code[ 3 ];
#include <sys/socketvar.h>
#endif
-#ifdef INET6
+#if 0 /*NRL INET6*/
#include <netinet6/in6.h>
#include <netinet6/in6_var.h>
#endif /* INET6 */
@@ -249,6 +286,18 @@ ether_output(ifp, m0, dst, rt0)
etype = htons(ETHERTYPE_IP);
break;
#endif
+#ifdef INET6
+ case AF_INET6:
+#ifndef OLDIP6OUTPUT
+ if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst))
+ return(0); /* it must be impossible, but... */
+#else
+ if (!nd6_resolve(ifp, rt, m, dst, (u_char *)edst))
+ return(0); /* if not yet resolves */
+#endif
+ etype = htons(ETHERTYPE_IPV6);
+ break;
+#endif
#ifdef NS
case AF_NS:
etype = htons(ETHERTYPE_NS);
@@ -273,7 +322,7 @@ ether_output(ifp, m0, dst, rt0)
mcopy = m_copy(m, 0, (int)M_COPYALL);
break;
#endif
-#ifdef INET6
+#if 0 /*NRL INET6*/
case AF_INET6:
/*
* The bottom line here is to either queue the outgoing packet
@@ -285,7 +334,7 @@ ether_output(ifp, m0, dst, rt0)
* If multicast dest., then use IPv6 -> Ethernet
* mcast mapping. Really simple.
*/
- ETHER_MAP_IN6_MULTICAST(((struct sockaddr_in6 *)dst)->sin6_addr,
+ ETHER_MAP_IPV6_MULTICAST(&((struct sockaddr_in6 *)dst)->sin6_addr,
edst);
} else {
/* Do unicast neighbor discovery stuff. */
@@ -583,7 +632,7 @@ decapsulate:
*/
case ETHERTYPE_IPV6:
schednetisr(NETISR_IPV6);
- inq = &ipv6intrq;
+ inq = &ip6intrq;
break;
#endif /* INET6 */
#ifdef IPX
@@ -828,9 +877,9 @@ u_char ether_ipmulticast_min[6] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 };
u_char ether_ipmulticast_max[6] = { 0x01, 0x00, 0x5e, 0x7f, 0xff, 0xff };
#ifdef INET6
-u_char ether_ipv6multicast_min[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };
-u_char ether_ipv6multicast_max[6] = { 0x33, 0x33, 0xff, 0xff, 0xff, 0xff };
-#endif /* INET6 */
+u_char ether_ip6multicast_min[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 };
+u_char ether_ip6multicast_max[6] = { 0x33, 0x33, 0xff, 0xff, 0xff, 0xff };
+#endif
/*
* Add an Ethernet multicast address or range of addresses to the list for a
@@ -877,17 +926,18 @@ ether_addmulti(ifr, ac)
#endif
#ifdef INET6
case AF_INET6:
- sin6 = (struct sockaddr_in6 *)&(ifr->ifr_addr);
+ sin6 = (struct sockaddr_in6 *)
+ &(((struct in6_ifreq *)ifr)->ifr_addr);
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
/*
* An unspecified IPv6 address means listen to all
* of the IPv6 multicast addresses on this Ethernet.
* (Multicast routers like this.)
*/
- bcopy(ether_ipv6multicast_min, addrlo, ETHER_ADDR_LEN);
- bcopy(ether_ipv6multicast_max, addrhi, ETHER_ADDR_LEN);
+ bcopy(ether_ip6multicast_min, addrlo, ETHER_ADDR_LEN);
+ bcopy(ether_ip6multicast_max, addrhi, ETHER_ADDR_LEN);
} else {
- ETHER_MAP_IN6_MULTICAST(sin6->sin6_addr, addrlo);
+ ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, addrlo);
bcopy(addrlo, addrhi, ETHER_ADDR_LEN);
}
break;
@@ -996,10 +1046,10 @@ ether_delmulti(ifr, ac)
* possibly all-routers for this interface afterwards
* is not a bad idea.)
*/
- bcopy(ether_ipv6multicast_min, addrlo, ETHER_ADDR_LEN);
- bcopy(ether_ipv6multicast_max, addrhi, ETHER_ADDR_LEN);
+ bcopy(ether_ip6multicast_min, addrlo, ETHER_ADDR_LEN);
+ bcopy(ether_ip6multicast_max, addrhi, ETHER_ADDR_LEN);
} else {
- ETHER_MAP_IN6_MULTICAST(sin6->sin6_addr, addrlo);
+ ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, addrlo);
bcopy(addrlo, addrhi, ETHER_ADDR_LEN);
}
break;
diff --git a/sys/net/if_faith.c b/sys/net/if_faith.c
new file mode 100644
index 00000000000..baadb22e398
--- /dev/null
+++ b/sys/net/if_faith.c
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+/*
+ * derived from
+ * @(#)if_loop.c 8.1 (Berkeley) 6/10/93
+ * Id: if_loop.c,v 1.22 1996/06/19 16:24:10 wollman Exp
+ */
+
+/*
+ * Loopback interface driver for protocol testing and timing.
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include "opt_inet.h"
+#endif
+
+#include "faith.h"
+#if NFAITH > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/sockio.h>
+#else
+#include <sys/ioctl.h>
+#endif
+#include <sys/time.h>
+#ifdef __bsdi__
+#include <machine/cpu.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#endif
+
+#include "bpfilter.h"
+
+#include <net/net_osdep.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+static int faithioctl __P((struct ifnet *, int, caddr_t));
+#else
+static int faithioctl __P((struct ifnet *, u_long, caddr_t));
+#endif
+int faithoutput __P((struct ifnet *, register struct mbuf *, struct sockaddr *,
+ register struct rtentry *));
+static void faithrtrequest __P((int, struct rtentry *, struct sockaddr *));
+
+#ifdef __FreeBSD__
+void faithattach __P((void *));
+PSEUDO_SET(faithattach, if_faith);
+#else
+void faithattach __P((int));
+#endif
+
+static struct ifnet faithif[NFAITH];
+
+#define FAITHMTU 1500
+
+/* ARGSUSED */
+void
+faithattach(faith)
+#ifdef __FreeBSD__
+ void *faith;
+#else
+ int faith;
+#endif
+{
+ register struct ifnet *ifp;
+ register int i;
+
+ for (i = 0; i < NFAITH; i++) {
+ ifp = &faithif[i];
+ bzero(ifp, sizeof(faithif[i]));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sprintf(ifp->if_xname, "faith%d", i);
+#else
+ ifp->if_name = "faith";
+ ifp->if_unit = i;
+#endif
+ ifp->if_mtu = FAITHMTU;
+ /* Change to BROADCAST experimentaly to announce its prefix. */
+ ifp->if_flags = /* IFF_LOOPBACK */ IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_ioctl = faithioctl;
+ ifp->if_output = faithoutput;
+ ifp->if_type = IFT_FAITH;
+ ifp->if_hdrlen = 0;
+ ifp->if_addrlen = 0;
+ if_attach(ifp);
+#if NBPFILTER > 0
+#ifdef HAVE_OLD_BPF
+ bpfattach(ifp, DLT_NULL, sizeof(u_int));
+#else
+ bpfattach(&ifp->if_bpf, ifp, DLT_NULL, sizeof(u_int));
+#endif
+#endif
+ }
+}
+
+int
+faithoutput(ifp, m, dst, rt)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ struct sockaddr *dst;
+ register struct rtentry *rt;
+{
+ int s, isr;
+ register struct ifqueue *ifq = 0;
+
+ if ((m->m_flags & M_PKTHDR) == 0)
+ panic("faithoutput no HDR");
+#if NBPFILTER > 0
+ /* BPF write needs to be handled specially */
+ if (dst->sa_family == AF_UNSPEC) {
+ dst->sa_family = *(mtod(m, int *));
+ m->m_len -= sizeof(int);
+ m->m_pkthdr.len -= sizeof(int);
+ m->m_data += sizeof(int);
+ }
+
+ if (ifp->if_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a faith header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer a to it).
+ */
+ struct mbuf m0;
+ u_int af = dst->sa_family;
+
+ m0.m_next = m;
+ m0.m_len = 4;
+ m0.m_data = (char *)&af;
+
+#ifdef HAVE_OLD_BPF
+ bpf_mtap(ifp, &m0);
+#else
+ bpf_mtap(ifp->if_bpf, &m0);
+#endif
+ }
+#endif
+
+ if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
+ m_freem(m);
+ return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
+ rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
+ }
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ switch (dst->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ifq = &ipintrq;
+ isr = NETISR_IP;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ ifq = &ip6intrq;
+ isr = NETISR_IPV6;
+ break;
+#endif
+ default:
+ m_freem(m);
+ return EAFNOSUPPORT;
+ }
+
+ /* XXX do we need more sanity checks? */
+
+ m->m_pkthdr.rcvif = ifp;
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ splx(s);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(ifq, m);
+ schednetisr(isr);
+ ifp->if_ipackets++;
+ ifp->if_ibytes += m->m_pkthdr.len;
+ splx(s);
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+faithrtrequest(cmd, rt, sa)
+ int cmd;
+ struct rtentry *rt;
+ struct sockaddr *sa;
+{
+ if (rt) {
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */
+ /*
+ * For optimal performance, the send and receive buffers
+ * should be at least twice the MTU plus a little more for
+ * overhead.
+ */
+ rt->rt_rmx.rmx_recvpipe =
+ rt->rt_rmx.rmx_sendpipe = 3 * FAITHMTU;
+ }
+}
+
+/*
+ * Process an ioctl request.
+ */
+/* ARGSUSED */
+static int
+faithioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+ int cmd;
+#else
+ u_long cmd;
+#endif
+ caddr_t data;
+{
+ register struct ifaddr *ifa;
+ register struct ifreq *ifr = (struct ifreq *)data;
+ register int error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP | IFF_RUNNING;
+ ifa = (struct ifaddr *)data;
+ ifa->ifa_rtrequest = faithrtrequest;
+ /*
+ * Everything else is done at a higher level.
+ */
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ break;
+#endif
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+#ifdef SIOCSIFMTU
+#ifndef __OpenBSD__
+ case SIOCSIFMTU:
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+#endif
+#endif
+
+ case SIOCSIFFLAGS:
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+#endif /* NFAITH > 0 */
diff --git a/sys/net/if_fddisubr.c b/sys/net/if_fddisubr.c
index 4a3f3668307..74c7c0944b7 100644
--- a/sys/net/if_fddisubr.c
+++ b/sys/net/if_fddisubr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_fddisubr.c,v 1.19 1999/07/28 20:02:41 fgsch Exp $ */
+/* $OpenBSD: if_fddisubr.c,v 1.20 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_fddisubr.c,v 1.5 1996/05/07 23:20:21 christos Exp $ */
/*
@@ -86,6 +86,14 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netipx/ipx_if.h>
#endif
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#endif
+#include <netinet6/nd6.h>
+#endif
+
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
@@ -194,6 +202,19 @@ fddi_output(ifp, m0, dst, rt0)
#endif
#ifdef INET6
case AF_INET6:
+#ifdef OLDIP6OUTPUT
+ if (!nd6_resolve(ifp, rt, m, dst, edst))
+ return (0); /* if not yet resolved */
+#else
+ if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst))
+ return 0;
+#endif
+ type = htons(ETHERTYPE_IPV6);
+ break;
+#endif
+#if 0 /*NRL IPv6*/
+#ifdef INET6
+ case AF_INET6:
/*
* The bottom line here is to either queue the outgoing packet
* in the discovery engine, or fill in edst with something
@@ -214,6 +235,7 @@ fddi_output(ifp, m0, dst, rt0)
type = htons(ETHERTYPE_IPV6);
break;
#endif /* INET6 */
+#endif
#ifdef IPX
case AF_IPX:
type = htons(ETHERTYPE_IPX);
@@ -492,7 +514,7 @@ fddi_input(ifp, fh, m)
#ifdef INET6
case ETHERTYPE_IPV6:
schednetisr(NETISR_IPV6);
- inq = &ipv6intrq;
+ inq = &ip6intrq;
break;
#endif /* INET6 */
#ifdef IPX
diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c
new file mode 100644
index 00000000000..4be073575d3
--- /dev/null
+++ b/sys/net/if_gif.c
@@ -0,0 +1,552 @@
+/* $OpenBSD: if_gif.c,v 1.1 1999/12/08 06:50:18 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.
+ */
+
+/*
+ * gif.c
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/malloc.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#if defined(__FreeBSD__) || __FreeBSD__ >= 3
+/*nothing*/
+#else
+#include <sys/ioctl.h>
+#endif
+#include <sys/time.h>
+#include <sys/syslog.h>
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/in_gif.h>
+#endif /* INET */
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_gif.h>
+#endif /* INET6 */
+
+#include <net/if_gif.h>
+
+#include "gif.h"
+#include "bpfilter.h"
+
+#include <net/net_osdep.h>
+
+#if NGIF > 0
+
+#ifdef __FreeBSD__
+void gifattach __P((void *));
+#else
+void gifattach __P((int));
+#endif
+
+/*
+ * gif global variable definitions
+ */
+int ngif = NGIF; /* number of interfaces */
+struct gif_softc *gif = 0;
+
+void
+gifattach(dummy)
+#ifdef __FreeBSD__
+ void *dummy;
+#else
+ int dummy;
+#endif
+{
+ register struct gif_softc *sc;
+ register int i;
+
+ gif = sc = malloc (ngif * sizeof(struct gif_softc), M_DEVBUF, M_WAIT);
+ bzero(sc, ngif * sizeof(struct gif_softc));
+ for (i = 0; i < ngif; sc++, i++) {
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sprintf(sc->gif_if.if_xname, "gif%d", i);
+#else
+ sc->gif_if.if_name = "gif";
+ sc->gif_if.if_unit = i;
+#endif
+ sc->gif_if.if_mtu = GIF_MTU;
+ sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
+ sc->gif_if.if_ioctl = gif_ioctl;
+ sc->gif_if.if_output = gif_output;
+ sc->gif_if.if_type = IFT_GIF;
+ if_attach(&sc->gif_if);
+#if NBPFILTER > 0
+#ifdef HAVE_OLD_BPF
+ bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
+#else
+ bpfattach(&sc->gif_if.if_bpf, &sc->gif_if, DLT_NULL, sizeof(u_int));
+#endif
+#endif
+ }
+}
+
+#ifdef __FreeBSD__
+PSEUDO_SET(gifattach, if_gif);
+#endif
+
+int
+gif_output(ifp, m, dst, rt)
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct sockaddr *dst;
+ struct rtentry *rt; /* added in net2 */
+{
+ register struct gif_softc *sc = (struct gif_softc*)ifp;
+ int error = 0;
+ static int called = 0; /* XXX: MUTEX */
+ int calllimit = 10; /* XXX: adhoc */
+
+ /*
+ * gif may cause infinite recursion calls when misconfigured.
+ * We'll prevent this by introducing upper limit.
+ * XXX: this mechanism may introduce another problem about
+ * mutual exclusion of the variable CALLED, especially if we
+ * use kernel thread.
+ */
+ if (++called >= calllimit) {
+ log(LOG_NOTICE,
+ "gif_output: recursively called too many times(%d)\n",
+ called);
+ m_freem(m);
+ error = EIO; /* is there better errno? */
+ goto end;
+ }
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ getmicrotime(&ifp->if_lastchange);
+#else
+ ifp->if_lastchange = time;
+#endif
+ m->m_flags &= ~(M_BCAST|M_MCAST);
+ if (!(ifp->if_flags & IFF_UP) ||
+#if 0
+ sc->gif_flags & GIFF_INUSE ||
+#endif
+ sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+ m_freem(m);
+ error = ENETDOWN;
+ goto end;
+ }
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer a to it).
+ */
+ struct mbuf m0;
+ u_int af = dst->sa_family;
+
+ m0.m_next = m;
+ m0.m_len = 4;
+ m0.m_data = (char *)&af;
+
+#ifdef HAVE_OLD_BPF
+ bpf_mtap(ifp, &m0);
+#else
+ bpf_mtap(ifp->if_bpf, &m0);
+#endif
+ }
+#endif
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+#if 0
+ s = splnet();
+ sc->gif_flags |= GIFF_INUSE;
+#endif
+
+ switch (sc->gif_psrc->sa_family) {
+#ifdef INET
+ case AF_INET:
+ error = in_gif_output(ifp, dst->sa_family, m, rt);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ error = in6_gif_output(ifp, dst->sa_family, m, rt);
+ break;
+#endif
+ default:
+ m_freem(m);
+ error = ENETDOWN;
+ }
+#if 0
+ sc->gif_flags &= ~GIFF_INUSE;
+ splx(s);
+#endif
+
+ end:
+ called = 0; /* reset recursion counter */
+ if (error) ifp->if_oerrors++;
+ return error;
+}
+
+void
+gif_input(m, af, gifp)
+ struct mbuf *m;
+ int af;
+ struct ifnet *gifp;
+{
+ int s, isr;
+ register struct ifqueue *ifq = 0;
+
+ if (gifp == NULL) {
+ /* just in case */
+ m_freem(m);
+ return;
+ }
+
+ if (m->m_pkthdr.rcvif)
+ m->m_pkthdr.rcvif = gifp;
+
+#if NBPFILTER > 0
+ if (gifp->if_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer a to it).
+ */
+ struct mbuf m0;
+ u_int af = AF_INET6;
+
+ m0.m_next = m;
+ m0.m_len = 4;
+ m0.m_data = (char *)&af;
+
+#ifdef HAVE_OLD_BPF
+ bpf_mtap(gifp, &m0);
+#else
+ bpf_mtap(gifp->if_bpf, &m0);
+#endif
+ }
+#endif /*NBPFILTER > 0*/
+
+ /*
+ * Put the packet to the network layer input queue according to the
+ * specified address family.
+ * Note: older versions of gif_input directly called network layer
+ * input functions, e.g. ip6_input, here. We changed the policy to
+ * prevent too many recursive calls of such input functions, which
+ * might cause kernel panic. But the change may introduce another
+ * problem; if the input queue is full, packets are discarded.
+ * We believed it rarely occurs and changed the policy. If we find
+ * it occurs more times than we thought, we may change the policy
+ * again.
+ */
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ ifq = &ipintrq;
+ isr = NETISR_IP;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ ifq = &ip6intrq;
+ isr = NETISR_IPV6;
+ break;
+#endif
+ default:
+ m_freem(m);
+ return;
+ }
+
+ s = splimp();
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq); /* update statistics */
+ m_freem(m);
+ splx(s);
+ return;
+ }
+ IF_ENQUEUE(ifq, m);
+ /* we need schednetisr since the address family may change */
+ schednetisr(isr);
+ gifp->if_ipackets++;
+ gifp->if_ibytes += m->m_pkthdr.len;
+ splx(s);
+
+ return;
+}
+
+
+int
+gif_ioctl(ifp, cmd, data)
+ struct ifnet *ifp;
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+ int cmd;
+#else
+ u_long cmd;
+#endif
+ caddr_t data;
+{
+ struct gif_softc *sc = (struct gif_softc*)ifp;
+ struct ifreq *ifr = (struct ifreq*)data;
+ int error = 0, size;
+ struct sockaddr *sa, *dst, *src;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ break;
+
+ case SIOCSIFDSTADDR:
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ switch (ifr->ifr_addr.sa_family) {
+#ifdef INET
+ case AF_INET: /* IP supports Multicast */
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6: /* IP6 supports Multicast */
+ break;
+#endif /* INET6 */
+ default: /* Other protocols doesn't support Multicast */
+ error = EAFNOSUPPORT;
+ break;
+ }
+#endif /*not FreeBSD3*/
+ break;
+
+#ifdef SIOCSIFMTU /* xxx */
+#ifndef __OpenBSD__
+ case SIOCGIFMTU:
+ break;
+ case SIOCSIFMTU:
+ {
+#ifdef __bsdi__
+ short mtu;
+ mtu = *(short *)ifr->ifr_data;
+#else
+ u_long mtu;
+ mtu = ifr->ifr_mtu;
+#endif
+ if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
+ return (EINVAL);
+ }
+ ifp->if_mtu = mtu;
+ }
+ break;
+#endif
+#endif /* SIOCSIFMTU */
+
+ case SIOCSIFPHYADDR:
+#ifdef INET6
+ case SIOCSIFPHYADDR_IN6:
+#endif /* INET6 */
+ switch (ifr->ifr_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ src = (struct sockaddr *)
+ &(((struct in_aliasreq *)data)->ifra_addr);
+ dst = (struct sockaddr *)
+ &(((struct in_aliasreq *)data)->ifra_dstaddr);
+
+ /* only one gif can have dst = INADDR_ANY */
+#define satosaddr(sa) (((struct sockaddr_in *)(sa))->sin_addr.s_addr)
+
+ if (satosaddr(dst) == INADDR_ANY) {
+ int i;
+ struct gif_softc *sc2;
+
+ for (i = 0, sc2 = gif; i < ngif; i++, sc2++) {
+ if (sc2 == sc) continue;
+ if (sc2->gif_pdst &&
+ satosaddr(sc2->gif_pdst)
+ == INADDR_ANY) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ }
+ }
+ size = sizeof(struct sockaddr_in);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ src = (struct sockaddr *)
+ &(((struct in6_aliasreq *)data)->ifra_addr);
+ dst = (struct sockaddr *)
+ &(((struct in6_aliasreq *)data)->ifra_dstaddr);
+
+ /* only one gif can have dst = in6addr_any */
+#define satoin6(sa) (&((struct sockaddr_in6 *)(sa))->sin6_addr)
+
+ if (IN6_IS_ADDR_UNSPECIFIED(satoin6(dst))) {
+ int i;
+ struct gif_softc *sc2;
+
+ for (i = 0, sc2 = gif; i < ngif; i++, sc2++) {
+ if (sc2 == sc) continue;
+ if (sc2->gif_pdst &&
+ IN6_IS_ADDR_UNSPECIFIED(
+ satoin6(sc2->gif_pdst)
+ )) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ }
+ }
+ size = sizeof(struct sockaddr_in6);
+ break;
+#endif /* INET6 */
+ default:
+ error = EPROTOTYPE;
+ goto bad;
+ break;
+ }
+ if (sc->gif_psrc != NULL)
+ free((caddr_t)sc->gif_psrc, M_IFADDR);
+ if (sc->gif_pdst != NULL)
+ free((caddr_t)sc->gif_pdst, M_IFADDR);
+
+ sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK);
+ bzero((caddr_t)sa, size);
+ bcopy((caddr_t)src, (caddr_t)sa, size);
+ sc->gif_psrc = sa;
+
+ sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK);
+ bzero((caddr_t)sa, size);
+ bcopy((caddr_t)dst, (caddr_t)sa, size);
+ sc->gif_pdst = sa;
+
+ ifp->if_flags |= (IFF_UP|IFF_RUNNING);
+ if_up(ifp); /* send up RTM_IFINFO */
+
+ break;
+
+ case SIOCGIFPSRCADDR:
+#ifdef INET6
+ case SIOCGIFPSRCADDR_IN6:
+#endif /* INET6 */
+ if (sc->gif_psrc == NULL) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ src = sc->gif_psrc;
+ switch (sc->gif_psrc->sa_family) {
+#ifdef INET
+ case AF_INET:
+ dst = &ifr->ifr_addr;
+ size = sizeof(struct sockaddr_in);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ dst = (struct sockaddr *)
+ &(((struct in6_ifreq *)data)->ifr_addr);
+ size = sizeof(struct sockaddr_in6);
+ break;
+#endif /* INET6 */
+ default:
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ bcopy((caddr_t)src, (caddr_t)dst, size);
+ break;
+
+ case SIOCGIFPDSTADDR:
+#ifdef INET6
+ case SIOCGIFPDSTADDR_IN6:
+#endif /* INET6 */
+ if (sc->gif_pdst == NULL) {
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ src = sc->gif_pdst;
+ switch (sc->gif_pdst->sa_family) {
+#ifdef INET
+ case AF_INET:
+ dst = &ifr->ifr_addr;
+ size = sizeof(struct sockaddr_in);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ dst = (struct sockaddr *)
+ &(((struct in6_ifreq *)data)->ifr_addr);
+ size = sizeof(struct sockaddr_in6);
+ break;
+#endif /* INET6 */
+ default:
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+ bcopy((caddr_t)src, (caddr_t)dst, size);
+ break;
+
+ case SIOCSIFFLAGS:
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ bad:
+ return error;
+}
+#endif /*NGIF > 0*/
diff --git a/sys/net/if_gif.h b/sys/net/if_gif.h
new file mode 100644
index 00000000000..1b4307d3642
--- /dev/null
+++ b/sys/net/if_gif.h
@@ -0,0 +1,86 @@
+/* $OpenBSD: if_gif.h,v 1.1 1999/12/08 06:50:18 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.
+ */
+
+/*
+ * if_gif.h
+ */
+
+#ifndef _NET_IF_GIF_H_
+#define _NET_IF_GIF_H_
+
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#if defined(_KERNEL) && !defined(_LKM)
+#include "opt_inet.h"
+#endif
+#endif
+
+#include <netinet/in.h>
+/* xxx sigh, why route have struct route instead of pointer? */
+
+struct gif_softc {
+ struct ifnet gif_if; /* common area */
+ struct sockaddr *gif_psrc; /* Physical src addr */
+ struct sockaddr *gif_pdst; /* Physical dst addr */
+ union {
+ struct route gifscr_ro; /* xxx */
+#ifdef INET6
+ struct route_in6 gifscr_ro6; /* xxx */
+#endif
+ } gifsc_gifscr;
+ int gif_flags;
+};
+
+#define gif_ro gifsc_gifscr.gifscr_ro
+#ifdef INET6
+#define gif_ro6 gifsc_gifscr.gifscr_ro6
+#endif
+
+#define GIFF_INUSE 0x1 /* gif is in use */
+
+#define GIF_MTU (1280) /* Default MTU */
+#define GIF_MTU_MIN (1280) /* Minimum MTU */
+#define GIF_MTU_MAX (8192) /* Maximum MTU */
+
+extern int ngif;
+extern struct gif_softc *gif;
+
+/* Prototypes */
+void gif_input __P((struct mbuf *, int, struct ifnet *));
+int gif_output __P((struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *));
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+int gif_ioctl __P((struct ifnet *, int, caddr_t));
+#else
+int gif_ioctl __P((struct ifnet *, u_long, caddr_t));
+#endif
+
+#endif /* _NET_IF_GIF_H_ */
diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c
index 57dbd3fe733..46eb07ec1fb 100644
--- a/sys/net/if_loop.c
+++ b/sys/net/if_loop.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: if_loop.c,v 1.11 1999/01/08 00:56:45 deraadt Exp $ */
+/* $OpenBSD: if_loop.c,v 1.12 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_loop.c,v 1.15 1996/05/07 02:40:33 thorpej 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.
*
@@ -78,6 +107,14 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/ip.h>
#endif
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#endif
+
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
@@ -103,13 +140,12 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <net/bpf.h>
#endif
-#ifdef INET6
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#endif /* INET6 */
-
-#define LOMTU (32768)
-
+#if defined(LARGE_LOMTU)
+#define LOMTU (131072 + MHLEN + MLEN)
+#else
+#define LOMTU (32768 + MHLEN + MLEN)
+#endif
+
struct ifnet loif[NLOOP];
void
@@ -181,6 +217,55 @@ looutput(ifp, m, dst, rt)
return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
}
+
+#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->m_next != NULL) {
+ struct mbuf *n;
+
+ MGETHDR(n, M_DONTWAIT, MT_HEADER);
+ if (n) {
+ MCLGET(n, M_DONTWAIT);
+ if ((n->m_flags & M_EXT) == 0) {
+ m_free(n);
+ n = NULL;
+ }
+ }
+ if (!n) {
+ printf("looutput: mbuf allocation failed\n");
+ m_freem(m);
+ return ENOBUFS;
+ }
+
+ n->m_pkthdr.rcvif = m->m_pkthdr.rcvif;
+ n->m_pkthdr.len = m->m_pkthdr.len;
+ if (m->m_pkthdr.len <= MCLBYTES) {
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t));
+ n->m_len = m->m_pkthdr.len;
+ m_freem(m);
+ } else {
+ m_copydata(m, 0, MCLBYTES, mtod(n, caddr_t));
+ m_adj(m, MCLBYTES);
+ n->m_len = MCLBYTES;
+ n->m_next = m;
+ m->m_flags &= ~M_PKTHDR;
+ }
+ m = n;
+ }
+#if 0
+ if (m && m->m_next != NULL) {
+ printf("loop: not contiguous...\n");
+ m_freem(m);
+ return ENOBUFS;
+ }
+#endif
+#endif
+
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
switch (dst->sa_family) {
@@ -193,7 +278,7 @@ looutput(ifp, m, dst, rt)
#endif
#ifdef INET6
case AF_INET6:
- ifq = &ipv6intrq;
+ ifq = &ip6intrq;
isr = NETISR_IPV6;
break;
#endif /* INET6 */
@@ -273,7 +358,7 @@ loioctl(ifp, cmd, data)
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
ifa = (struct ifaddr *)data;
- if (ifa != 0 && ifa->ifa_addr->sa_family == AF_ISO)
+ if (ifa != 0 /*&& ifa->ifa_addr->sa_family == AF_ISO*/)
ifa->ifa_rtrequest = lortrequest;
/*
* Everything else is done at a higher level.
@@ -293,7 +378,6 @@ loioctl(ifp, cmd, data)
case AF_INET:
break;
#endif
-
#ifdef INET6
case AF_INET6:
break;
diff --git a/sys/net/if_ppp.c b/sys/net/if_ppp.c
index 5aa095a7122..32d2aa9debf 100644
--- a/sys/net/if_ppp.c
+++ b/sys/net/if_ppp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_ppp.c,v 1.14 1998/07/12 04:33:20 angelos Exp $ */
+/* $OpenBSD: if_ppp.c,v 1.15 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_ppp.c,v 1.39 1997/05/17 21:11:59 christos Exp $ */
/*
@@ -102,6 +102,12 @@
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
+#else
+#ifdef _KERNEL
+#ifdef VJC
+#error ppp device with VJC assumes INET
+#endif
+#endif
#endif
#include "bpfilter.h"
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c
index 54965c3f782..05d51acf141 100644
--- a/sys/net/if_tun.c
+++ b/sys/net/if_tun.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_tun.c,v 1.27 1999/09/29 04:30:39 deraadt Exp $ */
+/* $OpenBSD: if_tun.c,v 1.28 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_tun.c,v 1.24 1996/05/07 02:40:48 thorpej Exp $ */
/*
@@ -221,11 +221,13 @@ tunclose(dev, flag, mode, p)
register struct ifaddr *ifa;
for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
ifa = ifa->ifa_list.tqe_next) {
+#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET) {
rtinit(ifa, (int)RTM_DELETE,
(tp->tun_flags & TUN_DSTADDR)?
RTF_HOST : 0);
}
+#endif
}
}
splx(s);
@@ -251,6 +253,7 @@ tuninit(tp)
tp->tun_flags &= ~(TUN_IASET|TUN_DSTADDR|TUN_BRDADDR);
for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
ifa = ifa->ifa_list.tqe_next) {
+#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *sin;
@@ -272,6 +275,7 @@ tuninit(tp)
} else
tp->tun_flags &= ~TUN_BRDADDR;
}
+#endif
}
return 0;
@@ -314,6 +318,8 @@ tun_ioctl(ifp, cmd, data)
((struct tun_softc *)(ifp->if_softc))->tun_if.if_mtu;
break;
#endif
+ case SIOCSIFFLAGS:
+ break;
default:
error = EINVAL;
}
@@ -612,6 +618,12 @@ tunwrite(dev, uio, ioflag)
isr = NETISR_IP;
break;
#endif
+#ifdef INET6
+ case AF_INET6:
+ ifq = &ip6intrq;
+ isr = NETISR_IPV6;
+ break;
+#endif
#ifdef NS
case AF_NS:
ifq = &nsintrq;
diff --git a/sys/net/if_types.h b/sys/net/if_types.h
index 0ef92cfbf7e..01361c1f727 100644
--- a/sys/net/if_types.h
+++ b/sys/net/if_types.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_types.h,v 1.3 1997/02/24 13:34:02 niklas Exp $ */
+/* $OpenBSD: if_types.h,v 1.4 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_types.h,v 1.7 1995/02/27 09:10:24 glass Exp $ */
/*
@@ -97,3 +97,9 @@
#define IFT_PROPVIRTUAL 0x35 /* Proprietary Virtual/internal */
#define IFT_PROPMUX 0x36 /* Proprietary Multiplexing */
#define IFT_ENC 0x37 /* Encapsulation */
+
+/* private usage... how should we define these? */
+#define IFT_GIF 0xf0
+#define IFT_DUMMY 0xf1
+#define IFT_PVC 0xf2
+#define IFT_FAITH 0xf3
diff --git a/sys/net/net_osdep.c b/sys/net/net_osdep.c
new file mode 100644
index 00000000000..55822ed49f3
--- /dev/null
+++ b/sys/net/net_osdep.c
@@ -0,0 +1,87 @@
+/* $OpenBSD: net_osdep.c,v 1.1 1999/12/08 06:50:18 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/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#if !defined(__FreeBSD__) || __FreeBSD__ < 3
+#include <sys/ioctl.h>
+#endif
+#include <sys/time.h>
+#include <sys/syslog.h>
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/bpf.h>
+
+#if 0
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/in_gif.h>
+#endif /* INET */
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_gif.h>
+#include <netinet6/in6_ifattach.h>
+#endif /* INET6 */
+#endif
+
+#if !(defined(__NetBSD__) || defined(__OpenBSD__))
+const char *
+if_name(ifp)
+ struct ifnet *ifp;
+{
+ static char nam[IFNAMSIZ + 10]; /*enough?*/
+
+#ifdef __bsdi__
+ sprintf(nam, "%s%d", ifp->if_name, ifp->if_unit);
+#else
+ snprintf(nam, sizeof(nam), "%s%d", ifp->if_name, ifp->if_unit);
+#endif
+ return nam;
+}
+#endif
diff --git a/sys/net/net_osdep.h b/sys/net/net_osdep.h
new file mode 100644
index 00000000000..99a5b7ded1c
--- /dev/null
+++ b/sys/net/net_osdep.h
@@ -0,0 +1,149 @@
+/* $OpenBSD: net_osdep.h,v 1.1 1999/12/08 06:50:18 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.
+ */
+/*
+ * glue for kernel code programming differences.
+ */
+
+/*
+ * OS dependencies:
+ *
+ * - privileged process
+ * NetBSD, FreeBSD 3
+ * struct proc *p;
+ * if (p && !suser(p->p_ucred, &p->p_acflag))
+ * privileged;
+ * OpenBSD, BSDI [34], FreeBSD 2
+ * struct socket *so;
+ * if (so->so_state & SS_PRIV)
+ * privileged;
+ * - foo_control
+ * NetBSD, FreeBSD 3
+ * needs to give struct proc * as argument
+ * OpenBSD, BSDI [34], FreeBSD 2
+ * do not need struct proc *
+ * - bpf:
+ * OpenBSD, NetBSD, BSDI [34]
+ * need caddr_t * (= if_bpf **) and struct ifnet *
+ * FreeBSD 2, FreeBSD 3
+ * need only struct ifnet * as argument
+ * - struct ifnet
+ * use queue.h? member names if name
+ * --- --- ---
+ * FreeBSD 2 no old standard if_name+unit
+ * FreeBSD 3 yes strange if_name+unit
+ * OpenBSD yes standard if_xname
+ * NetBSD yes standard if_xname
+ * BSDI [34] no old standard if_name+unit
+ * - usrreq
+ * NetBSD, OpenBSD, BSDI [34], FreeBSD 2
+ * single function with PRU_xx, arguments are mbuf
+ * FreeBSD 3
+ * separates functions, non-mbuf arguments
+ * - {set,get}sockopt
+ * NetBSD, OpenBSD, BSDI [34], FreeBSD 2
+ * manipulation based on mbuf
+ * FreeBSD 3
+ * non-mbuf manipulation using sooptcopy{in,out}()
+ * - timeout() and untimeout()
+ * NetBSD, OpenBSD, BSDI [34], FreeBSD 2
+ * timeout() is a void function
+ * FreeBSD 3
+ * timeout() is non-void, must keep returned value for untimeuot()
+ * - sysctl
+ * NetBSD, OpenBSD
+ * foo_sysctl()
+ * BSDI [34]
+ * foo_sysctl() but with different style
+ * FreeBSD 2, FreeBSD 3
+ * linker hack
+ *
+ * - if_ioctl
+ * NetBSD, FreeBSD 3, BSDI [34]
+ * 2nd argument is u_long cmd
+ * FreeBSD 2
+ * 2nd argument is int cmd
+ * - if attach routines
+ * NetBSD
+ * void xxattach(int);
+ * FreeBSD 2, FreeBSD 3
+ * void xxattach(void *);
+ * PSEUDO_SET(xxattach, if_xx);
+ *
+ * - ovbcopy()
+ * in NetBSD 1.4 or later, ovbcopy() is not supplied in the kernel.
+ * bcopy() is safe against overwrites.
+ * - splnet()
+ * NetBSD 1.4 or later requires splsoftnet().
+ * other operating systems use splnet().
+ *
+ * - dtom()
+ * NEVER USE IT!
+ *
+ * - struct ifnet for loopback interface
+ * BSDI3: struct ifnet loif;
+ * BSDI4: struct ifnet *loifp;
+ * NetBSD, OpenBSD, FreeBSD2: struct ifnet loif[NLOOP];
+ *
+ * odd thing is that many of them refers loif as ifnet *loif,
+ * not loif[NLOOP], from outside of if_loop.c.
+ */
+
+#ifndef __NET_NET_OSDEP_H_DEFINED_
+#define __NET_NET_OSDEP_H_DEFINED_
+#ifdef _KERNEL
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define if_name(ifp) ((ifp)->if_xname)
+#else
+struct ifnet;
+extern char *if_name __P((struct ifnet *));
+#endif
+
+#ifdef __FreeBSD__
+#define HAVE_OLD_BPF
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#define ifa_list ifa_link
+#define if_addrlist if_addrhead
+#define if_list if_link
+#endif
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 104000000
+#define ovbcopy bcopy
+#endif
+
+#if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#define HAVE_NRL_INPCB
+#endif
+
+#endif /*_KERNEL*/
+#endif /*__NET_NET_OSDEP_H_DEFINED_ */
diff --git a/sys/net/ppp_defs.h b/sys/net/ppp_defs.h
index cd075e14020..e3a435e3e01 100644
--- a/sys/net/ppp_defs.h
+++ b/sys/net/ppp_defs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ppp_defs.h,v 1.9 1999/02/11 19:52:11 mickey Exp $ */
+/* $OpenBSD: ppp_defs.h,v 1.10 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: ppp_defs.h,v 1.1 1995/07/04 06:28:26 paulus Exp $ */
/*
@@ -65,6 +65,7 @@
#define PPP_IPCP 0x8021 /* IP Control Protocol */
#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
#define PPP_IPXCP 0x802b /* IPX Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
#define PPP_CCP 0x80fd /* Compression Control Protocol */
#define PPP_LCP 0xc021 /* Link Control Protocol */
#define PPP_PAP 0xc023 /* Password Authentication Protocol */
diff --git a/sys/net/route.c b/sys/net/route.c
index 6602c31f2fa..23fab89eea2 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: route.c,v 1.15 1999/09/13 22:33:51 niklas Exp $ */
+/* $OpenBSD: route.c,v 1.16 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos 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) 1980, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -57,6 +86,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/ioctl.h>
+#include <sys/kernel.h>
#include <net/if.h>
#include <net/route.h>
@@ -250,6 +280,7 @@ rtfree(rt)
printf("rtfree: %p not freed (neg refs)\n", rt);
return;
}
+ rt_timer_remove_all(rt);
ifa = rt->rt_ifa;
if (ifa)
IFAFREE(ifa);
@@ -496,12 +527,18 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
case RTM_ADD:
if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == NULL)
senderr(ENETUNREACH);
+
+ /* The interface found in the previous statement may
+ * be overridden later by rt_setif. See the code
+ * for case RTM_ADD in rtsock.c:route_output.
+ */
makeroute:
R_Malloc(rt, struct rtentry *, sizeof(*rt));
if (rt == NULL)
senderr(ENOBUFS);
Bzero(rt, sizeof(*rt));
rt->rt_flags = RTF_UP | flags;
+ LIST_INIT(&rt->rt_timer);
if (rt_setgate(rt, dst, gateway)) {
Free(rt);
senderr(ENOBUFS);
@@ -511,6 +548,9 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
rt_maskedcopy(dst, ndst, netmask);
} else
Bcopy(dst, ndst, dst->sa_len);
+if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { /* XXX */
+ rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu;
+}
rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask,
rnh, rt->rt_nodes);
if (rn == NULL) {
@@ -646,7 +686,9 @@ rtinit(ifa, cmd, flags)
dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
if (cmd == RTM_DELETE) {
if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
- m = m_get(M_WAIT, MT_SONAME);
+ m = m_get(M_DONTWAIT, MT_SONAME);
+ if (m == NULL)
+ return(ENOBUFS);
deldst = mtod(m, struct sockaddr *);
rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
dst = deldst;
@@ -689,6 +731,7 @@ rtinit(ifa, cmd, flags)
IFAFREE(rt->rt_ifa);
rt->rt_ifa = ifa;
rt->rt_ifp = ifa->ifa_ifp;
+ rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu; /*XXX*/
ifa->ifa_refcnt++;
if (ifa->ifa_rtrequest)
ifa->ifa_rtrequest(RTM_ADD, rt, SA(NULL));
@@ -697,3 +740,204 @@ rtinit(ifa, cmd, flags)
}
return (error);
}
+
+/*
+ * Route timer routines. These routes allow functions to be called
+ * for various routes at any time. This is useful in supporting
+ * path MTU discovery and redirect route deletion.
+ *
+ * This is similar to some BSDI internal functions, but it provides
+ * for multiple queues for efficiency's sake...
+ */
+
+LIST_HEAD(, rttimer_queue) rttimer_queue_head;
+static int rt_init_done = 0;
+
+#define RTTIMER_CALLOUT(r) { \
+ if (r->rtt_func != NULL) { \
+ (*r->rtt_func)(r->rtt_rt, r); \
+ } else { \
+ rtrequest((int) RTM_DELETE, \
+ (struct sockaddr *)rt_key(r->rtt_rt), \
+ 0, 0, 0, 0); \
+ } \
+}
+
+/*
+ * Some subtle order problems with domain initialization mean that
+ * we cannot count on this being run from rt_init before various
+ * protocol initializations are done. Therefore, we make sure
+ * that this is run when the first queue is added...
+ */
+
+void
+rt_timer_init()
+{
+ assert(rt_init_done == 0);
+
+#if 0
+ pool_init(&rttimer_pool, sizeof(struct rttimer), 0, 0, 0, "rttmrpl",
+ 0, NULL, NULL, M_RTABLE);
+#endif
+
+ LIST_INIT(&rttimer_queue_head);
+ timeout(rt_timer_timer, NULL, hz); /* every second */
+ rt_init_done = 1;
+}
+
+struct rttimer_queue *
+rt_timer_queue_create(timeout)
+ u_int timeout;
+{
+ struct rttimer_queue *rtq;
+
+ if (rt_init_done == 0)
+ rt_timer_init();
+
+ R_Malloc(rtq, struct rttimer_queue *, sizeof *rtq);
+ if (rtq == NULL)
+ return (NULL);
+
+ rtq->rtq_timeout = timeout;
+ TAILQ_INIT(&rtq->rtq_head);
+ LIST_INSERT_HEAD(&rttimer_queue_head, rtq, rtq_link);
+
+ return (rtq);
+}
+
+void
+rt_timer_queue_change(rtq, timeout)
+ struct rttimer_queue *rtq;
+ long timeout;
+{
+
+ rtq->rtq_timeout = timeout;
+}
+
+
+void
+rt_timer_queue_destroy(rtq, destroy)
+ struct rttimer_queue *rtq;
+ int destroy;
+{
+ struct rttimer *r;
+
+ while ((r = TAILQ_FIRST(&rtq->rtq_head)) != NULL) {
+ LIST_REMOVE(r, rtt_link);
+ TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
+ if (destroy)
+ RTTIMER_CALLOUT(r);
+#if 0
+ pool_put(&rttimer_pool, r);
+#else
+ free(r, M_RTABLE);
+#endif
+ }
+
+ LIST_REMOVE(rtq, rtq_link);
+
+ /*
+ * Caller is responsible for freeing the rttimer_queue structure.
+ */
+}
+
+void
+rt_timer_remove_all(rt)
+ struct rtentry *rt;
+{
+ struct rttimer *r;
+
+ while ((r = LIST_FIRST(&rt->rt_timer)) != NULL) {
+ LIST_REMOVE(r, rtt_link);
+ TAILQ_REMOVE(&r->rtt_queue->rtq_head, r, rtt_next);
+#if 0
+ pool_put(&rttimer_pool, r);
+#else
+ free(r, M_RTABLE);
+#endif
+ }
+}
+
+int
+rt_timer_add(rt, func, queue)
+ struct rtentry *rt;
+ void(*func) __P((struct rtentry *, struct rttimer *));
+ struct rttimer_queue *queue;
+{
+ struct rttimer *r;
+ long current_time;
+ int s;
+
+ s = splclock();
+ current_time = mono_time.tv_sec;
+ splx(s);
+
+ /*
+ * If there's already a timer with this action, destroy it before
+ * we add a new one.
+ */
+ for (r = LIST_FIRST(&rt->rt_timer); r != NULL;
+ r = LIST_NEXT(r, rtt_link)) {
+ if (r->rtt_func == func) {
+ LIST_REMOVE(r, rtt_link);
+ TAILQ_REMOVE(&r->rtt_queue->rtq_head, r, rtt_next);
+#if 0
+ pool_put(&rttimer_pool, r);
+#else
+ free(r, M_RTABLE);
+#endif
+ break; /* only one per list, so we can quit... */
+ }
+ }
+
+#if 0
+ r = pool_get(&rttimer_pool, PR_NOWAIT);
+#else
+ r = (struct rttimer *)malloc(sizeof(*r), M_RTABLE, M_NOWAIT);
+#endif
+ if (r == NULL)
+ return (ENOBUFS);
+
+ r->rtt_rt = rt;
+ r->rtt_time = current_time;
+ r->rtt_func = func;
+ r->rtt_queue = queue;
+ LIST_INSERT_HEAD(&rt->rt_timer, r, rtt_link);
+ TAILQ_INSERT_TAIL(&queue->rtq_head, r, rtt_next);
+
+ return (0);
+}
+
+/* ARGSUSED */
+void
+rt_timer_timer(arg)
+ void *arg;
+{
+ struct rttimer_queue *rtq;
+ struct rttimer *r;
+ long current_time;
+ int s;
+
+ s = splclock();
+ current_time = mono_time.tv_sec;
+ splx(s);
+
+ s = splsoftnet();
+ for (rtq = LIST_FIRST(&rttimer_queue_head); rtq != NULL;
+ rtq = LIST_NEXT(rtq, rtq_link)) {
+ while ((r = TAILQ_FIRST(&rtq->rtq_head)) != NULL &&
+ (r->rtt_time + rtq->rtq_timeout) < current_time) {
+ LIST_REMOVE(r, rtt_link);
+ TAILQ_REMOVE(&rtq->rtq_head, r, rtt_next);
+ RTTIMER_CALLOUT(r);
+#if 0
+ pool_put(&rttimer_pool, r);
+#else
+ free(r, M_RTABLE);
+#endif
+ }
+ }
+ splx(s);
+
+ timeout(rt_timer_timer, NULL, hz); /* every second */
+}
diff --git a/sys/net/route.h b/sys/net/route.h
index cd880ad239b..2165d8330f6 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: route.h,v 1.6 1999/05/16 00:34:40 ho Exp $ */
+/* $OpenBSD: route.h,v 1.7 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */
/*
@@ -36,6 +36,8 @@
* @(#)route.h 8.3 (Berkeley) 4/19/94
*/
+#include <sys/queue.h>
+
/*
* Kernel resident routing tables.
*
@@ -105,6 +107,7 @@ struct rtentry {
struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols */
struct rtentry *rt_gwroute; /* implied entry for gatewayed routes */
struct rtentry *rt_parent; /* If cloned, parent of this route. */
+ LIST_HEAD(, rttimer) rt_timer; /* queue of timeouts for misc funcs */
};
#define rt_use rt_rmx.rmx_pksent
@@ -233,11 +236,34 @@ struct rt_addrinfo {
struct route_cb {
int ip_count;
+ int ip6_count;
int ns_count;
int iso_count;
int any_count;
};
+/*
+ * This structure, and the prototypes for the rt_timer_{init,remove_all,
+ * add,timer} functions all used with the kind permission of BSDI.
+ * These allow functions to be called for routes at specific times.
+ */
+
+struct rttimer {
+ TAILQ_ENTRY(rttimer) rtt_next; /* entry on timer queue */
+ LIST_ENTRY(rttimer) rtt_link; /* multiple timers per rtentry */
+ struct rttimer_queue *rtt_queue;/* back pointer to queue */
+ struct rtentry *rtt_rt; /* Back pointer to the route */
+ void (*rtt_func) __P((struct rtentry *,
+ struct rttimer *));
+ time_t rtt_time; /* When this timer was registered */
+};
+
+struct rttimer_queue {
+ long rtq_timeout;
+ TAILQ_HEAD(, rttimer) rtq_head;
+ LIST_ENTRY(rttimer_queue) rtq_link;
+};
+
#ifdef _KERNEL
#define RTFREE(rt) do { \
if ((rt)->rt_refcnt <= 1) \
@@ -270,6 +296,16 @@ void rt_newaddrmsg __P((int, struct ifaddr *, int, struct rtentry *));
int rt_setgate __P((struct rtentry *, struct sockaddr *,
struct sockaddr *));
void rt_setmetrics __P((u_long, struct rt_metrics *, struct rt_metrics *));
+int rt_timer_add __P((struct rtentry *,
+ void(*)(struct rtentry *, struct rttimer *),
+ struct rttimer_queue *));
+void rt_timer_init __P((void));
+struct rttimer_queue *
+ rt_timer_queue_create __P((u_int));
+void rt_timer_queue_change __P((struct rttimer_queue *, long));
+void rt_timer_queue_destroy __P((struct rttimer_queue *, int));
+void rt_timer_remove_all __P((struct rtentry *));
+void rt_timer_timer __P((void *));
void rtable_init __P((void **));
void rtalloc __P((struct route *));
struct rtentry *
diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c
index f842f8be969..57303703912 100644
--- a/sys/net/rtsock.c
+++ b/sys/net/rtsock.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: rtsock.c,v 1.7 1998/08/24 20:39:40 downsj Exp $ */
+/* $OpenBSD: rtsock.c,v 1.8 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd 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, 1991, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -68,6 +97,8 @@ static struct mbuf *
static int rt_msg2 __P((int,
struct rt_addrinfo *, caddr_t, struct walkarg *));
static void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+static void rt_setif __P((struct rtentry *, struct sockaddr *,
+ struct sockaddr *, struct sockaddr *));
/* Sleazy use of local variables throughout file, warning!!!! */
#define dst info.rti_info[RTAX_DST]
@@ -99,6 +130,8 @@ route_usrreq(so, req, m, nam, control)
int af = rp->rcb_proto.sp_protocol;
if (af == AF_INET)
route_cb.ip_count--;
+ else if (af == AF_INET6)
+ route_cb.ip6_count--;
else if (af == AF_NS)
route_cb.ns_count--;
else if (af == AF_ISO)
@@ -129,6 +162,8 @@ route_usrreq(so, req, m, nam, control)
}
if (af == AF_INET)
route_cb.ip_count++;
+ else if (af == AF_INET6)
+ route_cb.ip6_count++;
else if (af == AF_NS)
route_cb.ns_count++;
else if (af == AF_ISO)
@@ -159,7 +194,6 @@ route_output(m, va_alist)
struct rt_addrinfo info;
int len, error = 0;
struct ifnet *ifp = 0;
- struct ifaddr *ifa = 0;
struct socket *so;
va_list ap;
@@ -167,7 +201,7 @@ route_output(m, va_alist)
so = va_arg(ap, struct socket *);
va_end(ap);
-
+ bzero(&info, sizeof(info));
#define senderr(e) { error = e; goto flush;}
if (m == 0 || ((m->m_len < sizeof(int32_t)) &&
(m = m_pullup(m, sizeof(int32_t))) == 0))
@@ -219,6 +253,35 @@ route_output(m, va_alist)
error = rtrequest(RTM_ADD, dst, gate, netmask,
rtm->rtm_flags, &saved_nrt);
if (error == 0 && saved_nrt) {
+ /*
+ * If the route request specified an interface with
+ * IFA and/or IFP, we set the requested interface on
+ * the route with rt_setif. It would be much better
+ * to do this inside rtrequest, but that would
+ * require passing the desired interface, in some
+ * form, to rtrequest. Since rtrequest is called in
+ * so many places (roughly 40 in our source), adding
+ * a parameter is to much for us to swallow; this is
+ * something for the FreeBSD developers to tackle.
+ * Instead, we let rtrequest compute whatever
+ * interface it wants, then come in behind it and
+ * stick in the interface that we really want. This
+ * works reasonably well except when rtrequest can't
+ * figure out what interface to use (with
+ * ifa_withroute) and returns ENETUNREACH. Ideally
+ * it shouldn't matter if rtrequest can't figure out
+ * the interface if we're going to explicitly set it
+ * ourselves anyway. But practically we can't
+ * recover here because rtrequest will not do any of
+ * the work necessary to add the route if it can't
+ * find an interface. As long as there is a default
+ * route that leads to some interface, rtrequest will
+ * find an interface, so this problem should be
+ * rarely encountered.
+ * dwiggins@bbn.com
+ */
+
+ rt_setif(saved_nrt, ifpaddr, ifaaddr, gate);
rt_setmetrics(rtm->rtm_inits,
&rtm->rtm_rmx, &saved_nrt->rt_rmx);
saved_nrt->rt_refcnt--;
@@ -287,6 +350,10 @@ route_output(m, va_alist)
case RTM_CHANGE:
if (gate && rt_setgate(rt, rt_key(rt), gate))
senderr(EDQUOT);
+
+#if 1
+ rt_setif(rt, ifpaddr, ifaaddr, gate);
+#else
/* new gateway could require new ifaddr, ifp;
flags may also be different; ifp may be specified
by ll sockaddr when protocol address is ambiguous */
@@ -310,10 +377,13 @@ route_output(m, va_alist)
rt->rt_ifp = ifp;
}
}
+#endif
rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx,
&rt->rt_rmx);
+#if 0
if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate);
+#endif
if (genmask)
rt->rt_genmask = genmask;
/*
@@ -387,6 +457,56 @@ rt_setmetrics(which, in, out)
#undef metric
}
+/*
+ * Set route's interface given ifpaddr, ifaaddr, and gateway.
+ */
+static void
+rt_setif(rt, Ifpaddr, Ifaaddr, Gate)
+ struct rtentry *rt;
+ struct sockaddr *Ifpaddr, *Ifaaddr, *Gate;
+{
+ struct ifaddr *ifa = 0;
+ struct ifnet *ifp = 0;
+
+ /* new gateway could require new ifaddr, ifp;
+ flags may also be different; ifp may be specified
+ by ll sockaddr when protocol address is ambiguous */
+ if (Ifpaddr && (ifa = ifa_ifwithnet(Ifpaddr)) &&
+ (ifp = ifa->ifa_ifp) && (Ifaaddr || Gate))
+ ifa = ifaof_ifpforaddr(Ifaaddr ? Ifaaddr : Gate,
+ ifp);
+ else if (Ifpaddr && (ifp = if_withname(Ifpaddr)) ) {
+ ifa = Gate ? ifaof_ifpforaddr(Gate, ifp) :
+ TAILQ_FIRST(&ifp->if_addrlist);
+ }
+ else if ((Ifaaddr && (ifa = ifa_ifwithaddr(Ifaaddr))) ||
+ (Gate && (ifa = ifa_ifwithroute(rt->rt_flags,
+ rt_key(rt), Gate))))
+ ifp = ifa->ifa_ifp;
+ if (ifa) {
+ register struct ifaddr *oifa = rt->rt_ifa;
+ if (oifa != ifa) {
+ if (oifa && oifa->ifa_rtrequest)
+ oifa->ifa_rtrequest(RTM_DELETE,
+ rt, Gate);
+ IFAFREE(rt->rt_ifa);
+ rt->rt_ifa = ifa;
+ ifa->ifa_refcnt++;
+ rt->rt_ifp = ifp;
+ rt->rt_rmx.rmx_mtu = ifp->if_mtu;
+ if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, Gate);
+ } else
+ goto call_ifareq;
+ return;
+ }
+ call_ifareq:
+ /* XXX: to reset gateway to correct value, at RTM_CHANGE */
+ if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, Gate);
+}
+
+
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
diff --git a/sys/netccitt/if_x25subr.c b/sys/netccitt/if_x25subr.c
index b635df4ab74..1fc7118691f 100644
--- a/sys/netccitt/if_x25subr.c
+++ b/sys/netccitt/if_x25subr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_x25subr.c,v 1.3 1996/05/22 12:02:40 deraadt Exp $ */
+/* $OpenBSD: if_x25subr.c,v 1.4 1999/12/08 06:50:18 itojun Exp $ */
/* $NetBSD: if_x25subr.c,v 1.13 1996/05/09 22:29:25 scottr Exp $ */
/*
@@ -63,6 +63,10 @@
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
+#else
+#ifdef _KERNEL
+#error options CCITT assumes options INET
+#endif
#endif
#ifdef NS
diff --git a/sys/netinet/fil.c b/sys/netinet/fil.c
index adc8a0f0cd8..84bfd975173 100644
--- a/sys/netinet/fil.c
+++ b/sys/netinet/fil.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fil.c,v 1.15 1999/02/19 20:52:22 kjell Exp $ */
+/* $OpenBSD: fil.c,v 1.16 1999/12/08 06:50:18 itojun Exp $ */
/*
* Copyright (C) 1993-1998 by Darren Reed.
*
@@ -8,7 +8,7 @@
*/
#if !defined(lint)
static const char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed";
-static const char rcsid[] = "@(#)$Id: fil.c,v 1.15 1999/02/19 20:52:22 kjell Exp $";
+static const char rcsid[] = "@(#)$Id: fil.c,v 1.16 1999/12/08 06:50:18 itojun Exp $";
#endif
#include <sys/errno.h>
@@ -45,6 +45,11 @@ static const char rcsid[] = "@(#)$Id: fil.c,v 1.15 1999/02/19 20:52:22 kjell Exp
# include <net/af.h>
#endif
#include <net/route.h>
+#ifdef _KERNEL
+#ifndef INET
+#error ipfilter assumes options INET
+#endif
+#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
@@ -1137,7 +1142,7 @@ nodata:
* SUCH DAMAGE.
*
* @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
- * $Id: fil.c,v 1.15 1999/02/19 20:52:22 kjell Exp $
+ * $Id: fil.c,v 1.16 1999/12/08 06:50:18 itojun Exp $
*/
/*
* Copy data from an mbuf chain starting "off" bytes from the beginning,
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
new file mode 100644
index 00000000000..7bfb3b2c160
--- /dev/null
+++ b/sys/netinet/icmp6.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: icmp6.h,v 1.1 1999/12/08 06:50:18 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 _NETINET_ICMP6_H_
+#define _NETINET_ICMP6_H_
+
+#include <netinet6/icmp6.h>
+
+#endif /* !_NETINET_ICMP6_H_ */
diff --git a/sys/netinet/if_atm.c b/sys/netinet/if_atm.c
index 2250042ee94..a8022fabcf0 100644
--- a/sys/netinet/if_atm.c
+++ b/sys/netinet/if_atm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_atm.c,v 1.6 1997/09/28 23:09:55 deraadt Exp $ */
+/* $OpenBSD: if_atm.c,v 1.7 1999/12/08 06:50:18 itojun Exp $ */
/*
*
@@ -36,7 +36,7 @@
* IP <=> ATM address resolution.
*/
-#ifdef INET
+#if defined(INET) || defined(INET6)
#include <sys/param.h>
#include <sys/systm.h>
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
index 25045da1c93..7364cdf83c1 100644
--- a/sys/netinet/if_ether.h
+++ b/sys/netinet/if_ether.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_ether.h,v 1.9 1999/08/08 00:43:00 niklas Exp $ */
+/* $OpenBSD: if_ether.h,v 1.10 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: if_ether.h,v 1.22 1996/05/11 13:00:00 mycroft Exp $ */
/*
@@ -91,6 +91,23 @@ struct ether_header {
(enaddr)[4] = ((u_int8_t *)ipaddr)[2]; \
(enaddr)[5] = ((u_int8_t *)ipaddr)[3]; \
}
+
+/*
+ * Macro to map an IPv6 multicast address to an Ethernet multicast address.
+ * The high-order 16 bits of the Ethernet address are statically assigned,
+ * and the low-order 32 bits are taken from the low end of the IPv6 address.
+ */
+#define ETHER_MAP_IPV6_MULTICAST(ip6addr, enaddr) \
+ /* struct in6_addr *ip6addr; */ \
+ /* u_int8_t enaddr[ETHER_ADDR_LEN]; */ \
+{ \
+ (enaddr)[0] = 0x33; \
+ (enaddr)[1] = 0x33; \
+ (enaddr)[2] = ((u_int8_t *)ip6addr)[12]; \
+ (enaddr)[3] = ((u_int8_t *)ip6addr)[13]; \
+ (enaddr)[4] = ((u_int8_t *)ip6addr)[14]; \
+ (enaddr)[5] = ((u_int8_t *)ip6addr)[15]; \
+}
#endif
/*
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
index 61c70584d87..25bc976f535 100644
--- a/sys/netinet/igmp.c
+++ b/sys/netinet/igmp.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: igmp.c,v 1.5 1999/08/08 00:43:00 niklas Exp $ */
+/* $OpenBSD: igmp.c,v 1.6 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos 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.
+ */
+
+/*
* Internet Group Management Protocol (IGMP) routines.
*
* Written by Steve Deering, Stanford, May 1988.
@@ -122,6 +151,7 @@ igmp_input(m, va_alist)
va_dcl
#endif
{
+ int proto;
register int iphlen;
register struct ifnet *ifp = m->m_pkthdr.rcvif;
register struct ip *ip = mtod(m, struct ip *);
@@ -137,6 +167,7 @@ igmp_input(m, va_alist)
va_start(ap, m);
iphlen = va_arg(ap, int);
+ proto = va_arg(ap, int);
va_end(ap);
++igmpstat.igps_rcv_total;
@@ -384,7 +415,8 @@ igmp_input(m, va_alist)
* Pass all valid IGMP packets up to any process(es) listening
* on a raw IGMP socket.
*/
- rip_input(m);
+ rip_input(m, iphlen, proto);
+ return;
}
void
@@ -539,6 +571,9 @@ igmp_sendpkt(inm, type)
imo.imo_multicast_loop = 0;
#endif /* MROUTING */
+#if 0 /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
&imo, NULL);
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 799cd45d3c4..5f7414e6c30 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in.c,v 1.13 1999/04/20 20:06:11 niklas Exp $ */
+/* $OpenBSD: in.c,v 1.14 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */
/*
@@ -45,7 +45,12 @@
#include <sys/systm.h>
#include <net/if.h>
+#include <net/if_types.h>
#include <net/route.h>
+#include "gif.h"
+#if NGIF > 0
+#include <net/if_gif.h>
+#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
@@ -61,6 +66,11 @@
#ifdef INET
+static int in_mask2len __P((struct in_addr *));
+static void in_len2mask __P((struct in_addr *, int));
+static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
+ struct ifnet *));
+
#ifndef SUBNETSARELOCAL
#define SUBNETSARELOCAL 0
#endif
@@ -128,6 +138,44 @@ in_socktrim(ap)
}
}
+static int
+in_mask2len(mask)
+ struct in_addr *mask;
+{
+ int x, y;
+ u_char *p;
+
+ p = (u_char *)mask;
+ for (x = 0; x < sizeof(*mask); x++) {
+ if (p[x] != 0xff)
+ break;
+ }
+ y = 0;
+ if (x < sizeof(*mask)) {
+ for (y = 0; y < 8; y++) {
+ if ((p[x] & (0x80 >> y)) == 0)
+ break;
+ }
+ }
+ return x * 8 + y;
+}
+
+static void
+in_len2mask(mask, len)
+ struct in_addr *mask;
+ int len;
+{
+ int i;
+ u_char *p;
+
+ p = (u_char *)mask;
+ bzero(mask, sizeof(*mask));
+ for (i = 0; i < len / 8; i++)
+ p[i] = 0xff;
+ if (len % 8)
+ p[i] = (0xff00 >> (len % 8)) & 0xff;
+}
+
int in_interfaces; /* number of external internet interfaces */
/*
@@ -148,6 +196,31 @@ in_control(so, cmd, data, ifp)
struct sockaddr_in oldaddr;
int error, hostIsNew, maskIsNew;
+#if NGIF > 0
+ if (ifp && ifp->if_type == IFT_GIF) {
+ switch (cmd) {
+ case SIOCSIFPHYADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return(EPERM);
+ case SIOCGIFPSRCADDR:
+ case SIOCGIFPDSTADDR:
+ return gif_ioctl(ifp, cmd, data);
+ }
+ }
+#endif
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGLIFADDR:
+ if (!ifp)
+ return EINVAL;
+ return in_lifaddr_ioctl(so, cmd, data, ifp);
+ }
+
/*
* Find address for this interface, if it exists.
*/
@@ -335,6 +408,190 @@ in_control(so, cmd, data, 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:
+ * EINVAL since we can't deduce hostid part of the address.
+ * 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 in_ioctl()
+ */
+static int
+in_lifaddr_ioctl(so, cmd, data, ifp)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+{
+ struct if_laddrreq *iflr = (struct if_laddrreq *)data;
+ struct ifaddr *ifa;
+ struct sockaddr *sa;
+
+ /* sanity checks */
+ if (!data || !ifp) {
+ panic("invalid argument to in_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_INET)
+ return EINVAL;
+ if (sa->sa_len != sizeof(struct sockaddr_in))
+ return EINVAL;
+ /* XXX need improvement */
+ sa = (struct sockaddr *)&iflr->dstaddr;
+ if (sa->sa_family
+ && sa->sa_family != AF_INET)
+ return EINVAL;
+ if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in))
+ return EINVAL;
+ break;
+ default: /*shouldn't happen*/
+#if 0
+ panic("invalid cmd to in_lifaddr_ioctl");
+ /*NOTREACHED*/
+#else
+ return EOPNOTSUPP;
+#endif
+ }
+ if (sizeof(struct in_addr) * 8 < iflr->prefixlen)
+ return EINVAL;
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ {
+ struct in_aliasreq ifra;
+
+ if (iflr->flags & IFLR_PREFIX)
+ return EINVAL;
+
+ /* copy args to in_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 (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/
+ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
+ ((struct sockaddr *)&iflr->dstaddr)->sa_len);
+ }
+
+ ifra.ifra_mask.sin_family = AF_INET;
+ ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in);
+ in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen);
+
+ return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp);
+ }
+ case SIOCGLIFADDR:
+ case SIOCDLIFADDR:
+ {
+ struct in_ifaddr *ia;
+ struct in_addr mask, candidate, match;
+ struct sockaddr_in *sin;
+ int cmp;
+
+ bzero(&mask, sizeof(mask));
+ if (iflr->flags & IFLR_PREFIX) {
+ /* lookup a prefix rather than address. */
+ in_len2mask(&mask, iflr->prefixlen);
+
+ sin = (struct sockaddr_in *)&iflr->addr;
+ match.s_addr = sin->sin_addr.s_addr;
+ match.s_addr &= mask.s_addr;
+
+ /* if you set extra bits, that's wrong */
+ if (match.s_addr != sin->sin_addr.s_addr)
+ 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 */
+ in_len2mask(&mask, 32);
+ sin = (struct sockaddr_in *)&iflr->addr;
+ match.s_addr = sin->sin_addr.s_addr;
+
+ cmp = 1;
+ }
+ }
+
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!cmp)
+ break;
+ candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr;
+ candidate.s_addr &= mask.s_addr;
+ if (candidate.s_addr == match.s_addr)
+ break;
+ }
+ if (!ifa)
+ return EADDRNOTAVAIL;
+ ia = (struct in_ifaddr *)ifa;
+
+ if (cmd == SIOCGLIFADDR) {
+ /* fill in the if_laddrreq structure */
+ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len);
+
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
+ ia->ia_dstaddr.sin_len);
+ } else
+ bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
+
+ iflr->prefixlen =
+ in_mask2len(&ia->ia_sockmask.sin_addr);
+
+ iflr->flags = 0; /*XXX*/
+
+ return 0;
+ } else {
+ struct in_aliasreq ifra;
+
+ /* fill in_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.sin_len);
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
+ ia->ia_dstaddr.sin_len);
+ }
+ bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr,
+ ia->ia_sockmask.sin_len);
+
+ return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, ifp);
+ }
+ }
+ }
+
+ return EOPNOTSUPP; /*just for safety*/
+}
+
+/*
* Delete any existing route for an interface.
*/
void
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index faf5eb1ab0e..84a9cc056d7 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in.h,v 1.25 1999/10/28 03:29:49 angelos Exp $ */
+/* $OpenBSD: in.h,v 1.26 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */
/*
@@ -63,6 +63,7 @@
#define IPPROTO_IPV6 41 /* IPv6 in IPv6 */
#define IPPROTO_ROUTING 43 /* Routing header. */
#define IPPROTO_FRAGMENT 44 /* Fragmentation/reassembly header. */
+#define IPPROTO_RSVP 46 /* resource reservation */
#define IPPROTO_ESP 50 /* Encap. Security Payload */
#define IPPROTO_AH 51 /* Authentication header */
#define IPPROTO_ICMPV6 58 /* ICMP for IPv6 */
@@ -71,6 +72,8 @@
#define IPPROTO_EON 80 /* ISO cnlp */
#define IPPROTO_ETHERIP 97 /* Ethernet in IPv4 */
#define IPPROTO_ENCAP 98 /* encapsulation header */
+#define IPPROTO_PIM 103 /* Protocol indep. multicast */
+#define IPPROTO_IPCOMP 108 /* IP Payload Comp. Protocol */
#define IPPROTO_RAW 255 /* raw IP packet */
#define IPPROTO_MAX 256
@@ -133,6 +136,7 @@ struct in_addr {
in_addr_t s_addr;
};
+#if 0 /*NRL IPv6*/
/*
* IP Version 6 Internet address
*/
@@ -151,6 +155,10 @@ struct in6_addr {
#define s6_addr16 s6_u.s6u_addr16
#define s6_addr32 s6_u.s6u_addr32
};
+#endif
+
+/* last return value of *_input(), meaning "all job for this pkt is done". */
+#define IPPROTO_DONE 257
/*
* Definitions of bits in internet address integers.
@@ -216,6 +224,7 @@ struct in6_addr {
#define IN_LOOPBACKNET 127 /* official! */
+#if 0 /*NRL IPv6*/
/*
* Tests for IPv6 address types
*/
@@ -294,6 +303,7 @@ extern const struct in6_addr in6addr_any;
extern const struct in6_addr in6addr_loopback;
#define IN6ADDR_LOOPBACK_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}}
+#endif
/*
* IP Version 4 socket address.
@@ -306,6 +316,7 @@ struct sockaddr_in {
int8_t sin_zero[8];
};
+#if 0 /*NRL IPv6*/
/*
* IP Version 6 socket address.
*/
@@ -318,6 +329,9 @@ struct sockaddr_in6 {
struct in6_addr sin6_addr;
u_int32_t sin6_scope_id;
};
+#endif
+
+#define INET_ADDRSTRLEN 16
/*
* Structure used to describe IP options.
@@ -360,6 +374,7 @@ struct ip_opts {
#define IP_ESP_TRANS_LEVEL 21 /* u_char; transport encryption */
#define IP_ESP_NETWORK_LEVEL 22 /* u_char; full-packet encryption */
+#if 0 /* NRL IPv6 */
#define IPV6_MULTICAST_IF 23 /* u_int; set/get multicast interface */
#define IPV6_MULTICAST_HOPS 24 /* int; set/get multicast hop limit */
#define IPV6_MULTICAST_LOOP 25 /* u_int; set/get multicast loopback */
@@ -379,9 +394,14 @@ struct ip_opts {
#define IPV6_CHECKSUM 37 /* int; offset to place send checksum */
#define ICMPV6_FILTER 38 /* struct icmpv6_filter; get/set filter */
#define ICMP6_FILTER ICMP6_FILTER
+#endif
#define IPSEC_OUTSA 39 /* set the outbound SA for a socket */
+#if 0 /*KAME IPSEC*/
+#define IP_IPSEC_POLICY ?? /* struct; get/set security policy */
+#endif
+
/*
* Security levels - IPsec, not IPSO
*/
@@ -398,6 +418,7 @@ struct ip_opts {
#define IPSEC_ESP_TRANS_LEVEL_DEFAULT IPSEC_LEVEL_DEFAULT
#define IPSEC_ESP_NETWORK_LEVEL_DEFAULT IPSEC_LEVEL_DEFAULT
+#if 0 /* NRL IPv6 */
/*
* IPv6 Routing header types
*/
@@ -405,6 +426,7 @@ struct ip_opts {
#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor */
#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor */
+#endif
/*
* Defaults and limits for options
@@ -421,6 +443,7 @@ struct ip_mreq {
struct in_addr imr_interface; /* local IP address of interface */
};
+#if 0 /* NRL IPv6 */
/*
* Argument structure for IPV6_ADD_MEMBERSHIP and IPV6_DROP_MEMBERSHIP.
*/
@@ -436,6 +459,7 @@ struct in6_pktinfo {
struct in6_addr ipi6_addr;
unsigned int ipi6_ifindex;
};
+#endif
/*
* Argument for IP_PORTRANGE:
@@ -449,7 +473,9 @@ struct in6_pktinfo {
* Buffer lengths for strings containing printable IP addresses
*/
#define INET_ADDRSTRLEN 16
+#if 0 /* NRL IPv6 */
#define INET6_ADDRSTRLEN 46
+#endif
/*
* Definitions for inet sysctl operations.
@@ -577,7 +603,8 @@ struct in6_pktinfo {
#define IPCTL_IPPORT_HILASTAUTO 10
#define IPCTL_IPPORT_MAXQUEUE 11
#define IPCTL_ENCDEBUG 12
-#define IPCTL_MAXID 13
+#define IPCTL_GIF_TTL 13 /* default TTL for gif encap packet */
+#define IPCTL_MAXID 14
#define IPCTL_NAMES { \
{ 0, 0 }, \
@@ -593,8 +620,12 @@ struct in6_pktinfo {
{ "porthilast", CTLTYPE_INT }, \
{ "maxqueue", CTLTYPE_INT }, \
{ "encdebug", CTLTYPE_INT }, \
+ { "gifttl", CTLTYPE_INT }, \
}
+/* INET6 stuff */
+#include <netinet6/in6.h>
+
#ifndef _KERNEL
#include <sys/cdefs.h>
diff --git a/sys/netinet/in4_cksum.c b/sys/netinet/in4_cksum.c
new file mode 100644
index 00000000000..171d5467420
--- /dev/null
+++ b/sys/netinet/in4_cksum.c
@@ -0,0 +1,217 @@
+/* $OpenBSD: in4_cksum.c,v 1.1 1999/12/08 06:50:19 itojun Exp $ */
+
+/* $NetBSD: in_cksum.c,v 1.13 1996/10/13 02:03:03 christos 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.
+ */
+
+/*
+ * 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 <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ * This is only for IPv4 pseudo header checksum.
+ * No need to clear non-pseudo-header fields in IPv4 header.
+ * len is for actual payload size, and does not include IPv4 header and
+ * skipped header chain (off + len should be equal to the whole packet).
+ *
+ * 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);}
+
+int
+in4_cksum(m, nxt, off, len)
+ register struct mbuf *m;
+ u_int8_t nxt;
+ register int off, len;
+{
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+ struct ipovly ipov;
+
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ /* pseudo header */
+ if (off < sizeof(struct ipovly))
+ panic("offset too short");
+ bzero(&ipov, sizeof(ipov));
+ ipov.ih_len = htons(len);
+ ipov.ih_pr = nxt;
+ ipov.ih_src = mtod(m, struct ip *)->ip_src;
+ ipov.ih_dst = mtod(m, struct ip *)->ip_dst;
+ w = (u_int16_t *)&ipov;
+ /* assumes sizeof(ipov) == 20 */
+ 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];
+
+ /* skip unnecessary part */
+ while (m && off > 0) {
+ if (m->m_len > off)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = (u_int16_t *)(mtod(m, caddr_t) + off);
+ 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] = *(u_int8_t *)w;
+ sum += s_util.s;
+ w = (u_int16_t *)((u_int8_t *)w + 1);
+ mlen = m->m_len - off - 1;
+ len--;
+ } else
+ mlen = m->m_len - off;
+ off = 0;
+ 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_int8_t *)w;
+ w = (u_int16_t *)((int8_t *)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] = *(u_int8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(u_int8_t *)w;
+ }
+ if (len)
+ printf("cksum4: out of data\n");
+ 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/netinet/in_gif.c b/sys/netinet/in_gif.c
new file mode 100644
index 00000000000..5abb3007660
--- /dev/null
+++ b/sys/netinet/in_gif.c
@@ -0,0 +1,357 @@
+/* $OpenBSD: in_gif.c,v 1.1 1999/12/08 06:50: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.
+ */
+
+/*
+ * in_gif.c
+ */
+
+#ifdef __FreeBSD__
+#include "opt_mrouting.h"
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#ifdef __FreeBSD__
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#endif
+#if !defined(__FreeBSD__) || __FreeBSD__ < 3
+#include <sys/ioctl.h>
+#endif
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_gif.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_gif.h>
+#include <netinet/ip_ecn.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+
+#ifdef MROUTING
+#include <netinet/ip_mroute.h>
+#endif /* MROUTING */
+
+#include <net/if_gif.h>
+
+#include "gif.h"
+
+#include <machine/stdarg.h>
+
+#include <net/net_osdep.h>
+
+#if NGIF > 0
+int ip_gif_ttl = GIF_TTL;
+#else
+int ip_gif_ttl = 0;
+#endif
+#ifdef __FreeBSD__
+SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW,
+ &ip_gif_ttl, 0, "");
+#endif
+
+int
+in_gif_output(ifp, family, m, rt)
+ struct ifnet *ifp;
+ int family;
+ struct mbuf *m;
+ struct rtentry *rt;
+{
+ register struct gif_softc *sc = (struct gif_softc*)ifp;
+ struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst;
+ struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc;
+ struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst;
+ struct ip iphdr; /* capsule IP header, host byte ordered */
+ int proto, error;
+ u_int8_t tos;
+
+ if (sin_src == NULL || sin_dst == NULL ||
+ sin_src->sin_family != AF_INET ||
+ sin_dst->sin_family != AF_INET) {
+ 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 *);
+ tos = ip->ip_tos;
+ break;
+ }
+#endif /*INET*/
+#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 *);
+ tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
+ break;
+ }
+#endif /*INET6*/
+ default:
+#ifdef DIAGNOSTIC
+ printf("in_gif_output: warning: unknown family %d passed\n",
+ family);
+#endif
+ m_freem(m);
+ return EAFNOSUPPORT;
+ }
+
+ bzero(&iphdr, sizeof(iphdr));
+ iphdr.ip_src = sin_src->sin_addr;
+ if (ifp->if_flags & IFF_LINK0) {
+ /* multi-destination mode */
+ if (sin_dst->sin_addr.s_addr != INADDR_ANY)
+ iphdr.ip_dst = sin_dst->sin_addr;
+ else if (rt) {
+ if (family != AF_INET) {
+ m_freem(m);
+ return EINVAL; /*XXX*/
+ }
+ iphdr.ip_dst = ((struct sockaddr_in *)
+ (rt->rt_gateway))->sin_addr;
+ } else {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ } else {
+ /* bidirectional configured tunnel mode */
+ if (sin_dst->sin_addr.s_addr != INADDR_ANY)
+ iphdr.ip_dst = sin_dst->sin_addr;
+ else {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ }
+ iphdr.ip_p = proto;
+ /* version will be set in ip_output() */
+ iphdr.ip_ttl = ip_gif_ttl;
+ iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip);
+ if (ifp->if_flags & IFF_LINK1)
+ ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos);
+
+ /* prepend new IP header */
+ M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
+ if (m && m->m_len < sizeof(struct ip))
+ m = m_pullup(m, sizeof(struct ip));
+ if (m == NULL) {
+ printf("ENOBUFS in in_gif_output %d\n", __LINE__);
+ return ENOBUFS;
+ }
+
+ *(mtod(m, struct ip *)) = iphdr;
+
+ if (dst->sin_family != sin_dst->sin_family ||
+ dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) {
+ /* cache route doesn't match */
+ dst->sin_family = sin_dst->sin_family;
+ dst->sin_len = sizeof(struct sockaddr_in);
+ dst->sin_addr = sin_dst->sin_addr;
+ if (sc->gif_ro.ro_rt) {
+ RTFREE(sc->gif_ro.ro_rt);
+ sc->gif_ro.ro_rt = NULL;
+ }
+#if 0
+ sc->gif_if.if_mtu = GIF_MTU;
+#endif
+ }
+
+ if (sc->gif_ro.ro_rt == NULL) {
+ rtalloc(&sc->gif_ro);
+ if (sc->gif_ro.ro_rt == NULL) {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+#if 0
+ ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu
+ - sizeof(struct ip);
+#endif
+ }
+
+#ifdef IPSEC
+#ifndef __OpenBSD__ /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif
+#endif /*IPSEC*/
+#ifndef __OpenBSD__
+ error = ip_output(m, NULL, &sc->gif_ro, 0, NULL);
+#else
+ error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL);
+#endif
+ return(error);
+}
+
+void
+#if __STDC__
+in_gif_input(struct mbuf *m, ...)
+#else
+in_gif_input(m, va_alist)
+ struct mbuf *m;
+ va_dcl
+#endif
+{
+ int off, proto;
+ struct gif_softc *sc;
+ struct ifnet *gifp = NULL;
+ struct ip *ip;
+ int i, af;
+ va_list ap;
+ u_int8_t otos;
+
+ va_start(ap, m);
+ off = va_arg(ap, int);
+ proto = va_arg(ap, int);
+ va_end(ap);
+
+ ip = mtod(m, struct ip *);
+
+ /* this code will be soon improved. */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+ for (i = 0, sc = gif; i < ngif; i++, sc++) {
+ if (sc->gif_psrc == NULL
+ || sc->gif_pdst == NULL
+ || sc->gif_psrc->sa_family != AF_INET
+ || sc->gif_pdst->sa_family != AF_INET) {
+ continue;
+ }
+
+ if ((sc->gif_if.if_flags & IFF_UP) == 0)
+ continue;
+
+ if ((sc->gif_if.if_flags & IFF_LINK0)
+ && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
+ && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) {
+ gifp = &sc->gif_if;
+ continue;
+ }
+
+ if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr
+ && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr)
+ {
+ gifp = &sc->gif_if;
+ break;
+ }
+ }
+
+ if (gifp == NULL) {
+#ifdef MROUTING
+ /* for backward compatibility */
+ if (proto == IPPROTO_IPV4) {
+ ipip_input(m, off, proto);
+ return;
+ }
+#endif /*MROUTING*/
+ m_freem(m);
+ ipstat.ips_nogif++;
+ return;
+ }
+
+ otos = ip->ip_tos;
+ m_adj(m, off);
+
+ switch (proto) {
+#ifdef INET
+ case IPPROTO_IPV4:
+ {
+ struct ip *ip;
+ af = AF_INET;
+ if (m->m_len < sizeof(*ip)) {
+ m = m_pullup(m, sizeof(*ip));
+ if (!m)
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ if (gifp->if_flags & IFF_LINK1)
+ ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos);
+ break;
+ }
+#endif
+#ifdef INET6
+ case IPPROTO_IPV6:
+ {
+ struct ip6_hdr *ip6;
+ u_int8_t itos;
+ af = AF_INET6;
+ if (m->m_len < sizeof(*ip6)) {
+ m = m_pullup(m, sizeof(*ip6));
+ if (!m)
+ return;
+ }
+ ip6 = mtod(m, struct ip6_hdr *);
+ itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
+ if (gifp->if_flags & IFF_LINK1)
+ ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
+ ip6->ip6_flow &= ~htonl(0xff << 20);
+ ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
+ break;
+ }
+#endif /* INET6 */
+ default:
+ ipstat.ips_nogif++;
+ m_freem(m);
+ return;
+ }
+ gif_input(m, af, gifp);
+ return;
+}
diff --git a/sys/netinet/in_gif.h b/sys/netinet/in_gif.h
new file mode 100644
index 00000000000..a80e6fff158
--- /dev/null
+++ b/sys/netinet/in_gif.h
@@ -0,0 +1,42 @@
+/* $OpenBSD: in_gif.h,v 1.1 1999/12/08 06:50: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.
+ */
+
+#ifndef _NETINET_IN_GIF_H_
+#define _NETINET_IN_GIF_H_
+
+#define GIF_TTL 30
+
+extern int ip_gif_ttl;
+
+void in_gif_input __P((struct mbuf *, ...));
+int in_gif_output __P((struct ifnet *, int, struct mbuf *, struct rtentry *));
+
+#endif /*_NETINET_IN_GIF_H_*/
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 91b157519d6..71458addf74 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.c,v 1.34 1999/05/16 21:48:29 niklas Exp $ */
+/* $OpenBSD: in_pcb.c,v 1.35 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@@ -73,7 +73,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <dev/rndvar.h>
#ifdef INET6
-#include <netinet6/ipv6_var.h>
+#include <netinet6/ip6_var.h>
#endif /* INET6 */
#ifdef IPSEC
@@ -82,6 +82,12 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
extern int check_ipsec_policy __P((struct inpcb *, u_int32_t));
#endif
+#if 0 /*KAME IPSEC*/
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif /* IPSEC */
+
struct in_addr zeroin_addr;
extern int ipsec_auth_default_level;
@@ -165,6 +171,7 @@ in_pcballoc(so, v)
&inp->inp_laddr, inp->inp_lport), inp, inp_hash);
splx(s);
so->so_pcb = inp;
+ inp->inp_hops = -1;
#ifdef INET6
/*
@@ -355,10 +362,8 @@ in_pcbconnect(v, nam)
struct mbuf *nam;
{
register struct inpcb *inp = v;
- struct in_ifaddr *ia;
struct sockaddr_in *ifaddr = NULL;
register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
- struct sockaddr_in *sin2;
#ifdef INET6
if (sotopf(inp->inp_socket) == PF_INET6)
@@ -386,7 +391,10 @@ in_pcbconnect(v, nam)
sin->sin_addr = in_ifaddr.tqh_first->ia_broadaddr.sin_addr;
}
if (inp->inp_laddr.s_addr == INADDR_ANY) {
+#if 0
register struct route *ro;
+ struct sockaddr_in *sin2;
+ struct in_ifaddr *ia;
ia = (struct in_ifaddr *)0;
/*
@@ -446,10 +454,11 @@ in_pcbconnect(v, nam)
if (IN_MULTICAST(sin->sin_addr.s_addr) &&
#ifdef INET6
inp->inp_moptions != NULL &&
- !(inp->inp_flags & INP_IPV6_MCAST)) {
+ !(inp->inp_flags & INP_IPV6_MCAST))
#else
- inp->inp_moptions != NULL) {
+ inp->inp_moptions != NULL)
#endif
+ {
struct ip_moptions *imo;
struct ifnet *ifp;
@@ -465,6 +474,16 @@ in_pcbconnect(v, nam)
}
}
ifaddr = satosin(&ia->ia_addr);
+#else
+ int error;
+ ifaddr = in_selectsrc(sin, &inp->inp_route,
+ inp->inp_socket->so_options, inp->inp_moptions, &error);
+ if (ifaddr == NULL) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return error;
+ }
+#endif
}
if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, sin->sin_port,
inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
@@ -515,6 +534,14 @@ in_pcbdetach(v)
struct socket *so = inp->inp_socket;
int s;
+#if 0 /*KAME IPSEC*/
+ if (so->so_pcb) {
+ KEYDEBUG(KEYDEBUG_KEY_STAMP,
+ printf("DP call free SO=%p from in_pcbdetach\n", so));
+ key_freeso(so);
+ }
+ ipsec4_delete_pcbpolicy(inp);
+#endif /*IPSEC*/
so->so_pcb = 0;
sofree(so);
if (inp->inp_options)
@@ -522,10 +549,10 @@ in_pcbdetach(v)
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
#ifdef INET6
- if (inp->inp_flags & INP_IPV6_MCAST)
- ipv6_freemoptions(inp->inp_moptions6);
+ if (inp->inp_flags & INP_IPV6)
+ ip6_freemoptions(inp->inp_moptions6);
else
-#endif /* INET6 */
+#endif
ip_freemoptions(inp->inp_moptions);
#ifdef IPSEC
/* XXX IPsec cleanup here */
@@ -778,8 +805,9 @@ in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
if (!IN6_IS_ADDR_UNSPECIFIED(faddr6))
wildcard++;
}
- } else {
+ } else
#endif /* INET6 */
+ {
if (inp->inp_faddr.s_addr != INADDR_ANY) {
if (faddr.s_addr == INADDR_ANY)
wildcard++;
@@ -799,9 +827,7 @@ in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
if (laddr.s_addr != INADDR_ANY)
wildcard++;
}
-#ifdef INET6
}
-#endif /* INET6 */
if ((!wildcard || (flags & INPLOOKUP_WILDCARD)) &&
wildcard < matchwild) {
match = inp;
@@ -812,6 +838,100 @@ in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
return (match);
}
+struct sockaddr_in *
+in_selectsrc(sin, ro, soopts, mopts, errorp)
+ struct sockaddr_in *sin;
+ struct route *ro;
+ int soopts;
+ struct ip_moptions *mopts;
+ int *errorp;
+{
+ struct sockaddr_in *sin2;
+ struct in_ifaddr *ia;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ soopts & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((soopts & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ satosin(&ro->ro_dst)->sin_addr = sin->sin_addr;
+ rtalloc(ro);
+
+ /*
+ * It is important to bzero out the rest of the
+ * struct sockaddr_in when mixing v6 & v4!
+ */
+ sin2 = (struct sockaddr_in *)&ro->ro_dst;
+ bzero(sin2->sin_zero, sizeof(sin2->sin_zero));
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ if (ia == 0) {
+ u_int16_t fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
+ if (ia == 0)
+ ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_ifaddr.tqh_first;
+ if (ia == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return NULL;
+ }
+ }
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(sin->sin_addr.s_addr) &&
+#if 0 /*def INET6*/
+ mopts != NULL &&
+ !(inp->inp_flags & INP_IPV6_MCAST))
+#else
+ mopts != NULL)
+#endif
+ {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = mopts;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr.tqh_first; ia != 0;
+ ia = ia->ia_list.tqe_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return NULL;
+ }
+ }
+ }
+ return satosin(&ia->ia_addr);
+}
+
void
in_pcbrehash(inp)
struct inpcb *inp;
@@ -919,3 +1039,5 @@ in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
return (inp);
}
#endif /* INET6 */
+
+
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index cb753c3ae82..819fc5a594f 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,7 +1,36 @@
-/* $OpenBSD: in_pcb.h,v 1.17 1999/03/27 21:04:19 provos Exp $ */
+/* $OpenBSD: in_pcb.h,v 1.18 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos 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.
*
@@ -37,9 +66,12 @@
*/
#include <sys/queue.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/icmpv6.h>
+#if 0 /*KAME IPSEC*/
+#include <netinet6/ipsec.h>
+#endif
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
#include <netinet/ip_ipsp.h>
union inpaddru {
@@ -73,21 +105,23 @@ struct inpcb {
caddr_t inp_ppcb; /* pointer to per-protocol pcb */
union { /* Route (notice increased size). */
struct route ru_route;
- struct route6 ru_route6;
+ struct route_in6 ru_route6;
} inp_ru;
#define inp_route inp_ru.ru_route
#define inp_route6 inp_ru.ru_route6
int inp_flags; /* generic IP/datagram flags */
union { /* Header prototype. */
struct ip hu_ip;
- struct ipv6 hu_ipv6;
+ struct ip6_hdr hu_ipv6;
} inp_hu;
#define inp_ip inp_hu.hu_ip
#define inp_ipv6 inp_hu.hu_ipv6
struct mbuf *inp_options; /* IP options */
+ struct ip6_pktopts *inp_outputopts6; /* IP6 options for outgoing packets */
+ int inp_hops;
union {
struct ip_moptions *mou_mo; /* IPv4 multicast options */
- struct ipv6_moptions *mou_mo6; /* IPv6 multicast options */
+ struct ip6_moptions *mou_mo6; /* IPv6 multicast options */
} inp_mou;
#define inp_moptions inp_mou.mou_mo
#define inp_moptions6 inp_mou.mou_mo6
@@ -104,7 +138,12 @@ struct inpcb {
struct tdb *inp_tdb; /* If tdb_dst matches our dst, use */
int inp_fflowinfo; /* Foreign flowlabel & priority */
int inp_csumoffset;
- struct icmpv6_filter inp_filter;
+ struct icmp6_filter *inp_icmp6filt;
+#if 0 /*KAME IPSEC*/
+ struct secpolicy *inp_sp; /* security policy. It may not be
+ * used according to policy selection.
+ */
+#endif
};
struct inpcbtable {
@@ -144,6 +183,24 @@ struct inpcbtable {
* the mapped form of v6 addresses) */
#define INP_IPV6_MCAST 0x800 /* Set if inp_moptions points to ipv6 ones */
+#if 1 /*KAME*/
+/* flags in in6p_flags */
+#define IN6P_RECVOPTS 0x01 /* receive incoming IP6 options */
+#define IN6P_RECVRETOPTS 0x02 /* receive IP6 options for reply */
+#define IN6P_RECVDSTADDR 0x04 /* receive IP6 dst address */
+#define IN6P_HIGHPORT 0x10 /* user wants "high" port binding */
+#define IN6P_LOWPORT 0x20 /* user wants "low" port binding */
+#define IN6P_ANONPORT 0x40 /* port chosen for user */
+#define IN6P_FAITH 0x80 /* accept FAITH'ed connections */
+#define IN6P_PKTINFO 0x010000
+#define IN6P_HOPLIMIT 0x020000
+#define IN6P_NEXTHOP 0x040000
+#define IN6P_HOPOPTS 0x080000
+#define IN6P_DSTOPTS 0x100000
+#define IN6P_RTHDR 0x200000
+#define IN6P_CONTROLOPTS (0x3f0000 | IN6P_RECVOPTS | IN6P_RECVRETOPTS | IN6P_RECVDSTADDR)
+#endif
+
#define INPLOOKUP_WILDCARD 1
#define INPLOOKUP_SETLOCAL 2
#define INPLOOKUP_IPV6 4
@@ -201,4 +258,17 @@ void in_rtchange __P((struct inpcb *, int));
void in_setpeeraddr __P((struct inpcb *, struct mbuf *));
void in_setsockaddr __P((struct inpcb *, struct mbuf *));
int in_baddynamic __P((u_int16_t, u_int16_t));
+extern struct sockaddr_in *in_selectsrc __P((struct sockaddr_in *,
+ struct route *, int, struct ip_moptions *, int *));
+
+/* INET6 stuff */
+int in6_pcbnotify __P((struct inpcbtable *, struct sockaddr *,
+ u_int, struct in6_addr *, u_int, int,
+ void (*)(struct inpcb *, int)));
+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 inpcb *, struct ifnet *));
#endif
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index ed387d1efb3..179361ddbd0 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: in_proto.c,v 1.15 1999/10/28 03:21:51 angelos Exp $ */
+/* $OpenBSD: in_proto.c,v 1.16 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in_proto.c,v 1.14 1996/02/18 18:58:32 christos 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.
*
@@ -64,6 +93,14 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_pcb.h>
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet/ip6.h>
+#endif
+
#include <netinet/igmp_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
@@ -78,6 +115,19 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
* TCP/IP protocol family: IP, ICMP, UDP, TCP.
*/
+#if 0 /*KAME IPSEC*/
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <netinet6/ipcomp.h>
+#endif /* IPSEC */
+
+#include "gif.h"
+#if NGIF > 0
+#include <netinet/in_gif.h>
+#endif
+
#ifdef NSIP
#include <netns/ns_var.h>
#include <netns/idp_var.h>
@@ -107,7 +157,7 @@ void iplinit __P((void));
#endif
#ifdef INET6
-#include <netinet6/ipv6_var.h>
+#include <netinet6/ip6_var.h>
#endif /* INET6 */
#ifdef IPSEC
@@ -146,6 +196,20 @@ struct protosw inetsw[] = {
rip_usrreq,
0, 0, 0, 0, icmp_sysctl
},
+#if NGIF > 0
+{ SOCK_RAW, &inetdomain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
+ in_gif_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+},
+#ifdef INET6
+{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
+ in_gif_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+},
+#endif /* INET6 */
+#else /* NGIF */
#if defined(IPSEC) || defined(MROUTING)
{ SOCK_RAW, &inetdomain, IPPROTO_IPIP, PR_ATOMIC|PR_ADDR,
ip4_input, rip_output, 0, rip_ctloutput,
@@ -153,6 +217,7 @@ struct protosw inetsw[] = {
0, 0, 0, 0, ip4_sysctl
},
#endif /* MROUTING || IPSEC */
+#endif /*NGIF*/
{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR,
igmp_input, rip_output, 0, rip_ctloutput,
rip_usrreq,
@@ -204,10 +269,10 @@ struct protosw inetsw[] = {
0, 0, 0, 0, etherip_sysctl
},
#endif
-#ifdef INET6
+#if 0 /*NRL IPv6*/
/* IPv6 in IPv4 tunneled packets... */
{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
- ipv6_input, rip_output, ipv6_trans_ctlinput, rip_ctloutput,
+ ip6_input, rip_output, ipv6_trans_ctlinput, rip_ctloutput,
rip_usrreq,
0, 0, 0, 0
},
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index 7dafa91702d..c96bf076f15 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_var.h,v 1.2 1996/03/03 22:30:34 niklas Exp $ */
+/* $OpenBSD: in_var.h,v 1.3 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: in_var.h,v 1.16 1996/02/13 23:42:15 christos Exp $ */
/*
@@ -80,6 +80,7 @@ struct in_aliasreq {
TAILQ_HEAD(in_ifaddrhead, in_ifaddr);
extern struct in_ifaddrhead in_ifaddr;
extern struct ifqueue ipintrq; /* ip packet input queue */
+extern int inetctlerrmap[];
void in_socktrim __P((struct sockaddr_in *));
@@ -213,3 +214,7 @@ void in_delmulti __P((struct in_multi *));
void in_ifscrub __P((struct ifnet *, struct in_ifaddr *));
int in_control __P((struct socket *, u_long, caddr_t, struct ifnet *));
#endif
+
+
+/* INET6 stuff */
+#include <netinet6/in6_var.h>
diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h
index 524938bb9c7..a3fd5ea3549 100644
--- a/sys/netinet/ip.h
+++ b/sys/netinet/ip.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip.h,v 1.5 1999/07/06 20:17:52 cmetz Exp $ */
+/* $OpenBSD: ip.h,v 1.6 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: ip.h,v 1.9 1995/05/15 01:22:44 cgd Exp $ */
/*
@@ -77,6 +77,11 @@ struct ip {
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
/* IPTOS_LOWCOST 0x02 XXX */
+#if 1
+/* ECN bits proposed by Sally Floyd */
+#define IPTOS_CE 0x01 /* congestion experienced */
+#define IPTOS_ECT 0x02 /* ECN-capable transport */
+#endif
/*
* Definitions for IP precedence (also in ip_tos) (hopefully unused)
diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h
new file mode 100644
index 00000000000..33b19eba749
--- /dev/null
+++ b/sys/netinet/ip6.h
@@ -0,0 +1,3 @@
+/* $OpenBSD: ip6.h,v 1.1 1999/12/08 06:50:19 itojun Exp $ */
+
+#include <netinet6/ip6.h>
diff --git a/sys/netinet/ip_auth.c b/sys/netinet/ip_auth.c
index 4d7e5cce18c..ea64528f34e 100644
--- a/sys/netinet/ip_auth.c
+++ b/sys/netinet/ip_auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_auth.c,v 1.6 1999/11/22 04:35:11 deraadt Exp $ */
+/* $OpenBSD: ip_auth.c,v 1.7 1999/12/08 06:50:19 itojun Exp $ */
/*
* Copyright (C) 1998 by Darren Reed & Guido van Rooij.
*
@@ -7,7 +7,7 @@
* to the original author and the contributors.
*/
#if !defined(lint)
-static const char rcsid[] = "@(#)$Id: ip_auth.c,v 1.6 1999/11/22 04:35:11 deraadt Exp $";
+static const char rcsid[] = "@(#)$Id: ip_auth.c,v 1.7 1999/12/08 06:50:19 itojun Exp $";
#endif
#include <sys/errno.h>
@@ -53,6 +53,11 @@ static const char rcsid[] = "@(#)$Id: ip_auth.c,v 1.6 1999/11/22 04:35:11 deraad
#include <net/af.h>
#endif
#include <net/route.h>
+#ifdef _KERNEL
+#ifndef INET
+#error ipfilter assumes options INET
+#endif
+#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
diff --git a/sys/netinet/ip_ecn.c b/sys/netinet/ip_ecn.c
new file mode 100644
index 00000000000..ad8770fcfd7
--- /dev/null
+++ b/sys/netinet/ip_ecn.c
@@ -0,0 +1,148 @@
+/* $OpenBSD: ip_ecn.c,v 1.1 1999/12/08 06:50: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.
+ *
+ * KAME Id: ip_ecn.c,v 1.2 1999/07/30 12:17:15 itojun Exp
+ */
+/*
+ * ECN consideration on tunnel ingress/egress operation.
+ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet/ip6.h>
+#endif
+
+#include <netinet/ip_ecn.h>
+
+/*
+ * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation).
+ * call it after you've done the default initialization/copy for the outer.
+ */
+void
+ip_ecn_ingress(mode, outer, inner)
+ int mode;
+ u_int8_t *outer;
+ u_int8_t *inner;
+{
+ if (!outer || !inner)
+ panic("NULL pointer passed to ip_ecn_ingress");
+
+ switch (mode) {
+ case ECN_ALLOWED: /* ECN allowed */
+ *outer &= ~IPTOS_CE;
+ break;
+ case ECN_FORBIDDEN: /* ECN forbidden */
+ *outer &= ~(IPTOS_ECT | IPTOS_CE);
+ break;
+ case ECN_NOCARE: /* no consideration to ECN */
+ break;
+ }
+}
+
+/*
+ * modify inner ECN (TOS) field on egress operation (tunnel decapsulation).
+ * call it after you've done the default initialization/copy for the inner.
+ */
+void
+ip_ecn_egress(mode, outer, inner)
+ int mode;
+ u_int8_t *outer;
+ u_int8_t *inner;
+{
+ if (!outer || !inner)
+ panic("NULL pointer passed to ip_ecn_egress");
+
+ switch (mode) {
+ case ECN_ALLOWED:
+ if (*outer & IPTOS_CE)
+ *inner |= IPTOS_CE;
+ break;
+ case ECN_FORBIDDEN: /* ECN forbidden */
+ case ECN_NOCARE: /* no consideration to ECN */
+ break;
+ }
+}
+
+#ifdef INET6
+void
+ip6_ecn_ingress(mode, outer, inner)
+ int mode;
+ u_int32_t *outer;
+ u_int32_t *inner;
+{
+ u_int8_t outer8, inner8;
+
+ if (!outer || !inner)
+ panic("NULL pointer passed to ip6_ecn_ingress");
+
+ outer8 = (ntohl(*outer) >> 20) & 0xff;
+ inner8 = (ntohl(*inner) >> 20) & 0xff;
+ ip_ecn_ingress(mode, &outer8, &inner8);
+ *outer &= ~htonl(0xff << 20);
+ *outer |= htonl((u_int32_t)outer8 << 20);
+}
+
+void
+ip6_ecn_egress(mode, outer, inner)
+ int mode;
+ u_int32_t *outer;
+ u_int32_t *inner;
+{
+ u_int8_t outer8, inner8;
+
+ if (!outer || !inner)
+ panic("NULL pointer passed to ip6_ecn_egress");
+
+ outer8 = (ntohl(*outer) >> 20) & 0xff;
+ inner8 = (ntohl(*inner) >> 20) & 0xff;
+ ip_ecn_egress(mode, &outer8, &inner8);
+ *inner &= ~htonl(0xff << 20);
+ *inner |= htonl((u_int32_t)inner8 << 20);
+}
+#endif
diff --git a/sys/netinet/ip_ecn.h b/sys/netinet/ip_ecn.h
new file mode 100644
index 00000000000..e82daec9822
--- /dev/null
+++ b/sys/netinet/ip_ecn.h
@@ -0,0 +1,55 @@
+/* $OpenBSD: ip_ecn.h,v 1.1 1999/12/08 06:50: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.
+ *
+ * KAME Id: ip_ecn.h,v 1.2 1999/08/19 12:57:44 itojun Exp
+ */
+/*
+ * ECN consideration on tunnel ingress/egress operation.
+ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#if defined(_KERNEL) && !defined(_LKM)
+#include "opt_inet.h"
+#endif
+#endif
+
+#define ECN_ALLOWED 1 /* ECN allowed */
+#define ECN_FORBIDDEN 0 /* ECN forbidden */
+#define ECN_NOCARE (-1) /* no consideration to ECN */
+
+#if defined(KERNEL) || defined(_KERNEL)
+extern void ip_ecn_ingress __P((int, u_int8_t *, u_int8_t *));
+extern void ip_ecn_egress __P((int, u_int8_t *, u_int8_t *));
+#ifdef INET6
+extern void ip6_ecn_ingress __P((int, u_int32_t *, u_int32_t *));
+extern void ip6_ecn_egress __P((int, u_int32_t *, u_int32_t *));
+#endif
+#endif
diff --git a/sys/netinet/ip_fil.c b/sys/netinet/ip_fil.c
index ad7890d73b9..b0d51cf2f43 100644
--- a/sys/netinet/ip_fil.c
+++ b/sys/netinet/ip_fil.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_fil.c,v 1.24 1999/06/07 22:00:32 deraadt Exp $ */
+/* $OpenBSD: ip_fil.c,v 1.25 1999/12/08 06:50:19 itojun Exp $ */
/*
* Copyright (C) 1993-1998 by Darren Reed.
*
@@ -8,7 +8,7 @@
*/
#if !defined(lint)
static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-1995 Darren Reed";
-static const char rcsid[] = "@(#)$Id: ip_fil.c,v 1.24 1999/06/07 22:00:32 deraadt Exp $";
+static const char rcsid[] = "@(#)$Id: ip_fil.c,v 1.25 1999/12/08 06:50:19 itojun Exp $";
#endif
#ifndef SOLARIS
@@ -73,6 +73,11 @@ static const char rcsid[] = "@(#)$Id: ip_fil.c,v 1.24 1999/06/07 22:00:32 deraad
# endif
#endif
#include <net/route.h>
+#ifdef _KERNEL
+#ifndef INET
+#error ipfilter assumes options INET
+#endif
+#endif
#include <netinet/in.h>
#if !(defined(__sgi) && !defined(IFF_DRVRLOCK)) /* IRIX < 6 */
#include <netinet/in_var.h>
diff --git a/sys/netinet/ip_frag.c b/sys/netinet/ip_frag.c
index d3b059cbb5c..d7f1a39f030 100644
--- a/sys/netinet/ip_frag.c
+++ b/sys/netinet/ip_frag.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_frag.c,v 1.11 1999/02/05 05:58:51 deraadt Exp $ */
+/* $OpenBSD: ip_frag.c,v 1.12 1999/12/08 06:50:19 itojun Exp $ */
/*
* Copyright (C) 1993-1998 by Darren Reed.
*
@@ -8,7 +8,7 @@
*/
#if !defined(lint)
static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-1995 Darren Reed";
-static const char rcsid[] = "@(#)$Id: ip_frag.c,v 1.11 1999/02/05 05:58:51 deraadt Exp $";
+static const char rcsid[] = "@(#)$Id: ip_frag.c,v 1.12 1999/12/08 06:50:19 itojun Exp $";
#endif
#include <sys/errno.h>
@@ -51,6 +51,11 @@ static const char rcsid[] = "@(#)$Id: ip_frag.c,v 1.11 1999/02/05 05:58:51 deraa
#include <net/af.h>
#endif
#include <net/route.h>
+#ifdef _KERNEL
+#ifndef INET
+#error ipfilter assumes options INET
+#endif
+#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c
index bdd165d9fbe..5c7d16adb92 100644
--- a/sys/netinet/ip_icmp.c
+++ b/sys/netinet/ip_icmp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_icmp.c,v 1.18 1999/09/26 23:59:15 deraadt Exp $ */
+/* $OpenBSD: ip_icmp.c,v 1.19 1999/12/08 06:50:19 itojun Exp $ */
/* $NetBSD: ip_icmp.c,v 1.19 1996/02/13 23:42:22 christos Exp $ */
/*
@@ -72,6 +72,12 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/ip_var.h>
#include <netinet/icmp_var.h>
+#if 0 /*KAME IPSEC*/
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif
+
#include <machine/stdarg.h>
/*
@@ -86,6 +92,12 @@ int icmpbmcastecho = 0;
int icmpprintfs = 0;
#endif
+#if 0
+static int ip_next_mtu __P((int, int));
+#else
+/*static*/ int ip_next_mtu __P((int, int));
+#endif
+
extern struct protosw inetsw[];
/*
@@ -211,6 +223,7 @@ icmp_input(m, va_alist)
va_dcl
#endif
{
+ int proto;
register struct icmp *icp;
register struct ip *ip = mtod(m, struct ip *);
int icmplen = ip->ip_len;
@@ -224,6 +237,7 @@ icmp_input(m, va_alist)
va_start(ap, m);
hlen = va_arg(ap, int);
+ proto = va_arg(ap, int);
va_end(ap);
/*
@@ -267,6 +281,13 @@ icmp_input(m, va_alist)
printf("icmp_input, type %d code %d\n", icp->icmp_type,
icp->icmp_code);
#endif
+#if 0 /*KAME IPSEC*/
+ /* drop it if it does not match the policy */
+ if (ipsec4_in_reject(m, NULL)) {
+ ipsecstat.in_polvio++;
+ goto freeit;
+ }
+#endif
if (icp->icmp_type > ICMP_MAXTYPE)
goto raw;
icmpstat.icps_inhist[icp->icmp_type]++;
@@ -284,7 +305,7 @@ icmp_input(m, va_alist)
break;
case ICMP_UNREACH_NEEDFRAG:
-#ifdef INET6
+#if 0 /*NRL INET6*/
if (icp->icmp_nextmtu) {
extern int ipv6_trans_mtu
__P((struct mbuf **, int, int));
@@ -385,6 +406,10 @@ icmp_input(m, va_alist)
printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
#endif
icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
+ /*
+ * XXX if the packet contains [IPv4 AH TCP], we can't make a
+ * notification to TCP layer.
+ */
ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput;
if (ctlfunc)
(*ctlfunc)(code, sintosa(&icmpsrc), &icp->icmp_ip);
@@ -484,6 +509,9 @@ reflect:
(struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
sintosa(&icmpgw), (struct rtentry **)0);
pfctlinput(PRC_REDIRECT_HOST, sintosa(&icmpsrc));
+#if 0 /*KAME IPSEC*/
+ key_sa_routechange((struct sockaddr *)&icmpsrc);
+#endif
break;
/*
@@ -501,7 +529,7 @@ reflect:
}
raw:
- rip_input(m, 0);
+ rip_input(m, hlen, proto);
return;
freeit:
@@ -661,6 +689,9 @@ icmp_send(m, opts)
buf, inet_ntoa(ip->ip_src));
}
#endif
+#if 0 /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
(void) ip_output(m, opts, NULL, 0, NULL, NULL);
}
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 456572c9b34..a97d8cbd491 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_input.c,v 1.43 1999/11/29 16:22:29 ho Exp $ */
+/* $OpenBSD: ip_input.c,v 1.44 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */
/*
@@ -722,6 +722,8 @@ insert:
* Make header visible.
*/
ip->ip_len = next;
+ ip->ip_ttl = 0; /* xxx */
+ ip->ip_sum = 0;
ip->ip_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
LIST_REMOVE(fp, ipq_q);
@@ -1073,7 +1075,7 @@ save_rte(option, dst)
#ifdef DIAGNOSTIC
if (ipprintfs)
printf("save_rte: olen %d\n", olen);
-#endif
+#endif /* 0 */
if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
return;
bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
@@ -1254,6 +1256,9 @@ ip_forward(m, srcrt)
struct mbuf *mcopy;
n_long dest;
struct ifnet *destifp;
+#if 0 /*KAME IPSEC*/
+ struct ifnet dummyifp;
+#endif
dest = 0;
#ifdef DIAGNOSTIC
@@ -1330,6 +1335,9 @@ ip_forward(m, srcrt)
}
}
+#if 0 /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
(IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)),
0, NULL, NULL);
@@ -1367,8 +1375,57 @@ ip_forward(m, srcrt)
case EMSGSIZE:
type = ICMP_UNREACH;
code = ICMP_UNREACH_NEEDFRAG;
+#if 1 /*KAME IPSEC*/
if (ipforward_rt.ro_rt)
destifp = ipforward_rt.ro_rt->rt_ifp;
+#else
+ /*
+ * If the packet is routed over IPsec tunnel, tell the
+ * originator the tunnel MTU.
+ * tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
+ * XXX quickhack!!!
+ */
+ if (ipforward_rt.ro_rt) {
+ struct secpolicy *sp;
+ int ipsecerror;
+ int ipsechdr;
+ struct route *ro;
+
+ sp = ipsec4_getpolicybyaddr(mcopy,
+ IP_FORWARDING,
+ &ipsecerror);
+
+ if (sp == NULL)
+ destifp = ipforward_rt.ro_rt->rt_ifp;
+ else {
+ /* count IPsec header size */
+ ipsechdr = ipsec4_hdrsiz(mcopy, NULL);
+
+ /*
+ * find the correct route for outer IPv4
+ * header, compute tunnel MTU.
+ *
+ * XXX BUG ALERT
+ * The "dummyifp" code relies upon the fact
+ * that icmp_error() touches only ifp->if_mtu.
+ */
+ /*XXX*/
+ destifp = NULL;
+ if (sp->req != NULL
+ && sp->req->sa != NULL) {
+ ro = &sp->req->sa->saidx->sa_route;
+ if (ro->ro_rt && ro->ro_rt->rt_ifp) {
+ dummyifp.if_mtu =
+ ro->ro_rt->rt_ifp->if_mtu;
+ dummyifp.if_mtu -= ipsechdr;
+ destifp = &dummyifp;
+ }
+ }
+
+ key_freesp(sp);
+ }
+ }
+#endif /*IPSEC*/
ipstat.ips_cantfrag++;
break;
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
index 975e834e858..feb51fc1a00 100644
--- a/sys/netinet/ip_mroute.h
+++ b/sys/netinet/ip_mroute.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_mroute.h,v 1.4 1999/08/08 00:43:00 niklas Exp $ */
+/* $OpenBSD: ip_mroute.h,v 1.5 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: ip_mroute.h,v 1.10 1996/02/13 23:42:55 christos Exp $ */
/*
@@ -222,8 +222,12 @@ int legal_vif_num __P((int));
int ip_rsvp_vif_init __P((struct socket *, struct mbuf *));
int ip_rsvp_vif_done __P((struct socket *, struct mbuf *));
void ip_rsvp_force_done __P((struct socket *));
+#if 0
void rsvp_input __P((struct mbuf *, struct ifnet *));
#else
+void rsvp_input __P((struct mbuf *, int, int));
+#endif
+#else
int ip_mforward __P((struct mbuf *, struct ifnet *));
#endif
void ipip_input __P((struct mbuf *, ...));
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 186433bef12..ec98e1a0dba 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_output.c,v 1.54 1999/12/06 07:14:36 angelos Exp $ */
+/* $OpenBSD: ip_output.c,v 1.55 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */
/*
@@ -47,6 +47,9 @@
#include <sys/kernel.h>
#include <sys/proc.h>
+#include <vm/vm.h>
+#include <sys/proc.h>
+
#include <net/if.h>
#include <net/route.h>
@@ -84,6 +87,12 @@ extern u_int8_t get_sa_require __P((struct inpcb *));
#endif
+#if 0 /*KAME IPSEC*/
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif /*IPSEC*/
+
static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));
static void ip_mloopback
__P((struct ifnet *, struct mbuf *, struct sockaddr_in *));
@@ -153,6 +162,10 @@ ip_output(m0, va_alist)
+#if 0 /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+
#ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0)
panic("ip_output no HDR");
@@ -699,10 +712,21 @@ no_encap:
error = (*ifp->if_output)(ifp, m, sintosa(dst), ro->ro_rt);
goto done;
}
+
/*
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
+#if 0
+ /*
+ * If IPsec packet is too big for the interface, try fragment it.
+ * XXX This really is a quickhack. May be inappropriate.
+ * XXX fails if somebody is sending AH'ed packet, with:
+ * sizeof(packet without AH) < mtu < sizeof(packet with AH)
+ */
+ if (sab && ip->ip_p != IPPROTO_AH && (flags & IP_FORWARDING) == 0)
+ ip->ip_off &= ~IP_DF;
+#endif /*IPSEC*/
if (ip->ip_off & IP_DF) {
error = EMSGSIZE;
ipstat.ips_cantfrag++;
@@ -1066,6 +1090,30 @@ ip_ctloutput(op, so, level, optname, mp)
#endif
break;
+#if 0 /*KAME IPSEC*/
+ case IP_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ int len = 0;
+ int priv = 0;
+#ifdef __NetBSD__
+ if (p == 0 || suser(p->p_ucred, &p->p_acflag))
+ priv = 0;
+ else
+ priv = 1;
+#else
+ priv = (in6p->in6p_socket->so_state & SS_PRIV);
+#endif
+ if (m != 0) {
+ req = mtod(m, caddr_t);
+ len = m->m_len;
+ }
+ error = ipsec_set_policy(&inp->inp_sp,
+ optname, req, len, priv);
+ break;
+ }
+#endif /*IPSEC*/
+
default:
error = ENOPROTOOPT;
break;
@@ -1121,6 +1169,12 @@ ip_ctloutput(op, so, level, optname, mp)
*mtod(m, int *) = optval;
break;
+#if 0 /*KAME IPSEC*/
+ case IP_IPSEC_POLICY:
+ error = ipsec_get_policy(inp->inp_sp, mp);
+ break;
+#endif /*IPSEC*/
+
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index ccf7e9d2b92..fb12553a38a 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_var.h,v 1.11 1999/02/17 23:51:12 deraadt Exp $ */
+/* $OpenBSD: ip_var.h,v 1.12 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $ */
/*
@@ -145,6 +145,7 @@ struct ipstat {
u_long ips_badfrags; /* malformed fragments (bad length) */
u_long ips_rcvmemdrop; /* frags dropped for lack of memory */
u_long ips_toolong; /* ip length > max ip packet size */
+ u_long ips_nogif; /* no match gif found */
};
#ifdef _KERNEL
diff --git a/sys/netinet/tcp_debug.c b/sys/netinet/tcp_debug.c
index 9adb1879804..13e1c89eb52 100644
--- a/sys/netinet/tcp_debug.c
+++ b/sys/netinet/tcp_debug.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_debug.c,v 1.5 1999/07/02 20:39:07 cmetz Exp $ */
+/* $OpenBSD: tcp_debug.c,v 1.6 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_debug.c,v 1.10 1996/02/13 23:43:36 christos Exp $ */
/*
@@ -81,8 +81,10 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/tcp_debug.h>
#ifdef INET6
-#include <netinet6/ipv6.h>
-#include <netinet6/in6.h>
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/ip6.h>
#endif /* INET6 */
#ifdef TCPDEBUG
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index e2a6b6136d3..f080fb3c386 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_input.c,v 1.51 1999/11/15 05:50:59 hugh Exp $ */
+/* $OpenBSD: tcp_input.c,v 1.52 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
@@ -82,18 +82,32 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#endif /* IPSEC */
#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
#include <sys/domain.h>
#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
#include <netinet6/tcpipv6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#ifndef CREATE_IPV6_MAPPED
+#define CREATE_IPV6_MAPPED(a6, a4) \
+do { \
+ bzero(&(a6), sizeof(a6)); \
+ (a6).s6_addr[10] = (a6).s6_addr[11] = 0xff; \
+ *(u_int32_t *)&(a6).s6_addr[12] = (a4); \
+} while (0)
+#endif
struct tcpiphdr tcp_saveti;
struct tcpipv6hdr tcp_saveti6;
/* for the packet header length in the mbuf */
#define M_PH_LEN(m) (((struct mbuf *)(m))->m_pkthdr.len)
-#define M_V6_LEN(m) (M_PH_LEN(m) - sizeof(struct ipv6))
+#define M_V6_LEN(m) (M_PH_LEN(m) - sizeof(struct ip6_hdr))
#define M_V4_LEN(m) (M_PH_LEN(m) - sizeof(struct ip))
#endif /* INET6 */
@@ -111,6 +125,22 @@ extern u_long sb_max;
#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
/*
+ * Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint.
+ */
+#ifdef INET6
+#define ND6_HINT(tp) \
+do { \
+ if (tp && tp->t_inpcb && (tp->t_inpcb->inp_flags & INP_IPV6) \
+ && !(tp->t_inpcb->inp_flags & INP_IPV6_MAPPED) \
+ && tp->t_inpcb->inp_route6.ro_rt) { \
+ nd6_nud_hint(tp->t_inpcb->inp_route6.ro_rt, NULL); \
+ } \
+} while (0)
+#else
+#define ND6_HINT(tp)
+#endif
+
+/*
* Insert segment ti into reassembly queue of tcp with
* control block tp. Return TH_FIN if reassembly now includes
* a segment with FIN. The macro form does the common case inline
@@ -237,6 +267,7 @@ present:
nq = q->ipqe_q.le_next;
LIST_REMOVE(q, ipqe_q);
+ ND6_HINT(tp);
if (so->so_state & SS_CANTRCVMORE)
m_freem(q->ipqe_m);
else
@@ -295,6 +326,44 @@ done:
splx(s);
}
+#if defined(INET6) && !defined(TCP6)
+int
+tcp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+
+#if defined(NFAITH) && 0 < NFAITH
+ if (m->m_pkthdr.rcvif) {
+ if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+ /* XXX send icmp6 host/port unreach? */
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+ }
+#endif
+
+ /*
+ * draft-itojun-ipv6-tcp-to-anycast
+ * better place to put this in?
+ */
+ if (m->m_flags & M_ANYCAST6) {
+ if (m->m_len >= sizeof(struct ip6_hdr)) {
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_ADDR,
+ (caddr_t)&ip6->ip6_dst - (caddr_t)ip6);
+ } else
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+
+ tcp_input(m, *offp, proto);
+ return IPPROTO_DONE;
+}
+#endif
+
/*
* TCP input routine, follows pages 65-76 of the
* protocol specification dated September, 1981 very closely.
@@ -316,6 +385,7 @@ tcp_input(m, va_alist)
register int tiflags;
struct socket *so = NULL;
int todrop, acked, ourfinisacked, needoutput = 0;
+ int hdroptlen = 0;
short ostate = 0;
struct in_addr laddr;
int dropsocket = 0;
@@ -332,7 +402,7 @@ tcp_input(m, va_alist)
#ifdef INET6
struct in6_addr laddr6;
unsigned short is_ipv6; /* Type of incoming datagram. */
- struct ipv6 *ipv6 = NULL;
+ struct ip6_hdr *ipv6 = NULL;
#endif /* INET6 */
va_start(ap, m);
@@ -369,8 +439,15 @@ tcp_input(m, va_alist)
#else /* INET6 */
if (!is_ipv6)
#endif /* INET6 */
- if (iphlen > sizeof (struct ip))
+ if (iphlen > sizeof (struct ip)) {
+#if 0 /*XXX*/
ip_stripoptions(m, (struct mbuf *)0);
+#else
+ printf("extension headers are not allowed\n");
+ m_freem(m);
+ return;
+#endif
+ }
if (m->m_len < iphlen + sizeof(struct tcphdr)) {
if ((m = m_pullup2(m, iphlen + sizeof(struct tcphdr))) == 0) {
tcpstat.tcps_rcvshort++;
@@ -391,22 +468,28 @@ tcp_input(m, va_alist)
if (is_ipv6) {
#ifdef DIAGNOSTIC
- if (iphlen < sizeof(struct ipv6)) {
+ if (iphlen < sizeof(struct ip6_hdr)) {
m_freem(m);
return;
}
#endif /* DIAGNOSTIC */
/* strip off any options */
- if (iphlen > sizeof(struct ipv6)) {
+ if (iphlen > sizeof(struct ip6_hdr)) {
+#if 0 /*XXX*/
ipv6_stripoptions(m, iphlen);
- iphlen = sizeof(struct ipv6);
+#else
+ printf("extension headers are not allowed\n");
+ m_freem(m);
+ return;
+#endif
+ iphlen = sizeof(struct ip6_hdr);
}
ti = NULL;
- ipv6 = mtod(m, struct ipv6 *);
+ ipv6 = mtod(m, struct ip6_hdr *);
- if (in6_cksum(m, IPPROTO_TCP, tlen, sizeof(struct ipv6))) {
+ if (in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), tlen)) {
tcpstat.tcps_rcvbadsum++;
goto drop;
} /* endif in6_cksum */
@@ -453,7 +536,7 @@ tcp_input(m, va_alist)
}
#ifdef INET6
if (is_ipv6)
- ipv6 = mtod(m, struct ipv6 *);
+ ipv6 = mtod(m, struct ip6_hdr *);
else
#endif /* INET6 */
ti = mtod(m, struct tcpiphdr *);
@@ -495,8 +578,8 @@ tcp_input(m, va_alist)
findpcb:
#ifdef INET6
if (is_ipv6) {
- inp = in6_pcbhashlookup(&tcbtable, &ipv6->ipv6_src, th->th_sport,
- &ipv6->ipv6_dst, th->th_dport);
+ inp = in6_pcbhashlookup(&tcbtable, &ipv6->ip6_src, th->th_sport,
+ &ipv6->ip6_dst, th->th_dport);
} else
#endif /* INET6 */
inp = in_pcbhashlookup(&tcbtable, ti->ti_src, ti->ti_sport,
@@ -505,8 +588,8 @@ findpcb:
++tcpstat.tcps_pcbhashmiss;
#ifdef INET6
if (is_ipv6)
- inp = in_pcblookup(&tcbtable, &ipv6->ipv6_src,
- th->th_sport, &ipv6->ipv6_dst, th->th_dport,
+ inp = in_pcblookup(&tcbtable, &ipv6->ip6_src,
+ th->th_sport, &ipv6->ip6_dst, th->th_dport,
INPLOOKUP_WILDCARD | INPLOOKUP_IPV6);
else
#endif /* INET6 */
@@ -601,10 +684,10 @@ findpcb:
| INP_IPV6_MAPPED));
if ((inp->inp_flags & INP_IPV6) &&
!(inp->inp_flags & INP_IPV6_MAPPED)) {
- inp->inp_ipv6.ipv6_hoplimit =
- oldinpcb->inp_ipv6.ipv6_hoplimit;
- inp->inp_ipv6.ipv6_versfl =
- oldinpcb->inp_ipv6.ipv6_versfl;
+ inp->inp_ipv6.ip6_hlim =
+ oldinpcb->inp_ipv6.ip6_hlim;
+ inp->inp_ipv6.ip6_flow =
+ oldinpcb->inp_ipv6.ip6_flow;
}
}
#else /* INET6 */
@@ -613,11 +696,11 @@ findpcb:
inp->inp_lport = th->th_dport;
#ifdef INET6
if (is_ipv6) {
- inp->inp_laddr6 = ipv6->ipv6_dst;
+ inp->inp_laddr6 = ipv6->ip6_dst;
inp->inp_fflowinfo = htonl(0x0fffffff) &
- ipv6->ipv6_versfl;
+ ipv6->ip6_flow;
- /*inp->inp_options = ipv6_srcroute();*/ /* soon. */
+ /*inp->inp_options = ip6_srcroute();*/ /* soon. */
/* still need to tweak outbound options
processing to include this mbuf in
the right place and put the correct
@@ -656,7 +739,7 @@ findpcb:
#ifdef notyet
#ifdef INET6
if (is_ipv6)
- ipv6_icmp_error(m, ICMPV6_BLAH, ICMPV6_BLAH, 0);
+ icmp6_error(m, ICMPV6_BLAH, ICMPV6_BLAH, 0);
else
#endif /* INET6 */
icmp_error(m, ICMP_BLAH, ICMP_BLAH, 0, 0);
@@ -744,6 +827,7 @@ findpcb:
acked = th->th_ack - tp->snd_una;
tcpstat.tcps_rcvackpack++;
tcpstat.tcps_rcvackbyte += acked;
+ ND6_HINT(tp);
sbdrop(&so->so_snd, acked);
tp->snd_una = th->th_ack;
#if defined(TCP_SACK) || defined(TCP_NEWRENO)
@@ -797,12 +881,12 @@ findpcb:
tp->rcv_nxt += tlen;
tcpstat.tcps_rcvpack++;
tcpstat.tcps_rcvbyte += tlen;
+ ND6_HINT(tp);
/*
* Drop TCP, IP headers and TCP options then add data
* to socket buffer.
*/
- m->m_data += iphlen + off;
- m->m_len -= iphlen + off;
+ m_adj(m, iphlen + off);
sbappend(&so->so_rcv, m);
sorwakeup(so);
if (th->th_flags & TH_PUSH)
@@ -814,10 +898,9 @@ findpcb:
}
/*
- * Drop TCP, IP headers and TCP options.
+ * Compute mbuf offset to TCP data segment.
*/
- m->m_data += iphlen + off;
- m->m_len -= iphlen + off;
+ hdroptlen = iphlen + off;
/*
* Calculate amount of space in receive window,
@@ -865,7 +948,7 @@ findpcb:
if (th->th_dport == th->th_sport) {
#ifdef INET6
if (is_ipv6) {
- if (IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &ipv6->ipv6_dst))
+ if (IN6_ARE_ADDR_EQUAL(&ipv6->ip6_src, &ipv6->ip6_dst))
goto drop;
} else {
#endif /* INET6 */
@@ -886,7 +969,7 @@ findpcb:
#ifdef INET6
if (is_ipv6) {
/* XXX What about IPv6 Anycasting ?? :-( rja */
- if (IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst))
+ if (IN6_IS_ADDR_MULTICAST(&ipv6->ip6_dst))
goto drop;
} else
#endif /* INET6 */
@@ -912,13 +995,13 @@ findpcb:
sin6 = mtod(am, struct sockaddr_in6 *);
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(struct sockaddr_in6);
- sin6->sin6_addr = ipv6->ipv6_src;
+ sin6->sin6_addr = ipv6->ip6_src;
sin6->sin6_port = th->th_sport;
sin6->sin6_flowinfo = htonl(0x0fffffff) &
- inp->inp_ipv6.ipv6_versfl;
+ inp->inp_ipv6.ip6_flow;
laddr6 = inp->inp_laddr6;
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
- inp->inp_laddr6 = ipv6->ipv6_dst;
+ inp->inp_laddr6 = ipv6->ip6_dst;
/* This is a good optimization. */
if (in6_pcbconnect(inp, am)) {
inp->inp_laddr6 = laddr6;
@@ -982,6 +1065,7 @@ findpcb:
goto drop;
}
(void) m_free(am);
+ tp->pf = PF_INET;
#ifdef INET6
} /* if (inp->inp_flags & INP_IPV6) */
} /* if (is_ipv6) */
@@ -1210,7 +1294,7 @@ trimthenstep6:
tcpstat.tcps_rcvpartduppack++;
tcpstat.tcps_rcvpartdupbyte += todrop;
}
- m_adj(m, todrop);
+ hdroptlen += todrop; /* drop from head afterwards */
th->th_seq += todrop;
tlen -= todrop;
if (th->th_urp > todrop)
@@ -1295,7 +1379,11 @@ trimthenstep6:
* Close the tcb.
*/
if (tiflags & TH_RST) {
+#ifndef INET6
if (ti->ti_seq != tp->last_ack_sent)
+#else
+ if (th->th_seq != tp->last_ack_sent)
+#endif
goto drop;
switch (tp->t_state) {
@@ -1662,6 +1750,7 @@ trimthenstep6:
#endif
tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
}
+ ND6_HINT(tp);
if (acked > so->so_snd.sb_cc) {
tp->snd_wnd -= so->so_snd.sb_cc;
sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
@@ -1814,7 +1903,7 @@ step6:
&& (so->so_options & SO_OOBINLINE) == 0
#endif
)
- tcp_pulloutofband(so, th->th_urp, m); /* XXX? */
+ tcp_pulloutofband(so, th->th_urp, m, hdroptlen);
} else
/*
* If no out of band data is expected,
@@ -1845,9 +1934,12 @@ dodata: /* XXX */
tiflags = th->th_flags & TH_FIN;
tcpstat.tcps_rcvpack++;
tcpstat.tcps_rcvbyte += tlen;
+ ND6_HINT(tp);
+ m_adj(m, hdroptlen);
sbappend(&so->so_rcv, m);
sorwakeup(so);
} else {
+ m_adj(m, hdroptlen);
tiflags = tcp_reass(tp, th, m, &tlen);
tp->t_flags |= TF_ACKNOW;
}
@@ -1973,7 +2065,7 @@ dropwithreset:
if (is_ipv6) {
/* For following calls to tcp_respond */
ti = mtod(m, struct tcpiphdr *);
- if (IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst))
+ if (IN6_IS_ADDR_MULTICAST(&ipv6->ip6_dst))
goto drop;
} else {
#endif /* INET6 */
@@ -2515,12 +2607,13 @@ tcp_sack_partialack(tp, th)
* sequencing purposes.
*/
void
-tcp_pulloutofband(so, urgent, m)
+tcp_pulloutofband(so, urgent, m, off)
struct socket *so;
u_int urgent;
register struct mbuf *m;
+ int off;
{
- int cnt = urgent - 1;
+ int cnt = off + urgent - 1;
while (cnt >= 0) {
if (m->m_len > cnt) {
@@ -2665,7 +2758,7 @@ tcp_mss(tp, offer)
* Get a new IPv6 route if an IPv6 destination, otherwise, get
* and IPv4 route (including those pesky IPv4-mapped addresses).
*/
- bzero(ro,sizeof(struct route6));
+ bzero(ro,sizeof(struct route_in6));
if (sotopf(so) == AF_INET6) {
if (IN6_IS_ADDR_V4MAPPED(&inp->inp_faddr6)) {
/* Get an IPv4 route. */
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 23bc4f94b9f..8e73f4978f0 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_output.c,v 1.24 1999/12/02 16:31:17 deraadt Exp $ */
+/* $OpenBSD: tcp_output.c,v 1.25 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_output.c,v 1.16 1997/06/03 16:17:09 kml Exp $ */
/*
@@ -440,17 +440,13 @@ send:
* NOTE: we assume that the IP/TCP header plus TCP options
* always fit in a single mbuf, leaving room for a maximum
* link header, i.e.
- * max_linkhdr + sizeof(network header) + sizeof(struct tcphdr) +
- * optlen <= MHLEN
+ * max_linkhdr + sizeof(network header) + sizeof(struct tcphdr +
+ * optlen <= MHLEN
*/
optlen = 0;
-#if defined(INET) && defined(INET6)
switch (tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0: /* If tp->pf is 0, then assume IPv4 unless not avail */
+ case 0: /*default to PF_INET*/
#ifdef INET
case PF_INET:
hdrlen = sizeof(struct ip) + sizeof(struct tcphdr);
@@ -458,7 +454,7 @@ send:
#endif /* INET */
#ifdef INET6
case PF_INET6:
- hdrlen = sizeof(struct ipv6) + sizeof(struct tcphdr);
+ hdrlen = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
break;
#endif /* INET6 */
default:
@@ -626,13 +622,20 @@ send:
m->m_data -= hdrlen;
#else
MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m != NULL) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ m = NULL;
+ }
+ }
if (m == NULL) {
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
- if (len <= MHLEN - hdrlen - max_linkhdr) {
+ if (len <= MCLBYTES - hdrlen - max_linkhdr) {
m_copydata(so->so_snd.sb_mb, off, (int) len,
mtod(m, caddr_t) + hdrlen);
m->m_len += len;
@@ -664,6 +667,13 @@ send:
tcpstat.tcps_sndwinup++;
MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m != NULL) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ m = NULL;
+ }
+ }
if (m == NULL) {
error = ENOBUFS;
goto out;
@@ -763,12 +773,8 @@ send:
tp->snd_up = tp->snd_una; /* drag it along */
/* Put TCP length in pseudo-header */
-#if defined(INET) && defined(INET6)
switch (tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
if (len + optlen)
@@ -790,12 +796,8 @@ send:
memset(&sa, 0, sizeof(union sockaddr_union));
-#if defined(INET) && defined(INET6)
- switch(tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ switch (tp->pf) {
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
sa.sa.sa_len = sizeof(struct sockaddr_in);
@@ -807,7 +809,7 @@ send:
case AF_INET6:
sa.sa.sa_len = sizeof(struct sockaddr_in6);
sa.sa.sa_family = AF_INET6;
- sa.sin6.sin6_addr = mtod(m, struct ipv6 *)->ipv6_dst;
+ sa.sin6.sin6_addr = mtod(m, struct ip6_hdr *)->ip6_dst;
break;
#endif /* INET6 */
}
@@ -820,12 +822,8 @@ send:
MD5Init(&ctx);
-#if defined(INET) && defined(INET6)
- switch(tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ switch (tp->pf) {
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
{
@@ -875,12 +873,8 @@ send:
* Put TCP length in extended header, and then
* checksum extended header and data.
*/
-#if defined(INET) && defined(INET6)
switch (tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
th->th_sum = in_cksum(m, (int)(hdrlen + len));
@@ -888,8 +882,9 @@ send:
#endif /* INET */
#ifdef INET6
case AF_INET6:
- th->th_sum = in6_cksum(m, IPPROTO_TCP, hdrlen + len,
- sizeof(struct ipv6));
+ m->m_pkthdr.len = hdrlen + len;
+ th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
+ hdrlen - sizeof(struct ip6_hdr) + len);
break;
#endif /* INET6 */
}
@@ -981,12 +976,8 @@ send:
*/
m->m_pkthdr.len = hdrlen + len;
-#if defined(INET) && defined(INET6)
switch (tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
{
@@ -997,7 +988,6 @@ send:
ip->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl;
ip->ip_tos = tp->t_inpcb->inp_ip.ip_tos;
}
-
error = ip_output(m, tp->t_inpcb->inp_options,
&tp->t_inpcb->inp_route, so->so_options & SO_DONTROUTE,
0, tp->t_inpcb);
@@ -1006,16 +996,17 @@ send:
#ifdef INET6
case AF_INET6:
{
- struct ipv6 *ipv6;
+ struct ip6_hdr *ipv6;
- ipv6->ipv6_length = m->m_pkthdr.len -
- sizeof(struct ipv6);
- ipv6->ipv6_nexthdr = IPPROTO_TCP;
+ ipv6 = mtod(m, struct ip6_hdr *);
+ ipv6->ip6_plen = m->m_pkthdr.len -
+ sizeof(struct ip6_hdr);
+ ipv6->ip6_nxt = IPPROTO_TCP;
+ ipv6->ip6_hlim = in6_selecthlim(tp->t_inpcb, NULL);
}
-
- error = ipv6_output(m, &tp->t_inpcb->inp_route6,
- (so->so_options & SO_DONTROUTE), NULL, NULL,
- tp->t_inpcb->inp_socket);
+ error = ip6_output(m, tp->t_inpcb->inp_outputopts6,
+ &tp->t_inpcb->inp_route6,
+ (so->so_options & SO_DONTROUTE), NULL, NULL);
break;
#endif /* INET6 */
#ifdef TUBA
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index b8aa47c4e05..a63bcc2c3af 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_subr.c,v 1.20 1999/10/29 02:10:02 angelos Exp $ */
+/* $OpenBSD: tcp_subr.c,v 1.21 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_subr.c,v 1.22 1996/02/13 23:44:00 christos Exp $ */
/*
@@ -76,7 +76,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <dev/rndvar.h>
#ifdef INET6
-#include <netinet6/ipv6_var.h>
+#include <netinet6/ip6_var.h>
#include <netinet6/tcpipv6.h>
#include <sys/domain.h>
#endif /* INET6 */
@@ -118,7 +118,7 @@ int tcp_do_sack = TCP_DO_SACK; /* RFC 2018 selective ACKs */
int tcbhashsize = TCBHASHSIZE;
#ifdef INET6
-extern int ipv6_defhoplmt;
+extern int ip6_defhlim;
#endif /* INET6 */
/*
@@ -136,12 +136,12 @@ tcp_init()
#ifdef INET6
/*
- * Since sizeof(struct ipv6) > sizeof(struct ip), we
+ * Since sizeof(struct ip6_hdr) > sizeof(struct ip), we
* do max length checks/computations only on the former.
*/
- if (max_protohdr < (sizeof(struct ipv6) + sizeof(struct tcphdr)))
- max_protohdr = (sizeof(struct ipv6) + sizeof(struct tcphdr));
- if ((max_linkhdr + sizeof(struct ipv6) + sizeof(struct tcphdr)) >
+ if (max_protohdr < (sizeof(struct ip6_hdr) + sizeof(struct tcphdr)))
+ max_protohdr = (sizeof(struct ip6_hdr) + sizeof(struct tcphdr));
+ if ((max_linkhdr + sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) >
MHLEN)
panic("tcp_init");
#endif /* INET6 */
@@ -171,12 +171,8 @@ tcp_template(tp)
if (m == NULL)
return (0);
-#if defined(INET) && defined(INET6)
switch (tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch (0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
+ case 0: /*default to PF_INET*/
#ifdef INET
case AF_INET:
m->m_len = sizeof(struct ip);
@@ -184,7 +180,7 @@ tcp_template(tp)
#endif /* INET */
#ifdef INET6
case AF_INET6:
- m->m_len = sizeof(struct ipv6);
+ m->m_len = sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
}
@@ -204,12 +200,7 @@ tcp_template(tp)
}
}
-#if defined(INET) && defined(INET6)
switch(tp->pf) {
-#else /* defined(INET) && defined(INET6) */
- switch(0) {
-#endif /* defined(INET) && defined(INET6) */
- case 0:
#ifdef INET
case AF_INET:
{
@@ -232,22 +223,22 @@ tcp_template(tp)
#ifdef INET6
case AF_INET6:
{
- struct ipv6 *ipv6;
+ struct ip6_hdr *ipv6;
- ipv6 = mtod(m, struct ipv6 *);
+ ipv6 = mtod(m, struct ip6_hdr *);
- ipv6->ipv6_src = inp->inp_laddr6;
- ipv6->ipv6_dst = inp->inp_faddr6;
- ipv6->ipv6_versfl = htonl(0x60000000) |
- (inp->inp_ipv6.ipv6_versfl &
- htonl(0x0fffffff));
+ ipv6->ip6_src = inp->inp_laddr6;
+ ipv6->ip6_dst = inp->inp_faddr6;
+ ipv6->ip6_flow = htonl(0x60000000) |
+ (inp->inp_ipv6.ip6_flow & htonl(0x0fffffff));
+
- ipv6->ipv6_nexthdr = IPPROTO_TCP;
- ipv6->ipv6_length = htons(sizeof(struct tcphdr));
- ipv6->ipv6_hoplimit = inp->inp_ipv6.ipv6_hoplimit;
+ ipv6->ip6_nxt = IPPROTO_TCP;
+ ipv6->ip6_plen = htons(sizeof(struct tcphdr)); /*XXX*/
+ ipv6->ip6_hlim = in6_selecthlim(inp, NULL); /*XXX*/
th = (struct tcphdr *)(mtod(m, caddr_t) +
- sizeof(struct ipv6));
+ sizeof(struct ip6_hdr));
}
break;
#endif /* INET6 */
@@ -332,7 +323,7 @@ tcp_respond(tp, template, m, ack, seq, flags)
#ifdef INET6
if (is_ipv6)
bcopy(ti, mtod(m, caddr_t), sizeof(struct tcphdr) +
- sizeof(struct ipv6));
+ sizeof(struct ip6_hdr));
else
#endif /* INET6 */
bcopy(ti, mtod(m, caddr_t), sizeof(struct tcphdr) +
@@ -348,11 +339,11 @@ tcp_respond(tp, template, m, ack, seq, flags)
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
#ifdef INET6
if (is_ipv6) {
- m->m_len = sizeof(struct tcphdr) + sizeof(struct ipv6);
- xchg(((struct ipv6 *)ti)->ipv6_dst,\
- ((struct ipv6 *)ti)->ipv6_src,\
+ m->m_len = sizeof(struct tcphdr) + sizeof(struct ip6_hdr);
+ xchg(((struct ip6_hdr *)ti)->ip6_dst,\
+ ((struct ip6_hdr *)ti)->ip6_src,\
struct in6_addr);
- th = (void *)ti + sizeof(struct ipv6);
+ th = (void *)ti + sizeof(struct ip6_hdr);
} else
#endif /* INET6 */
{
@@ -365,8 +356,8 @@ tcp_respond(tp, template, m, ack, seq, flags)
}
#ifdef INET6
if (is_ipv6) {
- tlen += sizeof(struct tcphdr) + sizeof(struct ipv6);
- th = (struct tcphdr *)((caddr_t)ti + sizeof(struct ipv6));
+ tlen += sizeof(struct tcphdr) + sizeof(struct ip6_hdr);
+ th = (struct tcphdr *)((caddr_t)ti + sizeof(struct ip6_hdr));
} else
#endif /* INET6 */
{
@@ -391,15 +382,17 @@ tcp_respond(tp, template, m, ack, seq, flags)
#ifdef INET6
if (is_ipv6) {
- ((struct ipv6 *)ti)->ipv6_versfl = htonl(0x60000000);
- ((struct ipv6 *)ti)->ipv6_nexthdr = IPPROTO_TCP;
- ((struct ipv6 *)ti)->ipv6_hoplimit = MAXHOPLIMIT;
- ((struct ipv6 *)ti)->ipv6_length = tlen - sizeof(struct ipv6);
+ ((struct ip6_hdr *)ti)->ip6_flow = htonl(0x60000000);
+ ((struct ip6_hdr *)ti)->ip6_nxt = IPPROTO_TCP;
+ ((struct ip6_hdr *)ti)->ip6_hlim =
+ in6_selecthlim(tp ? tp->t_inpcb : NULL, NULL); /*XXX*/
+ ((struct ip6_hdr *)ti)->ip6_plen = tlen - sizeof(struct ip6_hdr);
th->th_sum = 0;
th->th_sum = in6_cksum(m, IPPROTO_TCP,
- ((struct ipv6 *)ti)->ipv6_length, sizeof(struct ipv6));
- HTONS(((struct ipv6 *)ti)->ipv6_length);
- ipv6_output(m, (struct route6 *)ro, 0, NULL, NULL, NULL);
+ sizeof(struct ip6_hdr), ((struct ip6_hdr *)ti)->ip6_plen);
+ HTONS(((struct ip6_hdr *)ti)->ip6_plen);
+ ip6_output(m, tp ? tp->t_inpcb->inp_outputopts6 : NULL,
+ (struct route_in6 *)ro, 0, NULL, NULL);
} else
#endif /* INET6 */
{
@@ -457,9 +450,13 @@ tcp_newtcpcb(inp)
*/
if ((inp->inp_flags & INP_IPV6) == 0)
tp->pf = PF_INET; /* If AF_INET socket, we can't do v6 from it. */
+#else
+ tp->pf = PF_INET;
+#endif
+#ifdef INET6
if (inp->inp_flags & INP_IPV6)
- inp->inp_ipv6.ipv6_hoplimit = ipv6_defhoplmt;
+ inp->inp_ipv6.ip6_hlim = ip6_defhlim;
else
#endif /* INET6 */
inp->inp_ip.ip_ttl = ip_defttl;
@@ -604,7 +601,7 @@ tcp_close(tp)
#ifdef INET6
if (tp->pf == PF_INET6)
i *= (u_long)(tp->t_maxseg + sizeof (struct tcphdr)
- + sizeof(struct ipv6));
+ + sizeof(struct ip6_hdr));
else
#endif /* INET6 */
i *= (u_long)(tp->t_maxseg +
@@ -698,6 +695,17 @@ tcp_notify(inp, error)
sowwakeup(so);
}
+#if defined(INET6) && !defined(TCP6)
+void
+tcp6_ctlinput(cmd, sa, d)
+ int cmd;
+ struct sockaddr *sa;
+ void *d;
+{
+ (void)tcp_ctlinput(cmd, sa, NULL); /*XXX*/
+}
+#endif
+
void *
tcp_ctlinput(cmd, sa, v)
int cmd;
@@ -725,14 +733,19 @@ tcp_ctlinput(cmd, sa, v)
#ifdef INET6
if (sa->sa_family == AF_INET6) {
if (ip) {
- struct ipv6 *ipv6 = (struct ipv6 *)ip;
+ struct ip6_hdr *ipv6 = (struct ip6_hdr *)ip;
th = (struct tcphdr *)(ipv6 + 1);
+#if 0 /*XXX*/
in6_pcbnotify(&tcbtable, sa, th->th_dport,
- &ipv6->ipv6_src, th->th_sport, cmd, notify);
- } else
+ &ipv6->ip6_src, th->th_sport, cmd, notify);
+#endif
+ } else {
+#if 0 /*XXX*/
in6_pcbnotify(&tcbtable, sa, 0,
(struct in6_addr *)&in6addr_any, 0, cmd, notify);
+#endif
+ }
} else
#endif /* INET6 */
{
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 4868d9728f4..d1995fad249 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_usrreq.c,v 1.36 1999/09/01 21:38:21 provos Exp $ */
+/* $OpenBSD: tcp_usrreq.c,v 1.37 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */
/*
@@ -100,6 +100,18 @@ extern struct baddynamicports baddynamicports;
int tcp_ident __P((void *, size_t *, void *, size_t));
+#if defined(INET6) && !defined(TCP6)
+int
+tcp6_usrreq(so, req, m, nam, control, p)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+ struct proc *p;
+{
+ return tcp_usrreq(so, req, m, nam, control);
+}
+#endif
+
/*
* Process a TCP user request for TCP tb. If this is a send request
* then m is the mbuf chain of send data. If this is a timer expiration
@@ -196,7 +208,12 @@ tcp_usrreq(so, req, m, nam, control)
* Give the socket an address.
*/
case PRU_BIND:
- error = in_pcbbind(inp, nam);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ error = in6_pcbbind(inp, nam);
+ else
+#endif
+ error = in_pcbbind(inp, nam);
if (error)
break;
#ifdef INET6
@@ -219,8 +236,14 @@ tcp_usrreq(so, req, m, nam, control)
* Prepare to accept connections.
*/
case PRU_LISTEN:
- if (inp->inp_lport == 0)
- error = in_pcbbind(inp, NULL);
+ if (inp->inp_lport == 0) {
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ error = in6_pcbbind(inp, NULL);
+ else
+#endif
+ error = in_pcbbind(inp, NULL);
+ }
/* If the in_pcbbind() above is called, the tp->pf
should still be whatever it was before. */
if (error == 0)
@@ -251,8 +274,16 @@ tcp_usrreq(so, req, m, nam, control)
error = EINVAL;
break;
}
+
+ if (inp->inp_lport == 0) {
+ error = in6_pcbbind(inp, NULL);
+ if (error)
+ break;
+ }
+ error = in6_pcbconnect(inp, nam);
} else if (sin->sin_family == AF_INET)
#endif /* INET6 */
+ {
if ((sin->sin_addr.s_addr == INADDR_ANY) ||
IN_MULTICAST(sin->sin_addr.s_addr) ||
in_broadcast(sin->sin_addr, NULL)) {
@@ -260,18 +291,20 @@ tcp_usrreq(so, req, m, nam, control)
break;
}
- /* Trying to connect to some broadcast address */
- if (in_broadcast(sin->sin_addr, NULL)) {
- error = EINVAL;
- break;
- }
-
- if (inp->inp_lport == 0) {
- error = in_pcbbind(inp, NULL);
- if (error)
+ /* Trying to connect to some broadcast address */
+ if (in_broadcast(sin->sin_addr, NULL)) {
+ error = EINVAL;
break;
+ }
+
+ if (inp->inp_lport == 0) {
+ error = in_pcbbind(inp, NULL);
+ if (error)
+ break;
+ }
+ error = in_pcbconnect(inp, nam);
}
- error = in_pcbconnect(inp, nam);
+
if (error)
break;
@@ -358,7 +391,12 @@ tcp_usrreq(so, req, m, nam, control)
* of the peer, storing through addr.
*/
case PRU_ACCEPT:
- in_setpeeraddr(inp, nam);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ in6_setpeeraddr(inp, nam);
+ else
+#endif
+ in_setpeeraddr(inp, nam);
break;
/*
@@ -446,11 +484,21 @@ tcp_usrreq(so, req, m, nam, control)
break;
case PRU_SOCKADDR:
- in_setsockaddr(inp, nam);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ in6_setsockaddr(inp, nam);
+ else
+#endif
+ in_setsockaddr(inp, nam);
break;
case PRU_PEERADDR:
- in_setpeeraddr(inp, nam);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ in6_setpeeraddr(inp, nam);
+ else
+#endif
+ in_setpeeraddr(inp, nam);
break;
/*
@@ -504,7 +552,7 @@ tcp_ctloutput(op, so, level, optname, mp)
* AF_INET6 sockets which get SET/GET options for IPv4.
*/
if (tp->pf == PF_INET6)
- error = ipv6_ctloutput(op, so, level, optname, mp);
+ error = ip6_ctloutput(op, so, level, optname, mp);
else
#endif /* INET6 */
error = ip_ctloutput(op, so, level, optname, mp);
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 25039955a3c..c22b3b8c61d 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_var.h,v 1.24 1999/08/06 18:17:38 deraadt Exp $ */
+/* $OpenBSD: tcp_var.h,v 1.25 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: tcp_var.h,v 1.17 1996/02/13 23:44:24 christos Exp $ */
/*
@@ -324,6 +324,9 @@ int tcp_attach __P((struct socket *));
void tcp_canceltimers __P((struct tcpcb *));
struct tcpcb *
tcp_close __P((struct tcpcb *));
+#if defined(INET6) && !defined(TCP6)
+void tcp6_ctlinput __P((int, struct sockaddr *, void *));
+#endif
void *tcp_ctlinput __P((int, struct sockaddr *, void *));
int tcp_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
struct tcpcb *
@@ -335,13 +338,16 @@ void tcp_dooptions __P((struct tcpcb *, u_char *, int, struct tcphdr *,
void tcp_drain __P((void));
void tcp_fasttimo __P((void));
void tcp_init __P((void));
+#if defined(INET6) && !defined(TCP6)
+int tcp6_input __P((struct mbuf **, int *, int));
+#endif
void tcp_input __P((struct mbuf *, ...));
int tcp_mss __P((struct tcpcb *, u_int));
struct tcpcb *
tcp_newtcpcb __P((struct inpcb *));
void tcp_notify __P((struct inpcb *, int));
int tcp_output __P((struct tcpcb *));
-void tcp_pulloutofband __P((struct socket *, u_int, struct mbuf *));
+void tcp_pulloutofband __P((struct socket *, u_int, struct mbuf *, int));
void tcp_quench __P((struct inpcb *, int));
int tcp_reass __P((struct tcpcb *, struct tcphdr *, struct mbuf *, int *));
void tcp_respond __P((struct tcpcb *, caddr_t, struct mbuf *, tcp_seq,
@@ -356,6 +362,10 @@ void tcp_trace __P((int, int, struct tcpcb *, caddr_t, int, int));
struct tcpcb *
tcp_usrclosed __P((struct tcpcb *));
int tcp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+#if defined(INET6) && !defined(TCP6)
+int tcp6_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
+#endif
int tcp_usrreq __P((struct socket *,
int, struct mbuf *, struct mbuf *, struct mbuf *));
void tcp_xmit_timer __P((struct tcpcb *, int));
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 644cecc7f98..ff65e76a6e2 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_usrreq.c,v 1.28 1999/11/04 11:24:24 ho Exp $ */
+/* $OpenBSD: udp_usrreq.c,v 1.29 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
@@ -84,13 +84,25 @@ extern int check_ipsec_policy __P((struct inpcb *, u_int32_t));
#include <machine/stdarg.h>
#ifdef INET6
-#include <netinet6/in6.h>
-#include <netinet6/ipv6.h>
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet6/ip6.h>
#include <netinet6/in6_var.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/ip6protosw.h>
+
+#ifndef CREATE_IPV6_MAPPED
+#define CREATE_IPV6_MAPPED(a6, a4) \
+do { \
+ bzero(&(a6), sizeof(a6)); \
+ (a6).s6_addr[10] = (a6).s6_addr[11] = 0xff; \
+ *(u_int32_t *)&(a6).s6_addr[12] = (a4); \
+} while (0)
+#endif
-extern int ipv6_defhoplmt;
+extern int ip6_defhlim;
#endif /* INET6 */
@@ -120,6 +132,29 @@ udp_init()
in_pcbinit(&udbtable, udbhashsize);
}
+#if defined(INET6) && !defined(TCP6)
+int
+udp6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+
+#if defined(NFAITH) && 0 < NFAITH
+ if (m->m_pkthdr.rcvif) {
+ if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+ /* XXX send icmp6 host/port unreach? */
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+ }
+#endif
+
+ udp_input(m, *offp, proto);
+ return IPPROTO_DONE;
+}
+#endif
+
void
#if __STDC__
udp_input(struct mbuf *m, ...)
@@ -146,7 +181,7 @@ udp_input(m, va_alist)
#endif /* INET6 */
} srcsa, dstsa;
#ifdef INET6
- struct ipv6 *ipv6;
+ struct ip6_hdr *ipv6;
struct sockaddr_in6 src_v4mapped;
#endif /* INET6 */
#ifdef IPSEC
@@ -182,7 +217,7 @@ udp_input(m, va_alist)
#ifdef INET6
case 6:
ip = NULL;
- ipv6 = mtod(m, struct ipv6 *);
+ ipv6 = mtod(m, struct ip6_hdr *);
srcsa.sa.sa_family = AF_INET6;
break;
#endif /* INET6 */
@@ -220,7 +255,7 @@ udp_input(m, va_alist)
}
#ifdef INET6
if (ipv6)
- ipv6 = mtod(m, struct ipv6 *);
+ ipv6 = mtod(m, struct ip6_hdr *);
else
#endif /* INET6 */
ip = mtod(m, struct ip *);
@@ -258,7 +293,7 @@ udp_input(m, va_alist)
/*
* In IPv6, the UDP checksum is ALWAYS used.
*/
- if ((uh->uh_sum = in6_cksum(m, IPPROTO_UDP, len, iphlen))) {
+ if ((uh->uh_sum = in6_cksum(m, IPPROTO_UDP, iphlen, len))) {
udpstat.udps_badsum++;
goto bad;
}
@@ -304,20 +339,30 @@ udp_input(m, va_alist)
srcsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
srcsa.sin6.sin6_family = AF_INET6;
srcsa.sin6.sin6_port = uh->uh_sport;
- srcsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ipv6->ipv6_versfl;
- srcsa.sin6.sin6_addr = ipv6->ipv6_src;
+ srcsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ipv6->ip6_flow;
+ srcsa.sin6.sin6_addr = ipv6->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&srcsa.sin6.sin6_addr))
+ srcsa.sin6.sin6_addr.s6_addr16[1] = 0;
+ if (m->m_pkthdr.rcvif) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&srcsa.sin6.sin6_addr)) {
+ srcsa.sin6.sin6_scope_id =
+ m->m_pkthdr.rcvif->if_index;
+ } else
+ srcsa.sin6.sin6_scope_id = 0;
+ } else
+ srcsa.sin6.sin6_scope_id = 0;
bzero(&dstsa, sizeof(struct sockaddr_in6));
dstsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
dstsa.sin6.sin6_family = AF_INET6;
dstsa.sin6.sin6_port = uh->uh_dport;
- dstsa.sin6.sin6_addr = ipv6->ipv6_dst;
+ dstsa.sin6.sin6_addr = ipv6->ip6_dst;
break;
#endif /* INET6 */
}
#ifdef INET6
- if ((ipv6 && IN6_IS_ADDR_MULTICAST(&ipv6->ipv6_dst)) ||
+ if ((ipv6 && IN6_IS_ADDR_MULTICAST(&ipv6->ip6_dst)) ||
(ip && IN_MULTICAST(ip->ip_dst.s_addr)) ||
(ip && in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))) {
#else /* INET6 */
@@ -361,7 +406,7 @@ udp_input(m, va_alist)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6,
- &ipv6->ipv6_dst))
+ &ipv6->ip6_dst))
continue;
} else
#endif /* INET6 */
@@ -374,7 +419,7 @@ udp_input(m, va_alist)
if (ipv6) {
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
- &ipv6->ipv6_src) ||
+ &ipv6->ip6_src) ||
inp->inp_fport != uh->uh_sport)
continue;
} else
@@ -391,8 +436,12 @@ udp_input(m, va_alist)
if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
#ifdef INET6
+#if 0 /*XXX*/
if (ipv6)
opts = ipv6_headertocontrol(m, iphlen, ((struct inpcb *)last->so_pcb)->inp_flags);
+#else
+ opts = NULL;
+#endif
#endif /* INET6 */
if (sbappendaddr(&last->so_rcv,
#ifdef INET6
@@ -436,9 +485,13 @@ udp_input(m, va_alist)
}
#ifdef INET6
+#if 0 /*XXX*/
if (ipv6)
opts = ipv6_headertocontrol(m, iphlen,
((struct inpcb *)last->so_pcb)->inp_flags);
+#else
+ opts = NULL;
+#endif
#endif /* INET6 */
if (sbappendaddr(&last->so_rcv,
#ifdef INET6
@@ -462,8 +515,8 @@ udp_input(m, va_alist)
*/
#ifdef INET6
if (ipv6)
- inp = in6_pcbhashlookup(&udbtable, &ipv6->ipv6_src, uh->uh_sport,
- &ipv6->ipv6_dst, uh->uh_dport);
+ inp = in6_pcbhashlookup(&udbtable, &ipv6->ip6_src, uh->uh_sport,
+ &ipv6->ip6_dst, uh->uh_dport);
else
#endif /* INET6 */
inp = in_pcbhashlookup(&udbtable, ip->ip_src, uh->uh_sport,
@@ -473,8 +526,8 @@ udp_input(m, va_alist)
#ifdef INET6
if (ipv6) {
inp = in_pcblookup(&udbtable,
- (struct in_addr *)&(ipv6->ipv6_src),
- uh->uh_sport, (struct in_addr *)&(ipv6->ipv6_dst),
+ (struct in_addr *)&(ipv6->ip6_src),
+ uh->uh_sport, (struct in_addr *)&(ipv6->ip6_dst),
uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_IPV6);
} else
#endif /* INET6 */
@@ -486,16 +539,21 @@ udp_input(m, va_alist)
udpstat.udps_noportbcast++;
goto bad;
}
- *ip = save_ip;
- HTONS(ip->ip_id);
- uh->uh_sum = savesum;
#ifdef INET6
- if (ipv6)
- ipv6_icmp_error(m, ICMPV6_UNREACH,
- ICMPV6_UNREACH_PORT,0);
- else
+ if (ipv6) {
+ icmp6_error(m, ICMP6_DST_UNREACH,
+ ICMP6_DST_UNREACH_NOPORT,0);
+ } else
#endif /* INET6 */
- icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
+ {
+ *ip = save_ip;
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_id);
+ HTONS(ip->ip_off);
+ uh->uh_sum = savesum;
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT,
+ 0, 0);
+ }
return;
}
}
@@ -528,9 +586,13 @@ udp_input(m, va_alist)
#ifdef INET6
if (ipv6) {
+#if 0 /*XXX*/
if (inp->inp_flags & INP_IPV6)
opts = ipv6_headertocontrol(m, iphlen,
inp->inp_flags);
+#else
+ opts = NULL;
+#endif
} else
if (ip)
#endif /* INET6 */
@@ -622,6 +684,42 @@ udp_notify(inp, errno)
sowwakeup(inp->inp_socket);
}
+#if defined(INET6) && !defined(TCP6)
+void
+udp6_ctlinput(cmd, sa, d)
+ int cmd;
+ struct sockaddr *sa;
+ void *d;
+{
+ struct sockaddr_in6 sa6;
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
+ int off;
+
+ if (sa == NULL)
+ return;
+ if (sa->sa_family != AF_INET6)
+ return;
+
+ /* decode parameter from icmp6. */
+ if (d != NULL) {
+ struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d;
+ ip6 = ip6cp->ip6c_ip6;
+ m = ip6cp->ip6c_m;
+ off = ip6cp->ip6c_off;
+ } else
+ return;
+
+ /* translate addresses into internal form */
+ sa6 = *(struct sockaddr_in6 *)sa;
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif)
+ sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+ sa = (struct sockaddr *)&sa6;
+
+ (void)udp_ctlinput(cmd, sa, (void *)ip6);
+}
+#endif
+
void *
udp_ctlinput(cmd, sa, v)
int cmd;
@@ -643,17 +741,24 @@ udp_ctlinput(cmd, sa, v)
ip = 0;
else if (errno == 0)
return NULL;
+ if (sa == NULL)
+ return NULL;
#ifdef INET6
if (sa->sa_family == AF_INET6) {
if (ip) {
- struct ipv6 *ipv6 = (struct ipv6 *)ip;
+ struct ip6_hdr *ipv6 = (struct ip6_hdr *)ip;
- uh = (struct udphdr *)((caddr_t)ipv6 + sizeof(struct ipv6));
+ uh = (struct udphdr *)((caddr_t)ipv6 + sizeof(struct ip6_hdr));
+#if 0 /*XXX*/
in6_pcbnotify(&udbtable, sa, uh->uh_dport,
- &(ipv6->ipv6_src), uh->uh_sport, cmd, udp_notify);
- } else
+ &(ipv6->ip6_src), uh->uh_sport, cmd, udp_notify);
+#endif
+ } else {
+#if 0 /*XXX*/
in6_pcbnotify(&udbtable, sa, 0,
(struct in6_addr *)&in6addr_any, 0, cmd, udp_notify);
+#endif
+ }
} else
#endif /* INET6 */
if (ip) {
@@ -685,6 +790,7 @@ udp_output(m, va_alist)
register struct in6_addr laddr6;
int v6packet = 0;
struct ifnet *forceif = NULL;
+ struct sockaddr_in6 *sin6 = NULL;
#endif /* INET6 */
int pcbflags = 0;
@@ -700,6 +806,10 @@ udp_output(m, va_alist)
#endif /* INET6 */
if (addr) {
+#ifdef INET6
+ sin6 = mtod(addr, struct sockaddr_in6 *);
+#endif
+
/*
* Save current PCB flags because they may change during
* temporary connection, particularly the INP_IPV6_UNDEC
@@ -710,16 +820,17 @@ udp_output(m, va_alist)
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
laddr6 = inp->inp_laddr6;
- else
+ else
#endif /* INET6 */
laddr = inp->inp_laddr;
#ifdef INET6
if (((inp->inp_flags & INP_IPV6) &&
!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) ||
- (inp->inp_faddr.s_addr != INADDR_ANY)) {
+ (inp->inp_faddr.s_addr != INADDR_ANY))
#else /* INET6 */
- if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (inp->inp_faddr.s_addr != INADDR_ANY)
#endif /* INET6 */
+ {
error = EISCONN;
goto release;
}
@@ -760,7 +871,7 @@ udp_output(m, va_alist)
m_freem(control);
M_PREPEND(m, v6packet ? (sizeof(struct udphdr) +
- sizeof(struct ipv6)) : sizeof(struct udpiphdr), M_DONTWAIT);
+ sizeof(struct ip6_hdr)) : sizeof(struct udpiphdr), M_DONTWAIT);
#else /* INET6 */
M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
#endif /* INET6 */
@@ -784,41 +895,101 @@ udp_output(m, va_alist)
*/
#ifdef INET6
if (v6packet) {
- struct ipv6 *ipv6 = mtod(m, struct ipv6 *);
+ struct ip6_hdr *ipv6 = mtod(m, struct ip6_hdr *);
struct udphdr *uh = (struct udphdr *)(mtod(m, caddr_t) +
- sizeof(struct ipv6));
- int payload = sizeof(struct ipv6);
-
- ipv6->ipv6_versfl = htonl(0x60000000) |
- (inp->inp_ipv6.ipv6_versfl & htonl(0x0fffffff));
+ sizeof(struct ip6_hdr));
+ int payload = sizeof(struct ip6_hdr);
+ struct in6_addr *faddr;
+ struct in6_addr *laddr;
+ struct ifnet *oifp = NULL;
+
+ ipv6->ip6_flow = htonl(0x60000000) |
+ (inp->inp_ipv6.ip6_flow & htonl(0x0fffffff));
- ipv6->ipv6_hoplimit = inp->inp_ipv6.ipv6_hoplimit;
- ipv6->ipv6_nexthdr = IPPROTO_UDP;
- ipv6->ipv6_src = inp->inp_laddr6;
- ipv6->ipv6_dst = inp->inp_faddr6;
- ipv6->ipv6_length = (u_short)len + sizeof(struct udphdr);
+ ipv6->ip6_nxt = IPPROTO_UDP;
+ ipv6->ip6_dst = inp->inp_faddr6;
+ /*
+ * If the scope of the destination is link-local,
+ * embed the interface
+ * index in the address.
+ *
+ * XXX advanced-api value overrides sin6_scope_id
+ */
+ faddr = &ipv6->ip6_dst;
+ if (IN6_IS_ADDR_LINKLOCAL(faddr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(faddr)) {
+ struct ip6_pktopts *optp = inp->inp_outputopts6;
+ struct in6_pktinfo *pi = NULL;
+ struct ip6_moptions *mopt = NULL;
+
+ /*
+ * XXX Boundary check is assumed to be already done in
+ * ip6_setpktoptions().
+ */
+ if (optp && (pi = optp->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
+ faddr->s6_addr16[1] = htons(pi->ipi6_ifindex);
+ oifp = ifindex2ifnet[pi->ipi6_ifindex];
+ }
+ else if (IN6_IS_ADDR_MULTICAST(faddr) &&
+ (mopt = inp->inp_moptions6) &&
+ mopt->im6o_multicast_ifp) {
+ oifp = mopt->im6o_multicast_ifp;
+ faddr->s6_addr16[1] = oifp->if_index;
+ } else if (sin6 && sin6->sin6_scope_id) {
+ /* boundary check */
+ if (sin6->sin6_scope_id < 0
+ || if_index < sin6->sin6_scope_id) {
+ error = ENXIO; /* XXX EINVAL? */
+ goto release;
+ }
+ /* XXX */
+ faddr->s6_addr16[1] =
+ htons(sin6->sin6_scope_id & 0xffff);
+ }
+ }
+ ipv6->ip6_hlim = in6_selecthlim(inp, oifp);
+ if (sin6) { /*XXX*/
+ laddr = in6_selectsrc(sin6, inp->inp_outputopts6,
+ inp->inp_moptions6,
+ &inp->inp_route6,
+ &inp->inp_laddr6, &error);
+ if (laddr == NULL) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto release;
+ }
+ } else
+ laddr = &inp->inp_laddr6;
+
+ ipv6->ip6_src = *laddr;
+
+ ipv6->ip6_plen = (u_short)len + sizeof(struct udphdr);
uh->uh_sport = inp->inp_lport;
uh->uh_dport = inp->inp_fport;
- uh->uh_ulen = htons(ipv6->ipv6_length);
+ uh->uh_ulen = htons(ipv6->ip6_plen);
uh->uh_sum = 0;
- if (control)
+ if (control) {
+#if 0 /*XXX*/
if ((error = ipv6_controltoheader(&m, control,
&forceif, &payload)))
goto release;
+#endif
+ }
/*
* Always calculate udp checksum for IPv6 datagrams
*/
- if (!(uh->uh_sum = in6_cksum(m, IPPROTO_UDP, len +
- sizeof(struct udphdr), payload)))
+ if (!(uh->uh_sum = in6_cksum(m, IPPROTO_UDP,
+ payload, len + sizeof(struct udphdr))))
uh->uh_sum = 0xffff;
- error = ipv6_output(m, &inp->inp_route6,
+ error = ip6_output(m, NULL, &inp->inp_route6,
inp->inp_socket->so_options & SO_DONTROUTE,
(inp->inp_flags & INP_IPV6_MCAST)?inp->inp_moptions6:NULL,
- forceif, inp->inp_socket);
+ &forceif);
} else
#endif /* INET6 */
{
@@ -901,6 +1072,19 @@ u_int udp_sendspace = 9216; /* really max datagram size */
u_int udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
/* 40 1K datagrams */
+#if defined(INET6) && !defined(TCP6)
+/*ARGSUSED*/
+int
+udp6_usrreq(so, req, m, addr, control, p)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *addr, *control;
+ struct proc *p;
+{
+ return udp_usrreq(so, req, m, addr, control);
+}
+#endif
+
/*ARGSUSED*/
int
udp_usrreq(so, req, m, addr, control)
@@ -947,8 +1131,8 @@ udp_usrreq(so, req, m, addr, control)
break;
#ifdef INET6
if (((struct inpcb *)so->so_pcb)->inp_flags & INP_IPV6)
- ((struct inpcb *) so->so_pcb)->inp_ipv6.ipv6_hoplimit =
- ipv6_defhoplmt;
+ ((struct inpcb *) so->so_pcb)->inp_ipv6.ip6_hlim =
+ ip6_defhlim;
else
#endif /* INET6 */
((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
@@ -960,7 +1144,12 @@ udp_usrreq(so, req, m, addr, control)
case PRU_BIND:
s = splsoftnet();
- error = in_pcbbind(inp, addr);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ error = in6_pcbbind(inp, addr);
+ else
+#endif
+ error = in_pcbbind(inp, addr);
splx(s);
break;
@@ -975,16 +1164,21 @@ udp_usrreq(so, req, m, addr, control)
error = EISCONN;
break;
}
+ s = splsoftnet();
+ error = in6_pcbconnect(inp, addr);
+ splx(s);
} else
#endif /* INET6 */
+ {
if (inp->inp_faddr.s_addr != INADDR_ANY) {
error = EISCONN;
break;
}
+ s = splsoftnet();
+ error = in_pcbconnect(inp, addr);
+ splx(s);
+ }
- s = splsoftnet();
- error = in_pcbconnect(inp, addr);
- splx(s);
if (error == 0)
soisconnected(so);
break;
@@ -1042,11 +1236,21 @@ udp_usrreq(so, req, m, addr, control)
break;
case PRU_SOCKADDR:
- in_setsockaddr(inp, addr);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ in6_setsockaddr(inp, addr);
+ else
+#endif /* INET6 */
+ in_setsockaddr(inp, addr);
break;
case PRU_PEERADDR:
- in_setpeeraddr(inp, addr);
+#ifdef INET6
+ if (inp->inp_flags & INP_IPV6)
+ in6_setpeeraddr(inp, addr);
+ else
+#endif /* INET6 */
+ in_setpeeraddr(inp, addr);
break;
case PRU_SENSE:
diff --git a/sys/netinet/udp_var.h b/sys/netinet/udp_var.h
index afc23036d32..e1e409e3985 100644
--- a/sys/netinet/udp_var.h
+++ b/sys/netinet/udp_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_var.h,v 1.8 1999/03/27 21:04:21 provos Exp $ */
+/* $OpenBSD: udp_var.h,v 1.9 1999/12/08 06:50:20 itojun Exp $ */
/* $NetBSD: udp_var.h,v 1.12 1996/02/13 23:44:41 christos Exp $ */
/*
@@ -90,6 +90,12 @@ struct udpstat {
struct inpcbtable udbtable;
struct udpstat udpstat;
+#if defined(INET6) && !defined(TCP6)
+void udp6_ctlinput __P((int, struct sockaddr *, void *));
+int udp6_input __P((struct mbuf **, int *, int));
+int udp6_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
+#endif
void *udp_ctlinput __P((int, struct sockaddr *, void *));
void udp_init __P((void));
void udp_input __P((struct mbuf *, ...));
diff --git a/sys/netinet6/dest6.c b/sys/netinet6/dest6.c
new file mode 100644
index 00000000000..5ed163e8bfe
--- /dev/null
+++ b/sys/netinet6/dest6.c
@@ -0,0 +1,123 @@
+/* $OpenBSD: dest6.c,v 1.1 1999/12/08 06:50: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.
+ */
+
+#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 <netinet6/ip6.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+
+/*
+ * Destination options header processing.
+ */
+int
+dest6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ register struct mbuf *m = *mp;
+ int off = *offp, dstoptlen, optlen;
+ struct ip6_dest *dstopts;
+ u_int8_t *opt;
+
+ /* validation of the length of the header */
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, sizeof(*dstopts));
+ if (dstopts == NULL)
+ return IPPROTO_DONE;
+#endif
+ dstoptlen = (dstopts->ip6d_len + 1) << 3;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE);
+ dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, dstoptlen);
+ if (dstopts == NULL)
+ return IPPROTO_DONE;
+#endif
+ off += dstoptlen;
+ dstoptlen -= sizeof(struct ip6_dest);
+ opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest);
+
+ /* search header for all options. */
+ for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) {
+ switch(*opt) {
+ case IP6OPT_PAD1:
+ optlen = 1;
+ break;
+ case IP6OPT_PADN:
+ if (dstoptlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ optlen = *(opt + 1) + 2;
+ break;
+ default: /* unknown option */
+ if (dstoptlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if ((optlen = ip6_unknown_opt(opt, m,
+ opt-mtod(m, u_int8_t *))) == -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/frag6.c b/sys/netinet6/frag6.c
new file mode 100644
index 00000000000..bf0612d9bef
--- /dev/null
+++ b/sys/netinet6/frag6.c
@@ -0,0 +1,668 @@
+/* $OpenBSD: frag6.c,v 1.1 1999/12/08 06:50: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.
+ */
+
+#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 <netinet6/ip6.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/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 *));
+
+int frag6_doing_reass;
+u_int frag6_nfragpackets;
+struct ip6q ip6q; /* ip6 reassemble queue */
+
+/* FreeBSD tweak */
+#if !defined(M_FTABLE) && (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header");
+#endif
+
+/*
+ * Initialise reassembly queue and fragment identifier.
+ */
+void
+frag6_init()
+{
+ struct timeval tv;
+
+ /*
+ * in many cases, random() here does NOT return random number
+ * as initialization during bootstrap time occur in fixed order.
+ */
+ microtime(&tv);
+ ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
+ ip6_id = random() ^ tv.tv_usec;
+}
+
+/*
+ * 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;
+ int offset = *offp, nxt, i, next;
+ int first_frag = 0;
+ u_short fragoff, frgpartlen;
+ 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;
+ }
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ rtalloc((struct route *)&ro);
+#else
+ rtcalloc((struct route *)&ro);
+#endif
+ 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,
+ (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ return IPPROTO_DONE;
+ }
+
+ ip6stat.ip6s_fragments++;
+ in6_ifstat_inc(dstifp, ifs6_reass_reqd);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ */
+#ifdef PULLDOWN_TEST
+ /* XXX too strong mbuf requirement in m_pulldown() world */
+#endif
+ offset += sizeof(struct ip6_frag);
+ m->m_data += offset;
+ m->m_len -= offset;
+
+ for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next)
+ if (ip6f->ip6f_ident == q6->ip6q_ident &&
+ 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;
+ frag6_nfragpackets++;
+
+ /*
+ * 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 (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) {
+ ip6stat.ip6s_fragoverflow++;
+ in6_ifstat_inc(dstifp, ifs6_reass_fail);
+ frag6_freef(ip6q.ip6q_prev);
+ }
+ q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE,
+ M_DONTWAIT);
+ if (q6 == NULL)
+ goto dropfrag;
+
+ frag6_insque(q6, &ip6q);
+
+ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
+#if 0
+ /*
+ * It is not necessarily the first segment; fragment offset
+ * might be non-0.
+ */
+ q6->ip6q_nxt = ip6f->ip6f_nxt;
+#endif
+#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) {
+ m->m_data -= offset;
+ m->m_len += offset;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ offset - sizeof(struct ip6_frag) + 2);
+ return(IPPROTO_DONE);
+ }
+ }
+ else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
+ m->m_data -= offset;
+ m->m_len += offset;
+ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
+ offset - sizeof(struct ip6_frag) + 2);
+ 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) {
+ struct ip6asfrag *af6dwn;
+
+ 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);
+
+ /* adjust pointer. */
+ merr->m_data -= af6->ip6af_offset;
+ merr->m_len += af6->ip6af_offset;
+ 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) + 2);
+ }
+ }
+ }
+
+ /* Override the IPv6 header */
+ ip6af = (struct ip6asfrag *)ip6;
+ 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) {
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the previous fragment\n",
+ i, ip6_sprintf(&q6->ip6q_src));
+ goto dropfrag;
+ }
+ }
+ if (af6 != (struct ip6asfrag *)q6) {
+ i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
+ if (i > 0) {
+ log(LOG_ERR, "%d bytes of a fragment from %s "
+ "overlaps the succeeding fragment",
+ i, ip6_sprintf(&q6->ip6q_src));
+ 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;
+ while (af6 != (struct ip6asfrag *)q6) {
+ while (t->m_next)
+ t = t->m_next;
+ t->m_next = IP6_REASS_MBUF(af6);
+ af6 = af6->ip6af_down;
+ }
+
+ /* adjust offset to point where the original next header starts */
+ offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
+ ip6 = (struct ip6_hdr *)ip6af;
+ 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);
+ else {
+ ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len);
+ m->m_data -= sizeof(struct ip6_frag);
+ }
+ m->m_data -= offset;
+ m->m_len += offset;
+
+ /*
+ * 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);
+ 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 */
+ m->m_data -= af6->ip6af_offset;
+ m->m_len += af6->ip6af_offset;
+ 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);
+ }
+ 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;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+frag6_slowtimo()
+{
+ struct ip6q *q6;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+#if 0
+ extern struct route_in6 ip6_forward_rt;
+#endif
+
+ 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) {
+ 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 00000000000..240364aad83
--- /dev/null
+++ b/sys/netinet6/icmp6.c
@@ -0,0 +1,2320 @@
+/* $OpenBSD: icmp6.c,v 1.1 1999/12/08 06:50: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
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#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/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/domain.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#ifdef __OpenBSD__
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/mld6_var.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
+#include <netinet6/in6_pcb.h>
+#else
+#include <netinet/in_pcb.h>
+#endif
+#include <netinet6/nd6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/ip6protosw.h>
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
+#undef IPSEC
+#endif
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif
+
+#include "faith.h"
+
+#include <net/net_osdep.h>
+
+extern struct domain inet6domain;
+extern struct ip6protosw inet6sw[];
+extern u_char ip6_protox[];
+
+struct icmp6stat icmp6stat;
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+extern struct in6pcb rawin6pcb;
+#else
+extern struct inpcbhead ripcb;
+#endif
+extern u_int icmp6errratelim;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
+extern int pmtu_expire;
+#endif
+
+#ifndef HAVE_NRL_INPCB
+static int icmp6_rip6_input __P((struct mbuf **, int));
+#endif
+static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
+static const char *icmp6_redirect_diag __P((struct in6_addr *,
+ struct in6_addr *, struct in6_addr *));
+static struct mbuf * ni6_input __P((struct mbuf *, int));
+static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
+ struct ifnet **));
+static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
+ struct ifnet *, int));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static struct rtentry *icmp6_mtudisc_clone __P((struct sockaddr *));
+static void icmp6_mtudisc_timeout __P((struct rtentry *, struct rttimer *));
+#endif
+
+#ifdef COMPAT_RFC1885
+static struct route_in6 icmp6_reflect_rt;
+#endif
+static struct timeval icmp6_nextsend = {0, 0};
+
+void
+icmp6_init()
+{
+ mld6_init();
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire);
+#endif
+}
+
+/*
+ * 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 prep;
+ int off;
+ u_char nxt;
+
+ icmp6stat.icp6s_error++;
+
+#ifdef M_DECRYPTED /*not openbsd*/
+ if (m->m_flags & M_DECRYPTED)
+ 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 the erroneous packet is also an ICMP error, discard it.
+ */
+ off = sizeof(struct ip6_hdr);
+ nxt = oip6->ip6_nxt;
+ while (1) { /* XXX: should avoid inf. loop explicitly? */
+ struct ip6_ext *ip6e;
+ struct icmp6_hdr *icp;
+
+ switch(nxt) {
+ case IPPROTO_IPV6:
+ case IPPROTO_IPV4:
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_ESP:
+ case IPPROTO_FRAGMENT:
+ /*
+ * ICMPv6 error must not be fragmented.
+ * XXX: but can we trust the sender?
+ */
+ default:
+ /* What if unknown header followed by ICMP error? */
+ goto generate;
+ case IPPROTO_ICMPV6:
+#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 */
+ goto generate;
+ }
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_AH:
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), );
+ ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off,
+ sizeof(*ip6e));
+ if (ip6e == NULL) {
+ /*XXX stat */
+ return;
+ }
+#endif
+ if (nxt == IPPROTO_AH)
+ off += (ip6e->ip6e_len + 2) << 2;
+ else
+ off += (ip6e->ip6e_len + 1) << 3;
+ nxt = ip6e->ip6e_nxt;
+ break;
+ }
+ }
+
+ freeit:
+ /*
+ * If we can't tell wheter or not we can generate ICMP6, free it.
+ */
+ m_freem(m);
+ return;
+
+ generate:
+ oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
+
+ /* Finally, do rate limitation check. */
+ 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);
+
+ prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
+ M_PREPEND(m, prep, M_DONTWAIT);
+ if (m && m->m_len < prep)
+ m = m_pullup(m, prep);
+ if (m == NULL) {
+ printf("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);
+
+ icmp6stat.icp6s_outhist[type]++;
+ icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/
+}
+
+/*
+ * 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;
+ struct sockaddr_in6 icmp6src;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
+ /* m might change if M_LOOP. So, call mtod after this */
+#endif
+
+ /*
+ * Locate icmp6 structure in mbuf, and check
+ * 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) {
+ log(LOG_ERR,
+ "ICMP6 checksum error(%d|%x) %s\n",
+ icmp6->icmp6_type,
+ sum,
+ ip6_sprintf(&ip6->ip6_src));
+ icmp6stat.icp6s_checksum++;
+ goto freeit;
+ }
+
+#if defined(NFAITH) && 0 < NFAITH
+ if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
+ /*
+ * 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;
+ }
+ }
+#endif
+
+#ifdef IPSEC
+ /* drop it if it does not match the default policy */
+ if (ipsec6_in_reject(m, NULL)) {
+ ipsecstat.in_polvio++;
+ goto freeit;
+ }
+#endif
+
+ icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
+ if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
+
+ switch (icmp6->icmp6_type) {
+
+ case ICMP6_DST_UNREACH:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
+ switch (code) {
+ 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);
+ case ICMP6_DST_UNREACH_ADDR:
+ code = PRC_UNREACH_HOST;
+ break;
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ code = PRC_UNREACH_SRCFAIL;
+ break;
+ 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;
+ {
+ u_int mtu = ntohl(icmp6->icmp6_mtu);
+ struct rtentry *rt = NULL;
+ struct sockaddr_in6 sin6;
+#ifdef __bsdi__
+ struct route_in6 ro6;
+#endif
+
+ if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
+ icmp6stat.icp6s_tooshort++;
+ goto freeit;
+ }
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off,
+ sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
+ IPPROTO_DONE);
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
+ sizeof(*icmp6) + sizeof(struct ip6_hdr));
+ if (icmp6 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ code = PRC_MSGSIZE;
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = PF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ rt = rtalloc1((struct sockaddr *)&sin6, 1); /*clone*/
+ if (!rt || (rt->rt_flags & RTF_HOST) == 0) {
+ if (rt)
+ RTFREE(rt);
+ rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
+ }
+#endif
+#ifdef __FreeBSD__
+ rt = rtalloc1((struct sockaddr *)&sin6, 0,
+ RTF_CLONING | RTF_PRCLONING);
+#endif /*__FreeBSD__*/
+#ifdef __bsdi__
+ bcopy(&sin6, &ro6.ro_dst, sizeof(struct sockaddr_in6));
+ ro6.ro_rt = 0;
+ rtcalloc((struct route *)&ro6);
+ rt = ro6.ro_rt;
+#endif /*__bsdi__*/
+
+ if (rt && (rt->rt_flags & RTF_HOST)
+ && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
+ if (mtu < IPV6_MMTU) {
+ /* xxx */
+ rt->rt_rmx.rmx_locks |= RTV_MTU;
+ } else if (mtu < rt->rt_ifp->if_mtu &&
+ rt->rt_rmx.rmx_mtu > mtu) {
+ rt->rt_rmx.rmx_mtu = mtu;
+ }
+ }
+ if (rt)
+ RTFREE(rt);
+
+ 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;
+
+ /*
+ * Prepare an internal mbuf. m_pullup() doesn't
+ * always copy the length we specified.
+ */
+ MGETHDR(n, M_DONTWAIT, n0->m_type);
+ if (n == NULL) {
+ /* Give up remote */
+ m_freem(n0);
+ break;
+ }
+ M_COPY_PKTHDR(n, n0);
+ /*
+ * 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);
+ n->m_pkthdr.len = 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));
+ n->m_pkthdr.len += n0->m_pkthdr.len;
+ n->m_next = n0;
+ n0->m_flags &= ~M_PKTHDR;
+ } else {
+ nip6 = mtod(n, struct ip6_hdr *);
+ nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
+ 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 MLD6_LISTENER_QUERY:
+ case MLD6_LISTENER_REPORT:
+ if (icmp6len < sizeof(struct mld6_hdr))
+ goto badlen;
+ if (icmp6->icmp6_type == MLD6_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);
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ mld6_input(m, off);
+ /* m stays. */
+ break;
+
+ case MLD6_LISTENER_DONE:
+ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
+ if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */
+ goto badlen;
+ break; /* nothing to be done in kernel */
+
+ case MLD6_MTRACE_RESP:
+ case MLD6_MTRACE:
+ /* XXX: these two are experimental. not officially defind. */
+ /* XXX: per-interface statistics? */
+ break; /* just pass it to the userland daemon */
+
+ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */
+ {
+ enum { WRU, FQDN } mode;
+
+ if (code != 0)
+ goto badcode;
+ if (icmp6len == sizeof(struct icmp6_hdr) + 4)
+ mode = WRU;
+ else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */
+ mode = FQDN;
+ else
+ goto badlen;
+
+#ifdef __FreeBSD__
+#define hostnamelen strlen(hostname)
+#endif
+ if (mode == FQDN) {
+ IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
+ IPPROTO_DONE);
+ n = ni6_input(m, off);
+ noff = sizeof(struct ip6_hdr);
+ }
+ else {
+ u_char *p;
+
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n == NULL) {
+ /* Give up remote */
+ 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));
+ p = (u_char *)(nicmp6 + 1);
+ bzero(p, 4);
+ bcopy(hostname, p + 4, hostnamelen);
+ noff = sizeof(struct ip6_hdr);
+ M_COPY_PKTHDR(n, m); /* just for recvif */
+ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_hdr) + 4 + hostnamelen;
+ 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;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_rs_input(m, 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;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_ra_input(m, 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;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_ns_input(m, 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;
+ IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
+ nd6_na_input(m, 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;
+ icmp6_redirect_input(m, 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:
+ printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
+ icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0);
+ 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 (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
+ icmp6stat.icp6s_tooshort++;
+ goto freeit;
+ }
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off,
+ sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
+ IPPROTO_DONE);
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
+ sizeof(*icmp6) + sizeof(struct ip6_hdr));
+ if (icmp6 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ bzero(&icmp6src, sizeof(icmp6src));
+ icmp6src.sin6_len = sizeof(struct sockaddr_in6);
+ icmp6src.sin6_family = AF_INET6;
+ icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
+
+ /* Detect the upper level protocol */
+ {
+ void (*ctlfunc) __P((int, struct sockaddr *, void *));
+ struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1);
+ u_int8_t nxt = eip6->ip6_nxt;
+ int eoff = off + sizeof(struct icmp6_hdr) +
+ sizeof(struct ip6_hdr);
+ struct ip6ctlparam ip6cp;
+
+ while (1) { /* XXX: should avoid inf. loop explicitly? */
+ struct ip6_ext *eh;
+
+ switch(nxt) {
+ case IPPROTO_ESP:
+ case IPPROTO_NONE:
+ goto passit;
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_AH:
+ case IPPROTO_FRAGMENT:
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, 0, eoff +
+ sizeof(struct ip6_ext),
+ IPPROTO_DONE);
+ eh = (struct ip6_ext *)(mtod(m, caddr_t)
+ + eoff);
+#else
+ IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
+ eoff, sizeof(*eh));
+ if (eh == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ if (nxt == IPPROTO_AH)
+ eoff += (eh->ip6e_len + 2) << 2;
+ else if (nxt == IPPROTO_FRAGMENT)
+ eoff += sizeof(struct ip6_frag);
+ else
+ eoff += (eh->ip6e_len + 1) << 3;
+ nxt = eh->ip6e_nxt;
+ break;
+ default:
+ goto notify;
+ }
+ }
+ notify:
+#ifndef PULLDOWN_TEST
+ icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+#else
+ IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
+ sizeof(*icmp6) + sizeof(struct ip6_hdr));
+ if (icmp6 == NULL) {
+ icmp6stat.icp6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+ ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
+ (inet6sw[ip6_protox[nxt]].pr_ctlinput);
+ if (ctlfunc) {
+ ip6cp.ip6c_m = m;
+ ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
+ ip6cp.ip6c_off = eoff;
+ (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp);
+ }
+ }
+ break;
+
+ badcode:
+ icmp6stat.icp6s_badcode++;
+ break;
+
+ badlen:
+ icmp6stat.icp6s_badlen++;
+ break;
+ }
+
+ passit:
+#ifdef HAVE_NRL_INPCB
+ rip6_input(&m, offp, IPPROTO_ICMPV6);
+#else
+ icmp6_rip6_input(&m, *offp);
+#endif
+ return IPPROTO_DONE;
+
+ freeit:
+ m_freem(m);
+ return IPPROTO_DONE;
+}
+
+/*
+ * Process a Node Information Query
+ */
+#ifdef __FreeBSD__
+#define hostnamelen strlen(hostname)
+#endif
+#ifndef offsetof /* XXX */
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+#endif
+
+static struct mbuf *
+ni6_input(m, off)
+ struct mbuf *m;
+ int off;
+{
+ struct icmp6_nodeinfo *ni6, *nni6;
+ struct mbuf *n = NULL;
+ u_int16_t qtype;
+ 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 */
+
+#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)
+ return NULL;
+#endif
+ qtype = ntohs(ni6->ni_qtype);
+
+ switch(qtype) {
+ case NI_QTYPE_NOOP:
+ break; /* no reply data */
+ case NI_QTYPE_SUPTYPES:
+ goto bad; /* xxx: to be implemented */
+ break;
+ case NI_QTYPE_FQDN:
+ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
+ hostnamelen;
+ break;
+ case NI_QTYPE_NODEADDR:
+ addrs = ni6_addrs(ni6, m, &ifp);
+ if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES)
+ replylen = MCLBYTES; /* XXX: we'll truncate later */
+
+ break;
+ default:
+ /*
+ * XXX: We must return a reply with the ICMP6 code
+ * `unknown Qtype' in this case. However we regard the case
+ * as an FQDN query for backward compatibility.
+ * Older versions set a random value to this field,
+ * so it rarely varies in the defined qtypes.
+ * But the mechanism is not reliable...
+ * maybe we should obsolete older versions.
+ */
+ qtype = NI_QTYPE_FQDN;
+ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
+ hostnamelen;
+ break;
+ }
+
+ /* allocate a mbuf to reply. */
+ MGETHDR(n, M_DONTWAIT, m->m_type);
+ if (n == NULL)
+ return(NULL);
+ M_COPY_PKTHDR(n, m); /* just for recvif */
+ if (replylen > MHLEN) {
+ if (replylen > MCLBYTES)
+ /*
+ * XXX: should we try to allocate more? But MCLBYTES is
+ * probably much larger than IPV6_MMTU...
+ */
+ 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_flags = 0;
+ break;
+ case NI_QTYPE_SUPTYPES:
+ goto bad; /* xxx: to be implemented */
+ break;
+ case NI_QTYPE_FQDN:
+ if (hostnamelen > 255) { /* XXX: rare case, but may happen */
+ printf("ni6_input: "
+ "hostname length(%d) is too large for reply\n",
+ hostnamelen);
+ goto bad;
+ }
+ fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
+ sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_nodeinfo));
+ nni6->ni_flags = 0; /* XXX: meaningless TTL */
+ fqdn->ni_fqdn_ttl = 0; /* ditto. */
+ fqdn->ni_fqdn_namelen = hostnamelen;
+ bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen);
+ break;
+ case NI_QTYPE_NODEADDR:
+ {
+ int lenlim, copied;
+
+ if (n->m_flags & M_EXT)
+ lenlim = MCLBYTES - sizeof(struct ip6_hdr) -
+ sizeof(struct icmp6_nodeinfo);
+ else
+ lenlim = MHLEN - sizeof(struct ip6_hdr) -
+ sizeof(struct icmp6_nodeinfo);
+ copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
+ /* XXX: reset mbuf length */
+ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
+ sizeof(struct icmp6_nodeinfo) + copied;
+ break;
+ }
+ default:
+ break; /* XXX impossible! */
+ }
+
+ nni6->ni_type = ICMP6_NI_REPLY;
+ nni6->ni_code = ICMP6_NI_SUCESS;
+ return(n);
+
+ bad:
+ if (n)
+ m_freem(n);
+ return(NULL);
+}
+#undef hostnamelen
+
+/*
+ * calculate the number of addresses to be returned in the node info reply.
+ */
+static int
+ni6_addrs(ni6, m, ifpp)
+ struct icmp6_nodeinfo *ni6;
+ struct mbuf *m;
+ struct ifnet **ifpp;
+{
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *ifa6;
+ register struct ifaddr *ifa;
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ int addrs = 0, addrsofif, iffound = 0;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+#else
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+#endif
+ {
+ addrsofif = 0;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) &&
+ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &ifa6->ia_addr.sin6_addr))
+ iffound = 1;
+
+ if (ifa6->ia6_flags & IN6_IFF_ANYCAST)
+ continue; /* we need only unicast addresses */
+
+ if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL |
+ NI_NODEADDR_FLAG_SITELOCAL |
+ NI_NODEADDR_FLAG_GLOBAL)) == 0)
+ continue;
+
+ /* What do we have to do about ::1? */
+ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
+ addrsofif++;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
+ addrsofif++;
+ break;
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
+ addrsofif++;
+ break;
+ default:
+ continue;
+ }
+ }
+ if (iffound) {
+ *ifpp = ifp;
+ return(addrsofif);
+ }
+
+ addrs += addrsofif;
+ }
+
+ return(addrs);
+}
+
+static int
+ni6_store_addrs(ni6, nni6, ifp0, resid)
+ struct icmp6_nodeinfo *ni6, *nni6;
+ struct ifnet *ifp0;
+ int resid;
+{
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ register struct ifnet *ifp = ifp0 ? ifp0 : ifnet;
+#else
+ register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
+#endif
+ register struct in6_ifaddr *ifa6;
+ register struct ifaddr *ifa;
+ int docopy, copied = 0;
+ u_char *cp = (u_char *)(nni6 + 1);
+
+ if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL))
+ return(0); /* needless to copy */
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (; ifp; ifp = ifp->if_next)
+#else
+ for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
+#endif
+ {
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ docopy = 0;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifa6 = (struct in6_ifaddr *)ifa;
+
+ if (ifa6->ia6_flags & IN6_IFF_ANYCAST) {
+ /* just experimental. not in the spec. */
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
+ docopy = 1;
+ else
+ continue;
+ }
+ else { /* unicast address */
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
+ continue;
+ else
+ docopy = 1;
+ }
+
+ /* What do we have to do about ::1? */
+ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
+ docopy = 1;
+ break;
+ case IPV6_ADDR_SCOPE_SITELOCAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
+ docopy = 1;
+ break;
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
+ docopy = 1;
+ break;
+ default:
+ continue;
+ }
+
+ if (docopy) {
+ if (resid < sizeof(struct in6_addr)) {
+ /*
+ * We give up much more copy.
+ * Set the truncate flag and return.
+ */
+ nni6->ni_flags |=
+ NI_NODEADDR_FLAG_TRUNCATE;
+ return(copied);
+ }
+ bcopy(&ifa6->ia_addr.sin6_addr, cp,
+ sizeof(struct in6_addr));
+ /* XXX: KAME link-local hack; remove ifindex */
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
+ ((struct in6_addr *)cp)->s6_addr16[1] = 0;
+ cp += sizeof(struct in6_addr);
+ resid -= sizeof(struct in6_addr);
+ copied += sizeof(struct in6_addr);
+ }
+ }
+ if (ifp0) /* we need search only on the specified IF */
+ break;
+ }
+
+ return(copied);
+}
+
+#ifndef HAVE_NRL_INPCB
+/*
+ * XXX almost dup'ed code with rip6_input.
+ */
+static int
+icmp6_rip6_input(mp, off)
+ struct mbuf **mp;
+ int off;
+{
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct in6pcb *in6p;
+ struct in6pcb *last = NULL;
+ struct sockaddr_in6 rip6src;
+ struct icmp6_hdr *icmp6;
+ struct mbuf *opts = NULL;
+
+ /* this is assumed to be safe. */
+ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
+
+ bzero(&rip6src, sizeof(rip6src));
+ rip6src.sin6_len = sizeof(struct sockaddr_in6);
+ rip6src.sin6_family = AF_INET6;
+ rip6src.sin6_addr = ip6->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
+ rip6src.sin6_addr.s6_addr16[1] = 0;
+ if (m->m_pkthdr.rcvif) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
+ rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
+ else
+ rip6src.sin6_scope_id = 0;
+ } else
+ rip6src.sin6_scope_id = 0;
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ LIST_FOREACH(in6p, &ripcb, inp_list)
+#else
+ for (in6p = rawin6pcb.in6p_next;
+ in6p != &rawin6pcb; in6p = in6p->in6p_next)
+#endif
+ {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if ((in6p->inp_vflag & INP_IPV6) == NULL)
+ 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;
+}
+#endif /*OpenBSD*/
+
+/*
+ * Reflect the ip6 packet back to the source.
+ * The caller MUST check if the destination is multicast or not.
+ * This function is usually called with a unicast destination which
+ * can be safely the source of the reply packet. But some exceptions
+ * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type).
+ * ``off'' points to the icmp6 header, counted from the top of the mbuf.
+ */
+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;
+#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)) {
+ printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
+ (u_long)off, (u_long)sizeof(struct ip6_hdr),
+ __FILE__, __LINE__);
+ goto bad;
+ }
+
+ /*
+ * If there are extra headers between IPv6 and ICMPv6, strip
+ * off that header first.
+ */
+ if (off > sizeof(struct ip6_hdr)) {
+ size_t 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 hack for link-local addresses */
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] =
+ htons(m->m_pkthdr.rcvif->if_index);
+ if (IN6_IS_ADDR_LINKLOCAL(&t))
+ t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
+
+#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) {
+#ifdef __FreeBSD__
+ RTFREE(icmp6_reflect_rt.ro_rt);
+#endif
+#ifdef __bsdi__
+ rtfree(icmp6_reflect_rt.ro_rt);
+#endif
+ 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;
+
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
+ RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&icmp6_reflect_rt.ro_rt);
+#endif
+ }
+
+ 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.
+ */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next)
+ if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
+ (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) {
+ 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 ourseleves.
+ */
+ src = &t;
+ }
+
+ if (src == 0)
+ /*
+ * We have not multicast routing yet. So this case matches
+ * to our multicast, our anycast or not to our unicast.
+ * Select a source address which has the same scope.
+ */
+ if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0)
+ src = &IA6_SIN6(ia)->sin6_addr;
+
+ if (src == 0)
+ goto bad;
+
+ ip6->ip6_src = *src;
+
+ ip6->ip6_flow = 0;
+ 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;
+ }
+
+ 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 IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+
+#ifdef COMPAT_RFC1885
+ ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif);
+#else
+ ip6_output(m, NULL, NULL, 0, NULL, &outif);
+#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];
+#if !defined(__OpenBSD__) && !defined(__bsdi__)
+ snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
+ ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
+#else
+ sprintf(buf, "(src=%s dst=%s tgt=%s)",
+ ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
+#endif
+ return buf;
+}
+
+void
+icmp6_redirect_input(m, off)
+ register 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 = (struct nd_redirect *)((caddr_t)ip6 + off);
+ 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 = nd_rd->nd_rd_target;
+ struct in6_addr reddst6 = nd_rd->nd_rd_dst;
+ union nd_opts ndopts;
+
+ if (!m || !ifp)
+ return;
+
+ /* XXX if we are router, we don't update route by icmp6 redirect */
+ if (ip6_forwarding)
+ return;
+ if (!icmp6_rediraccept)
+ return;
+
+ 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)) {
+ log(LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "must be from linklocal\n", ip6_sprintf(&src6));
+ return;
+ }
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "ICMP6 redirect sent from %s rejected; "
+ "hlim=%d (must be 255)\n",
+ ip6_sprintf(&src6), ip6->ip6_hlim);
+ return;
+ }
+ {
+ /* 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
+#ifdef __FreeBSD__
+ , 0UL
+#endif
+ );
+ if (rt) {
+ gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
+ if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
+ log(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);
+ return;
+ }
+ } else {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "no route found for redirect dst: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+ RTFREE(rt);
+ rt = NULL;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "redirect dst must be unicast: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+
+ 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) {
+ log(LOG_ERR,
+ "ICMP6 redirect rejected; "
+ "neither router case nor onlink case: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+ /* validation passed */
+
+ icmp6len -= sizeof(*nd_rd);
+ nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "icmp6_redirect_input: "
+ "invalid ND option, rejected: %s\n",
+ icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
+ return;
+ }
+
+ 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) {
+ log(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));
+ }
+
+ /* 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;
+#ifdef __bsdi__
+ extern int icmp_redirtimeout; /*XXX*/
+#endif
+
+ 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,
+#ifdef __bsdi__
+ icmp_redirtimeout
+#else
+ (struct rtentry **)NULL
+#endif /*__FreeBSD__, __NetBSD__, __bsdi__*/
+ );
+ }
+ /* finally update cached route in each socket via pfctlinput */
+ {
+ struct sockaddr_in6 sdst;
+#if 1
+#else
+ struct ip6protosw *pr;
+#endif
+
+ 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));
+#if 1
+ pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
+#else
+ /*
+ * do not use pfctlinput() here, we have different prototype for
+ * xx_ctlinput() in ip6proto.
+ */
+ for (pr = (struct ip6protosw *)inet6domain.dom_protosw;
+ pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW;
+ pr++) {
+ if (pr->pr_ctlinput) {
+ (*pr->pr_ctlinput)(PRC_REDIRECT_HOST,
+ (struct sockaddr *)&sdst, NULL, NULL, 0);
+ }
+ }
+#endif
+#ifdef IPSEC
+ key_sa_routechange((struct sockaddr *)&sdst);
+#endif
+ }
+}
+
+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;
+
+ /* 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 *);
+ if (nd6_is_addr_neighbor(&sip6->ip6_src, 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)
+ */
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (!m)
+ goto fail;
+ if (MHLEN < IPV6_MMTU)
+ MCLGET(m, M_DONTWAIT);
+ maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN;
+ maxlen = min(IPV6_MMTU, maxlen);
+ /* just for safety */
+ if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+ goto fail;
+
+ {
+ /* get ip6 linklocal address for ifp(my outgoing interface). */
+ struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp);
+ if (ia == 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;
+ /* 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;
+ if (!(rt_router->rt_flags & RTF_GATEWAY) &&
+ (rt_router->rt_flags & RTF_LLINFO) &&
+ (rt_router->rt_gateway->sa_family == AF_LINK) &&
+ (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) &&
+ sdl->sdl_alen) {
+ nd_opt = (struct nd_opt_hdr *)p;
+ nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+ len = 2 + ifp->if_addrlen;
+ len = (len + 7) & ~7; /*round by 8*/
+ nd_opt->nd_opt_len = len >> 3;
+ p += len;
+ lladdr = (char *)(nd_opt + 1);
+ bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
+ }
+ }
+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
+
+ {
+ /* 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\n", __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->m_next = m0;
+ m->m_pkthdr.len = m->m_len + m0->m_len;
+ }
+#ifdef M_DECRYPTED /*not openbsd*/
+noredhdropt:;
+#endif
+
+ 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... */
+#ifdef IPSEC
+ m->m_pkthdr.rcvif = NULL;
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, 0, NULL, &outif);
+ 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);
+}
+
+/*
+ * ICMPv6 socket option processing.
+ */
+int
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+icmp6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+#else
+icmp6_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+#endif
+{
+ int error = 0;
+ int optlen;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ register 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;
+#else
+#if defined(__OpenBSD__)
+ register struct inpcb *inp = sotoinpcb(so);
+#else
+ register struct in6pcb *in6p = sotoin6pcb(so);
+#endif
+ register struct mbuf *m = *mp;
+
+ optlen = m ? m->m_len : 0;
+#endif
+
+ if (level != IPPROTO_ICMPV6) {
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (op == PRCO_SETOPT && m)
+ (void)m_free(m);
+#endif
+ return EINVAL;
+ }
+
+ switch(op) {
+ case PRCO_SETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+ struct icmp6_filter *p;
+
+ if (optlen != sizeof(*p)) {
+ error = EMSGSIZE;
+ break;
+ }
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
+ optlen);
+#elif defined(__OpenBSD__)
+ p = mtod(m, struct icmp6_filter *);
+ if (!p || !inp->inp_icmp6filt) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(p, inp->inp_icmp6filt,
+ sizeof(struct icmp6_filter));
+ error = 0;
+#else
+ p = mtod(m, struct icmp6_filter *);
+ if (!p || !in6p->in6p_icmp6filt) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(p, in6p->in6p_icmp6filt,
+ sizeof(struct icmp6_filter));
+ error = 0;
+#endif
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (m)
+ (void)m_freem(m);
+#endif
+ break;
+
+ case PRCO_GETOPT:
+ switch (optname) {
+ case ICMP6_FILTER:
+ {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (inp->in6p_icmp6filt == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = sooptcopyout(sopt, inp->in6p_icmp6filt,
+ sizeof(struct icmp6_filter));
+#elif defined(__OpenBSD__)
+ struct icmp6_filter *p;
+
+ p = mtod(m, struct icmp6_filter *);
+ if (!p || !inp->inp_icmp6filt) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(inp->inp_icmp6filt, p,
+ sizeof(struct icmp6_filter));
+ error = 0;
+#else
+ struct icmp6_filter *p;
+
+ p = mtod(m, struct icmp6_filter *);
+ if (!p || !in6p->in6p_icmp6filt) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(in6p->in6p_icmp6filt, p,
+ sizeof(struct icmp6_filter));
+ error = 0;
+#endif
+ break;
+ }
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+
+ return(error);
+}
+
+/*
+ * 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 */
+{
+ struct timeval tp;
+ long sec_diff, usec_diff;
+
+ /* If we are not doing rate limitation, it is always okay to send */
+ if (!icmp6errratelim)
+ return 0;
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ microtime(&tp);
+ tp.tv_sec = time_second;
+#else
+ tp = time;
+#endif
+ if (tp.tv_sec < icmp6_nextsend.tv_sec
+ || (tp.tv_sec == icmp6_nextsend.tv_sec
+ && tp.tv_usec < icmp6_nextsend.tv_usec)) {
+ /* The packet is subject to rate limit */
+ return 1;
+ }
+ sec_diff = icmp6errratelim / 1000000;
+ usec_diff = icmp6errratelim % 1000000;
+ icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff;
+ if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) {
+ icmp6_nextsend.tv_sec++;
+ icmp6_nextsend.tv_usec -= 1000000;
+ }
+
+ /* it is okay to send this */
+ return 0;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static struct rtentry *
+icmp6_mtudisc_clone(dst)
+ struct sockaddr *dst;
+{
+ struct rtentry *rt;
+ int error;
+
+ rt = rtalloc1(dst, 1);
+ if (rt == 0)
+ return NULL;
+
+ /* If we didn't get a host route, allocate one */
+ if ((rt->rt_flags & RTF_HOST) == 0) {
+ struct rtentry *nrt;
+
+ error = rtrequest((int) RTM_ADD, dst,
+ (struct sockaddr *) rt->rt_gateway,
+ (struct sockaddr *) 0,
+ RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt);
+ if (error) {
+ rtfree(rt);
+ rtfree(nrt);
+ return NULL;
+ }
+ nrt->rt_rmx = rt->rt_rmx;
+ rtfree(rt);
+ rt = nrt;
+ }
+ error = rt_timer_add(rt, icmp6_mtudisc_timeout,
+ icmp6_mtudisc_timeout_q);
+ if (error) {
+ rtfree(rt);
+ return NULL;
+ }
+
+ return rt; /* caller need to call rtfree() */
+}
+
+static void
+icmp6_mtudisc_timeout(rt, r)
+ struct rtentry *rt;
+ struct rttimer *r;
+{
+ if (rt == NULL)
+ panic("icmp6_mtudisc_timeout: bad route to timeout");
+ if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
+ (RTF_DYNAMIC | RTF_HOST)) {
+ rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
+ } else {
+ if ((rt->rt_rmx.rmx_locks & RTV_MTU) == 0) {
+ rt->rt_rmx.rmx_mtu = 0;
+ }
+ }
+}
+#endif /*__NetBSD__ || __OpenBSD__*/
+
+#ifdef __bsdi__
+void
+icmp6_mtuexpire(rt, rtt)
+ struct rtentry *rt;
+ struct rttimer *rtt;
+{
+ rt->rt_flags |= RTF_PROBEMTU;
+ Free(rtt);
+}
+
+int *icmp6_sysvars[] = ICMPV6CTL_VARS;
+
+int
+icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ if (name[0] >= ICMPV6CTL_MAXID)
+ return (EOPNOTSUPP);
+ switch (name[0]) {
+#if 0
+ ICMPV6CTL_ND6_PRUNE:
+ ICMPV6CTL_ND6_DELAY:
+ ICMPV6CTL_ND6_UMAXTRIES:
+ ICMPV6CTL_ND6_MMAXTRIES:
+ ICMPV6CTL_ND6_USELOOPBACK:
+ ICMPV6CTL_ND6_PROXYALL:
+ /* need to check the value. */
+#endif
+ case ICMPV6CTL_STATS:
+ return sysctl_rdtrunc(oldp, oldlenp, newp, &icmp6stat,
+ sizeof(icmp6stat));
+
+ default:
+ return (sysctl_int_arr(icmp6_sysvars, name, namelen,
+ oldp, oldlenp, newp, newlen));
+ }
+}
+#endif /*__bsdi__*/
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+int
+icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return ENOTDIR;
+
+ switch (name[0]) {
+
+ case ICMPV6CTL_REDIRACCEPT:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &icmp6_rediraccept);
+ case ICMPV6CTL_REDIRTIMEOUT:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &icmp6_redirtimeout);
+ case ICMPV6CTL_STATS:
+ return sysctl_rdstruct(oldp, oldlenp, newp,
+ &icmp6stat, sizeof(icmp6stat));
+ case ICMPV6CTL_ERRRATELIMIT:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &icmp6errratelim);
+ case ICMPV6CTL_ND6_PRUNE:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_prune);
+ case ICMPV6CTL_ND6_DELAY:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_delay);
+ case ICMPV6CTL_ND6_UMAXTRIES:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_umaxtries);
+ case ICMPV6CTL_ND6_MMAXTRIES:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_mmaxtries);
+ case ICMPV6CTL_ND6_USELOOPBACK:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &nd6_useloopback);
+ case ICMPV6CTL_ND6_PROXYALL:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_proxyall);
+ default:
+ return ENOPROTOOPT;
+ }
+ /* NOTREACHED */
+}
+#endif /* __NetBSD__ */
diff --git a/sys/netinet6/icmp6.h b/sys/netinet6/icmp6.h
new file mode 100644
index 00000000000..e5c4d5716da
--- /dev/null
+++ b/sys/netinet6/icmp6.h
@@ -0,0 +1,657 @@
+/* $OpenBSD: icmp6.h,v 1.1 1999/12/08 06:50: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, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_ICMPV6_H_
+#define _NETINET6_ICMPV6_H_
+
+#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr)
+ - sizeof(struct icmp6_hdr) */
+
+struct icmp6_hdr {
+ u_int8_t icmp6_type; /* type field */
+ u_int8_t icmp6_code; /* code field */
+ u_int16_t icmp6_cksum; /* checksum field */
+ union {
+ u_int32_t icmp6_un_data32[1]; /* type-specific field */
+ u_int16_t icmp6_un_data16[2]; /* type-specific field */
+ u_int8_t icmp6_un_data8[4]; /* type-specific field */
+ } icmp6_dataun;
+};
+
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+#define icmp6_pptr icmp6_data32[0] /* parameter prob */
+#define icmp6_mtu icmp6_data32[0] /* packet too big */
+#define icmp6_id icmp6_data16[0] /* echo request/reply */
+#define icmp6_seq icmp6_data16[1] /* echo request/reply */
+#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */
+
+#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */
+#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */
+#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */
+#define ICMP6_PARAM_PROB 4 /* ip6 header bad */
+
+#define ICMP6_ECHO_REQUEST 128 /* echo service */
+#define ICMP6_ECHO_REPLY 129 /* echo reply */
+#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
+#define MLD6_LISTENER_QUERY 130 /* multicast listener query */
+#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
+#define MLD6_LISTENER_REPORT 131 /* multicast listener report */
+#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */
+#define MLD6_LISTENER_DONE 132 /* multicast listener done */
+
+#define ND_ROUTER_SOLICIT 133 /* router solicitation */
+#define ND_ROUTER_ADVERT 134 /* router advertisment */
+#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */
+#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */
+#define ND_REDIRECT 137 /* redirect */
+
+#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */
+
+#define ICMP6_WRUREQUEST 139 /* who are you request */
+#define ICMP6_WRUREPLY 140 /* who are you reply */
+#define ICMP6_FQDN_QUERY 139 /* FQDN query */
+#define ICMP6_FQDN_REPLY 140 /* FQDN reply */
+#define ICMP6_NI_QUERY 139 /* node information request */
+#define ICMP6_NI_REPLY 140 /* node information reply */
+
+/* The definitions below are experimental. TBA */
+#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */
+#define MLD6_MTRACE 142 /* mtrace messages */
+
+#define ICMP6_MAXTYPE 142
+
+#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
+#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */
+#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */
+#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */
+#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */
+#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */
+
+#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */
+#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */
+
+#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
+#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */
+#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */
+
+#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */
+
+#define ICMP6_NI_SUCESS 0 /* node information successful reply */
+#define ICMP6_NI_REFUSED 1 /* node information request is refused */
+#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */
+
+#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */
+#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */
+#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */
+
+/* Used in kernel only */
+#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */
+#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */
+
+/*
+ * Multicast Listener Discovery
+ */
+struct mld6_hdr {
+ struct icmp6_hdr mld6_hdr;
+ struct in6_addr mld6_addr; /* multicast address */
+};
+
+#define mld6_type mld6_hdr.icmp6_type
+#define mld6_code mld6_hdr.icmp6_code
+#define mld6_cksum mld6_hdr.icmp6_cksum
+#define mld6_maxdelay mld6_hdr.icmp6_data16[0]
+#define mld6_reserved mld6_hdr.icmp6_data16[1]
+
+/*
+ * Neighbor Discovery
+ */
+
+struct nd_router_solicit { /* router solicitation */
+ struct icmp6_hdr nd_rs_hdr;
+ /* could be followed by options */
+};
+
+#define nd_rs_type nd_rs_hdr.icmp6_type
+#define nd_rs_code nd_rs_hdr.icmp6_code
+#define nd_rs_cksum nd_rs_hdr.icmp6_cksum
+#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0]
+
+struct nd_router_advert { /* router advertisement */
+ struct icmp6_hdr nd_ra_hdr;
+ u_int32_t nd_ra_reachable; /* reachable time */
+ u_int32_t nd_ra_retransmit; /* retransmit timer */
+ /* could be followed by options */
+};
+
+#define nd_ra_type nd_ra_hdr.icmp6_type
+#define nd_ra_code nd_ra_hdr.icmp6_code
+#define nd_ra_cksum nd_ra_hdr.icmp6_cksum
+#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0]
+#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1]
+#define ND_RA_FLAG_MANAGED 0x80
+#define ND_RA_FLAG_OTHER 0x40
+#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
+
+struct nd_neighbor_solicit { /* neighbor solicitation */
+ struct icmp6_hdr nd_ns_hdr;
+ struct in6_addr nd_ns_target; /*target address */
+ /* could be followed by options */
+};
+
+#define nd_ns_type nd_ns_hdr.icmp6_type
+#define nd_ns_code nd_ns_hdr.icmp6_code
+#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
+#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
+
+struct nd_neighbor_advert { /* neighbor advertisement */
+ struct icmp6_hdr nd_na_hdr;
+ struct in6_addr nd_na_target; /* target address */
+ /* could be followed by options */
+};
+
+#define nd_na_type nd_na_hdr.icmp6_type
+#define nd_na_code nd_na_hdr.icmp6_code
+#define nd_na_cksum nd_na_hdr.icmp6_cksum
+#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0]
+#if BYTE_ORDER == BIG_ENDIAN
+#define ND_NA_FLAG_ROUTER 0x80000000
+#define ND_NA_FLAG_SOLICITED 0x40000000
+#define ND_NA_FLAG_OVERRIDE 0x20000000
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define ND_NA_FLAG_ROUTER 0x80
+#define ND_NA_FLAG_SOLICITED 0x40
+#define ND_NA_FLAG_OVERRIDE 0x20
+#endif
+#endif
+
+struct nd_redirect { /* redirect */
+ struct icmp6_hdr nd_rd_hdr;
+ struct in6_addr nd_rd_target; /* target address */
+ struct in6_addr nd_rd_dst; /* destination address */
+ /* could be followed by options */
+};
+
+#define nd_rd_type nd_rd_hdr.icmp6_type
+#define nd_rd_code nd_rd_hdr.icmp6_code
+#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
+#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
+
+struct nd_opt_hdr { /* Neighbor discovery option header */
+ u_int8_t nd_opt_type;
+ u_int8_t nd_opt_len;
+ /* followed by option specific data*/
+};
+
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define ND_OPT_PREFIX_INFORMATION 3
+#define ND_OPT_REDIRECTED_HEADER 4
+#define ND_OPT_MTU 5
+
+struct nd_opt_prefix_info { /* prefix information */
+ u_int8_t nd_opt_pi_type;
+ u_int8_t nd_opt_pi_len;
+ u_int8_t nd_opt_pi_prefix_len;
+ u_int8_t nd_opt_pi_flags_reserved;
+ u_int32_t nd_opt_pi_valid_time;
+ u_int32_t nd_opt_pi_preferred_time;
+ u_int32_t nd_opt_pi_reserved2;
+ struct in6_addr nd_opt_pi_prefix;
+};
+
+#define ND_OPT_PI_FLAG_ONLINK 0x80
+#define ND_OPT_PI_FLAG_AUTO 0x40
+
+struct nd_opt_rd_hdr { /* redirected header */
+ u_int8_t nd_opt_rh_type;
+ u_int8_t nd_opt_rh_len;
+ u_int16_t nd_opt_rh_reserved1;
+ u_int32_t nd_opt_rh_reserved2;
+ /* followed by IP header and data */
+};
+
+struct nd_opt_mtu { /* MTU option */
+ u_int8_t nd_opt_mtu_type;
+ u_int8_t nd_opt_mtu_len;
+ u_int16_t nd_opt_mtu_reserved;
+ u_int32_t nd_opt_mtu_mtu;
+};
+
+/*
+ * icmp6 namelookup
+ */
+
+struct icmp6_namelookup {
+ struct icmp6_hdr icmp6_nl_hdr;
+ u_int64_t icmp6_nl_nonce;
+ u_int32_t icmp6_nl_ttl;
+#if 0
+ u_int8_t icmp6_nl_len;
+ u_int8_t icmp6_nl_name[3];
+#endif
+ /* could be followed by options */
+};
+
+/*
+ * icmp6 node information
+ */
+struct icmp6_nodeinfo {
+ struct icmp6_hdr icmp6_ni_hdr;
+ u_int64_t icmp6_ni_nonce;
+ /* could be followed by reply data */
+};
+
+#define ni_type icmp6_ni_hdr.icmp6_type
+#define ni_code icmp6_ni_hdr.icmp6_code
+#define ni_cksum icmp6_ni_hdr.icmp6_cksum
+#define ni_qtype icmp6_ni_hdr.icmp6_data16[0]
+#define ni_flags icmp6_ni_hdr.icmp6_data16[1]
+
+
+#define NI_QTYPE_NOOP 0 /* NOOP */
+#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */
+#define NI_QTYPE_FQDN 2 /* FQDN */
+#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define NI_SUPTYPE_FLAG_COMPRESS 0x1
+#define NI_FQDN_FLAG_VALIDTTL 0x1
+#define NI_NODEADDR_FLAG_LINKLOCAL 0x1
+#define NI_NODEADDR_FLAG_SITELOCAL 0x2
+#define NI_NODEADDR_FLAG_GLOBAL 0x4
+#define NI_NODEADDR_FLAG_ALL 0x8
+#define NI_NODEADDR_FLAG_TRUNCATE 0x10
+#define NI_NODEADDR_FLAG_ANYCAST 0x20 /* just experimental. not in spec */
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define NI_SUPTYPE_FLAG_COMPRESS 0x0100
+#define NI_FQDN_FLAG_VALIDTTL 0x0100
+#define NI_NODEADDR_FLAG_LINKLOCAL 0x0100
+#define NI_NODEADDR_FLAG_SITELOCAL 0x0200
+#define NI_NODEADDR_FLAG_GLOBAL 0x0400
+#define NI_NODEADDR_FLAG_ALL 0x0800
+#define NI_NODEADDR_FLAG_TRUNCATE 0x1000
+#define NI_NODEADDR_FLAG_ANYCAST 0x2000 /* just experimental. not in spec */
+#endif
+
+struct ni_reply_fqdn {
+ u_int32_t ni_fqdn_ttl; /* TTL */
+ u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */
+ u_int8_t ni_fqdn_name[3]; /* XXX: alignment */
+};
+
+/*
+ * Router Renumbering. as router-renum-08.txt
+ */
+#if BYTE_ORDER == BIG_ENDIAN /* net byte order */
+struct icmp6_router_renum { /* router renumbering header */
+ struct icmp6_hdr rr_hdr;
+ u_int8_t rr_segnum;
+ u_int8_t rr_test : 1;
+ u_int8_t rr_reqresult : 1;
+ u_int8_t rr_forceapply : 1;
+ u_int8_t rr_specsite : 1;
+ u_int8_t rr_prevdone : 1;
+ u_int8_t rr_flags_reserved : 3;
+ u_int16_t rr_maxdelay;
+ u_int32_t rr_reserved;
+};
+#elif BYTE_ORDER == LITTLE_ENDIAN
+struct icmp6_router_renum { /* router renumbering header */
+ struct icmp6_hdr rr_hdr;
+ u_int8_t rr_segnum;
+ u_int8_t rr_flags_reserved : 3;
+ u_int8_t rr_prevdone : 1;
+ u_int8_t rr_specsite : 1;
+ u_int8_t rr_forceapply : 1;
+ u_int8_t rr_reqresult : 1;
+ u_int8_t rr_test : 1;
+ u_int16_t rr_maxdelay;
+ u_int32_t rr_reserved;
+};
+#endif /* BYTE_ORDER */
+
+#define rr_type rr_hdr.icmp6_type
+#define rr_code rr_hdr.icmp6_code
+#define rr_cksum rr_hdr.icmp6_cksum
+#define rr_seqnum rr_hdr.icmp6_data32[0]
+
+struct rr_pco_match { /* match prefix part */
+ u_int8_t rpm_code;
+ u_int8_t rpm_len;
+ u_int8_t rpm_ordinal;
+ u_int8_t rpm_matchlen;
+ u_int8_t rpm_minlen;
+ u_int8_t rpm_maxlen;
+ u_int16_t rpm_reserved;
+ struct in6_addr rpm_prefix;
+};
+
+#define RPM_PCO_ADD 1
+#define RPM_PCO_CHANGE 2
+#define RPM_PCO_SETGLOBAL 3
+#define RPM_PCO_MAX 4
+
+#if BYTE_ORDER == BIG_ENDIAN /* net byte order */
+struct rr_pco_use { /* use prefix part */
+ u_int8_t rpu_uselen;
+ u_int8_t rpu_keeplen;
+ u_int8_t rpu_mask_onlink : 1;
+ u_int8_t rpu_mask_autonomous : 1;
+ u_int8_t rpu_mask_reserved : 6;
+ u_int8_t rpu_onlink : 1;
+ u_int8_t rpu_autonomous : 1;
+ u_int8_t rpu_raflags_reserved : 6;
+ u_int32_t rpu_vltime;
+ u_int32_t rpu_pltime;
+ u_int32_t rpu_decr_vltime : 1;
+ u_int32_t rpu_decr_pltime : 1;
+ u_int32_t rpu_flags_reserved : 6;
+ u_int32_t rpu_reserved : 24;
+ struct in6_addr rpu_prefix;
+};
+#elif BYTE_ORDER == LITTLE_ENDIAN
+struct rr_pco_use { /* use prefix part */
+ u_int8_t rpu_uselen;
+ u_int8_t rpu_keeplen;
+ u_int8_t rpu_mask_reserved : 6;
+ u_int8_t rpu_mask_autonomous : 1;
+ u_int8_t rpu_mask_onlink : 1;
+ u_int8_t rpu_raflags_reserved : 6;
+ u_int8_t rpu_autonomous : 1;
+ u_int8_t rpu_onlink : 1;
+ u_int32_t rpu_vltime;
+ u_int32_t rpu_pltime;
+ u_int32_t rpu_flags_reserved : 6;
+ u_int32_t rpu_decr_pltime : 1;
+ u_int32_t rpu_decr_vltime : 1;
+ u_int32_t rpu_reserved : 24;
+ struct in6_addr rpu_prefix;
+};
+#endif /* BYTE_ORDER */
+
+#if BYTE_ORDER == BIG_ENDIAN /* net byte order */
+struct rr_result { /* router renumbering result message */
+ u_int8_t rrr_reserved;
+ u_int8_t rrr_flags_reserved : 6;
+ u_int8_t rrr_outofbound : 1;
+ u_int8_t rrr_forbidden : 1;
+ u_int8_t rrr_ordinal;
+ u_int8_t rrr_matchedlen;
+ u_int32_t rrr_ifid;
+ struct in6_addr rrr_prefix;
+};
+#elif BYTE_ORDER == LITTLE_ENDIAN
+struct rr_result { /* router renumbering result message */
+ u_int8_t rrr_reserved;
+ u_int8_t rrr_forbidden : 1;
+ u_int8_t rrr_outofbound : 1;
+ u_int8_t rrr_flags_reserved : 6;
+ u_int8_t rrr_ordinal;
+ u_int8_t rrr_matchedlen;
+ u_int32_t rrr_ifid;
+ struct in6_addr rrr_prefix;
+};
+#endif /* BYTE_ORDER */
+
+/*
+ * icmp6 filter structures.
+ */
+
+struct icmp6_filter {
+ u_int32_t icmp6_filter[8];
+};
+
+#ifdef _KERNEL
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ { \
+ int i; u_char *p; \
+ p = (u_char *)filterp; \
+ for (i = 0; i < sizeof(struct icmp6_filter); i++) \
+ p[i] = 0xff; \
+ }
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ bzero(filterp, sizeof(struct icmp6_filter))
+#else /* _KERNEL */
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ memset(filterp, 0xff, sizeof(struct icmp6_filter))
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ memset(filterp, 0x00, sizeof(struct icmp6_filter))
+#endif /* _KERNEL */
+
+#define ICMP6_FILTER_SETPASS(type, filterp) \
+ (((filterp)->icmp6_filter[(type) >> 5]) |= (1 << ((type) & 31)))
+#define ICMP6_FILTER_SETBLOCK(type, filterp) \
+ (((filterp)->icmp6_filter[(type) >> 5]) &= ~(1 << ((type) & 31)))
+#define ICMP6_FILTER_WILLPASS(type, filterp) \
+ ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) != 0)
+#define ICMP6_FILTER_WILLBLOCK(type, filterp) \
+ ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) == 0)
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol version 6.
+ */
+struct icmp6stat {
+/* statistics related to icmp6 packets generated */
+ u_quad_t icp6s_error; /* # of calls to icmp6_error */
+ u_quad_t icp6s_canterror; /* no error 'cuz old was icmp */
+ u_quad_t icp6s_toofreq; /* no error 'cuz rate limitation */
+ u_quad_t icp6s_outhist[256];
+/* statistics related to input message processed */
+ u_quad_t icp6s_badcode; /* icmp6_code out of range */
+ u_quad_t icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */
+ u_quad_t icp6s_checksum; /* bad checksum */
+ u_quad_t icp6s_badlen; /* calculated bound mismatch */
+ u_quad_t icp6s_reflect; /* number of responses */
+ u_quad_t icp6s_inhist[256];
+ u_quad_t icp6s_nd_toomanyopt; /* too many ND options */
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPV6CTL_STATS 1
+#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */
+#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */
+#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */
+#define ICMPV6CTL_ND6_PRUNE 6
+#define ICMPV6CTL_ND6_DELAY 8
+#define ICMPV6CTL_ND6_UMAXTRIES 9
+#define ICMPV6CTL_ND6_MMAXTRIES 10
+#define ICMPV6CTL_ND6_USELOOPBACK 11
+#define ICMPV6CTL_ND6_PROXYALL 12
+#define ICMPV6CTL_MAXID 13
+
+#define ICMPV6CTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "rediraccept", CTLTYPE_INT }, \
+ { "redirtimeout", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { "errratelimit", CTLTYPE_INT }, \
+ { "nd6_prune", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { "nd6_delay", CTLTYPE_INT }, \
+ { "nd6_umaxtries", CTLTYPE_INT }, \
+ { "nd6_mmaxtries", CTLTYPE_INT }, \
+ { "nd6_useloopback", CTLTYPE_INT }, \
+ { "nd6_proxyall", CTLTYPE_INT }, \
+}
+
+#define ICMPV6CTL_VARS { \
+ 0, \
+ 0, \
+ &icmp6_rediraccept, \
+ &icmp6_redirtimeout, \
+ 0, \
+ 0, \
+ &icmp6errratelim, \
+ &nd6_prune, \
+ 0, \
+ &nd6_delay, \
+ &nd6_umaxtries, \
+ &nd6_mmaxtries, \
+ &nd6_useloopback, \
+ &nd6_proxyall, \
+}
+
+#define RTF_PROBEMTU RTF_PROTO1
+
+#ifdef _KERNEL
+# ifdef __STDC__
+struct rtentry;
+struct rttimer;
+struct in6_multi;
+# endif
+void icmp6_init __P((void));
+void icmp6_paramerror __P((struct mbuf *, int));
+void icmp6_error __P((struct mbuf *, int, int, int));
+int icmp6_input __P((struct mbuf **, int *, int));
+void icmp6_fasttimo __P((void));
+void icmp6_reflect __P((struct mbuf *, size_t));
+void icmp6_prepare __P((struct mbuf *));
+void icmp6_redirect_input __P((struct mbuf *, int));
+void icmp6_redirect_output __P((struct mbuf *, struct rtentry *));
+#ifdef __bsdi__
+int icmp6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+void icmp6_mtuexpire __P((struct rtentry *, struct rttimer *));
+#endif /*__bsdi__*/
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int icmp6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+#endif
+
+/* XXX: is this the right place for these macros? */
+#define icmp6_ifstat_inc(ifp, tag) \
+do { \
+ if ((ifp) && (ifp)->if_index <= if_index \
+ && (ifp)->if_index < icmp6_ifstatmax \
+ && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \
+ icmp6_ifstat[(ifp)->if_index]->tag++; \
+ } \
+} while (0)
+
+#define icmp6_ifoutstat_inc(ifp, type, code) \
+do { \
+ icmp6_ifstat_inc(ifp, ifs6_out_msg); \
+ if (type < ICMP6_INFOMSG_MASK) \
+ icmp6_ifstat_inc(ifp, ifs6_out_error); \
+ switch(type) { \
+ case ICMP6_DST_UNREACH: \
+ icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \
+ if (code == ICMP6_DST_UNREACH_ADMIN) \
+ icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \
+ break; \
+ case ICMP6_PACKET_TOO_BIG: \
+ icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \
+ break; \
+ case ICMP6_TIME_EXCEEDED: \
+ icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \
+ break; \
+ case ICMP6_PARAM_PROB: \
+ icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \
+ break; \
+ case ICMP6_ECHO_REQUEST: \
+ icmp6_ifstat_inc(ifp, ifs6_out_echo); \
+ break; \
+ case ICMP6_ECHO_REPLY: \
+ icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \
+ break; \
+ case MLD6_LISTENER_QUERY: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \
+ break; \
+ case MLD6_LISTENER_REPORT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \
+ break; \
+ case MLD6_LISTENER_DONE: \
+ icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \
+ break; \
+ case ND_ROUTER_SOLICIT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \
+ break; \
+ case ND_ROUTER_ADVERT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \
+ break; \
+ case ND_NEIGHBOR_SOLICIT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \
+ break; \
+ case ND_NEIGHBOR_ADVERT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \
+ break; \
+ case ND_REDIRECT: \
+ icmp6_ifstat_inc(ifp, ifs6_out_redirect); \
+ break; \
+ } \
+} while (0)
+
+extern int icmp6_rediraccept; /* accept/process redirects */
+extern int icmp6_redirtimeout; /* cache time for redirect routes */
+#endif /* _KERNEL */
+
+#endif /* not _NETINET6_ICMPV6_H_ */
+
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 6d2a1a0e50d..a3b74ae4157 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1,942 +1,2265 @@
/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+ * 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
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
#include <sys/param.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
#include <sys/ioctl.h>
+#endif
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
+#include <sys/sockio.h>
#include <sys/systm.h>
-#if __NetBSD__ || __FreeBSD__
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
#include <sys/proc.h>
-#endif /* __NetBSD__ || __FreeBSD__ */
+#endif
+#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 "gif.h"
+#if NGIF > 0
+#include <net/if_gif.h>
+#endif
+#include <net/if_dl.h>
#include <netinet/in.h>
-
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
+#include <netinet/in_var.h>
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <netinet/if_ether.h>
+#endif
+
+#include <netinet6/nd6.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/mld6_var.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/in6_ifattach.h>
+
+#include <net/net_osdep.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address");
+#endif
/*
- * Globals
+ * Definitions of some costant IP6 addresses.
*/
-
-struct ifnet *mcastdefault = NULL; /* Should be changeable by sysctl(). */
-
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;
+
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
+ struct ifnet *, struct proc *));
+#else
+static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
+ struct ifnet *));
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+struct in6_multihead in6_multihead; /* XXX BSS initialization */
+#else
/*
- * External globals
+ * This structure is used to keep track of in6_multi chains which belong to
+ * deleted interface addresses.
*/
+static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */
-extern struct sockaddr_in6 in6_allones;
-extern struct in6_ifaddr *in6_ifaddr;
-extern struct in6_ifnet *in6_ifnet;
-extern int ipv6forwarding;
-
-static void setmcastdef __P((register struct ifnet *));
-void del_in6_ifnet __P((struct ifnet *));
-struct in6_ifnet *add_in6_ifnet __P((struct ifnet *, int *));
-int in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *));
-int in6_ifinit __P((register struct ifnet *, register struct in6_ifaddr *, struct sockaddr_in6 *, int, int));
-void addrconf_dad __P((struct in6_ifaddr *));
-
-/*----------------------------------------------------------------------
- * Set the default multicast interface. In single-homed case, this will
- * always be the non-loopback interface. In multi-homed cases, the function
- * should be able to set one accordingly. The multicast route entry
- * (ff00::/8) will have its rt_ifp point to this interface, and its rt_ifa
- * point to whatever rtrequest() does. The rt_ifa should be more intelligently
- * set eventually.
- ----------------------------------------------------------------------*/
+struct multi6_kludge {
+ LIST_ENTRY(multi6_kludge) mk_entry;
+ struct ifnet *mk_ifp;
+ struct in6_multihead mk_head;
+};
+#endif
-static void
-setmcastdef(ifp)
- register struct ifnet *ifp;
+/*
+ * Determine whether an IP6 address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ */
+int
+in6_canforward(src, dst)
+ struct in6_addr *src, *dst;
{
-#ifdef __FreeBSD__
-struct ifaddr *ifa = ifp->if_addrhead.tqh_first;
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- struct ifaddr *ifa = ifp->if_addrlist.tqh_first;
-#else /* __NetBSD__ || __OpenBSD__ */
- struct ifaddr *ifa = ifp->if_addrlist;
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- struct sockaddr_dl lsdl;
- struct sockaddr_in6 lsin6;
- struct rtentry *newrt=NULL;
- int s;
+ if (IN6_IS_ADDR_LINKLOCAL(src) ||
+ IN6_IS_ADDR_LINKLOCAL(dst) ||
+ IN6_IS_ADDR_MULTICAST(dst))
+ return(0);
+ return(1);
+}
- if (ifp == mcastdefault)
- return;
+/*
+ * Check if the loopback entry will be automatically generated.
+ * if 0 returned, will not be automatically generated.
+ * if 1 returned, will be automatically generated.
+ */
+static int
+in6_is_ifloop_auto(struct ifaddr *ifa)
+{
+#ifdef __OpenBSD__
+ return 0;
+#else
+#define SIN6(s) ((struct sockaddr_in6 *)s)
+ /*
+ * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT),
+ * or netmask is all0 or all1, then cloning will not happen,
+ * then we can't rely on its loopback entry generation.
+ */
+ if ((ifa->ifa_flags & RTF_CLONING) == 0 ||
+ (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) ||
+ (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6)
+ &&
+ IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr,
+ &in6mask128)) ||
+ ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0)
+ return 0;
+ else
+ return 1;
+#undef SIN6
+#endif
+}
- /*
- * If NULL, nuke any mcast entry.
- */
+/*
+ * Subroutine for in6_ifaddloop() and in6_ifremloop().
+ * This routine does actual work.
+ */
+static void
+in6_ifloop_request(int cmd, struct ifaddr *ifa)
+{
+ struct sockaddr_in6 lo_sa;
+ struct sockaddr_in6 all1_sa;
+ struct rtentry *nrt = NULL;
+
+ bzero(&lo_sa, sizeof(lo_sa));
+ bzero(&all1_sa, sizeof(all1_sa));
+ lo_sa.sin6_family = AF_INET6;
+ lo_sa.sin6_len = sizeof(struct sockaddr_in6);
+ all1_sa = lo_sa;
+ lo_sa.sin6_addr = in6addr_loopback;
+ all1_sa.sin6_addr = in6mask128;
+
+ /* So we add or remove static loopback entry, here. */
+ rtrequest(cmd, ifa->ifa_addr,
+ (struct sockaddr *)&lo_sa,
+ (struct sockaddr *)&all1_sa,
+ RTF_UP|RTF_HOST, &nrt);
+
+ /*
+ * Make sure rt_ifa be equal to IFA, the second argument of the
+ * function.
+ * We need this because when we refer rt_ifa->ia6_flags in ip6_input,
+ * we assume that the rt_ifa points to the address instead of the
+ * loopback address.
+ */
+ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) {
+ nrt->rt_ifa->ifa_refcnt--;
+ ifa->ifa_refcnt++;
+ nrt->rt_ifa = ifa;
+ }
+ if (nrt)
+ nrt->rt_refcnt--;
+}
- /*
- * Find link addr for ifp.
- */
+/*
+ * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link).
+ * Because, KAME needs loopback rtentry for ownaddr check in
+ * ip6_input().
+ */
+static void
+in6_ifaddloop(struct ifaddr *ifa)
+{
+ if (!in6_is_ifloop_auto(ifa)) {
+ struct rtentry *rt;
- while (ifa != NULL && ifa->ifa_addr->sa_family != AF_LINK)
+ /* If there is no loopback entry, allocate one. */
+ rt = rtalloc1(ifa->ifa_addr, 0
#ifdef __FreeBSD__
- ifa = ifa->ifa_link.tqe_next;
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- ifa = ifa->ifa_list.tqe_next;
-#else /* __NetBSD__ || __OpenBSD__ */
- ifa = ifa->ifa_next;
-#endif /* __NetBSD__ || __OpenBSD__ */
+ , 0
#endif /* __FreeBSD__ */
+ );
+ if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
+ in6_ifloop_request(RTM_ADD, ifa);
+ if (rt)
+ rt->rt_refcnt--;
+ }
+}
- if (ifa == NULL)
- panic("Can't find AF_LINK for new multicast default interface.");
-
- bcopy(ifa->ifa_addr,&lsdl,ifa->ifa_addr->sa_len);
- DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)&lsdl));
- lsdl.sdl_alen = 0;
- lsdl.sdl_slen = 0;
- lsdl.sdl_nlen = 0;
-
- /*
- * Delete old route, and add new one.
- */
-
- bzero(&lsin6,sizeof(lsin6));
- lsin6.sin6_family = AF_INET6;
- lsin6.sin6_len = sizeof(lsin6);
- lsin6.sin6_addr.s6_addr[0]=0xff;
-
- /* Neat property, mask and value are identical! */
-
- s = splnet();
- rtrequest(RTM_DELETE,(struct sockaddr *)&lsin6,NULL,
- (struct sockaddr *)&lsin6,0,NULL);
- /*
- *
- * NB: If we clone, we have mcast dests being on a route.
- * Consider multihomed system with processes talking to the
- * same mcast group, but out different interfaces.
- *
- * Also, the RTM_ADD will do its best to find a "source address" to stick
- * in the rt_ifa field. (See ipv6_rtrequest.c for this code.)
- */
- rtrequest(RTM_ADD,(struct sockaddr *)&lsin6,(struct sockaddr *)&lsdl,
- (struct sockaddr *)&lsin6,0,&newrt);
- if (newrt == NULL)
- panic("Assigning default multicast if.");
- newrt->rt_rmx.rmx_mtu = ifp->if_mtu;
- newrt->rt_refcnt--;
- mcastdefault = ifp;
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * Delete an "IPv6 interface". Only called inside splnet().
- ----------------------------------------------------------------------*/
+/*
+ * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
+ * if it exists.
+ */
+static void
+in6_ifremloop(struct ifaddr *ifa)
+{
+ if (!in6_is_ifloop_auto(ifa)) {
+ struct in6_ifaddr *ia;
+ int ia_count = 0;
+
+ /* If only one ifa for the loopback entry, delete it. */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa),
+ &ia->ia_addr.sin6_addr)) {
+ ia_count++;
+ if (ia_count > 1)
+ break;
+ }
+ }
+ if (ia_count == 1)
+ in6_ifloop_request(RTM_DELETE, ifa);
+ }
+}
-void
-del_in6_ifnet(ifp)
- struct ifnet *ifp;
+/*
+ * Subroutine for in6_ifaddproxy() and in6_ifremproxy().
+ * This routine does actual work.
+ * call in6_addmulti() when cmd == 1.
+ * call in6_delmulti() when cmd == 2.
+ */
+static int
+in6_ifproxy_request(int cmd, struct in6_ifaddr *ia)
{
- struct in6_ifnet *i6ifp,*prev = NULL;
+ int error = 0;
+
+ /*
+ * If we have an IPv6 dstaddr on adding p2p interface,
+ * join dstaddr's solicited multicast on necessary interface.
+ */
+ if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) &&
+ ia->ia_dstaddr.sin6_family == AF_INET6 &&
+ !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ struct in6_ifaddr *ia_lan;
+
+ /*
+ * TODO: Join only on some specified interfaces by some
+ * configuration.
+ * Unsolicited Neighbor Advertisements will be also necessary.
+ *
+ * Now, join on interfaces which meets following.
+ * -IFF_BROADCAST and IFF_MULTICAST
+ * (NBMA is out of scope)
+ * -the prefix value is same as p2p dstaddr
+ */
+ for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) {
+ struct in6_addr llsol;
+
+ if ((ia_lan->ia_ifp->if_flags &
+ (IFF_BROADCAST|IFF_MULTICAST)) !=
+ (IFF_BROADCAST|IFF_MULTICAST))
+ continue;
+ if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia),
+ IA6_IN6(ia_lan),
+ IA6_MASKIN6(ia_lan)))
+ continue;
+ if (ia_lan->ia_ifp == ia->ia_ifp)
+ continue;
+
+ /* init llsol */
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_dstaddr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ if (cmd == 1)
+ (void)in6_addmulti(&llsol,
+ ia_lan->ia_ifp,
+ &error);
+ else if (cmd == 2) {
+ struct in6_multi *in6m;
+
+ IN6_LOOKUP_MULTI(llsol,
+ ia_lan->ia_ifp,
+ in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ }
+ }
+ return error;
+}
- for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next)
- {
- if (i6ifp->i6ifp_ifp == ifp)
- break;
- prev = i6ifp;
- }
+static int
+in6_ifaddproxy(struct in6_ifaddr *ia)
+{
+ return(in6_ifproxy_request(1, ia));
+}
- if (i6ifp == NULL)
- panic("Ooooh boy, consistency mismatch in del_in6_ifnet!");
+static void
+in6_ifremproxy(struct in6_ifaddr *ia)
+{
+ in6_ifproxy_request(2, ia);
+}
- if (--(i6ifp->i6ifp_numaddrs) == 0)
- {
- while (i6ifp->i6ifp_multiaddrs != NULL)
- {
- i6ifp->i6ifp_multiaddrs->in6m_refcount = 1;
- in6_delmulti(i6ifp->i6ifp_multiaddrs);
- }
- if (prev == NULL)
- in6_ifnet = i6ifp->i6ifp_next;
- else prev->i6ifp_next = i6ifp->i6ifp_next;
- free(i6ifp,M_I6IFP);
- }
-}
-
-/*----------------------------------------------------------------------
- * Add a new "IPv6 interface". Only called inside splnet().
- * Perhaps send router adverts when this gets called. For now, they
- * are issued when duplicate address detection succeeds on link-locals.
- * See ipv6_addrconf.c for details.
- ----------------------------------------------------------------------*/
-
-struct in6_ifnet *
-add_in6_ifnet(ifp, new)
- struct ifnet *ifp; /* Assume an in6_ifaddr with this ifp is already
- allocated and linked into the master list. */
- int *new; /* XXX */
-{
- struct in6_ifnet *i6ifp;
-
- *new = 0;
- for (i6ifp = in6_ifnet; i6ifp != NULL; i6ifp = i6ifp->i6ifp_next)
- if (i6ifp->i6ifp_ifp == ifp)
- break;
-
- if (i6ifp == NULL)
- {
- i6ifp = malloc(sizeof(*i6ifp),M_I6IFP,M_NOWAIT);
- if (i6ifp == NULL)
+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 = ifindex2ifnet[idx];
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- printf("DANGER! Malloc for i6ifp failed.\n");
- return NULL;
+ 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;
}
- i6ifp->i6ifp_ifp = ifp;
- i6ifp->i6ifp_multiaddrs = NULL;
- i6ifp->i6ifp_numaddrs = 1;
- /* Other inits... */
- i6ifp->i6ifp_next = in6_ifnet;
- in6_ifnet = i6ifp;
- *new = 1;
- }
- return i6ifp;
+ return -1;
}
-/*----------------------------------------------------------------------
- * This function is called by the PRU_CONTROL handlers in both TCP and UDP.
- * (Actually raw_ipv6 might need a PRU_CONTROL handler, but raw_ip doesn't
- * have one.)
- ----------------------------------------------------------------------*/
+int
+in6_mask2len(mask)
+ struct in6_addr *mask;
+{
+ int x, y;
+
+ for (x = 0; x < sizeof(*mask); x++) {
+ if (mask->s6_addr8[x] != 0xff)
+ break;
+ }
+ y = 0;
+ if (x < sizeof(*mask)) {
+ for (y = 0; y < 8; y++) {
+ if ((mask->s6_addr8[x] & (0x80 >> y)) == 0)
+ break;
+ }
+ }
+ 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;
+}
+
+int in6_interfaces; /* number of external internet interfaces */
+
+#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa))
+#define ia62ifa(ia6) ((struct ifaddr *)(ia6))
int
-#if __NetBSD__ || __FreeBSD__
-in6_control(so, cmd, data, ifp, internal, p)
-#else /* __NetBSD__ || __FreeBSD__ */
-in6_control(so, cmd, data, ifp, internal)
-#endif /* __NetBSD__ || __FreeBSD__ */
- struct socket *so;
-#if __NetBSD__
- u_long cmd;
-#else /* __NetBSD__ */
- int cmd;
-#endif /* __NetBSD__ */
- caddr_t data;
- register struct ifnet *ifp;
- int internal;
-#if __NetBSD__ || __FreeBSD__
- struct proc *p;
-#endif /* __NetBSD__ || __FreeBSD__ */
-{
- register struct inet6_ifreq *ifr = (struct inet6_ifreq *)data;
- register struct in6_ifaddr *i6a = 0;
- struct in6_ifaddr *oi6a;
- struct inet6_aliasreq *ifra = (struct inet6_aliasreq *)data;
- struct sockaddr_in6 oldaddr;
- int error, hostIsNew, maskIsNew, ifnetIsNew = 0;
-#if !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__
- struct ifaddr *ifa;
-#endif /* !__NetBSD__ && !__OpenBSD__ && !__FreeBSD__ */
-
- /*
- * If given an interface, find first IPv6 address on that interface.
- * I may want to change how this is searched. I also may want to
- * discriminate between link-local, site-local, v4-compatible, etc.
- *
- * This is used by the SIOCGIFADDR_INET6, and other such things.
- * Those ioctls() currently assume only one IPv6 address on an interface.
- * This is not a good assumption, and this code will have to be modified
- * to correct that assumption.
- */
- if (ifp)
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp)
- break;
-
- switch (cmd)
- {
- case SIOCAIFADDR_INET6:
- case SIOCDIFADDR_INET6:
- case SIOCVIFADDR_INET6:
- /*
- * For adding and deleting an address, find an exact match for
- * that address. Note that ifr_addr and ifra_addr are in the same
- * place, so even though VIFADDR uses a different struct than AIFADDR,
- * the match will still occur.
- */
- if (ifra->ifra_addr.sin6_family == AF_INET6 &&
- (cmd != SIOCDIFADDR_INET6 ||
- !IN6_IS_ADDR_UNSPECIFIED(&ifra->ifra_addr.sin6_addr)))
- for (oi6a = i6a; i6a; i6a = i6a->i6a_next)
- {
- if (i6a->i6a_ifp == ifp &&
- IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ifra->ifra_addr.sin6_addr))
- break; /* Out of for loop. */
- }
-
- /*
- * You can't delete what you don't have...
- */
- if (cmd == SIOCDIFADDR_INET6 && i6a == 0)
- return EADDRNOTAVAIL;
-
- /*
- * User program requests verification of address. No harm done in
- * letting ANY program use this ioctl(), so we put code in for it
- * here.
- *
- * If I found the i6a, check if I'm not sure. Return EWOULDBLOCK if
- * not sure, return 0 if sure. Return EADDRNOTAVAIL if not available
- * (i.e. DAD failed.).
- */
- if (cmd == SIOCVIFADDR_INET6) {
- if (i6a == NULL) {
- return EADDRNOTAVAIL;
- } else {
- if (i6a->i6a_addrflags & I6AF_NOTSURE) {
- return EWOULDBLOCK;
- } else {
- return 0;
- }
- }
- }
-
- /* FALLTHROUGH TO... */
-
- case SIOCSIFDSTADDR_INET6:
-#if __NetBSD__ || __FreeBSD__
- if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)) )
-#else /* __NetBSD__ || __FreeBSD__ */
- if ((so->so_state & SS_PRIV) == 0)
-#endif /* __NetBSD__ || __FreeBSD__ */
- return EPERM;
-
- if (ifp==0)
- panic("in6_control, ifp==0");
- if (i6a == NULL)
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+in6_control(so, cmd, data, ifp, p)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct proc *p;
+#else
+in6_control(so, cmd, data, ifp)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+#endif
+{
+ struct in6_ifreq *ifr = (struct in6_ifreq *)data;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ struct ifaddr *ifa;
+#endif
+ struct in6_ifaddr *ia, *oia;
+ struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
+ struct sockaddr_in6 oldaddr, net;
+ int error = 0, hostIsNew, prefixIsNew;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ time_t time_second = (time_t)time.tv_sec;
+#endif
+ int privileged;
+
+ privileged = 0;
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (p && !suser(p->p_ucred, &p->p_acflag))
+ privileged++;
+#else
+ if ((so->so_state & SS_PRIV) != 0)
+ privileged++;
+#endif
+
+ /*
+ * xxx should prevent processes for link-local addresses?
+ */
+#if NGIF > 0
+ if (ifp && ifp->if_type == IFT_GIF) {
+ switch (cmd) {
+ case SIOCSIFPHYADDR_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFPSRCADDR_IN6:
+ case SIOCGIFPDSTADDR_IN6:
+ return gif_ioctl(ifp, cmd, data);
+ }
+ }
+#endif
+ switch (cmd) {
+ case SIOCGETSGCNT_IN6:
+ case SIOCGETMIFCNT_IN6:
+ return (mrt6_ioctl(cmd, data));
+ }
+
+ if (ifp == 0)
+ return(EOPNOTSUPP);
+
+ switch (cmd) {
+ case SIOCSNDFLUSH_IN6:
+ case SIOCSPFXFLUSH_IN6:
+ case SIOCSRTRFLUSH_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFINFO_IN6:
+ case SIOCGDRLST_IN6:
+ case SIOCGPRLST_IN6:
+ case SIOCGNBRINFO_IN6:
+ return(nd6_ioctl(cmd, data, ifp));
+ }
+
+ switch (cmd) {
+ case SIOCSIFPREFIX_IN6:
+ case SIOCDIFPREFIX_IN6:
+ case SIOCAIFPREFIX_IN6:
+ case SIOCCIFPREFIX_IN6:
+ case SIOCSGIFPREFIX_IN6:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGIFPREFIX_IN6:
+ return(in6_prefix_ioctl(so, cmd, data, ifp));
+ }
+
+ switch (cmd) {
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ if (!privileged)
+ return(EPERM);
+ /*fall through*/
+ case SIOCGLIFADDR:
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+ return in6_lifaddr_ioctl(so, cmd, data, ifp, p);
+#else
+ return in6_lifaddr_ioctl(so, cmd, data, ifp);
+#endif
+ }
+
+ /*
+ * Find address for this interface, if it exists.
+ */
{
- struct in6_ifaddr *tmp;
-
- /*
- * Create new in6_ifaddr (IPv6 interface address) for additions
- * and destination settings.
- */
- if (!(tmp = (struct in6_ifaddr *)malloc(sizeof(struct in6_ifaddr),
- M_IFADDR,M_NOWAIT)))
+
+ 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) {
+ /* interface ID is not embedded by the user */
+ sa6->sin6_addr.s6_addr16[1] =
+ htons(ifp->if_index);
+ }
+ else
+ if (sa6->sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index))
+ return(EINVAL); /* ifid is contradict */
+ 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? */
+ }
+ }
+ }
+#if 0
+ if (ifra->ifra_addr.sin6_family == AF_INET6) {
+ ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
+ }
+#else
+ ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr);
+#endif
+
+ switch (cmd) {
+
+ case SIOCDIFADDR_IN6:
+ if (ia == 0)
+ return(EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+ case SIOCAIFADDR_IN6:
+ case SIOCSIFADDR_IN6:
+ case SIOCSIFNETMASK_IN6:
+ case SIOCSIFDSTADDR_IN6:
+ if (!privileged)
+ return(EPERM);
+ if (ia == 0) {
+ ia = (struct in6_ifaddr *)
+ malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+ if (ia == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr
+ = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask
+ = (struct sockaddr *)&ia->ia_prefixmask;
+
+ ia->ia_ifp = ifp;
+ if ((oia = in6_ifaddr) != NULL) {
+ for ( ; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ if ((ifa = ifp->if_addrlist) != NULL) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ continue;
+ ifa->ifa_next = ia62ifa(ia);
+ } else
+ ifp->if_addrlist = ia62ifa(ia);
+#else
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist,
+ (struct ifaddr *)ia, ifa_list);
+#endif
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0)
+ in6_interfaces++; /*XXX*/
+ }
+
+ if (cmd == SIOCAIFADDR_IN6) {
+ /* sanity for overflow - beware unsigned */
+ struct in6_addrlifetime *lt;
+ lt = &ifra->ifra_lifetime;
+ if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_vltime + time_second < time_second) {
+ return EINVAL;
+ }
+ if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
+ && lt->ia6t_pltime + time_second < time_second) {
+ return EINVAL;
+ }
+ }
+ break;
+
+ case SIOCGIFADDR_IN6:
+ /* 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 == 0)
+ return(EADDRNOTAVAIL);
+ break;
+ case SIOCSIFALIFETIME_IN6:
{
- return ENOBUFS;
+ struct in6_addrlifetime *lt;
+
+ if (!privileged)
+ return(EPERM);
+ if (ia == 0)
+ 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);
+ 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 SIOCSIFDSTADDR_IN6:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return(EINVAL);
+ oldaddr = ia->ia_dstaddr;
+ ia->ia_dstaddr = ifr->ifr_dstaddr;
+
+ /* link-local index check */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
+ /* interface ID is not embedded by the user */
+ ia->ia_dstaddr.sin6_addr.s6_addr16[1]
+ = htons(ifp->if_index);
+ }
+ else
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
+ }
+
+ if (ifp->if_ioctl && (error = (ifp->if_ioctl)
+ (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
+ ia->ia_dstaddr = oldaddr;
+ return(error);
+ }
+ if (ia->ia_flags & IFA_ROUTE) {
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ ia->ia_ifa.ifa_dstaddr =
+ (struct sockaddr *)&ia->ia_dstaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ }
+ break;
+
+ 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 SIOCSIFADDR_IN6:
+ return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1));
+
+ case SIOCSIFNETMASK_IN6:
+ ia->ia_prefixmask = ifr->ifr_addr;
+ bzero(&net, sizeof(net));
+ net.sin6_len = sizeof(struct sockaddr_in6);
+ net.sin6_family = AF_INET6;
+ net.sin6_port = htons(0);
+ net.sin6_flowinfo = htonl(0);
+ net.sin6_addr.s6_addr32[0]
+ = ia->ia_addr.sin6_addr.s6_addr32[0] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[0];
+ net.sin6_addr.s6_addr32[1]
+ = ia->ia_addr.sin6_addr.s6_addr32[1] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[1];
+ net.sin6_addr.s6_addr32[2]
+ = ia->ia_addr.sin6_addr.s6_addr32[2] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[2];
+ net.sin6_addr.s6_addr32[3]
+ = ia->ia_addr.sin6_addr.s6_addr32[3] &
+ ia->ia_prefixmask.sin6_addr.s6_addr32[3];
+ ia->ia_net = net;
+ break;
+
+ case SIOCAIFADDR_IN6:
+ prefixIsNew = 0;
+ hostIsNew = 1;
+
+ if (ifra->ifra_addr.sin6_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr,
+ &ia->ia_addr.sin6_addr))
+ hostIsNew = 0;
+
+ if (ifra->ifra_prefixmask.sin6_len) {
+ in6_ifscrub(ifp, ia);
+ ia->ia_prefixmask = ifra->ifra_prefixmask;
+ prefixIsNew = 1;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sin6_family == AF_INET6)) {
+ in6_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ /* link-local index check: should be a separate function? */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
+ /*
+ * interface ID is not embedded by
+ * the user
+ */
+ ia->ia_dstaddr.sin6_addr.s6_addr16[1]
+ = htons(ifp->if_index);
+ }
+ else
+ if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ ia->ia_dstaddr = oldaddr;
+ return(EINVAL); /* ifid is contradict */
+ }
+ }
+ prefixIsNew = 1; /* We lie; but effect's the same */
+ }
+ if (ifra->ifra_addr.sin6_family == AF_INET6 &&
+ (hostIsNew || prefixIsNew))
+ error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ if (ifra->ifra_addr.sin6_family == AF_INET6
+ && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
+ int error_local = 0;
+
+ /*
+ * 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_local);
+ if (error == 0)
+ error = error_local;
+ }
+ /* Join dstaddr's solicited multicast if necessary. */
+ if (nd6_proxyall && hostIsNew) {
+ int error_local;
+
+ error_local = in6_ifaddproxy(ia);
+ if (error == 0)
+ error = error_local;
+ }
+
+ ia->ia6_flags = ifra->ifra_flags;
+ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/
+
+ 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;
+
+ /*
+ * Perform DAD, if needed.
+ * XXX It may be of use, if we can administratively
+ * disable DAD.
+ */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+#if 0
+ case IFT_ATM:
+ case IFT_SLIP:
+ case IFT_PPP:
+#endif
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+ break;
+ case IFT_DUMMY:
+ case IFT_FAITH:
+ case IFT_GIF:
+ case IFT_LOOP:
+ default:
+ break;
+ }
+
+ if (hostIsNew) {
+ int iilen;
+ int error_local = 0;
+
+ iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) -
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr);
+ error_local = in6_prefix_add_ifid(iilen, ia);
+ if (error == 0)
+ error = error_local;
+ }
+
+ return(error);
+
+ case SIOCDIFADDR_IN6:
+ in6_ifscrub(ifp, ia);
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ /*
+ * delete solicited multicast addr for deleting host id
+ */
+ struct in6_multi *in6m;
+ struct in6_addr llsol;
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+ /* Leave dstaddr's solicited multicast if necessary. */
+ if (nd6_proxyall)
+ in6_ifremproxy(ia);
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ if ((ifa = ifp->if_addrlist) == ia62ifa(ia))
+ ifp->if_addrlist = ifa->ifa_next;
+ else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != ia62ifa(ia)))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ia62ifa(ia)->ifa_next;
+ else
+ printf("Couldn't unlink in6_ifaddr from ifp\n");
+ }
+#else
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#endif
+ oia = ia;
+ if (oia == (ia = in6_ifaddr))
+ in6_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink in6_ifaddr from list\n");
+ }
+ {
+ int iilen;
+
+ iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) -
+ in6_mask2len(&oia->ia_prefixmask.sin6_addr);
+ in6_prefix_remove_ifid(iilen, oia);
+ }
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (oia->ia6_multiaddrs.lh_first == NULL) {
+ IFAFREE(&oia->ia_ifa);
+ break;
+ }
+ else
+ in6_savemkludge(oia);
+#endif
+
+ IFAFREE((&oia->ia_ifa));
+ break;
+
+ default:
+ if (ifp == 0 || ifp->if_ioctl == 0)
+ return(EOPNOTSUPP);
+ return((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+ return(0);
+}
+
+/*
+ * 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
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+in6_lifaddr_ioctl(so, cmd, data, ifp, p)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+ struct proc *p;
+#else
+in6_lifaddr_ioctl(so, cmd, data, ifp)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ struct ifnet *ifp;
+#endif
+{
+ 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*/
+ }
- bzero(tmp,sizeof(struct in6_ifaddr));
- /*
- * Set NOTSURE addrflag before putting in list.
- */
- tmp->i6a_addrflags = I6AF_NOTSURE;
- if ((i6a = in6_ifaddr))
+ 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:
{
- for (; i6a->i6a_next; i6a=i6a->i6a_next)
- ;
- i6a->i6a_next = tmp;
+ 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);
+ 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_family = AF_INET6;
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen);
+
+ ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX;
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+ return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p);
+#else
+ return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp);
+#endif
}
- else in6_ifaddr = tmp;
- i6a = tmp;
-#ifdef __FreeBSD__
- TAILQ_INSERT_TAIL(&ifp->if_addrhead, (struct ifaddr *)i6a,
- ifa_link);
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)i6a,
- ifa_list);
-#else /* __NetBSD__ || __OpenBSD__ */
- if (ifa = ifp->if_addrlist)
+ case SIOCGLIFADDR:
+ case SIOCDLIFADDR:
{
- for (; ifa->ifa_next; ifa=ifa->ifa_next)
- ;
- ifa->ifa_next = (struct ifaddr *)i6a;
+ 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;
+ }
+ }
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!cmp)
+ break;
+ bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
+ 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) {
+ /* fill in the if_laddrreq structure */
+ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
+
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
+ bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
+ ia->ia_dstaddr.sin6_len);
+ } else
+ bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
+
+ iflr->prefixlen =
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr);
+
+ 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);
+ }
+ bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr,
+ ia->ia_prefixmask.sin6_len);
+
+ ifra.ifra_flags = ia->ia6_flags;
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+ return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ ifp, p);
+#else
+ return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ ifp);
+#endif
+ }
}
- else ifp->if_addrlist = (struct ifaddr *)i6a;
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr;
- i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr;
- i6a->i6a_ifa.ifa_netmask
- = (struct sockaddr *)&i6a->i6a_sockmask;
- i6a->i6a_sockmask.sin6_len = sizeof(struct sockaddr_in6);
- i6a->i6a_ifp = ifp;
-
- /*
- * Add address to IPv6 interface lists.
- */
- i6a->i6a_i6ifp = add_in6_ifnet(ifp, &ifnetIsNew);
- }
- break;
- case SIOCGIFADDR_INET6:
- case SIOCGIFNETMASK_INET6:
- case SIOCGIFDSTADDR_INET6:
- /*
- * Can't get information on what is not there...
- */
- if (i6a == NULL)
- return EADDRNOTAVAIL;
- break;
-
- default:
- return EOPNOTSUPP;
- }
-
- switch (cmd)
- {
- /*
- * The following three cases assume that there is only one address per
- * interface; this is not good in IPv6-land. Unfortunately, the
- * ioctl() interface, is such that I'll have to rewrite the way things
- * work here, either that, or curious user programs will have to troll
- * /dev/kmem (like netstat(8) does).
- */
- case SIOCGIFADDR_INET6:
- bcopy(&(i6a->i6a_addr),&(ifr->ifr_addr),sizeof(struct sockaddr_in6));
- break;
-
- case SIOCGIFDSTADDR_INET6:
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
- return EINVAL;
- bcopy(&(i6a->i6a_dstaddr),&(ifr->ifr_dstaddr),
- sizeof(struct sockaddr_in6));
- break;
-
- case SIOCGIFNETMASK_INET6:
- bcopy(&(i6a->i6a_sockmask),&(ifr->ifr_addr),sizeof(struct sockaddr_in6));
- break;
-
- case SIOCSIFDSTADDR_INET6:
- i6a->i6a_addrflags &= ~I6AF_NOTSURE;
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
- return EINVAL;
- oldaddr = i6a->i6a_dstaddr;
- i6a->i6a_dstaddr = *(struct sockaddr_in6 *)&ifr->ifr_dstaddr;
- if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR,
- (caddr_t)i6a)))
- {
- i6a->i6a_dstaddr = oldaddr;
- return error;
}
- if (i6a->i6a_flags & IFA_ROUTE)
- {
- i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
- rtinit(&(i6a->i6a_ifa), RTM_DELETE, RTF_HOST);
- i6a->i6a_ifa.ifa_dstaddr = (struct sockaddr *)&i6a->i6a_dstaddr;
- rtinit(&(i6a->i6a_ifa), RTM_ADD, RTF_HOST|RTF_UP);
- }
- break;
-
- /*
- * For adding new IPv6 addresses to an interface, I stuck to the way
- * that IPv4 uses, pretty much.
- */
- case SIOCAIFADDR_INET6:
- maskIsNew = 0;
- hostIsNew = 1;
- error = 0;
- if (i6a->i6a_addr.sin6_family == AF_INET6) {
- if (ifra->ifra_addr.sin6_len == 0) {
- {
- bcopy(&(i6a->i6a_addr),&(ifra->ifra_addr),
- sizeof(struct sockaddr_in6));
- hostIsNew = 0;
- }
+
+ return EOPNOTSUPP; /*just for safety*/
+}
+
+/*
+ * Delete any existing route for an interface.
+ */
+void
+in6_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct in6_ifaddr *ia;
+{
+ if ((ia->ia_flags & IFA_ROUTE) == 0)
+ return;
+ if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+
+ /* Remove ownaddr's loopback rtentry, if it exists. */
+ in6_ifremloop(&(ia->ia_ifa));
+}
+
+/*
+ * Initialize an interface's intetnet6 address
+ * and routing table entry.
+ */
+int
+in6_ifinit(ifp, ia, sin6, scrub)
+ struct ifnet *ifp;
+ struct in6_ifaddr *ia;
+ struct sockaddr_in6 *sin6;
+ int scrub;
+{
+ struct sockaddr_in6 oldaddr;
+ int error, flags = RTF_UP;
+ int s = splimp();
+
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *sin6;
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ splx(s);
+ ia->ia_addr = oldaddr;
+ return(error);
+ }
+
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ break;
+ case IFT_PPP:
+ ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ break;
+ }
+
+ splx(s);
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ in6_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ /* xxx
+ * in_socktrim
+ */
+ /*
+ * Add route for the network.
+ */
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ if (ifp->if_flags & IFF_LOOPBACK) {
+ ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+ flags |= RTF_HOST;
+ } else if (ifp->if_flags & IFF_POINTOPOINT) {
+ if (ia->ia_dstaddr.sin6_family != AF_INET6)
+ return(0);
+ flags |= RTF_HOST;
+ }
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+ ia->ia_flags |= IFA_ROUTE;
+
+ /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */
+ in6_ifaddloop(&(ia->ia_ifa));
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (ifp->if_flags & IFF_MULTICAST)
+ in6_restoremkludge(ia, ifp);
+#endif
+
+ return(error);
+}
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+/*
+ * Multicast address kludge:
+ * If there were any multicast addresses attached to this interface address,
+ * either move them to another address on this interface, or save them until
+ * such time as this interface is reconfigured for IPv6.
+ */
+void
+in6_savemkludge(oia)
+ struct in6_ifaddr *oia;
+{
+ struct in6_ifaddr *ia;
+ struct in6_multi *in6m, *next;
+
+ IFP_TO_IA6(oia->ia_ifp, ia);
+ if (ia) { /* there is another address */
+ for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){
+ next = in6m->in6m_entry.le_next;
+ IFAFREE(&in6m->in6m_ia->ia_ifa);
+ ia->ia_ifa.ifa_refcnt++;
+ in6m->in6m_ia = ia;
+ LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
+ }
+ } else { /* last address on this if deleted, save */
+ struct multi6_kludge *mk;
+
+ mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
+
+ LIST_INIT(&mk->mk_head);
+ mk->mk_ifp = oia->ia_ifp;
+
+ for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next){
+ next = in6m->in6m_entry.le_next;
+ LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
+ }
+
+ if (mk->mk_head.lh_first != NULL) {
+ LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
+ }
+ else {
+ FREE(mk, M_IPMADDR);
+ }
+ }
+}
+
+/*
+ * Continuation of multicast address hack:
+ * If there was a multicast group list previously saved for this interface,
+ * then we re-attach it to the first address configured on the i/f.
+ */
+void
+in6_restoremkludge(ia, ifp)
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp;
+{
+ struct multi6_kludge *mk;
+
+ for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
+ if (mk->mk_ifp == ifp) {
+ struct in6_multi *in6m, *next;
+
+ for (in6m = mk->mk_head.lh_first; in6m; in6m = next){
+ next = in6m->in6m_entry.le_next;
+ LIST_INSERT_HEAD(&ia->ia6_multiaddrs,
+ in6m, in6m_entry);
+ }
+ LIST_REMOVE(mk, mk_entry);
+ free(mk, M_IPMADDR);
+ break;
+ }
+ }
+}
+
+/*
+ * Add an address to the list of IP6 multicast addresses for a
+ * given interface.
+ */
+struct in6_multi *
+in6_addmulti(maddr6, ifp, errorp)
+ register struct in6_addr *maddr6;
+ register struct ifnet *ifp;
+ int *errorp;
+{
+ struct in6_ifaddr *ia;
+ struct in6_ifreq ifr;
+ struct in6_multi *in6m;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+
+ *errorp = 0;
+ /*
+ * See if address already in list.
+ */
+ IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
+ if (in6m != NULL) {
+ /*
+ * Found it; just increment the refrence count.
+ */
+ in6m->in6m_refcount++;
} else {
- if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, &i6a->i6a_addr.sin6_addr))
- hostIsNew = 0;
- }
- }
+ /*
+ * New address; allocate a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ in6m = (struct in6_multi *)
+ malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
+ if (in6m == NULL) {
+ splx(s);
+ *errorp = ENOBUFS;
+ return(NULL);
+ }
+ in6m->in6m_addr = *maddr6;
+ in6m->in6m_ifp = ifp;
+ in6m->in6m_refcount = 1;
+ IFP_TO_IA6(ifp, ia);
+ if (ia == NULL) {
+ free(in6m, M_IPMADDR);
+ splx(s);
+ *errorp = EADDRNOTAVAIL; /* appropriate? */
+ return(NULL);
+ }
+ in6m->in6m_ia = ia;
+ ia->ia_ifa.ifa_refcnt++; /* gain a reference */
+ LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
+
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
+ ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ifr.ifr_addr.sin6_family = AF_INET6;
+ ifr.ifr_addr.sin6_addr = *maddr6;
+ if (ifp->if_ioctl == NULL)
+ *errorp = ENXIO; /* XXX: appropriate? */
+ else
+ *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
+ (caddr_t)&ifr);
+ if (*errorp) {
+ LIST_REMOVE(in6m, in6m_entry);
+ free(in6m, M_IPMADDR);
+ splx(s);
+ return(NULL);
+ }
+ /*
+ * Let MLD6 know that we have joined a new IP6 multicast
+ * group.
+ */
+ mld6_start_listening(in6m);
+ }
+ splx(s);
+ return(in6m);
+}
- if (ifra->ifra_mask.sin6_len)
- {
- in6_ifscrub(ifp,i6a);
- bcopy(&(ifra->ifra_mask),&(i6a->i6a_sockmask),
- sizeof(struct sockaddr_in6));
- maskIsNew = 1;
+/*
+ * Delete a multicast address record.
+ */
+void
+in6_delmulti(in6m)
+ struct in6_multi *in6m;
+{
+ struct in6_ifreq ifr;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+
+ if (--in6m->in6m_refcount == 0) {
+ /*
+ * No remaining claims to this record; let MLD6 know
+ * that we are leaving the multicast group.
+ */
+ mld6_stop_listening(in6m);
+
+ /*
+ * Unlink from list.
+ */
+ LIST_REMOVE(in6m, in6m_entry);
+ IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
+
+ /*
+ * Notify the network driver to update its multicast
+ * reception filter.
+ */
+ bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6));
+ ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ifr.ifr_addr.sin6_family = AF_INET6;
+ ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
+ (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
+ SIOCDELMULTI, (caddr_t)&ifr);
+ free(in6m, M_IPMADDR);
+ }
+ splx(s);
+}
+#else /* not FreeBSD3 */
+/*
+ * Add an address to the list of IP6 multicast addresses for a
+ * given interface.
+ */
+struct in6_multi *
+in6_addmulti(maddr6, ifp, errorp)
+ register struct in6_addr *maddr6;
+ register 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 ((ifp->if_flags & IFF_POINTOPOINT) &&
- (ifra->ifra_dstaddr.sin6_family == AF_INET6))
- {
- in6_ifscrub(ifp,i6a);
- bcopy(&(ifra->ifra_dstaddr),&(i6a->i6a_dstaddr),
- sizeof(struct sockaddr_in6));
- maskIsNew = 1; /* We lie, simply so that in6_ifinit() will be
- called to initialize the peer's address. */
+ /*
+ * 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);
}
- if (ifra->ifra_addr.sin6_family == AF_INET6 && (hostIsNew || maskIsNew))
- error = in6_ifinit(ifp,i6a,&ifra->ifra_addr,0,!internal);
- /* else i6a->i6a_addrflags &= ~I6AF_NOTSURE; */
- if (error == EEXIST) /* XXX, if route exists, we should be ok */
- error = 0;
+ 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);
+}
- if (hostIsNew && !ifnetIsNew /* && (!error || error == EEXIST) */)
- {
- if (i6a->i6a_i6ifp)
- i6a->i6a_i6ifp->i6ifp_numaddrs++;
- else
- panic("in6_control: missing i6ifp");
+/*
+ * 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);
}
- return error;
+ /* XXX - should be separate API for when we have an ifma? */
+ if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
+ splx(s);
+}
+#endif /* not FreeBSD3 */
- case SIOCDIFADDR_INET6:
- in6_ifscrub(ifp, i6a);
- /*
- * If last address on this interface, delete IPv6 interface record.
- */
- del_in6_ifnet(ifp);
+/*
+ * Find an IPv6 interface link-local address specific to an interface.
+ */
+struct in6_ifaddr *
+in6ifa_ifpforlinklocal(ifp)
+ struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
-#ifdef __FreeBSD__
- TAILQ_REMOVE(&ifp->if_addrhead, (struct ifaddr *)i6a, ifa_link);
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)i6a, ifa_list);
-#else /* __NetBSD__ || __OpenBSD__ */
- if ((ifa = ifp->if_addrlist) == (struct ifaddr *)i6a)
- ifp->if_addrlist = ifa->ifa_next;
- else
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- while (ifa->ifa_next &&
- (ifa->ifa_next != (struct ifaddr *)i6a))
- ifa=ifa->ifa_next;
- if (ifa->ifa_next)
- ifa->ifa_next = i6a->i6a_ifa.ifa_next;
- else
- DPRINTF(IDL_ERROR, ("Couldn't unlink in6_ifaddr from ifp!\n"));
- }
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- oi6a = i6a;
- if (oi6a == (i6a = in6_ifaddr))
- in6_ifaddr = i6a->i6a_next;
- else
+ 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)))
+ 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;
+{
+ register struct ifaddr *ifa;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- while (i6a->i6a_next && (i6a->i6a_next != oi6a))
- i6a = i6a->i6a_next;
- if (i6a->i6a_next)
- i6a->i6a_next = oi6a->i6a_next;
- else
- DPRINTF(IDL_ERROR, ("Didn't unlink in6_ifaddr from list.\n"));
+ 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;
}
- IFAFREE((&oi6a->i6a_ifa)); /* For the benefit of routes pointing
- to this ifa. */
- break;
- default:
- DPRINTF(IDL_ERROR,
- ("in6_control(): Default case not implemented.\n"));
- return EOPNOTSUPP;
- }
+ return((struct in6_ifaddr *)ifa);
+}
- return 0;
+/*
+ * Convert IP6 address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+static int ip6round = 0;
+char *
+ip6_sprintf(addr)
+register struct in6_addr *addr;
+{
+ static char ip6buf[8][48];
+ register int i;
+ register char *cp;
+ register u_short *a = (u_short *)addr;
+ register u_char *d;
+ int 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 = (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]);
}
-/*----------------------------------------------------------------------
- * in6_ifscrub:
- * Delete any existing route for an IPv6 interface.
- ----------------------------------------------------------------------*/
+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);
+}
+
+/*
+ * Get a scope of the address. Node-local, link-local, site-local or global.
+ */
int
-in6_ifscrub(ifp,i6a)
- register struct ifnet *ifp;
- register struct in6_ifaddr *i6a;
+in6_addrscope (addr)
+struct in6_addr *addr;
{
- if (!(i6a->i6a_flags & IFA_ROUTE))
- return 1;
+ 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 (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
- rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, RTF_HOST);
- else
- rtinit(&(i6a->i6a_ifa), (int)RTM_DELETE, 0);
- i6a->i6a_flags &= ~IFA_ROUTE;
- return 0;
+ 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;
}
-/*----------------------------------------------------------------------
- * Initialize an IPv6 address for an interface.
- *
- * When I get around to doing duplicate address detection, this is probably
- * the place to do it.
- ----------------------------------------------------------------------*/
+/*
+ * return length of part which dst and src are equal
+ * hard coding...
+ */
int
-in6_ifinit(ifp, i6a, sin6, scrub, useDAD)
- register struct ifnet *ifp;
- register struct in6_ifaddr *i6a;
- struct sockaddr_in6 *sin6;
- int scrub;
- int useDAD;
-{
- int s, error, flags = RTF_UP;
- struct sockaddr_in6 oldaddr;
-
- DPRINTF(IDL_EVENT,("Before splimp in in6_ifinit()\n"));
- s = splimp();
-
- bcopy(&(i6a->i6a_addr),&oldaddr,sizeof(struct sockaddr_in6));
- bcopy(sin6,&(i6a->i6a_addr),sizeof(struct sockaddr_in6));
-
- /*
- * Give the interface a chance to initialize
- * if this is its first address,
- * and to validate the address if necessary.
- */
-
- if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR,
- (caddr_t)i6a)))
- {
- bcopy(&oldaddr,&(i6a->i6a_addr),sizeof(struct sockaddr_in6));
- splx(s);
- return error;
- }
-
- /*
- * IPv4 in 4.4BSD sets the RTF_CLONING flag here if it's an Ethernet.
- * I delay this until later.
- */
-
- splx(s);
- DPRINTF(IDL_EVENT,("After splx() in in6_ifinit().\n"));
-
- sin6->sin6_port = 0;
-
- if (scrub)
- {
- i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
- in6_ifscrub(ifp, i6a);
- i6a->i6a_ifa.ifa_addr = (struct sockaddr *)&i6a->i6a_addr;
- }
-
- /*
- * Adjust the sin6_len such that it only counts mask bytes with
- * 1's in them.
- */
-
- {
- register char *cpbase = (char *)&(i6a->i6a_sockmask.sin6_addr);
- register char *cp = cpbase + sizeof(struct in6_addr);
-
- i6a->i6a_sockmask.sin6_len = 0;
- while (--cp >=cpbase)
- if (*cp)
- {
- i6a->i6a_sockmask.sin6_len = 1 + cp - (char *)&(i6a->i6a_sockmask);
- break;
- }
- }
-
- /*
- * Add route. Also, set some properties of the interface address here.
- * (Properties include permanance, lifetime, etc.)
- */
-
- i6a->i6a_ifa.ifa_metric = ifp->if_metric;
- i6a->i6a_ifa.ifa_rtrequest = ipv6_rtrequest; /* Want this to be true
- for ALL IPv6 ifaddrs. */
- if (ifp->if_flags & IFF_LOOPBACK)
- {
- useDAD = 0;
- i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr;
- flags |= RTF_HOST;
-
- /* Loopback is definitely a permanent address. */
- if (IN6_IS_ADDR_LOOPBACK(&i6a->i6a_addr.sin6_addr))
- i6a->i6a_addrflags |= I6AF_PERMANENT;
- }
- else if (ifp->if_flags & IFF_POINTOPOINT)
- {
- useDAD = 0; /* ??!!?? */
- if (i6a->i6a_dstaddr.sin6_family != AF_INET6)
- return 0;
+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;
+}
- flags |= RTF_HOST;
- }
- else
- {
- /*
- * No b-cast in IPv6, therefore the ifa_broadaddr (concidentally the
- * dest address filled in above...) should be set to NULL!
- */
- i6a->i6a_ifa.ifa_broadaddr = NULL;
-
- if (IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
- {
- flags |= RTF_HOST;
- i6a->i6a_ifa.ifa_dstaddr = i6a->i6a_ifa.ifa_addr;
-
- /*
- * Possibly do other stuff specific to link-local addresses, hence
- * keeping this separate from IFF_LOOPBACK case above. I may move
- * the link-local check to || with IFF_LOOPBACK.
- *
- * Other stuff includes setting i6a_preflen so when addrconf
- * needs to know what part of the link-local is used for uniqueness,
- * it doesn't have to gyrate.
- */
- switch(i6a->i6a_ifp->if_type)
- {
- case IFT_ETHER:
- i6a->i6a_preflen = 64;
- break;
- default:
- DPRINTF(IDL_ERROR,("Can't set i6a_preflen for type %d.\n",\
- i6a->i6a_ifp->if_type));
- break;
- }
+int
+in6_are_prefix_equal(p1, p2, len)
+ struct in6_addr *p1, *p2;
+ int len;
+{
+ int bytelen, bitlen;
- i6a->i6a_addrflags |= (I6AF_LINKLOC | I6AF_PERMANENT);
+ /* sanity check */
+ if (0 > len || len > 128) {
+ log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n",
+ len);
+ return(0);
}
- else
+
+ 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(ifp, dst)
+ register struct ifnet *ifp;
+ register struct in6_addr *dst;
+{
+ int dst_scope = in6_addrscope(dst), blen = -1, tlen;
+ struct ifaddr *ifa;
+ struct in6_ifaddr *besta = NULL, *ia;
+ 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.
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- if (!(i6a->i6a_sockmask.sin6_len == sizeof(struct sockaddr_in6) &&
- IN6_ARE_ADDR_EQUAL(&i6a->i6a_sockmask.sin6_addr, &in6_allones.sin6_addr)))
- flags |= RTF_CLONING; /* IMHO, ALL network routes
- have the cloning bit set for next-hop
- resolution if they aren't loopback or
- pt. to pt. */
- i6a->i6a_addrflags |= I6AF_PREFIX; /* I'm a 'prefix list entry'. */
- }
- }
-
- if ((error = rtinit(&(i6a->i6a_ifa), RTM_ADD,flags)) == 0)
- {
- i6a->i6a_flags |= IFA_ROUTE;
- }
-
- /*
- * If the interface supports multicast, join the appropriate
- * multicast groups (all {nodes, routers}) on that interface.
- *
- * Also join the solicited nodes discovery multicast group for that
- * destination.
- */
- if (ifp->if_flags & IFF_MULTICAST)
- {
- struct in6_multi *rc;
-
- /* NOTE2: Set default multicast interface here.
- Set up cloning route for ff00::0/8 */
- if (ifp->if_type != IFT_LOOP && mcastdefault == NULL)
- setmcastdef(ifp);
-
- {
- struct in6_addr addr = IN6ADDR_ALLNODES_INIT;
-
- rc = in6_addmulti(&addr, ifp);
- };
-
- /* All-routers, if forwarding */
- if (ipv6forwarding) {
- struct in6_addr addr = IN6ADDR_ALLROUTERS_INIT;
-
- rc = in6_addmulti(&addr, ifp);
- };
-
- {
- struct in6_addr addr = IN6ADDR_ALLHOSTS_INIT;
-
- rc = in6_addmulti(&addr, ifp);
- };
-
- /* Solicited-nodes. */
- {
- struct in6_addr addr = IN6ADDR_SN_PREFIX_INIT;
-
- addr.s6_addr[13] = i6a->i6a_addr.sin6_addr.s6_addr32[13];
- addr.s6_addr[14] = i6a->i6a_addr.sin6_addr.s6_addr32[14];
- addr.s6_addr[15] = i6a->i6a_addr.sin6_addr.s6_addr32[15];
-
- DDO(IDL_EVENT, dump_in6_addr(&addr));
-
- rc=in6_addmulti(&addr, ifp);
- };
+ 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;
+
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IPV6_ADDR_SCOPE_GLOBAL !=
+ in6_addrscope(&(ia->ia_addr.sin6_addr)))
+ continue;
+ /* XXX: is there any case to allow anycast? */
+ if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0)
+ continue;
+ if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) {
+ if (ip6_use_deprecated)
+ dep[1] = (struct in6_ifaddr *)ifa;
+ continue;
+ }
+ return ia;
+ }
-#if 0
- addr.s6_addr32[0] = htonl(0xff020000);
- addr.s6_addr32[1] = 0;
- addr.s6_addr32[2] = htonl(1);
- addr.s6_addr32[3] = i6a->i6a_addr.sin6_addr.s6_addr32[3] | htonl(0xff000000);
-#endif /* 0 */
- }
+ /* use the last-resort values, that are, deprecated addresses */
+ if (dep[0])
+ return dep[0];
+ if (dep[1])
+ return dep[1];
- if (useDAD /*&& error != 0*/)
- addrconf_dad(i6a);
- else
- i6a->i6a_addrflags &= ~I6AF_NOTSURE;
+ return NULL;
+}
+
+/*
+ * return the best address out of the same scope. if no address was
+ * found, return the first valid address from designated IF.
+ */
- return error;
+struct in6_ifaddr *
+in6_ifawithifp(ifp, dst)
+ register struct ifnet *ifp;
+ register 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.
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ 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);
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ 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;
}
-/*----------------------------------------------------------------------
- * Add IPv6 multicast address. IPv6 multicast addresses are handled
- * pretty much like IP multicast addresses for now.
- *
- * Multicast addresses hang off in6_ifaddr's. Eventually, they should hang
- * off the link-local multicast address, this way, there are no ambiguities.
- ----------------------------------------------------------------------*/
-
-struct in6_multi *in6_addmulti(addr,ifp)
- register struct in6_addr *addr;
- struct ifnet *ifp;
-
-{
- register struct in6_multi *in6m;
- struct inet6_ifreq ifr;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ifr.ifr_addr;
- struct in6_ifnet *i6ifp;
- int s = splnet();
-
- /*
- * See if address is already in list..
- */
-
- IN6_LOOKUP_MULTI(addr,ifp,in6m);
-
- if (in6m != NULL)
- {
- /* Increment the reference count. */
- in6m->in6m_refcount++;
- }
- else
- {
-#if __FreeBSD__
- struct ifmultiaddr *ifma;
-#endif /* __FreeBSD__ */
- /*
- * Otherwise, allocate a new m-cast record and link it to
- * the interface's multicast list.
- */
-
- if ((in6m=malloc(sizeof(struct in6_multi),M_IPMADDR,M_NOWAIT)) == NULL)
+/*
+ * perform DAD when interface becomes IFF_UP.
+ */
+void
+in6_if_up(ifp)
+ struct ifnet *ifp;
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia;
+ struct sockaddr_dl *sdl;
+ int type;
+#ifdef __bsdi__
+ u_char ea[ETHER_ADDR_LEN];
+#else
+ struct ether_addr ea;
+#endif
+ int off;
+ int dad_delay; /* delay ticks before DAD output */
+
+ bzero(&ea, sizeof(ea));
+ sdl = NULL;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- splx(s);
- return NULL;
- }
- bzero(in6m,sizeof(struct in6_multi));
- in6m->in6m_addr = *addr;
- in6m->in6m_refcount = 1;
- in6m->in6m_ifp = ifp;
-
- for(i6ifp = in6_ifnet; i6ifp != NULL && i6ifp->i6ifp_ifp != ifp;
- i6ifp = i6ifp->i6ifp_next)
- ;
- if (i6ifp == NULL)
+ if (ifa->ifa_addr->sa_family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) {
+ goto dad;
+ }
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ break;
+ }
+
+ switch (ifp->if_type) {
+ case IFT_SLIP:
+ case IFT_PPP:
+ case IFT_DUMMY:
+ case IFT_GIF:
+ case IFT_FAITH:
+ type = IN6_IFT_P2P;
+ in6_ifattach(ifp, type, 0, 1);
+ break;
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ATM:
+ type = IN6_IFT_802;
+ if (sdl == NULL)
+ break;
+ off = sdl->sdl_nlen;
+ if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0)
+ in6_ifattach(ifp, type, LLADDR(sdl), 0);
+ break;
+ case IFT_ARCNET:
+ type = IN6_IFT_ARCNET;
+ if (sdl == NULL)
+ break;
+ off = sdl->sdl_nlen;
+ if (sdl->sdl_data[off] != 0) /* XXX ?: */
+ in6_ifattach(ifp, type, LLADDR(sdl), 0);
+ break;
+ default:
+ break;
+ }
+
+dad:
+ dad_delay = 0;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
{
- free(in6m,M_IPMADDR);
- splx(s);
- return NULL;
- }
- in6m->in6m_i6ifp = i6ifp;
- in6m->in6m_next = i6ifp->i6ifp_multiaddrs;
- i6ifp->i6ifp_multiaddrs = in6m;
-
- /*
- * Ask the network driver to update its multicast reception
- * filter appropriately for the new address.
- */
- sin6->sin6_family=AF_INET6;
- sin6->sin6_len=sizeof(struct sockaddr_in6);
- sin6->sin6_addr = *addr;
- sin6->sin6_port = 0;
- sin6->sin6_flowinfo = 0;
-
-
-#if __FreeBSD__
- if (if_addmulti(ifp, (struct sockaddr *) sin6, &ifma))
-#else /* __FreeBSD */
- if (ifp->if_ioctl == NULL ||
- (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0)
-#endif /* __FreeBSD__ */
+ 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);
+ }
+}
+
+/*
+ * Calculate max IPv6 MTU through all the interfaces and store it
+ * to in6_maxmtu.
+ */
+void
+in6_setmaxmtu()
+{
+ unsigned long maxmtu = 0;
+ struct ifnet *ifp;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+#else
+ for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
+#endif
{
- i6ifp->i6ifp_multiaddrs = in6m->in6m_next;
- free(in6m,M_IPMADDR);
- splx(s);
- return NULL;
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ nd_ifinfo[ifp->if_index].linkmtu > maxmtu)
+ maxmtu = nd_ifinfo[ifp->if_index].linkmtu;
}
-#ifdef __FreeBSD__
- ifma->ifma_protospec = in6m;
-#endif /* __FreeBSD__ */
-
- /* Tell IGMP that we've joined a new group. */
- /*ipv6_igmp_joingroup(in6m);*/
- }
- splx(s);
- return in6m;
+ if (maxmtu) /* update only when maxmtu is positive */
+ in6_maxmtu = maxmtu;
}
-/*----------------------------------------------------------------------
- * Delete IPv6 multicast address.
- ----------------------------------------------------------------------*/
+#ifdef MAPPED_ADDR_ENABLED
+/*
+ * 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_delmulti(in6m)
- register struct in6_multi *in6m;
-{
- register struct in6_multi **p;
- struct inet6_ifreq ifr;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&(ifr.ifr_addr);
- int s = splnet();
-
- if (--in6m->in6m_refcount == 0)
- {
- /* Tell IGMP that I'm bailing this group. */
- /* ipv6_igmp_leavegroup(in6m);*/
-
- /* Unlink from list. */
- for (p = &(in6m->in6m_i6ifp->i6ifp_multiaddrs);
- *p != in6m;
- p = &(*p)->in6m_next)
- ;
- *p = (*p)->in6m_next;
-
- /*
- * Notify the network driver to update its multicast reception
- * filter.
- */
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- sin6->sin6_port = 0;
- sin6->sin6_flowinfo = 0;
- sin6->sin6_addr = in6m->in6m_addr;
- (*(in6m->in6m_ifp->if_ioctl))(in6m->in6m_ifp, SIOCDELMULTI,
- (caddr_t)&ifr);
-
- free(in6m,M_IPMADDR);
- }
- splx(s);
+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;
+}
+#endif /* MAPPED_ADDR_ENABLED */
+
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index c4229a22807..8bafafe7bb9 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,192 +1,650 @@
/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+ * 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 _NETINET6_IN6_H_
+#define _NETINET6_IN6_H_
+
+#if !defined(_XOPEN_SOURCE)
+#include <sys/queue.h>
+#endif
+
+/*
+ * Identification of the network protocol stack
+ */
+#define __KAME__
+#define __KAME_VERSION "19991208/OpenBSD-current"
+
+/*
+ * 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_ANONMIX 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.
+ */
+
+#define IPV6PORT_RESERVED 1024
+#define IPV6PORT_ANONMIN 49152
+#define IPV6PORT_ANONMAX 65535
+#define IPV6PORT_RESERVEDMIN 600
+#define IPV6PORT_RESERVEDMAX (IPV6PORT_RESERVED-1)
+
+/*
+ * IPv6 address
+ */
+struct in6_addr {
+ union {
+ u_int8_t __u6_addr8[16];
+ u_int16_t __u6_addr16[8];
+ u_int32_t __u6_addr32[4];
+ } __u6_addr; /* 128-bit IP6 address */
+};
+
+#define s6_addr __u6_addr.__u6_addr8
+#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
+
+/*
+ * Socket address for IPv6
+ */
+#if !defined(_XOPEN_SOURCE)
+#define SIN6_LEN
+#endif
+struct sockaddr_in6 {
+ u_char sin6_len; /* length of this struct(sa_family_t)*/
+ u_char sin6_family; /* AF_INET6 (sa_family_t) */
+ u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/
+ u_int32_t sin6_flowinfo; /* IP6 flow information */
+ struct in6_addr sin6_addr; /* IP6 address */
+ u_int32_t sin6_scope_id; /* intface scope id */
+};
+
+/*
+ * 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 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 */
-#ifndef _NETINET6_IN6_H
-#define _NETINET6_IN6_H 1
+/*
+ * 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
-#if !defined(_NETINET_IN_H) && !defined(_NETINET_IN_H_)
-#error in6.h should no longer be included directly; include <netinet/in.h>
-#endif /* !defined(_NETINET_IN_H) && !defined(_NETINET_IN_H_) */
-#if __bsdi__ && !defined(_BSDI_VERSION)
-#include <sys/param.h>
-#endif /* __bsdi__ && !defined(_BSDI_VERSION) */
+/*
+ * Definition of some useful macros to handle IP6 addresses
+ */
+#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 }}}
-/* IPPROTO type macros. */
+extern const struct in6_addr in6addr_any;
+extern const struct in6_addr in6addr_loopback;
+extern const struct in6_addr in6addr_nodelocal_allnodes;
+extern const struct in6_addr in6addr_linklocal_allnodes;
+extern const struct in6_addr in6addr_linklocal_allrouters;
-#define IS_PREFRAG(x) ( (x)==IPPROTO_HOPOPTS || (x)==IPPROTO_ROUTING || \
- (x) == IPPROTO_DSTOPTS)
-#define IS_IPV6OPT(x) ( (x)==IPPROTO_FRAGMENT || (x) == IPPROTO_AH || \
- IS_PRFRAG(x) )
+/*
+ * 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), (b), sizeof(struct in6_addr)) == 0)
+#else
+#define IN6_ARE_ADDR_EQUAL(a, b) \
+ (memcmp((a), (b), sizeof(struct in6_addr)) == 0)
+#endif
-#define CREATE_IPV6_MAPPED(v6, v4) { \
- v6.s6_addr32[0] = 0; \
- v6.s6_addr32[1] = 0; \
- v6.s6_addr32[2] = htonl(0xffff); \
- v6.s6_addr32[3] = v4; }
+/*
+ * Unspecified
+ */
+#define IN6_IS_ADDR_UNSPECIFIED(a) \
+ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[12]) == 0))
-#if BYTE_ORDER == BIG_ENDIAN
+/*
+ * Loopback
+ */
+#define IN6_IS_ADDR_LOOPBACK(a) \
+ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[12]) == ntohl(1)))
-#define SET_IN6_ALLNODES(a) {(a).s6_addr32[0]=0xff000000;(a).s6_addr32[3]=1;\
- (a).s6_addr32[1]=0;(a).s6_addr32[2]=0;}
-#define SET_IN6_ALLROUTERS(a) {(a).s6_addr32[0]=0xff000000;(a).s6_addr32[3]=2;\
- (a).s6_addr32[1]=0;(a).s6_addr32[2]=0;}
+/*
+ * IPv4 compatible
+ */
+#define IN6_IS_ADDR_V4COMPAT(a) \
+ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[12]) != 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[12]) != ntohl(1)))
-#define SET_IN6_MCASTSCOPE(a,bits) {(a).s6_addr32[0]&=0xfff0ffff;\
- (a).s6_addr32[0]|=(bits<<16);}
-#define GET_IN6_MCASTSCOPE(a) ( ((a).s6_addr32[0] & 0x000f0000) >> 16 )
+/*
+ * Mapped
+ */
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \
+ (*(u_int32_t *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
-#else /* BYTE_ORDER == LITTLE_ENDIAN */
+/*
+ * 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
-#define SET_IN6_ALLNODES(a) {(a).s6_addr32[0]=0xff;(a).s6_addr32[3]=0x01000000;\
- (a).s6_addr32[1] = 0; (a).s6_addr32[2] = 0;}
-#define SET_IN6_ALLROUTERS(a) {(a).s6_addr32[0]=0xff;(a).s6_addr32[3]=0x02000000;\
- (a).s6_addr32[1] = 0; (a).s6_addr32[2] = 0;}
+/*
+ * 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)
-#define SET_IN6_MCASTSCOPE(a,bits) {(a).s6_addr32[0]&=0xfffff0ff;\
- (a).s6_addr32[0]|=(bits<<8);}
-#define GET_IN6_MCASTSCOPE(a) ( ((a).s6_addr32[0] & 0x00000f00) >>8)
+#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
-#endif /* BYTE_ORDER == {BIG,LITTLE}_ENDIAN */
+/*
+ * 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
/*
- * IP options for IPv6. Note I use the IPV6_* semantics for IPv6-
- * specific options. Another reason for the inclusion of <netinet/in.h> is
- * for the options that are common between IPv6 and IPv4.
+ * Wildcard Socket
*/
+#if 0 /*pre-RFC2553*/
+#define IN6_IS_ADDR_ANY(a) IN6_IS_ADDR_UNSPECIFIED(a)
+#endif
-#define IN6_MAX_MEMBERSHIPS 20 /* Maximum number of multicast memberships. */
-#define IPV6_DEFAULT_MCAST_HOPS 1
-#define IPV6_DEFAULT_MCAST_LOOP 1
+/*
+ * KAME Scope
+ */
+#ifdef _KERNEL /*nonstandard*/
+#define IN6_IS_SCOPE_LINKLOCAL(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)))
+#endif
/*
+ * IP6 route structure
+ */
+#if !defined(_XOPEN_SOURCE)
+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_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */
+/* no hdrincl */
+#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */
+#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */
+#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */
+#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */
+#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */
+#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */
+#define IPV6_MULTICAST_IF 9 /* u_char; set/get IP6 multicast i/f */
+#define IPV6_MULTICAST_HOPS 10 /* u_char; set/get IP6 multicast hops */
+#define IPV6_MULTICAST_LOOP 11 /* u_char; set/get IP6 multicast loopback */
+#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */
+#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */
+#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */
+#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */
+#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */
+#define IPV6_HOPLIMIT 20 /* bool; hop limit */
+#define IPV6_NEXTHOP 21 /* bool; next hop addr */
+#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */
+#define IPV6_DSTOPTS 23 /* bool; destination option */
+#define IPV6_RTHDR 24 /* bool; routing header */
+#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */
+#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */
+#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */
+
+#if 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
+
+#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;
+ u_int ipv6mr_interface;
+};
+
+/*
+ * IPV6_PKTINFO: Packet information(RFC2292 sec 5)
+ */
+struct in6_pktinfo {
+ struct in6_addr ipi6_addr; /* src/dst IPv6 address */
+ u_int ipi6_ifindex; /* send/recv interface index */
+};
+
+/*
+ * 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 */
+
+#if !defined(_XOPEN_SOURCE)
+/*
* Definitions for inet6 sysctl operations.
*
* Third level is protocol number.
* Fourth level is desired variable within that protocol.
*/
+#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */
-#define IPV6PROTO_MAXID (IPPROTO_ICMPV6 + 1) /* don't list to IPPROTO_MAX. */
-
-#define CTL_IPV6PROTO_NAMES { \
- { "ipv6", CTLTYPE_NODE }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { "ipv4", CTLTYPE_NODE }, \
- { 0, 0 }, \
- { "tcp", CTLTYPE_NODE }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
+#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 }, \
- { "udp", CTLTYPE_NODE }, \
{ 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 }, \
- { 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 }, \
- { "esp", CTLTYPE_NODE }, \
- { "ah", CTLTYPE_NODE }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { 0, 0 }, \
- { "icmpv6", CTLTYPE_NODE }, \
+ { "pim6", CTLTYPE_NODE }, \
}
-
+
/*
- * Names for IPv6 sysctl objects
+ * Names for IP sysctl objects
*/
-
-#define IPV6CTL_FORWARDING 1 /* act as router */
-#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding */
-#define IPV6CTL_DEFTTL 3 /* default TTL */
+#define IPV6CTL_FORWARDING 1 /* act as router */
+#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/
+#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */
#ifdef notyet
-#define IPV6CTL_DEFMTU 4 /* default MTU */
+#define IPV6CTL_DEFMTU 4 /* default MTU */
#endif
-#define IPV6CTL_STATS 5
-#define IPV6CTL_ROUTERSOLICIT 6
-#define IPV6CTL_MAXID 7
+#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 */
+#ifdef MAPPED_ADDR_ENABLED
+#define IPV6CTL_MAPPED_ADDR 23
+#endif /* MAPPED_ADDR_ENABLED */
+/* New entries should be added here from current IPV6CTL_MAXID value. */
+#define IPV6CTL_MAXID 24
+
+#ifdef MAPPED_ADDR_ENABLED
+#define IPV6CTL_NAMES_MAPPED_ADDR "mapped_addr"
+#define IPV6CTL_TYPE_MAPPED_ADDR CTLTYPE_INT
+#define IPV6CTL_VARS_MAPPED_ADDR &ip6_mapped_addr_on
+#else /* MAPPED_ADDR_ENABLED */
+#define IPV6CTL_NAMES_MAPPED_ADDR 0
+#define IPV6CTL_TYPE_MAPPED_ADDR 0
+#define IPV6CTL_VARS_MAPPED_ADDR 0
+#endif /* MAPPED_ADDR_ENABLED */
-#define IPV6CTL_NAMES { \
+#define IPV6CTL_NAMES { \
{ 0, 0 }, \
{ "forwarding", CTLTYPE_INT }, \
{ "redirect", CTLTYPE_INT }, \
- { "ttl", CTLTYPE_INT }, \
+ { "hlim", CTLTYPE_INT }, \
{ "mtu", CTLTYPE_INT }, \
- { "stats", CTLTYPE_STRUCT }, \
- { "routersolicit", CTLTYPE_INT }, \
- { 0, 0 }, \
+ { "forwsrcrt", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
+ { "mrtproto", CTLTYPE_INT }, \
+ { "maxfragpackets", CTLTYPE_INT }, \
+ { "sourcecheck", CTLTYPE_INT }, \
+ { "sourcecheck_logint", CTLTYPE_INT }, \
+ { "accept_rtadv", CTLTYPE_INT }, \
+ { "keepfaith", CTLTYPE_INT }, \
+ { "log_interval", CTLTYPE_INT }, \
+ { "hdrnestlimit", CTLTYPE_INT }, \
+ { "dad_count", CTLTYPE_INT }, \
+ { "auto_flowlabel", CTLTYPE_INT }, \
+ { "defmcasthlim", CTLTYPE_INT }, \
+ { "gifhlim", CTLTYPE_INT }, \
+ { "kame_version", CTLTYPE_STRING }, \
+ { "use_deprecated", CTLTYPE_INT }, \
+ { "rr_prune", CTLTYPE_INT }, \
+ { IPV6CTL_NAMES_MAPPED_ADDR, IPV6CTL_TYPE_MAPPED_ADDR }, \
}
#define IPV6CTL_VARS { \
0, \
- &ipv6forwarding, \
+ &ip6_forwarding, \
+ &ip6_sendredirects, \
+ &ip6_defhlim, \
+ 0, \
+ &ip6_forward_srcrt, \
+ 0, \
+ 0, \
0, \
- &ipv6_defhoplmt, \
+ &ip6_maxfragpackets, \
+ &ip6_sourcecheck, \
+ &ip6_sourcecheck_interval, \
+ &ip6_accept_rtadv, \
+ &ip6_keepfaith, \
+ &ip6_log_interval, \
+ &ip6_hdrnestlimit, \
+ &ip6_dad_count, \
+ &ip6_auto_flowlabel, \
+ &ip6_defmcasthlim, \
+ &ip6_gif_hlim, \
0, \
- &ipv6rsolicit \
+ &ip6_use_deprecated, \
+ &ip6_rr_prune, \
+ IPV6CTL_VARS_MAPPED_ADDR, \
}
+#endif /* !_XOPEN_SOURCE */
-/* Cheesy hack for if net/route.h included... */
-#ifdef RTM_VERSION
-/*
- * sizeof(struct sockaddr_in6) > sizeof(struct sockaddr), therefore, I
- * need to define...
- */
-struct route6
-{
- struct rtentry *ro_rt;
- struct sockaddr_in6 ro_dst;
-};
-#endif RTM_VERSION
+#ifdef _KERNEL
+struct cmsghdr;
+
+int in6_canforward __P((struct in6_addr *, struct in6_addr *));
+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 *));
+#ifdef MAPPED_ADDR_ENABLED
+struct sockaddr;
+
+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));
+#endif /* MAPPED_ADDR_ENABLED */
+
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
+#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa))
+#endif /* _KERNEL */
+
+__BEGIN_DECLS
+struct cmsghdr;
+
+extern int inet6_option_space(int);
+extern int inet6_option_init(void *, struct cmsghdr **, int);
+extern int inet6_option_append(struct cmsghdr *, const u_int8_t *, int, int);
+extern u_int8_t *inet6_option_alloc(struct cmsghdr *, int, int, int);
+extern int inet6_option_next(const struct cmsghdr *, u_int8_t **);
+extern int inet6_option_find(const struct cmsghdr *, u_int8_t **, int);
+
+extern size_t inet6_rthdr_space __P((int, int));
+extern struct cmsghdr *inet6_rthdr_init __P((void *, int));
+extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *,
+ unsigned int));
+extern int inet6_rthdr_lasthop __P((struct cmsghdr *, unsigned int));
+#if 0 /* not implemented yet */
+extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *));
+#endif
+extern int inet6_rthdr_segments __P((const struct cmsghdr *));
+extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int));
+extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int));
+__END_DECLS
-#if defined(_KERNEL) || defined(KERNEL)
-/* Function prototypes go here. */
-int in6_cksum __P((struct mbuf *,int, u_int, u_int));
-#endif /* defined(_KERNEL) || defined(KERNEL) */
-#endif /* _NETINET6_IN6_H */
+#endif /* !_NETINET6_IN6_H_ */
diff --git a/sys/netinet6/in6_cksum.c b/sys/netinet6/in6_cksum.c
index 0288b74daaf..16bb1ea124a 100644
--- a/sys/netinet6/in6_cksum.c
+++ b/sys/netinet6/in6_cksum.c
@@ -1,17 +1,35 @@
/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+ * 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 Regents of the University of California.
- * All rights reserved.
+ * 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
@@ -41,192 +59,260 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * (Originally from) @(#)in_cksum.c 7.3 (Berkeley) 6/28/90
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/param.h>
-#include <sys/types.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
-
#include <netinet/in.h>
-#include <netinet6/in6.h>
-#include <netinet6/ipv6.h>
+#include <netinet6/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);}
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+static union {
+ u_int16_t phs[4];
+ struct {
+ u_int32_t ph_len;
+ u_int8_t ph_zero[3];
+ u_int8_t ph_nxt;
+ } ph;
+} uph;
-/*----------------------------------------------------------------------
- * in6_cksum() takes an mbuf chain (with a IPv6 header at the beginning), and
- * computes the checksum. This also assumes that the IPv6 header is pulled
- * up into the first mbuf in the chain, and that said header has valid
- * source and destination fields. I do the pseudo-header first, then
- * I do the rest. Unlike v4, I treat the pseudo-header separately, so
- * transports don't have to do funky gymnastics with zeroing out headers.
- ----------------------------------------------------------------------*/
+/*
+ * m MUST contain a continuous IP6 header.
+ * off is a 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, proto, len, start)
- struct mbuf *m; /* Chain, complete with IPv6 header. */
- int proto; /* Protocol number of HLP that needs sum. */
- uint len; /* Length of stuff to checksum. Note that len
- and proto are what get computed in the pseudo-
- header. Len is a uint because of potential
- jumbograms. */
- uint start; /* How far (in bytes) into chain's data to
- start remainder of computation. (e.g. Where
- the TCP segment begins. */
+in6_cksum(m, nxt, off, len)
+ register struct mbuf *m;
+ u_int8_t nxt;
+ u_int32_t off, len;
{
- u_short *w;
- int sum = 0;
- int mlen = 0;
- int byte_swapped = 0;
- struct ipv6 *header;
- short done = 0;
- union
- {
- uint8_t c[2];
- uint16_t s;
- } s_util;
- union
- {
- uint16_t s[2];
- uint32_t l;
- } l_util;
-
- /*
- * Get pseudo-header summed up. Assume ipv6 is first.
- * I do pseudo-header here because it'll save bletch in both TCP and
- * UDP.
- */
-
- header = mtod(m,struct ipv6 *);
-
- w = (u_short *)&(header->ipv6_src);
- /* Source address */
- sum+=w[0]; sum+=w[1]; sum+=w[2]; sum+=w[3];
- sum+=w[4]; sum+=w[5]; sum+=w[6]; sum+=w[7];
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+#if 0
+ int srcifid = 0, dstifid = 0;
+#endif
+ struct ip6_hdr *ip6;
+
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
- /* Destination address */
- sum+=w[8]; sum+=w[9]; sum+=w[10]; sum+=w[11];
- sum+=w[12]; sum+=w[13]; sum+=w[14]; sum+=w[15];
+ /* sanity check */
+ if (m->m_pkthdr.len < off + len) {
+ panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)\n",
+ m->m_pkthdr.len, off, len);
+ }
- /* Next header value for transport layer. */
- sum += htons(proto);
+ /*
+ * 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;
- /* Length of transport header and transport data. */
- l_util.l = htonl(len);
- sum+=l_util.s[0];
- sum+=l_util.s[1];
+ /* 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];
- /* Find starting point for rest of data.. */
-
- while (!done)
- if (m->m_len >start)
- {
- done = 1;
- mlen = m->m_len - start;
- }
- else
- {
- start -= m->m_len;
+#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;
- }
-
- for (;m && len; m = m->m_next) {
- if (m->m_len == 0)
- continue;
- w = (u_short *)(m->m_data + (done ? start : 0));
-
- 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.
+ * Lastly calculate a summary of the rest of mbufs.
*/
- s_util.c[1] = *(char *)w;
- sum += s_util.s;
- w = (u_short *)((char *)w + 1);
- mlen = m->m_len - 1;
- len--;
- }
- else
- if (!done)
- mlen = m->m_len;
- else done=0;
-
- if (len < mlen)
- mlen = len;
- len -= mlen;
-
- /*
- * Force to even boundary.
- */
-#ifdef __alpha__
- if ((1 & (long) w) && (mlen > 0)) {
-#else
- if ((1 & (int) w) && (mlen > 0)) {
-#endif
- REDUCE;
- sum <<= 8;
- s_util.c[0] = *(u_char *)w;
- w = (u_short *)((char *)w + 1);
- mlen--;
- byte_swapped = 1;
- }
- /*
- * Unroll the loop to make overhead from
- * branches 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)
- printf("in6_cksum: out of data\n");
- 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);
+
+ 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\n");
+ 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 00000000000..54bef8d5282
--- /dev/null
+++ b/sys/netinet6/in6_gif.c
@@ -0,0 +1,307 @@
+/* $OpenBSD: in6_gif.c,v 1.1 1999/12/08 06:50:21 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.
+ */
+
+/*
+ * in6_gif.c
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#include <sys/protosw.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 <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_gif.h>
+#ifdef INET6
+#include <netinet6/ip6.h>
+#endif
+#include <netinet/ip_ecn.h>
+
+#include <net/if_gif.h>
+
+#include <net/net_osdep.h>
+
+int
+in6_gif_output(ifp, family, m, rt)
+ struct ifnet *ifp;
+ int family; /* family of the packet to be encapsulate. */
+ struct mbuf *m;
+ struct rtentry *rt;
+{
+ 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 DIAGNOSTIC
+ 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;
+ 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;
+ if (ifp->if_flags & IFF_LINK0) {
+ /* multi-destination mode */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
+ ip6->ip6_dst = sin6_dst->sin6_addr;
+ else if (rt) {
+ if (family != AF_INET6) {
+ m_freem(m);
+ return EINVAL; /*XXX*/
+ }
+ ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
+ } else {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ } else {
+ /* bidirectional configured tunnel mode */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
+ ip6->ip6_dst = sin6_dst->sin6_addr;
+ else {
+ m_freem(m);
+ return ENETUNREACH;
+ }
+ }
+ if (ifp->if_flags & IFF_LINK1) {
+ otos = 0;
+ ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
+ 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 0
+ ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
+ - sizeof(struct ip6_hdr);
+#endif
+ }
+
+#ifdef IPSEC
+#ifndef __OpenBSD__ /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif
+#endif /*IPSEC*/
+ return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
+}
+
+int in6_gif_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
+{
+ struct mbuf *m = *mp;
+ struct gif_softc *sc;
+ struct ifnet *gifp = NULL;
+ struct ip6_hdr *ip6;
+ int i;
+ int af = 0;
+ u_int32_t otos;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+#define satoin6(sa) (((struct sockaddr_in6 *)(sa))->sin6_addr)
+ for (i = 0, sc = gif; i < ngif; i++, sc++) {
+ if (sc->gif_psrc == NULL ||
+ sc->gif_pdst == NULL ||
+ sc->gif_psrc->sa_family != AF_INET6 ||
+ sc->gif_pdst->sa_family != AF_INET6) {
+ continue;
+ }
+ if ((sc->gif_if.if_flags & IFF_UP) == 0)
+ continue;
+ if ((sc->gif_if.if_flags & IFF_LINK0) &&
+ IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
+ IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
+ gifp = &sc->gif_if;
+ continue;
+ }
+ if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
+ IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
+ gifp = &sc->gif_if;
+ break;
+ }
+ }
+
+ if (gifp == NULL) {
+ 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);
+ 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);
+ break;
+ }
+#endif
+ default:
+ ip6stat.ip6s_nogif++;
+ m_freem(m);
+ return IPPROTO_DONE;
+ }
+
+ gif_input(m, af, gifp);
+ return IPPROTO_DONE;
+}
diff --git a/sys/netinet6/in6_gif.h b/sys/netinet6/in6_gif.h
new file mode 100644
index 00000000000..4779b5c9369
--- /dev/null
+++ b/sys/netinet6/in6_gif.h
@@ -0,0 +1,40 @@
+/* $OpenBSD: in6_gif.h,v 1.1 1999/12/08 06:50:21 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
+
+int in6_gif_input __P((struct mbuf **, int *, int));
+int in6_gif_output __P((struct ifnet *, int, struct mbuf *, struct rtentry *));
+
+#endif /*_NETINET6_IN6_GIF_H_*/
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
new file mode 100644
index 00000000000..5736bde4d88
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.c
@@ -0,0 +1,745 @@
+/* $OpenBSD: in6_ifattach.c,v 1.1 1999/12/08 06:50:21 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>
+#ifdef __bsdi__
+#include <crypto/md5.h>
+#elif defined(__OpenBSD__)
+#include <sys/md5k.h>
+#else
+#include <sys/md5.h>
+#endif
+
+#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>
+#ifndef __NetBSD__
+#include <netinet/if_ether.h>
+#endif
+
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#include <net/net_osdep.h>
+
+static struct in6_addr llsol;
+
+struct in6_ifstat **in6_ifstat = NULL;
+struct icmp6_ifstat **icmp6_ifstat = NULL;
+size_t in6_ifstatmax = 0;
+size_t icmp6_ifstatmax = 0;
+unsigned long in6_maxmtu = 0;
+
+int found_first_ifid = 0;
+#define IFID_LEN 8
+static char first_ifid[IFID_LEN];
+
+static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t));
+static int gen_rand_eui64 __P((u_int8_t *));
+
+static int
+laddr_to_eui64(dst, src, len)
+ u_int8_t *dst;
+ u_int8_t *src;
+ size_t len;
+{
+ static u_int8_t zero[8];
+
+ bzero(zero, sizeof(zero));
+
+ switch (len) {
+ case 6:
+ if (bcmp(zero, src, 6) == 0)
+ return EINVAL;
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = 0xff;
+ dst[4] = 0xfe;
+ dst[5] = src[3];
+ dst[6] = src[4];
+ dst[7] = src[5];
+ break;
+ case 8:
+ if (bcmp(zero, src, 8) == 0)
+ return EINVAL;
+ bcopy(src, dst, len);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Generate a last-resort interface identifier, when the machine has no
+ * IEEE802/EUI64 address sources.
+ * The address should be random, and should not change across reboot.
+ */
+static int
+gen_rand_eui64(dst)
+ u_int8_t *dst;
+{
+ MD5_CTX ctxt;
+ u_int8_t digest[16];
+#ifdef __FreeBSD__
+ int hostnamelen = strlen(hostname);
+#endif
+
+ /* generate 8bytes of pseudo-random value. */
+ bzero(&ctxt, sizeof(ctxt));
+ MD5Init(&ctxt);
+ MD5Update(&ctxt, hostname, hostnamelen);
+ MD5Final(digest, &ctxt);
+
+ /* assumes sizeof(digest) > sizeof(first_ifid) */
+ bcopy(digest, dst, 8);
+
+ /* make sure to set "u" bit to local, and "g" bit to individual. */
+ dst[0] &= 0xfe;
+ dst[0] |= 0x02; /* EUI64 "local" */
+
+ return 0;
+}
+
+/*
+ * Find first ifid on list of interfaces.
+ * This is assumed that ifp0's interface token (for example, IEEE802 MAC)
+ * is globally unique. We may need to have a flag parameter in the future.
+ */
+int
+in6_ifattach_getifid(ifp0)
+ struct ifnet *ifp0;
+{
+ struct ifnet *ifp;
+ struct ifaddr *ifa;
+ u_int8_t *addr = NULL;
+ int addrlen = 0;
+ struct sockaddr_dl *sdl;
+
+ if (found_first_ifid)
+ return 0;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifp = ifnet; ifp; ifp = ifp->if_next)
+#else
+ for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next)
+#endif
+ {
+ if (ifp0 != NULL && ifp0 != ifp)
+ continue;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl == NULL)
+ continue;
+ if (sdl->sdl_alen == 0)
+ continue;
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ATM:
+ /* IEEE802/EUI64 cases - what others? */
+ addr = LLADDR(sdl);
+ addrlen = sdl->sdl_alen;
+ /*
+ * to copy ifid from IEEE802/EUI64 interface,
+ * u bit of the source needs to be 0.
+ */
+ if ((addr[0] & 0x02) != 0)
+ break;
+ goto found;
+ case IFT_ARCNET:
+ /*
+ * ARCnet interface token cannot be used as
+ * globally unique identifier due to its
+ * small bitwidth.
+ */
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#ifdef DEBUG
+ printf("in6_ifattach_getifid: failed to get EUI64");
+#endif
+ return EADDRNOTAVAIL;
+
+found:
+ if (laddr_to_eui64(first_ifid, addr, addrlen) == 0)
+ found_first_ifid = 1;
+
+ if (found_first_ifid) {
+ printf("%s: supplying EUI64: "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ if_name(ifp),
+ first_ifid[0] & 0xff, first_ifid[1] & 0xff,
+ first_ifid[2] & 0xff, first_ifid[3] & 0xff,
+ first_ifid[4] & 0xff, first_ifid[5] & 0xff,
+ first_ifid[6] & 0xff, first_ifid[7] & 0xff);
+
+ /* invert u bit to convert EUI64 to RFC2373 interface ID. */
+ first_ifid[0] ^= 0x02;
+
+ return 0;
+ } else {
+#ifdef DEBUG
+ printf("in6_ifattach_getifid: failed to get EUI64");
+#endif
+ return EADDRNOTAVAIL;
+ }
+}
+
+void
+in6_ifattach(ifp, type, laddr, noloop)
+ struct ifnet *ifp;
+ u_int type;
+ caddr_t laddr;
+ /* size_t laddrlen; */
+ int noloop;
+{
+ static size_t if_indexlim = 8;
+ struct sockaddr_in6 mltaddr;
+ struct sockaddr_in6 mltmask;
+ struct sockaddr_in6 gate;
+ struct sockaddr_in6 mask;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ struct ifaddr **ifap;
+#endif
+
+ struct in6_ifaddr *ia, *ib, *oia;
+ struct ifaddr *ifa;
+ int rtflag = 0;
+
+ if (type == IN6_IFT_P2P && found_first_ifid == 0) {
+ printf("%s: no ifid available for IPv6 link-local address\n",
+ if_name(ifp));
+#if 0
+ return;
+#else
+ /* last resort */
+ if (gen_rand_eui64(first_ifid) == 0) {
+ printf("%s: using random value as EUI64: "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ if_name(ifp),
+ first_ifid[0] & 0xff, first_ifid[1] & 0xff,
+ first_ifid[2] & 0xff, first_ifid[3] & 0xff,
+ first_ifid[4] & 0xff, first_ifid[5] & 0xff,
+ first_ifid[6] & 0xff, first_ifid[7] & 0xff);
+ /*
+ * invert u bit to convert EUI64 to RFC2373 interface
+ * ID.
+ */
+ first_ifid[0] ^= 0x02;
+
+ found_first_ifid = 1;
+ }
+#endif
+ }
+
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ printf("%s: not multicast capable, IPv6 not enabled\n",
+ if_name(ifp));
+ return;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * To prevent to assign link-local address to PnP network
+ * cards multiple times.
+ * This is lengthy for P2P and LOOP but works.
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ ifa = ifp->if_addrlist;
+ if (ifa != NULL) {
+ for ( ; ifa; ifa = ifa->ifa_next) {
+ ifap = &ifa->ifa_next;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
+ return;
+ }
+ } else
+ ifap = &ifp->if_addrlist;
+#else
+ ifa = TAILQ_FIRST(&ifp->if_addrlist);
+ if (ifa != NULL) {
+ for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr))
+ return;
+ }
+ } else {
+ TAILQ_INIT(&ifp->if_addrlist);
+ }
+#endif
+
+ /*
+ * link-local address
+ */
+ ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+ ia->ia_ifp = ifp;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ *ifap = (struct ifaddr *)ia;
+#else
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#endif
+ /*
+ * Also link into the IPv6 address chain beginning with in6_ifaddr.
+ * kazu opposed it, but itojun & jinmei wanted.
+ */
+ if ((oia = in6_ifaddr) != NULL) {
+ for (; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+
+ ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_prefixmask.sin6_family = AF_INET6;
+ ia->ia_prefixmask.sin6_addr = in6mask64;
+
+ bzero(&ia->ia_addr, sizeof(struct sockaddr_in6));
+ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_addr.sin6_family = AF_INET6;
+ ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+ ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
+
+ switch (type) {
+ case IN6_IFT_LOOP:
+ ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
+ ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
+ break;
+ case IN6_IFT_802:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ rtflag = RTF_CLONING;
+ /* fall through */
+ case IN6_IFT_P2P802:
+ if (laddr == NULL)
+ break;
+ /* XXX use laddrlen */
+ if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8],
+ laddr, 6) != 0) {
+ break;
+ }
+ /* invert u bit to convert EUI64 to RFC2373 interface ID. */
+ ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02;
+ if (found_first_ifid == 0)
+ in6_ifattach_getifid(ifp);
+ bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
+ ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_dstaddr.sin6_family = AF_INET6;
+ break;
+ case IN6_IFT_P2P:
+ bcopy((caddr_t)first_ifid,
+ (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8],
+ IFID_LEN);
+ bzero(&ia->ia_dstaddr, sizeof(struct sockaddr_in6));
+ ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_dstaddr.sin6_family = AF_INET6;
+ break;
+ case IN6_IFT_ARCNET:
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ rtflag = RTF_CLONING;
+ if (laddr == NULL)
+ break;
+
+ /* make non-global IF id out of link-level address */
+ bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7);
+ ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr;
+ }
+
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ if (ifp->if_ioctl != NULL) {
+ int s;
+ int error;
+
+ /*
+ * give the interface a chance to initialize, in case this
+ * is the first address to be added.
+ */
+ s = splimp();
+ error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
+ splx(s);
+
+ if (error) {
+ switch (error) {
+ case EAFNOSUPPORT:
+ printf("%s: IPv6 not supported\n",
+ if_name(ifp));
+ break;
+ default:
+ printf("%s: SIOCSIFADDR error %d\n",
+ if_name(ifp), error);
+ break;
+ }
+
+ /* undo changes */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ *ifap = NULL;
+#else
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#endif
+ if (oia)
+ oia->ia_next = ia->ia_next;
+ else
+ in6_ifaddr = ia->ia_next;
+ free(ia, M_IFADDR);
+ return;
+ }
+ }
+
+ /* add route to the interface. */
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_prefixmask,
+ RTF_UP|rtflag,
+ (struct rtentry **)0);
+ ia->ia_flags |= IFA_ROUTE;
+
+ if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) {
+ /*
+ * route local address to loopback
+ */
+ bzero(&gate, sizeof(gate));
+ gate.sin6_len = sizeof(struct sockaddr_in6);
+ gate.sin6_family = AF_INET6;
+ gate.sin6_addr = in6addr_loopback;
+ bzero(&mask, sizeof(mask));
+ mask.sin6_len = sizeof(struct sockaddr_in6);
+ mask.sin6_family = AF_INET6;
+ mask.sin6_addr = in6mask64;
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&gate,
+ (struct sockaddr *)&mask,
+ RTF_UP|RTF_HOST,
+ (struct rtentry **)0);
+ }
+
+ /*
+ * loopback address
+ */
+ ib = (struct in6_ifaddr *)NULL;
+ if (type == IN6_IFT_LOOP) {
+ ib = (struct in6_ifaddr *)
+ malloc(sizeof(*ib), M_IFADDR, M_WAITOK);
+ bzero((caddr_t)ib, sizeof(*ib));
+ ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr;
+ ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr;
+ ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask;
+ ib->ia_ifp = ifp;
+
+ ia->ia_next = ib;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ ia->ia_ifa.ifa_next = (struct ifaddr *)ib;
+#else
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib,
+ ifa_list);
+#endif
+
+ ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ib->ia_prefixmask.sin6_family = AF_INET6;
+ ib->ia_prefixmask.sin6_addr = in6mask128;
+ ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ib->ia_addr.sin6_family = AF_INET6;
+ ib->ia_addr.sin6_addr = in6addr_loopback;
+#ifdef __bsdi__
+ /*
+ * It is necessary to set the loopback address to the dstaddr
+ * field at least for BSDI. Without this setting, the BSDI
+ * version of ifa_ifwithroute() rejects to add a route
+ * to the loopback interface.
+ */
+ ib->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ ib->ia_dstaddr.sin6_family = AF_INET6;
+ ib->ia_dstaddr.sin6_addr = in6addr_loopback;
+#endif
+
+ ib->ia_ifa.ifa_metric = ifp->if_metric;
+
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&ib->ia_prefixmask,
+ RTF_UP|RTF_HOST,
+ (struct rtentry **)0);
+
+ ib->ia_flags |= IFA_ROUTE;
+ }
+
+ /*
+ * join multicast
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ int error; /* not used */
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ /* Restore saved multicast addresses(if any). */
+ in6_restoremkludge(ia, ifp);
+#endif
+
+ 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);
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&mltmask,
+ RTF_UP|RTF_CLONING, /* xxx */
+ (struct rtentry **)0);
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+
+ if (type == IN6_IFT_LOOP) {
+ /*
+ * join node-local all-nodes address
+ */
+ mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+ rtrequest(RTM_ADD,
+ (struct sockaddr *)&mltaddr,
+ (struct sockaddr *)&ib->ia_addr,
+ (struct sockaddr *)&mltmask,
+ RTF_UP,
+ (struct rtentry **)0);
+ (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+ } else {
+ /*
+ * join solicited multicast address
+ */
+ bzero(&llsol, sizeof(llsol));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+ (void)in6_addmulti(&llsol, ifp, &error);
+ }
+ }
+
+ /* 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);
+
+ /* mark the address TENTATIVE, if needed. */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+#if 0
+ case IFT_ATM:
+ case IFT_SLIP:
+ case IFT_PPP:
+#endif
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ /* nd6_dad_start() will be called in in6_if_up */
+ break;
+ case IFT_DUMMY:
+ case IFT_GIF: /*XXX*/
+ case IFT_LOOP:
+ case IFT_FAITH:
+ default:
+ break;
+ }
+
+ return;
+}
+
+void
+in6_ifdetach(ifp)
+ struct ifnet *ifp;
+{
+ struct in6_ifaddr *ia, *oia;
+ struct ifaddr *ifa;
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ struct ifaddr *ifaprev = NULL;
+#endif
+ struct rtentry *rt;
+ short rtflags;
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6
+ || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) {
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ ifaprev = ifa;
+#endif
+ 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
+#ifdef __FreeBSD__
+ , 0UL
+#endif
+ ))) {
+ 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 */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ if (ifaprev)
+ ifaprev->ifa_next = ifa->ifa_next;
+ else
+ ifp->if_addrlist = ifa->ifa_next;
+#else
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#endif
+
+ /* 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;
+#ifdef DEBUG
+ else
+ printf("%s: didn't unlink in6ifaddr from "
+ "list\n", if_name(ifp));
+#endif
+ }
+
+ free(ia, M_IFADDR);
+ }
+}
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
new file mode 100644
index 00000000000..d7023e7bfd4
--- /dev/null
+++ b/sys/netinet6/in6_ifattach.h
@@ -0,0 +1,50 @@
+/* $OpenBSD: in6_ifattach.h,v 1.1 1999/12/08 06:50:21 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_IFATTACH_H_
+#define _NETINET6_IN6_IFATTACH_H_
+
+#ifdef _KERNEL
+extern int found_first_ifid;
+
+int in6_ifattach_getifid __P((struct ifnet *));
+void in6_ifattach_p2p __P((void));
+void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int));
+void in6_ifdetach __P((struct ifnet *));
+#endif /* _KERNEL */
+
+#define IN6_IFT_LOOP 1
+#define IN6_IFT_P2P 2
+#define IN6_IFT_802 3
+#define IN6_IFT_P2P802 4
+#define IN6_IFT_ARCNET 5
+
+#endif /* _NETINET6_IN6_IFATTACH_H_ */
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 0d99d5a1454..f101e229833 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -68,10 +68,9 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
-#include <netinet6/in6.h>
#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
#if __OpenBSD__
#undef IPSEC
@@ -147,6 +146,7 @@ extern int ipport_hilastauto;
* I also put it here, because, quite frankly, it belongs here, not in
* ip{v6,}_input().
*/
+#if 0
u_char inet6ctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
@@ -155,6 +155,7 @@ u_char inet6ctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
ENOPROTOOPT
};
+#endif
/*----------------------------------------------------------------------
* Bind an address (or at least a port) to an PF_INET6 socket.
@@ -323,7 +324,7 @@ in6_pcbbind(inp, nam)
inp->inp_laddr6 = sin6->sin6_addr;
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
- inp->inp_ipv6.ipv6_versfl = htonl(0x60000000) |
+ inp->inp_ipv6.ip6_flow = htonl(0x60000000) |
(sin6->sin6_flowinfo & htonl(0x0fffffff));
/*
@@ -643,13 +644,13 @@ in6_pcbladdr(inp, nam, plocal_sin)
#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
struct in6_ifaddr *ti6a = NULL;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
{
/* Find first (non-link-local if possible) address for
source usage. If multiple link-locals, use last one found. */
- if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ if (IN6_IS_ADDR_LINKLOCAL(&IA6_SIN6(i6a)->sin6_addr))
ti6a=i6a;
- else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ else if (!IN6_IS_ADDR_LOOPBACK(&IA6_SIN6(i6a)->sin6_addr))
break;
}
if (i6a == NULL && ti6a != NULL)
@@ -658,7 +659,7 @@ in6_pcbladdr(inp, nam, plocal_sin)
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
{
- register struct route6 *ro;
+ register struct route_in6 *ro;
i6a = NULL;
/*
@@ -689,6 +690,7 @@ in6_pcbladdr(inp, nam, plocal_sin)
rtalloc((struct route *)ro);
}
+#if 0 /* NRL IPv6*/
if (ro->ro_rt == NULL)
{
/*
@@ -700,6 +702,7 @@ in6_pcbladdr(inp, nam, plocal_sin)
ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
rtalloc((struct route *)ro);
}
+#endif
if (ro->ro_rt == NULL)
{
/*
@@ -774,13 +777,13 @@ in6_pcbladdr(inp, nam, plocal_sin)
{
struct in6_ifaddr *ti6a = NULL;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
{
/* Find first (non-local if possible) address for
source usage. If multiple locals, use last one found. */
- if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ if (IN6_IS_ADDR_LINKLOCAL(&IA6_SIN6(i6a)->sin6_addr))
ti6a=i6a;
- else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ else if (!IN6_IS_ADDR_LOOPBACK(&IA6_SIN6(i6a)->sin6_addr))
break;
}
if (i6a == NULL && ti6a != NULL)
@@ -797,15 +800,15 @@ in6_pcbladdr(inp, nam, plocal_sin)
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && inp->inp_moptions6 != NULL &&
(inp->inp_flags & INP_IPV6_MCAST))
{
- struct ipv6_moptions *i6mo;
+ struct ip6_hdr_moptions *im6o;
struct ifnet *ifp;
- i6mo = inp->inp_moptions6;
- if (i6mo->i6mo_multicast_ifp != NULL)
+ im6o = inp->inp_moptions6;
+ if (im6o->im6o_multicast_ifp != NULL)
{
- ifp = i6mo->i6mo_multicast_ifp;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp) /* Linkloc vs. global? */
+ ifp = im6o->im6o_multicast_ifp;
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
+ if (i6a->ia_ifp == ifp) /* Linkloc vs. global? */
break;
if (i6a == NULL)
return EADDRNOTAVAIL;
@@ -838,6 +841,7 @@ in6_pcbconnect(inp, nam)
#else /* __FreeBSD__ */
struct mbuf *nam;
#endif /* __FreeBSD__ */
+#if 0
{
struct in6_ifaddr *i6a;
struct sockaddr_in6 *ifaddr = NULL;
@@ -944,13 +948,13 @@ in6_pcbconnect(inp, nam)
#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
struct in6_ifaddr *ti6a = NULL;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
{
/* Find first (non-link-local if possible) address for
source usage. If multiple link-locals, use last one found. */
- if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ if (IN6_IS_ADDR_LINKLOCAL(&IA6_SIN6(i6a)->sin6_addr))
ti6a=i6a;
- else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ else if (!IN6_IS_ADDR_LOOPBACK(&IA6_SIN6(i6a)->sin6_addr))
break;
}
if (i6a == NULL && ti6a != NULL)
@@ -959,8 +963,7 @@ in6_pcbconnect(inp, nam)
if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
{
- register struct route6 *ro;
-
+ register struct route_in6 *ro;
i6a = NULL;
/*
@@ -991,6 +994,7 @@ in6_pcbconnect(inp, nam)
rtalloc((struct route *)ro);
}
+#if 0 /* NRL IPv6*/
if (ro->ro_rt == NULL)
{
/*
@@ -1002,6 +1006,7 @@ in6_pcbconnect(inp, nam)
ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
rtalloc((struct route *)ro);
}
+#endif
if (ro->ro_rt == NULL)
{
/*
@@ -1012,6 +1017,7 @@ in6_pcbconnect(inp, nam)
return ENETUNREACH;
}
+#if 0 /*NRL IPv6*/
if (ro->ro_rt->rt_ifa == NULL)
{
/*
@@ -1040,6 +1046,7 @@ in6_pcbconnect(inp, nam)
if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
panic("Oops2, I'm forgetting something after verify_onlink().");
}
+#endif
/*
@@ -1076,13 +1083,13 @@ in6_pcbconnect(inp, nam)
{
struct in6_ifaddr *ti6a = NULL;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
{
/* Find first (non-local if possible) address for
source usage. If multiple locals, use last one found. */
- if (IN6_IS_ADDR_LINKLOCAL(&I6A_SIN(i6a)->sin6_addr))
+ if (IN6_IS_ADDR_LINKLOCAL(&IA6_SIN6(i6a)->sin6_addr))
ti6a=i6a;
- else if (!IN6_IS_ADDR_LOOPBACK(&I6A_SIN(i6a)->sin6_addr))
+ else if (!IN6_IS_ADDR_LOOPBACK(&IA6_SIN6(i6a)->sin6_addr))
break;
}
if (i6a == NULL && ti6a != NULL)
@@ -1099,22 +1106,22 @@ in6_pcbconnect(inp, nam)
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && inp->inp_moptions6 != NULL &&
(inp->inp_flags & INP_IPV6_MCAST))
{
- struct ipv6_moptions *i6mo;
+ struct ip6_moptions *im6o;
struct ifnet *ifp;
- i6mo = inp->inp_moptions6;
- if (i6mo->i6mo_multicast_ifp != NULL)
+ im6o = inp->inp_moptions6;
+ if (im6o->im6o_multicast_ifp != NULL)
{
- ifp = i6mo->i6mo_multicast_ifp;
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp) /* Linkloc vs. global? */
+ ifp = im6o->im6o_multicast_ifp;
+ for (i6a = in6_ifaddr; i6a; i6a = i6a->ia_next)
+ if (i6a->ia_ifp == ifp) /* Linkloc vs. global? */
break;
if (i6a == NULL)
return EADDRNOTAVAIL;
}
}
- ifaddr = (struct sockaddr_in6 *)&i6a->i6a_addr;
+ ifaddr = (struct sockaddr_in6 *)&i6a->ia_addr;
}
#if __FreeBSD__
@@ -1155,7 +1162,7 @@ in6_pcbconnect(inp, nam)
/*
* Assumes user specify flowinfo in network order.
*/
- inp->inp_ipv6.ipv6_versfl = htonl(0x60000000) |
+ inp->inp_ipv6.ip6_flow = htonl(0x60000000) |
(sin6->sin6_flowinfo & htonl(0x0fffffff));
#if __NetBSD__
@@ -1175,6 +1182,139 @@ in6_pcbconnect(inp, nam)
#endif /* __FreeBSD__ */
return 0;
}
+#else
+{
+ struct in6_addr *in6a = NULL;
+ struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
+ struct in6_pktinfo *pi;
+ struct ifnet *ifp = NULL; /* outgoing interface */
+ int error = 0;
+ struct in6_addr mapped;
+
+ (void)&in6a; /* XXX fool gcc */
+
+ if (nam->m_len != sizeof(*sin6))
+ return(EINVAL);
+ if (sin6->sin6_family != AF_INET6)
+ return(EAFNOSUPPORT);
+ if (sin6->sin6_port == 0)
+ return(EADDRNOTAVAIL);
+
+ /* sanity check for mapped address case */
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6))
+ inp->inp_laddr6.s6_addr16[5] = htons(0xffff);
+ if (!IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6))
+ return EINVAL;
+ } else {
+ if (IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6))
+ return EINVAL;
+ }
+
+ /*
+ * If the scope of the destination is link-local, embed the interface
+ * index in the address.
+ */
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
+ /* XXX boundary check is assumed to be already done. */
+ /* XXX sin6_scope_id is weaker than advanced-api. */
+ if (inp->inp_outputopts6 &&
+ (pi = inp->inp_outputopts6->ip6po_pktinfo) &&
+ pi->ipi6_ifindex) {
+ sin6->sin6_addr.s6_addr16[1] = htons(pi->ipi6_ifindex);
+ ifp = ifindex2ifnet[pi->ipi6_ifindex];
+ }
+ else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
+ inp->inp_moptions6 &&
+ inp->inp_moptions6->im6o_multicast_ifp) {
+ sin6->sin6_addr.s6_addr16[1] =
+ htons(inp->inp_moptions6->im6o_multicast_ifp->if_index);
+ ifp = ifindex2ifnet[inp->inp_moptions6->im6o_multicast_ifp->if_index];
+ } else if (sin6->sin6_scope_id) {
+ /* boundary check */
+ if (sin6->sin6_scope_id < 0
+ || if_index < sin6->sin6_scope_id) {
+ return ENXIO; /* XXX EINVAL? */
+ }
+ sin6->sin6_addr.s6_addr16[1]
+ = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/
+ ifp = ifindex2ifnet[sin6->sin6_scope_id];
+ }
+ }
+
+ /* Source address selection. */
+ if (IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6)
+ && inp->inp_laddr6.s6_addr32[3] == 0) {
+ struct sockaddr_in sin, *sinp;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr,
+ sizeof(sin.sin_addr));
+ sinp = in_selectsrc(&sin, (struct route *)&inp->inp_route6,
+ inp->inp_socket->so_options, NULL, &error);
+ if (sinp == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return(error);
+ }
+ bzero(&mapped, sizeof(mapped));
+ mapped.s6_addr16[5] = htons(0xffff);
+ bcopy(&sinp->sin_addr, &mapped.s6_addr32[3], sizeof(sinp->sin_addr));
+ in6a = &mapped;
+ } else {
+ /*
+ * XXX: in6_selectsrc might replace the bound local address
+ * with the address specified by setsockopt(IPV6_PKTINFO).
+ * Is it the intended behavior?
+ */
+ in6a = in6_selectsrc(sin6, inp->inp_outputopts6,
+ inp->inp_moptions6,
+ &inp->inp_route6,
+ &inp->inp_laddr6, &error);
+ if (in6a == 0) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return(error);
+ }
+ }
+ if (inp->inp_route6.ro_rt)
+ ifp = inp->inp_route6.ro_rt->rt_ifp;
+
+ inp->inp_ipv6.ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp);
+
+ if (in_pcblookup(inp->inp_table,
+ &sin6->sin6_addr,
+ sin6->sin6_port,
+ IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ?
+ in6a : &inp->inp_laddr6,
+ inp->inp_lport,
+ INPLOOKUP_IPV6))
+ return(EADDRINUSE);
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)
+ || (IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6)
+ && inp->inp_laddr6.s6_addr32[3] == 0)) {
+ if (inp->inp_lport == 0)
+ (void)in6_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr6 = *in6a;
+ }
+ inp->inp_faddr6 = sin6->sin6_addr;
+ inp->inp_fport = sin6->sin6_port;
+ /*
+ * xxx kazu flowlabel is necessary for connect?
+ * but if this line is missing, the garbage value remains.
+ */
+ inp->inp_ipv6.ip6_flow = sin6->sin6_flowinfo;
+ /* configure NRL flags properly */
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ inp->inp_flags |= INP_IPV6_MAPPED;
+ inp->inp_flags &= ~INP_IPV6_UNDEC;
+ }
+ in_pcbrehash(inp);
+ return(0);
+}
+#endif
/*----------------------------------------------------------------------
* Pass some notification to all connections of a protocol
@@ -1356,7 +1496,6 @@ in6_setsockaddr(inp, nam)
{
register struct sockaddr_in6 *sin6;
-
#if __FreeBSD__
/*
* In FreeBSD we have to allocate the sockaddr_in6 structure since we aren't
diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h
new file mode 100644
index 00000000000..f329098242a
--- /dev/null
+++ b/sys/netinet6/in6_pcb.h
@@ -0,0 +1,178 @@
+/* $OpenBSD: in6_pcb.h,v 1.1 1999/12/08 06:50:21 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_
+
+#include <sys/queue.h>
+
+/*
+ * Common structure pcb for internet protocol implementation.
+ * Here are stored pointers to local and foreign host table
+ * entries, local and foreign socket numbers, and pointers
+ * up (to a socket structure) and down (to a protocol-specific)
+ * control block.
+ */
+struct icmp6_filter;
+struct inpcbpolicy;
+
+struct in6pcb {
+ struct in6pcb *in6p_next, *in6p_prev;
+ /* pointers to other pcb's */
+ struct in6pcb *in6p_head; /* pointer back to chain of
+ in6pcb's for this protocol */
+ struct in6_addr in6p_faddr; /* foreign host table entry */
+ u_short in6p_fport; /* foreign port */
+ struct in6_addr in6p_laddr; /* local host table entry */
+ u_short in6p_lport; /* local port */
+ u_int32_t in6p_flowinfo; /* priority and flowlabel */
+ struct socket *in6p_socket; /* back pointer to socket */
+ caddr_t in6p_ppcb; /* pointer to per-protocol pcb */
+ struct route_in6 in6p_route; /* placeholder for routing entry */
+ int in6p_flags; /* generic IP6/datagram flags */
+ int in6p_hops; /* default hop limit */
+ struct ip6_hdr in6p_ip6; /* header prototype */
+ struct mbuf *in6p_options; /* IP6 options */
+ struct ip6_pktopts *in6p_outputopts; /* IP6 options for outgoing packets */
+ struct ip6_moptions *in6p_moptions; /* IP6 multicast options */
+ u_short in6p_ifindex;
+ /* should move the following just after next/prev */
+ LIST_ENTRY(in6pcb) in6p_hlist; /* hash chain */
+ u_long in6p_hash; /* hash value */
+#if 1 /*IPSEC*/
+ struct inpcbpolicy *in6p_sp; /* security policy. */
+#endif
+ struct icmp6_filter *in6p_icmp6filt;
+ int in6p_cksum; /* IPV6_CHECKSUM setsockopt */
+};
+
+#define in6p_ip6_nxt in6p_ip6.ip6_nxt /* for KAME src sync over BSD*'s */
+
+/* flags in in6p_flags */
+#define IN6P_RECVOPTS 0x01 /* receive incoming IP6 options */
+#define IN6P_RECVRETOPTS 0x02 /* receive IP6 options for reply */
+#define IN6P_RECVDSTADDR 0x04 /* receive IP6 dst address */
+#define IN6P_HIGHPORT 0x10 /* user wants "high" port binding */
+#define IN6P_LOWPORT 0x20 /* user wants "low" port binding */
+#define IN6P_ANONPORT 0x40 /* port chosen for user */
+#define IN6P_FAITH 0x80 /* accept FAITH'ed connections */
+#define IN6P_PKTINFO 0x010000
+#define IN6P_HOPLIMIT 0x020000
+#define IN6P_NEXTHOP 0x040000
+#define IN6P_HOPOPTS 0x080000
+#define IN6P_DSTOPTS 0x100000
+#define IN6P_RTHDR 0x200000
+#define IN6P_CONTROLOPTS (0x3f0000 | IN6P_RECVOPTS | IN6P_RECVRETOPTS | IN6P_RECVDSTADDR)
+
+#define IN6PLOOKUP_WILDCARD 1
+#define IN6PLOOKUP_SETLOCAL 2
+
+/* compute hash value for foreign and local in6_addr and port */
+#define IN6_HASH(faddr, fport, laddr, lport) \
+ (((faddr)->s6_addr32[0] ^ (faddr)->s6_addr32[1] ^ \
+ (faddr)->s6_addr32[2] ^ (faddr)->s6_addr32[3] ^ \
+ (laddr)->s6_addr32[0] ^ (laddr)->s6_addr32[1] ^ \
+ (laddr)->s6_addr32[2] ^ (laddr)->s6_addr32[3]) \
+ + (fport) + (lport))
+
+#define sotoin6pcb(so) ((struct in6pcb *)(so)->so_pcb)
+
+#ifdef _KERNEL
+void in6_losing __P((struct in6pcb *));
+int in6_pcballoc __P((struct socket *, struct in6pcb *));
+int in6_pcbbind __P((struct in6pcb *, struct mbuf *));
+int in6_pcbconnect __P((struct in6pcb *, struct mbuf *));
+void in6_pcbdetach __P((struct in6pcb *));
+void in6_pcbdisconnect __P((struct in6pcb *));
+struct in6pcb *
+ in6_pcblookup __P((struct in6pcb *,
+ struct in6_addr *, u_int, struct in6_addr *,
+ u_int, int));
+int in6_pcbnotify __P((struct in6pcb *, struct sockaddr *,
+ u_int, struct in6_addr *, u_int, int,
+ void (*)(struct in6pcb *, int)));
+int in6_pcbsetport __P((struct in6_addr *, struct in6pcb *));
+void in6_rtchange __P((struct in6pcb *, int));
+void in6_setpeeraddr __P((struct in6pcb *, struct mbuf *));
+void in6_setsockaddr __P((struct in6pcb *, struct mbuf *));
+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 *));
+
+#ifndef TCP6
+extern struct rtentry *
+ in6_pcbrtentry __P((struct in6pcb *));
+extern struct in6pcb *in6_pcblookup_connect __P((struct in6pcb *,
+ struct in6_addr *, u_int, struct in6_addr *, u_int, int));
+extern struct in6pcb *in6_pcblookup_bind __P((struct in6pcb *,
+ struct in6_addr *, u_int, int));
+#endif
+#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 00000000000..20239469405
--- /dev/null
+++ b/sys/netinet6/in6_prefix.c
@@ -0,0 +1,1149 @@
+/* $OpenBSD: in6_prefix.c,v 1.1 1999/12/08 06:50:21 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>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#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>
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+#include <sys/proc.h>
+#endif
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/ip6_var.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix");
+static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid");
+#endif
+
+struct rr_prhead rr_prefix;
+
+#include <net/net_osdep.h>
+
+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));
+
+/*
+ * 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 = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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
+ */
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ 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 = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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", 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 = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ 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?",
+ 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 = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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 = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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 !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ 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;
+
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next)
+ if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
+ (sizeof(struct in6_addr) << 3) -
+ rpp->rp_plen))
+ break;
+ return rap;
+}
+
+static int
+assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
+{
+ 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;
+#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;
+}
+
+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;
+
+ 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;
+
+ /* 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));
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__NetBSD__)
+ so.so_state |= SS_PRIV;
+#endif
+
+ 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;
+ else if (rap->ra_addr != ia) {
+ /* There may be some inconsistencies between addrs. */
+ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix"
+ "has already another ia %p(%s) on its ifid list",
+ 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 = assigne_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);
+ free(rap, M_RR_ADDR);
+ }
+}
+
+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 */
+
+ 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 */
+ rap->ra_addr = ia6;
+ ia6->ia6_ifpr = rp2ifpr(rpp);
+ return;
+ }
+ if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
+ rap->ra_addr = ia6;
+ 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",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
+ ip6_sprintf(IA6_IN6(ia6)),
+ in6_mask2len(&ia6->ia_prefixmask.sin6_addr));
+ 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
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+ , curproc
+#endif
+ );
+ 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",
+ 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 = new->rp_ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next) {
+ 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.
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ while (!LIST_EMPTY(&new->rp_addrhead))
+#else
+ while (new->rp_addrhead.lh_first != NULL)
+#endif
+ {
+ rap = LIST_FIRST(&new->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ if (search_ifidwithprefix(rpp, &rap->ra_ifid)
+ != NULL) {
+ 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", __LINE__);
+ return(ENOBUFS);
+ }
+ /* initilization */
+ *rpp = *new;
+ LIST_INIT(&rpp->rp_addrhead);
+ /* move rp_addr entries of new to rpp */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ while (!LIST_EMPTY(&new->rp_addrhead))
+#else
+ while (new->rp_addrhead.lh_first != NULL)
+#endif
+ {
+ 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 = ifp->if_prefixlist) != NULL) {
+ for ( ; ifpr->ifpr_next;
+ ifpr = ifpr->ifpr_next)
+ continue;
+ ifpr->ifpr_next = rp2ifpr(rpp);
+ }
+ else
+ ifp->if_prefixlist = 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.
+ */
+ for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
+ rap = rap->ra_entry.le_next) {
+ 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 = ifp->if_prefixlist) == rp2ifpr(rpp))
+ ifp->if_prefixlist = ifpr->ifpr_next;
+ else {
+ while (ifpr->ifpr_next &&
+ (ifpr->ifpr_next != rp2ifpr(rpp)))
+ ifpr = ifpr->ifpr_next;
+ if (ifpr->ifpr_next)
+ ifpr->ifpr_next = rp2ifpr(rpp)->ifpr_next;
+ 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", __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));
+ for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL;
+ orap = orap->ra_entry.le_next) {
+ 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->ia_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
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ while (!LIST_EMPTY(&rpp->rp_addrhead))
+#else
+ while (rpp->rp_addrhead.lh_first != NULL)
+#endif
+ {
+ struct rp_addr *rap;
+
+ rap = LIST_FIRST(&rpp->rp_addrhead);
+ LIST_REMOVE(rap, ra_entry);
+ 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 = ifp->if_prefixlist; ifpr; ifpr = nextifpr) {
+ nextifpr = ifpr->ifpr_next;
+ 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;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ 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 socket *so, struct rr_prefix *rpp, u_char origin)
+{
+ struct in6_aliasreq ifra;
+ 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)
+ 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;
+
+ bzero(&ifra, sizeof(ifra));
+ strncpy(ifra.ifra_name, if_name(rpp->rp_ifp),
+ sizeof(ifra.ifra_name));
+ ifra.ifra_addr = rap->ra_addr->ia_addr;
+ ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr;
+ ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask;
+
+ error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
+ rpp->rp_ifp
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+ , curproc
+#endif
+ );
+ if (error != 0)
+ log(LOG_ERR, "in6_prefix.c: delete_each_prefix:"
+ "deletion of an addr %s/%d failed because"
+ "in6_control failed for error %d",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr),
+ rpp->rp_plen, error);
+
+ free(rap, M_RR_ADDR);
+ }
+ rp_remove(rpp);
+
+ return error;
+}
+
+static void
+delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin)
+{
+ struct ifprefix *ifpr, *nextifpr;
+
+ /* delete prefixes marked as tobe deleted */
+ for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr) {
+ nextifpr = ifpr->ifpr_next;
+ if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
+ ifpr->ifpr_type != IN6_PREFIX_RR)
+ continue;
+ if (ifpr2rp(ifpr)->rp_statef_delmark)
+ (void)delete_each_prefix(so, ifpr2rp(ifpr), origin);
+ }
+}
+
+static int
+link_stray_ia6s(struct rr_prefix *rpp)
+{
+ struct ifaddr *ifa;
+
+#if (defined(__FreeBSD__) && __FreeBSD__ < 3) || defined(__bsdi__)
+ for (ifa = rpp->rp_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ 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",
+ ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
+ ip6_sprintf(RP_IN6(rpp)),
+ rpp->rp_plen);
+ continue;
+ }
+ if ((error = assigne_ra_entry(rpp,
+ (sizeof(rap->ra_ifid) << 3) -
+ rpp->rp_plen,
+ (struct in6_ifaddr *)ifa)) != 0)
+ return error;
+ }
+ return 0;
+}
+
+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)",
+ (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(so, 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)",
+ (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;
+ }
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa != NULL) {
+ if ((error = create_ra_entry(&rap)) != 0) {
+ free_rp_entries(&rp_tmp);
+ break;
+ }
+ /* 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(so, rpp, ipr->ipr_origin);
+ break;
+ }
+ return error;
+}
+
+void
+in6_rr_timer(void *ignored_arg)
+{
+ int s;
+ struct rr_prefix *rpp;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz);
+
+ s = splnet();
+ /* expire */
+ rpp = LIST_FIRST(&rr_prefix);
+ while (rpp) {
+ if (rpp->rp_expire && rpp->rp_expire < time_second) {
+ struct rr_prefix *next_rpp;
+ struct socket so;
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__NetBSD__)
+ so.so_state |= SS_PRIV;
+#endif
+
+ next_rpp = LIST_NEXT(rpp, rp_entry);
+ delete_each_prefix(&so, 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 00000000000..d8f0484b488
--- /dev/null
+++ b/sys/netinet6/in6_prefix.h
@@ -0,0 +1,88 @@
+/* $OpenBSD: in6_prefix.h,v 1.1 1999/12/08 06:50:21 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.
+ */
+
+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 *));
+int delete_each_prefix __P((struct socket *so, struct rr_prefix *rpp,
+ u_char origin));
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index 873aab95b4e..e9903a4f54e 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -1,395 +1,715 @@
/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
+ * 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
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
#include <sys/param.h>
#include <sys/socket.h>
+#if defined(__FreeBSD__)
+#include <sys/socketvar.h>
+#endif
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
+#ifdef __FreeBSD__
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#endif
#include <net/if.h>
-#include <net/route.h>
#include <net/radix.h>
+#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(TCP6)) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
#include <netinet/ip.h>
#include <netinet/ip_var.h>
+#endif
+#if (defined(__NetBSD__) && !defined(TCP6)) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
#include <netinet/in_pcb.h>
+#endif
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__) && !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet6/in6_pcb.h>
+#endif
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
-
+#else
+#if defined(__NetBSD__) && !defined(TCP6)
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
-/* #include <netinet/tcpip.h> */
-#if __FreeBSD__ && defined(_NETINET_IN_PCB_H_)
-#undef _NETINET_IN_PCB_H_
#include <netinet/tcp_var.h>
-#define _NETINET_IN_PCB_H_
-#else /* __FreeBSD__ */
-#include <netinet/tcp_var.h>
-#endif /* __FreeBSD__ */
#include <netinet/tcpip.h>
#include <netinet/tcp_debug.h>
+#else
+#include <netinet6/tcp6.h>
+#include <netinet6/tcp6_fsm.h>
+#include <netinet6/tcp6_seq.h>
+#include <netinet6/tcp6_timer.h>
+#include <netinet6/tcp6_var.h>
+#endif
+#endif
+
+#if !defined(__OpenBSD__) && !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet6/udp6.h>
+#include <netinet6/udp6_var.h>
+#endif
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
+#include <netinet6/pim6_var.h>
-#if __OpenBSD__
+#include <netinet6/nd6.h>
+#ifdef __FreeBSD__
+#include <netinet6/in6_prefix.h>
+#endif
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
+#endif
#ifdef IPSEC
-#include <sys/osdep.h>
-#include <netsec/ipsec.h>
-#endif /* IPSEC */
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <netinet6/ipcomp.h>
+#endif /*IPSEC*/
-#if __FreeBSD__
-#include <sys/sysctl.h>
-#endif /* __FreeBSD__ */
+#include <netinet6/ip6protosw.h>
-extern struct domain inet6domain;
-
-#define CAST (void *)
-
-#if !__FreeBSD__
-struct protosw inet6sw[] = {
-/* normal protocol switch */
- {
- 0, &inet6domain, 0, 0, /* NOTE: This 0 is the same as IPPROTO_HOPOPTS,
- but we specially demux IPPROTO_HOPOPTS
- in ipv6_input(). */
- CAST ipv6_hop, CAST ipv6_output, 0, 0, /* Watch for hop-by-hop input! */
- 0,
- ipv6_init, 0, ipv6_slowtimo, ipv6_drain, ipv6_sysctl
- },
-
- /* ICMPv6 entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR,
- CAST ipv6_icmp_input, CAST ipv6_icmp_output, 0, ripv6_ctloutput,
- ipv6_icmp_usrreq,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, ipv6_icmp_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
-
- /* IPv6-in-IPv6 tunnel entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
- CAST ipv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- ripv6_usrreq,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, ipv6_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
-
- /* IPv4-in-IPv6 tunnel entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
- CAST ipv4_input, 0, 0, 0,
- 0,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, ip_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
-
- /* Fragment entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT, PR_ATOMIC|PR_ADDR,
- CAST ipv6_reasm, 0, 0, 0,
- 0,
- 0, 0, 0, 0, 0
- },
-
-
- /* UDP entry */
-
- /*
- * Eventually, that ipv6_ctloutput() will have to be replaced with a
- * udp_ctloutput(), which knows whether or not to redirect things down to
- * IP or IPv6 appropriately.
- */
-
- {
- SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
- CAST udp_input, 0, CAST udp_ctlinput, ipv6_ctloutput,
- udp_usrreq,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, udp_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
-
- /* TCP entry */
-
- {
- SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
- CAST tcp_input, 0, CAST tcp_ctlinput, tcp_ctloutput,
- tcp_usrreq,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, tcp_sysctl /* init, fasttimo, etc. in v4 protosw already! */
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0 /* init, fasttimo, etc. in v4 protosw already! */
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
+#include "gif.h"
+#if NGIF > 0
+#include <netinet6/in6_gif.h>
+#endif
-#ifdef IPSEC
- /* IPv6 & IPv4 Authentication Header */
- {
- SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
- CAST ipsec_ah_input, 0, 0, 0,
- 0,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, ipsec_ah_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
+#include <net/net_osdep.h>
-#ifdef IPSEC_ESP
- /* IPv6 & IPv4 Encapsulating Security Payload Header */
- {
- SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
- CAST ipsec_esp_input, 0, 0, 0,
- 0,
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- 0, 0, 0, 0, ipsec_esp_sysctl
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- 0, 0, 0, 0, 0
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- },
-#endif /* IPSEC_ESP */
-#endif /* IPSEC */
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
- /* Unknown header. */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
- CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- ripv6_usrreq,
- 0,0,0,0,0
- },
-
- /* Raw wildcard */
- {
- SOCK_RAW, &inet6domain, 0, PR_ATOMIC|PR_ADDR,
- CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- ripv6_usrreq,
- ripv6_init,0,0,0,0
- },
-};
-#else /* !__FreeBSD__ */
-extern struct pr_usrreqs nousrreqs;
-struct protosw inet6sw[] = {
- {
- 0, &inet6domain, 0, 0, /* NOTE: This 0 is the same as IPPROTO_HOPOPTS,
- but we specially demux IPPROTO_HOPOPTS
- in ipv6_input(). */
- CAST ipv6_hop, CAST ipv6_output, 0, 0, /* Watch for hop-by-hop input! */
- 0,
- ipv6_init, 0, ipv6_slowtimo, ipv6_drain,
- &nousrreqs
- },
-
- /* ICMPv6 entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC|PR_ADDR,
- CAST ipv6_icmp_input, CAST ipv6_icmp_output, 0, CAST ripv6_ctloutput,
- 0,
- 0, 0, 0, 0,
- &ipv6_icmp_usrreqs,
- },
-
- /* IPv6-in-IPv6 tunnel entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
- CAST ipv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- 0,
- 0, 0, 0, 0,
- &ripv6_usrreqs
- },
-
- /* IPv4-in-IPv6 tunnel entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
- CAST ipv4_input, 0, 0, 0,
- 0,
- 0, 0, 0, 0,
- &nousrreqs
- },
-
- /* Fragment entry */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT, PR_ATOMIC|PR_ADDR,
- CAST ipv6_reasm, 0, 0, 0,
- 0,
- 0, 0, 0, 0,
- &nousrreqs
- },
-
-
- /* UDP entry */
-
- /*
- * Eventually, that ipv6_ctloutput() will have to be replaced with a
- * udp_ctloutput(), which knows whether or not to redirect things down to
- * IP or IPv6 appropriately.
- */
-
- {
- SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
- CAST udp_input, 0, CAST udp_ctlinput, ipv6_ctloutput,
- 0,
- udp_init, 0, 0, 0,
- &udp_usrreqs
- },
-
- /* TCP entry */
-
- {
- SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
- CAST tcp_input, 0, CAST tcp_ctlinput, tcp_ctloutput,
- 0,
- tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
- &tcp_usrreqs,
- },
+/*
+ * TCP/IP protocol family: IP6, ICMP6, UDP, TCP.
+ */
-#ifdef IPSEC
- /* IPv6 & IPv4 Authentication Header */
- {
- SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
- CAST ipsec_ah_input, 0, 0, 0,
- 0,
- 0, 0, 0, 0,
- &nousrreqs
- },
+extern struct domain inet6domain;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static struct pr_usrreqs nousrreqs;
+#endif
+struct ip6protosw inet6sw[] = {
+{ 0, &inet6domain, IPPROTO_IPV6, 0,
+ 0, 0, 0, 0,
+ 0,
+ ip6_init, 0, frag6_slowtimo, frag6_drain,
+#ifndef __FreeBSD__
+ ip6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &nousrreqs,
+# endif
+#endif
+},
+{ SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC | PR_ADDR,
+ udp6_input, 0, udp6_ctlinput, ip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0, 0,
+#elif defined(HAVE_NRL_INPCB)
+ udp6_usrreq, 0,
+#else
+ udp6_usrreq, udp6_init,
+#endif
+ 0, 0, 0,
+#ifndef __FreeBSD__
+#ifdef HAVE_NRL_INPCB
+ udp_sysctl,
+#else
+ udp6_sysctl,
+#endif
+#else
+# if __FreeBSD__ >= 3
+ &udp6_usrreqs,
+# endif
+#endif
+},
+#ifdef TCP6
+{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED | PR_WANTRCVD,
+ tcp6_input, 0, tcp6_ctlinput, tcp6_ctloutput,
+ tcp6_usrreq,
+ tcp6_init, tcp6_fasttimo, tcp6_slowtimo, tcp6_drain,
+#ifndef __FreeBSD__
+ tcp6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &tcp6_usrreqs,
+# endif
+#endif
+},
+#else
+{ SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED | PR_WANTRCVD,
+ tcp6_input, 0, tcp6_ctlinput, tcp_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0,
+#elif defined(HAVE_NRL_INPCB)
+ tcp6_usrreq,
+#else
+ tcp_usrreq,
+#endif
+#ifdef INET /* don't call timeout routines twice */
+ tcp_init, 0, 0, tcp_drain,
+#else
+ tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
+#endif
+#ifndef __FreeBSD__
+ tcp_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &tcp6_usrreqs,
+# endif
+#endif
+},
+#endif /*TCP6*/
+{ SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR,
+ rip6_input, rip6_output, 0, rip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0,
+#else
+ rip6_usrreq,
+#endif
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &rip6_usrreqs
+#endif
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR,
+ icmp6_input, rip6_output, 0, rip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0,
+#else
+ rip6_usrreq,
+#endif
+ icmp6_init, icmp6_fasttimo, 0, 0,
+#ifndef __FreeBSD__
+ icmp6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &rip6_usrreqs
+# endif
+#endif
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR,
+ dest6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &nousrreqs
+#endif
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR,
+ route6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &nousrreqs
+#endif
+},
+{ SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR,
+ frag6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &nousrreqs
+#endif
+},
+#ifdef IPSEC
+{ SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR,
+ ah6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#ifndef __FreeBSD__
+ ipsec6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &nousrreqs,
+# endif
+#endif
+},
#ifdef IPSEC_ESP
- /* IPv6 & IPv4 Encapsulating Security Payload Header */
- {
- SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
- CAST ipsec_esp_input, 0, 0, 0,
- 0,
- 0, 0, 0, 0,
- &nousrreqs
- },
-#endif /* IPSEC_ESP */
+{ SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR,
+ esp6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#ifndef __FreeBSD__
+ ipsec6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &nousrreqs,
+# endif
+#endif
+},
+#endif
+{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR,
+ ipcomp6_input, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#ifndef __FreeBSD__
+ ipsec6_sysctl,
+#else
+# if __FreeBSD__ >= 3
+ &nousrreqs,
+# endif
+#endif
+},
#endif /* IPSEC */
-
- /* Unknown header. */
-
- {
- SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
- CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- 0,
- 0,0,0,0,
- &ripv6_usrreqs
- },
-
- /* Raw wildcard */
- {
- SOCK_RAW, &inet6domain, 0, PR_ATOMIC|PR_ADDR,
- CAST ripv6_input, CAST ripv6_output, 0, ripv6_ctloutput,
- 0,
- ripv6_init,0,0,0,
- &ripv6_usrreqs,
- },
+#if NGIF > 0
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR,
+ in6_gif_input,0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &nousrreqs
+#endif
+},
+#ifdef INET6
+{ SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR,
+ in6_gif_input,0, 0, 0,
+ 0,
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &nousrreqs
+#endif
+},
+#endif /* INET6 */
+#endif /* GIF */
+{ SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR,
+ pim6_input, rip6_output, 0, rip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0,
+#else
+ rip6_usrreq,
+#endif
+ 0, 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &rip6_usrreqs
+# endif
+},
+/* raw wildcard */
+{ SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR,
+ rip6_input, rip6_output, 0, rip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 0, 0,
+#else
+ rip6_usrreq, rip6_init,
+#endif
+ 0, 0, 0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ &rip6_usrreqs
+#endif
+},
};
-#endif /* !__FreeBSD__ */
+#ifdef __FreeBSD__
+extern int in6_inithead __P((void **, int));
+#endif
-#if !__FreeBSD__
struct domain inet6domain =
-{
- PF_INET6, "IPv6", 0, 0, 0,
- inet6sw, &inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
- /*
- * FreeBSD's IPv4 replaces rn_inithead() with an IPv4-specific function.
- * Our IPv6 uses the ifa->ifa_rtrequest() function pointer to intercept
- * rtrequest()s. The consequence of this is that we use the generic
- * rn_inithead().
- */
- rn_inithead, 64, sizeof(struct sockaddr_in6)
-};
-#else /* !__FreeBSD__ */
-struct domain inet6domain =
-{
- PF_INET6, "IPv6", 0, 0, 0,
- inet6sw, &inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
- /*
- * FreeBSD's IPv4 replaces rn_inithead() with an IPv4-specific function.
- * Our IPv6 uses the ifa->ifa_rtrequest() function pointer to intercept
- * rtrequest()s. The consequence of this is that we use the generic
- * rn_inithead().
- */
- rn_inithead, 64, sizeof(struct sockaddr_in6)
-};
+ { AF_INET6, "internet6", 0, 0, 0,
+ (struct protosw *)inet6sw,
+ (struct protosw *)&inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0,
+#ifdef __FreeBSD__
+ in6_inithead,
+#else
+ rn_inithead,
+#endif
+ offsetof(struct sockaddr_in6, sin6_addr) << 3,
+ sizeof(struct sockaddr_in6) };
+#ifdef __FreeBSD__
DOMAIN_SET(inet6);
-#endif /* !__FreeBSD__ */
+#endif
-/* Eventually, make these go away -- if you want to be a router, twiddle the
- sysctls before bringing up your interfaces */
+/*
+ * 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 = 200;
+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;
+#if NGIF > 0
+int ip6_gif_hlim = GIF_HLIM;
+#else
+int ip6_gif_hlim = 0;
+#endif
+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. */
+#ifdef MAPPED_ADDR_ENABLED
+int ip6_mapped_addr_on = 1;
+#endif /* MAPPED_ADDR_ENABLED */
+
+u_int32_t ip6_id = 0UL;
+int ip6_keepfaith = 0;
+time_t ip6_log_time = (time_t)0L;
+
+/* icmp6 */
+#ifndef __bsdi__
+/*
+ * 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;
+#endif
+
+/* 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 */
+u_int icmp6errratelim = 1000; /* 1000usec = 1msec */
+
+#ifdef TCP6
+/* TCP on IP6 parameters */
+int tcp6_sendspace = 1024 * 8;
+int tcp6_recvspace = 1024 * 8;
+int tcp6_mssdflt = TCP6_MSS;
+int tcp6_rttdflt = TCP6TV_SRTTDFLT / PR_SLOWHZ;
+int tcp6_do_rfc1323 = 1;
+int tcp6_conntimeo = TCP6TV_KEEP_INIT; /* initial connection timeout */
+int tcp6_43maxseg = 0;
+int tcp6_pmtu = 0;
-#ifndef IPV6FORWARDING
-#ifdef IPV6GATEWAY
-#define IPV6FORWARDING 1
+/*
+ * Parameters for keepalive option.
+ * Connections for which SO_KEEPALIVE is set will be probed
+ * after being idle for a time of tcp6_keepidle (in units of PR_SLOWHZ).
+ * Starting at that time, the connection is probed at intervals
+ * of tcp6_keepintvl (same units) until a response is received
+ * or until tcp6_keepcnt probes have been made, at which time
+ * the connection is dropped. Note that a tcp6_keepidle value
+ * under 2 hours is nonconformant with RFC-1122, Internet Host Requirements.
+ */
+int tcp6_keepidle = TCP6TV_KEEP_IDLE; /* time before probing idle */
+int tcp6_keepintvl = TCP6TV_KEEPINTVL; /* interval betwn idle probes */
+int tcp6_keepcnt = TCP6TV_KEEPCNT; /* max idle probes */
+int tcp6_maxpersistidle = TCP6TV_KEEP_IDLE; /* max idle time in persist */
+
+#ifndef INET_SERVER
+#define TCP6_LISTEN_HASH_SIZE 17
+#define TCP6_CONN_HASH_SIZE 97
+#define TCP6_SYN_HASH_SIZE 293
+#define TCP6_SYN_BUCKET_SIZE 35
#else
-#define IPV6FORWARDING 0
-#endif /* IPV6GATEWAY */
-#endif /* IPV6FORWARDING */
-
-#ifndef IPV6RSOLICIT
-#if IPV6FORWARDING
-#define IPV6RSOLICIT 0
-#else /* IPV6FORWARDING */
-#define IPV6RSOLICIT 1
-#endif /* IPV6FORWARDING */
-#endif /* IPV6RSOLICIT */
-
-#ifndef IFQMAXLEN
-#define IFQMAXLEN IFQ_MAXLEN
-#endif
-
-int ipv6forwarding = IPV6FORWARDING;
-int ipv6rsolicit = IPV6RSOLICIT;
-int ipv6_defhoplmt = MAXHOPLIMIT;
-int ipv6qmaxlen = IFQMAXLEN;
-
-#if __FreeBSD__
-SYSCTL_NODE(_net_inet, IPPROTO_IPV6, ipv6, CTLFLAG_RW, 0, "IPV6");
-SYSCTL_NODE(_net_inet, IPPROTO_ICMPV6, icmpv6, CTLFLAG_RW, 0, "ICMPV6");
+#define TCP6_LISTEN_HASH_SIZE 97
+#define TCP6_CONN_HASH_SIZE 9973
+#define TCP6_SYN_HASH_SIZE 997
+#define TCP6_SYN_BUCKET_SIZE 35
+#endif
+int tcp6_listen_hash_size = TCP6_LISTEN_HASH_SIZE;
+int tcp6_conn_hash_size = TCP6_CONN_HASH_SIZE;
+struct tcp6_hash_list tcp6_listen_hash[TCP6_LISTEN_HASH_SIZE],
+ tcp6_conn_hash[TCP6_CONN_HASH_SIZE];
+
+int tcp6_syn_cache_size = TCP6_SYN_HASH_SIZE;
+int tcp6_syn_cache_limit = TCP6_SYN_HASH_SIZE*TCP6_SYN_BUCKET_SIZE;
+int tcp6_syn_bucket_limit = 3*TCP6_SYN_BUCKET_SIZE;
+struct syn_cache_head6 tcp6_syn_cache[TCP6_SYN_HASH_SIZE];
+struct syn_cache_head6 *tcp6_syn_cache_first;
+int tcp6_syn_cache_interval = 8; /* runs timer every 4 seconds */
+int tcp6_syn_cache_timeo = TCP6TV_KEEP_INIT;
+
+/*
+ * Parameters for computing a desirable data segment size
+ * given an upper bound (either interface MTU, or peer's MSS option)_.
+ * As applications tend to use a buffer size that is a multiple
+ * of kilobytes, try for something that divides evenly. However,
+ * do not round down too much.
+ *
+ * Round segment size down to a multiple of TCP6_ROUNDSIZE if this
+ * does not result in lowering by more than (size/TCP6_ROUNDFRAC).
+ * For example, round 536 to 512. Older versions of the system
+ * effectively used MCLBYTES (1K or 2K) as TCP6_ROUNDSIZE, with
+ * a value of 1 for TCP6_ROUNDFRAC (eliminating its effect).
+ * We round to a multiple of 256 for SLIP.
+ */
+#ifndef TCP6_ROUNDSIZE
+#define TCP6_ROUNDSIZE 256 /* round to multiple of 256 */
+#endif
+#ifndef TCP6_ROUNDFRAC
+#define TCP6_ROUNDFRAC 10 /* round down at most N/10, or 10% */
+#endif
+
+int tcp6_roundsize = TCP6_ROUNDSIZE;
+int tcp6_roundfrac = TCP6_ROUNDFRAC;
+#endif /*TCP6*/
+
+/* UDP on IP6 parameters */
+int udp6_sendspace = 9216; /* really max datagram size */
+int udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6));
+ /* 40 1K datagrams */
+
+#ifdef __FreeBSD__
+/*
+ * 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_forwarding SYSCTL_HANDLER_ARGS
+{
+ int error = 0;
+ int old_ip6_forwarding;
+ int changed;
+
+ error = SYSCTL_OUT(req, arg1, sizeof(int));
+ if (error || !req->newptr)
+ return (error);
+ old_ip6_forwarding = ip6_forwarding;
+ error = SYSCTL_IN(req, arg1, sizeof(int));
+ if (error != 0)
+ return (error);
+ changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0);
+ if (changed == 0)
+ return (error);
+ if (ip6_forwarding != 0) { /* host becomes router */
+ int s = splnet();
+ struct nd_prefix *pr, *next;
+
+ for (pr = nd_prefix.lh_first; pr; pr = next) {
+ next = pr->ndpr_next;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ prelist_remove(pr);
+ }
+ splx(s);
+ } else { /* router becomes host */
+ struct socket so;
+
+ /* XXX: init dummy so */
+ bzero(&so, sizeof(so));
+ while(!LIST_EMPTY(&rr_prefix))
+ delete_each_prefix(&so, LIST_FIRST(&rr_prefix),
+ PR_ORIG_KERNEL);
+ }
+
+ return (error);
+}
+
+SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding,
+ CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding,
+ "I", "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS,
+ redirect, CTLFLAG_RW, &ip6_sendredirects, 0, "");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM,
+ hlim, CTLFLAG_RW, &ip6_defhlim, 0, "");
+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, "");
+#ifdef MAPPED_ADDR_ENABLED
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR,
+ mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, "");
+#endif /* MAPPED_ADDR_ENABLED */
+
+/* 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_ERRRATELIMIT,
+ errratelimit, CTLFLAG_RW, &icmp6errratelim, 0, "");
+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_ND6_PROXYALL,
+ nd6_proxyall, CTLFLAG_RW, &nd6_proxyall, 0, "");
+
+#if __FreeBSD__ < 3
+/* net.inet6.udp6 */
+SYSCTL_INT(_net_inet6_udp6, UDP6CTL_SENDMAX,
+ sendmax, CTLFLAG_RW, &udp6_sendspace, 0, "");
+SYSCTL_INT(_net_inet6_udp6, UDP6CTL_RECVSPACE,
+ recvspace, CTLFLAG_RW, &udp6_recvspace, 0, "");
+
+/* net.inet6.tcp6 */
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_MSSDFLT,
+ mssdflt, CTLFLAG_RW, &tcp6_mssdflt, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_DO_RFC1323,
+ do_rfc1323, CTLFLAG_RW, &tcp6_do_rfc1323, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_KEEPIDLE,
+ keepidle, CTLFLAG_RW, &tcp6_keepidle, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_KEEPINTVL,
+ keepintvl, CTLFLAG_RW, &tcp6_keepintvl, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_KEEPCNT,
+ keepcnt, CTLFLAG_RW, &tcp6_keepcnt, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_MAXPERSISTIDLE,
+ maxpersistidle, CTLFLAG_RW, &tcp6_maxpersistidle, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_SENDSPACE,
+ sendspace, CTLFLAG_RW, &tcp6_sendspace, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_RECVSPACE,
+ recvspace, CTLFLAG_RW, &tcp6_recvspace, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_CONNTIMEO,
+ conntimeo, CTLFLAG_RW, &tcp6_conntimeo, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_PMTU,
+ pmtu, CTLFLAG_RW, &tcp6_pmtu, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_PMTU_EXPIRE,
+ pmtu_expire, CTLFLAG_RW, &pmtu_expire, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_PMTU_PROBE,
+ pmtu_probe, CTLFLAG_RW, &pmtu_probe, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_43MAXSEG,
+ pmtu_43maxseg, CTLFLAG_RW, &tcp6_43maxseg, 0, "");
+SYSCTL_STRUCT(_net_inet6_tcp6, TCP6CTL_STATS, stats, CTLFLAG_RD,
+ &tcp6stat, tcp6stat, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_SYN_CACHE_LIMIT,
+ syn_cache_limit, CTLFLAG_RW, &tcp6_syn_cache_limit, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_SYN_BUCKET_LIMIT,
+ syn_bucket_limit, CTLFLAG_RW, &tcp6_syn_bucket_limit, 0, "");
+SYSCTL_INT(_net_inet6_tcp6, TCP6CTL_SYN_CACHE_INTER,
+ syn_cache_interval, CTLFLAG_RW, &tcp6_syn_cache_interval, 0, "");
+#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 3) */
+
#endif /* __FreeBSD__ */
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
new file mode 100644
index 00000000000..2f222592ca4
--- /dev/null
+++ b/sys/netinet6/in6_src.c
@@ -0,0 +1,346 @@
+/* $OpenBSD: in6_src.c,v 1.1 1999/12/08 06:50:21 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
+ */
+
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+
+#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/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#if 0
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#ifndef __bsdi__
+#include "loop.h"
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+extern struct ifnet loif[NLOOP];
+#endif
+
+/*
+ * Return an IPv6 address, which is the most appropriate for given
+ * destination and user specified options.
+ * If necessary, this function lookups the routing table and return
+ * an entry to the caller for later use.
+ */
+struct in6_addr *
+in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp)
+ struct sockaddr_in6 *dstsock;
+ struct ip6_pktopts *opts;
+ struct ip6_moptions *mopts;
+ struct route_in6 *ro;
+ struct in6_addr *laddr;
+ int *errorp;
+{
+ struct in6_addr *dst;
+ struct in6_ifaddr *ia6 = 0;
+ struct in6_pktinfo *pi = NULL;
+
+ dst = &dstsock->sin6_addr;
+ *errorp = 0;
+
+ /*
+ * If the source address is explicitly specified by the caller,
+ * use it.
+ */
+ if (opts && (pi = opts->ip6po_pktinfo) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr))
+ return(&pi->ipi6_addr);
+
+ /*
+ * If the source address is not specified but the socket(if any)
+ * is already bound, use the bound address.
+ */
+ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
+ return(laddr);
+
+ /*
+ * If the caller doesn't specify the source address but
+ * the outgoing interface, use an address associated with
+ * the interface.
+ */
+ if (pi && pi->ipi6_ifindex) {
+ /* XXX boundary check is assumed to be already done. */
+ ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a link-local unicast address or
+ * a multicast address, and if the outgoing interface is specified
+ * by the sin6_scope_id filed, use an address associated with the
+ * interface.
+ * XXX: We're now trying to define more specific semantics of
+ * sin6_scope_id field, so this part will be rewritten in
+ * the near future.
+ */
+ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) &&
+ dstsock->sin6_scope_id) {
+ /*
+ * I'm not sure if boundary check for scope_id is done
+ * somewhere...
+ */
+ if (dstsock->sin6_scope_id < 0 ||
+ if_index < dstsock->sin6_scope_id) {
+ *errorp = ENXIO; /* XXX: better error? */
+ return(0);
+ }
+ ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id],
+ dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+
+ /*
+ * If the destination address is a multicast address and
+ * the outgoing interface for the address is specified
+ * by the caller, use an address associated with the interface.
+ * There is a sanity check here; if the destination has node-local
+ * scope, the outgoing interfacde should be a loopback address.
+ * Even if the outgoing interface is not specified, we also
+ * choose a loopback interface as the outgoing interface.
+ */
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+ struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL;
+#ifdef __bsdi__
+#if _BSDI_VERSION >= 199802
+ extern struct ifnet *loifp;
+#else
+ extern struct ifnet loif;
+ struct ifnet *loifp = &loif;
+#endif
+#endif
+
+ if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) {
+#ifdef __bsdi__
+ ifp = loifp;
+#else
+ ifp = &loif[0];
+#endif
+ }
+
+ if (ifp) {
+ ia6 = in6_ifawithscope(ifp, dst);
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If the next hop address for the packet is specified
+ * by caller, use an address associated with the route
+ * to the next hop.
+ */
+ {
+ struct sockaddr_in6 *sin6_next;
+ struct rtentry *rt;
+
+ if (opts && opts->ip6po_nexthop) {
+ sin6_next = satosin6(opts->ip6po_nexthop);
+ rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL);
+ if (rt) {
+ ia6 = in6_ifawithscope(rt->rt_ifp, dst);
+ if (ia6 == 0)
+ ia6 = ifatoia6(rt->rt_ifa);
+ }
+ if (ia6 == 0) {
+ *errorp = EADDRNOTAVAIL;
+ return(0);
+ }
+ return(&satosin6(&ia6->ia_addr)->sin6_addr);
+ }
+ }
+
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ if (ro) {
+ if (ro->ro_rt &&
+ !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0) {
+ /* No route yet, so try to acquire one */
+ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
+ ro->ro_dst.sin6_family = AF_INET6;
+ ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro->ro_dst.sin6_addr = *dst;
+ if (IN6_IS_ADDR_MULTICAST(dst)) {
+#ifdef __FreeBSD__
+ ro->ro_rt = rtalloc1(&((struct route *)ro)
+ ->ro_dst, 0, 0UL);
+#endif /*__FreeBSD__*/
+#if defined(__bsdi__) || defined(__NetBSD__)
+ ro->ro_rt = rtalloc1(&((struct route *)ro)
+ ->ro_dst, 0);
+#endif /*__bsdi__*/
+ } else {
+#ifdef __FreeBSD__
+ rtcalloc((struct route *)ro);
+#else
+ rtalloc((struct route *)ro);
+#endif
+ }
+ }
+
+ /*
+ * 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 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(inp, ifp)
+ struct inpcb *inp;
+ struct ifnet *ifp;
+{
+ if (inp && inp->inp_hops >= 0)
+ return(inp->inp_hops);
+ else if (ifp)
+ return(nd_ifinfo[ifp->if_index].chlim);
+ else
+ return(ip6_defhlim);
+}
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 143b00a9ce1..9db4753fbd2 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,199 +1,646 @@
/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+ * 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 1
+#ifndef _NETINET6_IN6_VAR_H_
+#define _NETINET6_IN6_VAR_H_
/*
- * IPv6 interface request and alias request. Use sockaddr_in6 because
- * it is larger than sockaddr.
+ * 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.
*/
-struct inet6_ifreq
-{
-#if !__OpenBSD__ && !defined(IFNAMSIZ)
-#define IFNAMSIZ 16
-#endif /* !__OpenBSD__ && !defined(IFNAMSIZ) */
- char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
- union {
- struct sockaddr_in6 ifru_addr;
- struct sockaddr_in6 ifru_dstaddr;
- struct sockaddr_in6 ifru_broadaddr;
- short ifru_flags;
- int ifru_metric;
- caddr_t ifru_data;
- } ifr_ifru;
-#define ifr_addr ifr_ifru.ifru_addr /* address */
-#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */
-#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
-#define ifr_flags ifr_ifru.ifru_flags /* flags */
-#define ifr_metric ifr_ifru.ifru_metric /* metric */
-#define ifr_data ifr_ifru.ifru_data /* for use by interface */
- };
+/*
+ * 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: throuw 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 */
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ LIST_HEAD(in6_multihead, in6_multi) ia6_multiaddrs;
+ /* list of multicast addresses */
+#endif
+ int ia6_flags;
+
+ struct in6_addrlifetime ia6_lifetime; /* NULL = infty */
+ struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */
+};
+
+/*
+ * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12).
+ */
+struct in6_ifstat {
+ u_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 */
+};
/*
- * IPv6 interface "alias" request. Used to add interface addresses. This
- * may be needed to be expanded to pass down/up permanancy information, and
- * possibly deprecation lifetime values. (That is, if the kernel doesn't
- * compute that stuff itself.)
+ * 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;
-struct inet6_aliasreq
-{
- char ifra_name[IFNAMSIZ];
- struct sockaddr_in6 ifra_addr;
- struct sockaddr_in6 ifra_dstaddr;
-#define ifra_broadaddr ifra_dstaddr
- struct sockaddr_in6 ifra_mask;
+ /*
+ * 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;
};
-/* ioctl()'s for stuff with inet6_{aliasreq,ifreq} (gag!) */
+struct in6_ifreq {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr_in6 ifru_addr;
+ struct sockaddr_in6 ifru_dstaddr;
+ short 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;
+ } ifr_ifru;
+};
-#define SIOCDIFADDR_INET6 _IOW('i',25, struct inet6_ifreq) /* delete IF addr */
-#define SIOCAIFADDR_INET6 _IOW('i',26, struct inet6_aliasreq)/* add/chg IFalias */
-#define SIOCGIFADDR_INET6 _IOWR('i',33, struct inet6_ifreq) /* get ifnet address */
-#define SIOCGIFDSTADDR_INET6 _IOWR('i',34, struct inet6_ifreq) /* get dst address */
-#define SIOCSIFDSTADDR_INET6 _IOW('i', 14, struct inet6_ifreq) /* set dst address */
-#define SIOCGIFNETMASK_INET6 _IOWR('i',37, struct inet6_ifreq) /* get netmask */
+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;
+};
-#define SIOCVIFADDR_INET6 _IOW('i',69,struct inet6_ifreq) /* Verify IPv6 addr */
+/* prefix type macro */
+#define IN6_PREFIX_ND 1
+#define IN6_PREFIX_RR 2
/*
- * INET6 interface address. This might also serve as the prefix list,
- * with the help of the I6AF_PREFIX flag.
+ * 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_ifaddr
-{
- struct ifaddr i6a_ifa; /* protocol-independent info (32 bytes) */
-#define i6a_ifp i6a_ifa.ifa_ifp
-#if defined(_KERNEL) || defined(KERNEL)
-#define i6a_flags i6a_ifa.ifa_flags
-#endif /* defined(_KERNEL) || defined(KERNEL) */
-
- /* All sorts of INET6-specific junk, some of it, very similar to IP's
- in_ifaddr. */
-
- /* Put any subnetting, etc here. */
-
- struct in6_ifaddr *i6a_next;
- struct in6_ifnet *i6a_i6ifp; /* Pointer to IPv6 interface info */
- struct sockaddr_in6 i6a_addr; /* Address. */
- struct sockaddr_in6 i6a_dstaddr; /* Dest. if PPP link. */
- struct sockaddr_in6 i6a_sockmask; /* Netmask. This is IPv6, so
- there is no "subnet/net"
- distinction. */
-
- /*
- * IPv6 addresses have lifetimes. Put in expiration information in
- * here. A DEPRECATED address is still valid for inbound, but not for
- * outbound. An EXPIRED address is invalid for both inbound and outbound,
- * and should be put out of its misery (and our in6_ifaddr list) ASAP.
- */
- u_long i6a_preferred; /* Preferred lifetime. */
- u_long i6a_expire; /* Expiration time. */
- u_short i6a_preflen; /* Prefix length for link-locals
- (in bits). */
- u_short i6a_addrflags; /* Additional flags because
- ifa_flags isn't big enough. */
+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 I6A_SIN(i6a) (&(((struct in6_ifaddr *)(i6a))->i6a_addr))
-#define IS_EXPIRED(i6a) ((i6a->i6a_expire != 0 && \
- i6a->i6a_expire < time.tv_sec))
-#define IS_DEPRECATED(i6a) ((i6a)->i6a_addrflags & I6AF_DEPRECATED)
-
-#define I6AF_LINKLOC 0x1 /* Link-local address. Saves the IS_IN6_LINKLOC
- check. */
-#define I6AF_PERMANENT 0x2 /* Permanent address */
-#define I6AF_PREFIX 0x4 /* I am a, "prefix list entry," meaning that
- the portion of the address inside the mask
- is directly attached to the link. */
-#define I6AF_NOTSURE 0x8 /* I'm not sure if I'm allowed to be used yet.
- This is designed for use with addresses
- that haven't been verified as unique on a
- link yet. */
-#define I6AF_DEPRECATED 0x10 /* The use of this address should be discouraged.
- The address should not be used as a source
- address for new communications. The address
- is still valid for receiving packets. */
+#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
/*
- * IPv6 multicast structures and macros.
+ * 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)
+#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq)
+#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq)
+#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq)
+#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq)
+
+#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq)
+#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq)
+
+#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq)
+#define SIOCGIFPSRCADDR_IN6 _IOWR('i', 71, struct in6_ifreq)
+#define SIOCGIFPDSTADDR_IN6 _IOWR('i', 72, struct in6_ifreq)
+
+#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
+
+#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist)
+#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist)
+#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq)
+#define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq)
+#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo)
+#define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq)
+#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq)
+
+#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq)
+#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq)
+#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq)
+#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq)
+
+#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */
+#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */
+#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */
+#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */
+#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \
+ 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 */
-struct in6_multi
-{
- struct in6_multi *in6m_next; /* Ptr. to next one. */
- struct in6_addr in6m_addr; /* Multicast address. */
- struct ifnet *in6m_ifp; /* Pointer to interface. */
- struct in6_ifnet *in6m_i6ifp; /* Back ptr. to IPv6 if info. */
- uint in6m_refcount; /* Number of membership claims by
- sockets. */
- uint in6m_timer; /* IGMP membership report timer. */
+#define IN6_IFF_ANYCAST 0x01 /* anycast address */
+#define IN6_IFF_TENTATIVE 0x02 /* tentative address */
+#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */
+#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */
+#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */
+
+/* do not input/output */
+#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)
+
+#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 ifqueue ip6intrq; /* IP6 packet input queue */
+extern struct in6_addr zeroin6_addr;
+extern u_char inet6ctlerrmap[];
+extern unsigned long in6_maxmtu;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_IPMADDR);
+#endif /* MALLOC_DECLARE */
+#endif
+
+/*
+ * Macro for finding the internet address structure (in6_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+
+#define IFP_TO_IA6(ifp, ia) \
+/* struct ifnet *ifp; */ \
+/* struct in6_ifaddr *ia; */ \
+do { \
+ struct ifaddr *ifa; \
+ for (ifa = (ifp)->if_addrlist; ifa; ifa = ifa->ifa_next) { \
+ if (!ifa->ifa_addr) \
+ continue; \
+ if (ifa->ifa_addr->sa_family == AF_INET6) \
+ break; \
+ } \
+ (ia) = (struct in6_ifaddr *)ifa; \
+} while (0)
+
+#else
+
+#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 */
+
+#endif
+
+/*
+ * 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 */
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ struct in6_ifaddr *in6m_ia; /* back pointer to in6_ifaddr */
+#else
+ struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */
+#endif
+ u_int in6m_refcount; /* # membership claims by sockets */
+ u_int in6m_state; /* state of the membership */
+ u_int in6m_timer; /* MLD6 listener report timer */
};
-#if defined(_KERNEL) || defined(KERNEL)
-/* General case IN6 multicast lookup. Can be optimized out in certain
- places (like netinet6/ipv6_input.c ?). */
-
-#define IN6_LOOKUP_MULTI(addr,ifp,in6m) \
-{\
- register struct in6_ifnet *i6ifp;\
-\
- for (i6ifp=in6_ifnet; i6ifp != NULL && i6ifp->i6ifp_ifp != ifp;\
- i6ifp=i6ifp->i6ifp_next)\
- ;\
- if (i6ifp == NULL)\
- in6m=NULL;\
- else\
- for ((in6m) = i6ifp->i6ifp_multiaddrs;\
- (in6m) != NULL && !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr,(addr));\
- (in6m) = (in6m)->in6m_next) ;\
-}
-
-
-#define IN6_MCASTOPTS 0x2
-
-#define ETHER_MAP_IN6_MULTICAST(in6addr,enaddr) { \
- (enaddr)[0] = 0x33; \
- (enaddr)[1] = 0x33; \
- (enaddr)[2] = in6addr.s6_addr[12]; \
- (enaddr)[3] = in6addr.s6_addr[13]; \
- (enaddr)[4] = in6addr.s6_addr[14]; \
- (enaddr)[5] = in6addr.s6_addr[15]; \
- }
-
-#define IN6ADDR_ALLNODES_INIT {{{ 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}}
-#define IN6ADDR_ALLROUTERS_INIT {{{ 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 }}}
-#define IN6ADDR_ALLHOSTS_INIT {{{ 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3 }}}
-#define IN6ADDR_SN_PREFIX_INIT {{{ 0xff,2,0,0,0,0,0,0,0,0,0,1,0xff,0,0,0 }}}
-
-struct in6_ifnet
-{
- struct in6_ifnet *i6ifp_next; /* Next in list. */
- struct ifnet *i6ifp_ifp; /* Back pointer to actual interface. */
- struct in6_multi *i6ifp_multiaddrs; /* Multicast addresses for this
- interface. */
- uint i6ifp_numaddrs; /* Number of IPv6 addresses on this
- interface. */
-
- /* Addrconf and ND variables will go here. */
+#ifdef _KERNEL
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead;
+#endif
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of eht in6_multi records.
+ */
+struct in6_multistep {
+ struct in6_ifaddr *i_ia;
+ struct in6_multi *i_in6m;
};
-extern struct ifqueue ipv6intrq;
+/*
+ * 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.
+ */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+
+#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
+/* struct in6_addr addr; */ \
+/* struct ifnet *ifp; */ \
+/* struct in6_multi *in6m; */ \
+do { \
+ register struct ifmultiaddr *ifma; \
+ for (ifma = (ifp)->if_multiaddrs.lh_first; ifma; \
+ ifma = ifma->ifma_link.le_next) { \
+ 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)
+
+#else /* not FreeBSD3 */
+
+#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \
+/* struct in6_addr addr; */ \
+/* struct ifnet *ifp; */ \
+/* struct in6_multi *in6m; */ \
+do { \
+ register struct in6_ifaddr *ia; \
+ \
+ IFP_TO_IA6((ifp), ia); \
+ if (ia == NULL) \
+ (in6m) = NULL; \
+ else \
+ for ((in6m) = ia->ia6_multiaddrs.lh_first; \
+ (in6m) != NULL && \
+ !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &(addr)); \
+ (in6m) = in6m->in6m_entry.le_next) \
+ continue; \
+} 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 = (in6m)->in6m_entry.le_next; \
+ else \
+ while ((step).i_ia != NULL) { \
+ (in6m) = (step).i_ia->ia6_multiaddrs.lh_first; \
+ (step).i_ia = (step).i_ia->ia_next; \
+ if ((in6m) != NULL) { \
+ (step).i_in6m = (in6m)->in6m_entry.le_next; \
+ break; \
+ } \
+ } \
+} while (0)
+
+#define IN6_FIRST_MULTI(step, in6m) \
+/* struct in6_multistep step; */ \
+/* struct in6_multi *in6m */ \
+do { \
+ (step).i_ia = in6_ifaddr; \
+ (step).i_in6m = NULL; \
+ IN6_NEXT_MULTI((step), (in6m)); \
+} while (0)
+
+#endif /* not FreeBSD3 */
-int ipv6_discov_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *,
- struct sockaddr *, u_char *));
-#endif /* defined(_KERNEL) || defined(KERNEL) */
+int in6_ifinit __P((struct ifnet *,
+ struct in6_ifaddr *, struct sockaddr_in6 *, int));
+struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *,
+ int *));
+void in6_delmulti __P((struct in6_multi *));
+void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *));
+extern int in6_ifindex2scopeid __P((int));
+extern int in6_mask2len __P((struct in6_addr *));
+extern void in6_len2mask __P((struct in6_addr *, int));
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+int in6_control __P((struct socket *,
+ u_long, caddr_t, struct ifnet *, struct proc *));
+#else
+int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *));
+#endif
+void in6_savemkludge __P((struct in6_ifaddr *));
+void in6_setmaxmtu __P((void));
+void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *));
+struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *));
+struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *,
+ struct in6_addr *));
+char *ip6_sprintf __P((struct in6_addr *));
+int in6_matchlen __P((struct in6_addr *, struct in6_addr *));
+int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2,
+ int 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));
+#endif /* _KERNEL */
-#endif /* _NETINET6_IN6_VAR_H */
+#endif /* _NETINET6_IN6_VAR_H_ */
diff --git a/sys/netinet6/ip6.h b/sys/netinet6/ip6.h
new file mode 100644
index 00000000000..f7d70ea1731
--- /dev/null
+++ b/sys/netinet6/ip6.h
@@ -0,0 +1,287 @@
+/* $OpenBSD: ip6.h,v 1.1 1999/12/08 06:50:21 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.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET6_IPV6_H_
+#define _NETINET6_IPV6_H_
+
+/*
+ * Definition for internet protocol version 6.
+ * RFC 2460
+ */
+
+struct ip6_hdr {
+ union {
+ struct ip6_hdrctl {
+ u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */
+ u_int16_t ip6_un1_plen; /* payload length */
+ u_int8_t ip6_un1_nxt; /* next header */
+ u_int8_t ip6_un1_hlim; /* hop limit */
+ } ip6_un1;
+ u_int8_t ip6_un2_vfc; /* 4 bits version, 4 bits class */
+ } ip6_ctlun;
+ struct in6_addr ip6_src; /* source address */
+ struct in6_addr ip6_dst; /* destination address */
+};
+
+#define ip6_vfc ip6_ctlun.ip6_un2_vfc
+#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
+#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
+#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
+#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
+#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
+
+#define IPV6_VERSION 0x60
+#define IPV6_VERSION_MASK 0xf0
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */
+#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */
+#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */
+#endif /* LITTLE_ENDIAN */
+#endif
+#if 1
+/* ECN bits proposed by Sally Floyd */
+#define IP6TOS_CE 0x01 /* congestion experienced */
+#define IP6TOS_ECT 0x02 /* ECN-capable transport */
+#endif
+
+/*
+ * Extension Headers
+ */
+
+struct ip6_ext {
+ u_char ip6e_nxt;
+ u_char ip6e_len;
+};
+
+/* Hop-by-Hop options header */
+/* XXX should we pad it to force alignment on an 8-byte boundary? */
+struct ip6_hbh {
+ u_int8_t ip6h_nxt; /* next header */
+ u_int8_t ip6h_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* Destination options header */
+/* XXX should we pad it to force alignment on an 8-byte boundary? */
+struct ip6_dest {
+ u_int8_t ip6d_nxt; /* next header */
+ u_int8_t ip6d_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* Option types and related macros */
+#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
+#define IP6OPT_PADN 0x01 /* 00 0 00001 */
+#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
+#define IP6OPT_JUMBO_LEN 6
+#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */
+#define IP6OPT_RTALERT_LEN 4
+#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
+#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
+#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
+#define IP6OPT_MINLEN 2
+
+#define IP6OPT_TYPE(o) ((o) & 0xC0)
+#define IP6OPT_TYPE_SKIP 0x00
+#define IP6OPT_TYPE_DISCARD 0x40
+#define IP6OPT_TYPE_FORCEICMP 0x80
+#define IP6OPT_TYPE_ICMP 0xC0
+
+#define IP6OPT_MUTABLE 0x20
+
+/* Routing header */
+struct ip6_rthdr {
+ u_int8_t ip6r_nxt; /* next header */
+ u_int8_t ip6r_len; /* length in units of 8 octets */
+ u_int8_t ip6r_type; /* routing type */
+ u_int8_t ip6r_segleft; /* segments left */
+ /* followed by routing type specific data */
+};
+
+/* Type 0 Routing header */
+struct ip6_rthdr0 {
+ u_int8_t ip6r0_nxt; /* next header */
+ u_int8_t ip6r0_len; /* length in units of 8 octets */
+ u_int8_t ip6r0_type; /* always zero */
+ u_int8_t ip6r0_segleft; /* segments left */
+ u_int8_t ip6r0_reserved; /* reserved field */
+ u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */
+ struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */
+};
+
+/* Fragment header */
+struct ip6_frag {
+ u_int8_t ip6f_nxt; /* next header */
+ u_int8_t ip6f_reserved; /* reserved field */
+ u_int16_t ip6f_offlg; /* offset, reserved, and flag */
+ u_int32_t ip6f_ident; /* identification */
+};
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Internet implementation parameters.
+ */
+#define IPV6_MAXHLIM 255 /* maximun hoplimit */
+#define IPV6_DEFHLIM 64 /* default hlim */
+#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */
+#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */
+
+#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */
+#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/
+
+/*
+ * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the
+ * target header (including IPv6 itself, extension headers and
+ * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers
+ * to store incoming data into one internal mbuf or one or more external
+ * mbufs(never into two or more internal mbufs). Thus, the third case is
+ * supposed to never be matched but is prepared just in case.
+ */
+
+#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \
+do { \
+ if ((m)->m_next != NULL) { \
+ if (((m)->m_flags & M_LOOP) && \
+ ((m)->m_len < (off) + (hlen)) && \
+ (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ return ret; \
+ } else if ((m)->m_flags & M_EXT) { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ m_freem(m); \
+ return ret; \
+ } \
+ } else { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_exthdrtoolong++; \
+ m_freem(m); \
+ return ret; \
+ } \
+ } \
+ } \
+ else { \
+ if ((m)->m_len < (off) + (hlen)) { \
+ ip6stat.ip6s_tooshort++; \
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \
+ m_freem(m); \
+ return ret; \
+ } \
+ } \
+} while (0)
+
+#ifdef __NetBSD__
+/*
+ * IP6_EXTHDR_GET ensures that intermediate protocol header (from "off" to
+ * "len") is located in single mbuf, on contiguous memory region.
+ * The pointer to the region will be returned to pointer variable "val",
+ * with type "typ".
+ * IP6_EXTHDR_GET0 does the same, except that it aligns the structure at the
+ * very top of mbuf. GET0 is likely to make memory copy than GET.
+ *
+ * XXX we're now testing this, needs m_pulldown()
+ */
+#define IP6_EXTHDR_GET(val, typ, m, off, len) \
+do { \
+ struct mbuf *t; \
+ int tmp; \
+ t = m_pulldown((m), (off), (len), &tmp); \
+ if (t) { \
+ if (t->m_len < tmp + (len)) \
+ panic("m_pulldown malfunction"); \
+ (val) = (typ)(mtod(t, caddr_t) + tmp); \
+ } else \
+ (val) = (typ)NULL; \
+} while (0)
+
+#define IP6_EXTHDR_GET0(val, typ, m, off, len) \
+do { \
+ struct mbuf *t; \
+ t = m_pulldown((m), (off), (len), NULL); \
+ if (t) { \
+ if (t->m_len < (len)) \
+ panic("m_pulldown malfunction"); \
+ (val) = (typ)mtod(t, caddr_t); \
+ } else \
+ (val) = (typ)NULL; \
+} while (0)
+
+#endif /*NetBSD*/
+
+#endif /* not _NETINET_IPV6_H_ */
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
new file mode 100644
index 00000000000..df8b3879648
--- /dev/null
+++ b/sys/netinet6/ip6_forward.c
@@ -0,0 +1,450 @@
+/* $OpenBSD: ip6_forward.c,v 1.1 1999/12/08 06:50:21 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.
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#endif
+
+#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 <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/nd6.h>
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
+#undef IPSEC
+#endif
+
+#ifdef IPSEC_IPV6FWD
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif /* IPSEC_IPV6FWD */
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#include <net/net_osdep.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 *);
+ register struct sockaddr_in6 *dst;
+ register struct rtentry *rt;
+ int error, type = 0, code = 0;
+ struct mbuf *mcopy = NULL;
+#ifdef IPSEC_IPV6FWD
+ struct secpolicy *sp = NULL;
+#endif
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+#ifdef IPSEC_IPV6FWD
+ /*
+ * Check AH/ESP integrity.
+ */
+ /*
+ * Don't increment ip6s_cantforward because this is the check
+ * before forwarding packet actually.
+ */
+ if (ipsec6_in_reject(m, NULL)) {
+ ipsec6stat.in_polvio++;
+ m_freem(m);
+ return;
+ }
+#endif /*IPSEC_IPV6FWD*/
+
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
+ ip6stat.ip6s_cantforward++;
+ ip6stat.ip6s_badscope++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
+ if (ip6_log_time + ip6_log_interval < time_second) {
+ char addr[INET6_ADDRSTRLEN];
+ ip6_log_time = time_second;
+ strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr));
+ log(LOG_DEBUG,
+ "cannot forward "
+ "from %s to %s nxt %d received on %s\n",
+ addr, ip6_sprintf(&ip6->ip6_dst),
+ ip6->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_IPV6FWD
+ /* get a security policy for this packet */
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ ip6stat.ip6s_cantforward++;
+ 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;
+#if 0 /* XXX allocate a route (ro, dst) again later */
+ ro = (struct route_in6 *)state.ro;
+ dst = (struct sockaddr_in6 *)state.dst;
+#endif
+ key_freesp(sp);
+
+ if (error) {
+ /* 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_IPV6FWD */
+
+ dst = &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 */
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&ip6_forward_rt,
+ RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&ip6_forward_rt);
+#endif
+ }
+
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, 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;
+
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&ip6_forward_rt);
+#endif
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_noroute++;
+ /* XXX in6_ifstat_inc(rt->rt_ifp, 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;
+ if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
+ in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
+ if (mcopy) {
+ /*
+ * XXX
+ * When we do IPsec tunnel ingress, we need to play
+ * with if_mtu value (decrement IPsec header size
+ * from mtu value). see ip_input().
+ */
+ icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,
+ rt->rt_ifp->if_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)
+ type = ND_REDIRECT;
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (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;
+ }
+#endif
+
+#ifdef OLDIP6OUTPUT
+ error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m,
+ (struct sockaddr *)dst,
+ ip6_forward_rt.ro_rt);
+#else
+ error = nd6_output(rt->rt_ifp, m, dst, rt);
+#endif
+ 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_input.c b/sys/netinet6/ip6_input.c
new file mode 100644
index 00000000000..9573ae2def9
--- /dev/null
+++ b/sys/netinet6/ip6_input.c
@@ -0,0 +1,1320 @@
+/* $OpenBSD: ip6_input.c,v 1.1 1999/12/08 06:50:21 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_input.c 8.2 (Berkeley) 1/4/94
+ */
+
+#ifdef __FreeBSD__
+#include "opt_ip6fw.h"
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#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>
+#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
+#include <sys/proc.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifdef INET
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#endif /*INET*/
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet/in_pcb.h>
+#endif
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#if !((defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802))
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/in6_ifattach.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#include <netinet6/ip6protosw.h>
+
+/* we need it for NLOOP. */
+#ifndef __bsdi__
+#include "loop.h"
+#endif
+#include "faith.h"
+
+#include "gif.h"
+#include "bpfilter.h"
+
+#include <net/net_osdep.h>
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
+#undef IPSEC
+#endif
+
+extern struct domain inet6domain;
+extern struct ip6protosw inet6sw[];
+#ifdef __bsdi__
+#if _BSDI_VERSION < 199802
+extern struct ifnet loif;
+#else
+extern struct ifnet *loifp;
+#endif
+#endif
+
+u_char ip6_protox[IPPROTO_MAX];
+static int ip6qmaxlen = IFQ_MAXLEN;
+struct in6_ifaddr *in6_ifaddr;
+struct ifqueue ip6intrq;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+extern struct ifnet loif[NLOOP];
+#endif
+int ip6_forward_srcrt; /* XXX */
+int ip6_sourcecheck; /* XXX */
+int ip6_sourcecheck_interval; /* XXX */
+
+#ifdef IPV6FIREWALL
+/* firewall hooks */
+ip6_fw_chk_t *ip6_fw_chk_ptr;
+ip6_fw_ctl_t *ip6_fw_ctl_ptr;
+#endif
+
+struct ip6stat ip6stat;
+
+static void ip6_init2 __P((void *));
+
+static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *));
+
+#if defined(PTR)
+extern int ip6_protocol_tr;
+
+int ptr_in6 __P((struct mbuf *, struct mbuf **));
+extern void ip_forward __P((struct mbuf *, 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()
+{
+ register struct ip6protosw *pr;
+ register int i;
+ struct timeval tv;
+
+ 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;
+ nd6_init();
+ frag6_init();
+#ifdef IPV6FIREWALL
+ ip6_fw_init();
+#endif
+ /*
+ * 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;
+
+#ifndef __FreeBSD__
+ ip6_init2((void *)0);
+#endif
+}
+
+static void
+ip6_init2(dummy)
+ void *dummy;
+{
+#ifndef __bsdi__
+ int i;
+#endif
+ int ret;
+#if defined(__bsdi__) && _BSDI_VERSION < 199802
+ struct ifnet *loifp = &loif;
+#endif
+
+ /* get EUI64 from somewhere */
+ ret = in6_ifattach_getifid(NULL);
+
+ /*
+ * to route local address of p2p link to loopback,
+ * assign loopback address first.
+ */
+#ifdef __bsdi__
+ in6_ifattach(loifp, IN6_IFT_LOOP, NULL, 0);
+#else
+ for (i = 0; i < NLOOP; i++)
+ in6_ifattach(&loif[i], IN6_IFT_LOOP, NULL, 0);
+#endif
+
+ /* nd6_timer_init */
+ timeout(nd6_timer, (caddr_t)0, hz);
+ /* router renumbering prefix list maintenance */
+ timeout(in6_rr_timer, (caddr_t)0, hz);
+}
+
+#ifdef __FreeBSD__
+/* cheat */
+SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip6_init2, NULL);
+#endif
+
+/*
+ * IP6 input interrupt handling. Just pass the packet to ip6_input.
+ */
+void
+ip6intr()
+{
+ int s;
+ struct mbuf *m;
+
+ for (;;) {
+ s = splimp();
+ IF_DEQUEUE(&ip6intrq, m);
+ splx(s);
+ if (m == 0)
+ return;
+ ip6_input(m);
+ }
+}
+
+#ifdef __FreeBSD__
+NETISR_SET(NETISR_IPV6, ip6intr);
+#endif
+
+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;
+#if defined(__bsdi__) && _BSDI_VERSION < 199802
+ struct ifnet *loifp = &loif;
+#endif
+
+#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
+
+ /*
+ * mbuf statistics by kazu
+ */
+ if (m->m_flags & M_EXT) {
+ if (m->m_next)
+ ip6stat.ip6s_mext2m++;
+ else
+ ip6stat.ip6s_mext1++;
+ } else {
+ if (m->m_next) {
+ if (m->m_flags & M_LOOP) {
+#ifdef __bsdi__
+ ip6stat.ip6s_m2m[loifp->if_index]++; /*XXX*/
+#else
+ ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/
+#endif
+ }
+ else if (m->m_pkthdr.rcvif->if_index <= 31)
+ ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++;
+ else
+ ip6stat.ip6s_m2m[0]++;
+ } else
+ ip6stat.ip6s_m1++;
+ }
+
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive);
+ ip6stat.ip6s_total++;
+
+#ifndef PULLDOWN_TEST
+ /* XXX is the line really necessary? */
+ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/);
+#endif
+
+ if (m->m_len < sizeof(struct ip6_hdr)) {
+ struct ifnet *inifp;
+ 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;
+ }
+
+ ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (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;
+ }
+#endif
+
+ /*
+ * Scope check
+ */
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) ||
+ IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) {
+ 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)) {
+ if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ } else {
+ ip6stat.ip6s_badscope++;
+ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+ goto bad;
+ }
+ }
+
+ if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) {
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ }
+ } else {
+ 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 defined(PTR)
+ /*
+ *
+ */
+ if (ip6_protocol_tr)
+ {
+ struct mbuf *m1 = NULL;
+
+ switch (ptr_in6(m, &m1))
+ {
+ case IPPROTO_IP: goto mcastcheck;
+ case IPPROTO_IPV4: ip_forward(m1, 0); break;
+ case IPPROTO_IPV6: ip6_forward(m1, 0); break;
+ case IPPROTO_MAX: /* discard this packet */
+ default:
+ }
+
+ if (m != m1)
+ m_freem(m);
+
+ return;
+ }
+
+ mcastcheck:
+#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
+ */
+ if (ip6_forward_rt.ro_rt == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
+ &ip6_forward_rt.ro_dst.sin6_addr)) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6));
+ ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ip6_forward_rt.ro_dst.sin6_family = AF_INET6;
+ ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst;
+
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&ip6_forward_rt);
+#endif
+ }
+
+#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.
+ */
+ if (ip6_forward_rt.ro_rt &&
+ (ip6_forward_rt.ro_rt->rt_flags &
+ (RTF_HOST|RTF_GATEWAY)) == RTF_HOST &&
+#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;
+ /* packet to tentative address must not be received */
+ if (ia6->ia6_flags & IN6_IFF_ANYCAST)
+ m->m_flags |= M_ANYCAST6;
+ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) {
+ /* this interface is ready */
+ ours = 1;
+ deliverifp = ia6->ia_ifp; /* correct? */
+ goto hbhcheck;
+ } else {
+ /* this interface is not ready, fall through */
+ }
+ }
+
+ /*
+ * FAITH(Firewall Aided Internet Translator)
+ */
+#if defined(NFAITH) && 0 < NFAITH
+ 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;
+ }
+ }
+#endif
+
+#if 0
+ {
+ /*
+ * Last resort: check in6_ifaddr for incoming interface.
+ * The code is here until I update the "goto ours hack" code above
+ * working right.
+ */
+ struct ifaddr *ifa;
+ for (ifa = m->m_pkthdr.rcvif->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_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ip6->ip6_dst)) {
+ ours = 1;
+ deliverifp = ifa->ifa_ifp;
+ goto hbhcheck;
+ }
+ }
+ }
+#endif
+
+ /*
+ * 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:
+ /*
+ * 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 *);
+#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;
+ }
+
+ /*
+ * Tell launch routine the next header
+ */
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS && deliverifp != NULL) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
+ if (ia6)
+ ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len;
+ }
+#endif
+ 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;
+ }
+
+ nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
+ }
+ return;
+ bad:
+ m_freem(m);
+}
+
+/*
+ * 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;
+{
+ register 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().
+ */
+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;
+
+ for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) {
+ switch(*opt) {
+ case IP6OPT_PAD1:
+ optlen = 1;
+ break;
+ case IP6OPT_PADN:
+ if (hbhlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ optlen = *(opt + 1) + 2;
+ break;
+ case IP6OPT_RTALERT:
+ /* XXX may need check for alignment */
+ if (hbhlen < IP6OPT_RTALERT_LEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2)
+ /* XXX: should we discard the packet? */
+ log(LOG_ERR, "length of router alert opt is inconsitent(%d)",
+ *(opt + 1));
+ optlen = IP6OPT_RTALERT_LEN;
+ bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2);
+ *rtalertp = ntohs(rtalert_val);
+ break;
+ case IP6OPT_JUMBO:
+ /* XXX may need check for alignment */
+ if (hbhlen < IP6OPT_JUMBO_LEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2)
+ /* XXX: should we discard the packet? */
+ log(LOG_ERR, "length of jumbopayload opt "
+ "is inconsistent(%d)",
+ *(opt + 1));
+ optlen = IP6OPT_JUMBO_LEN;
+
+ /*
+ * We can simply cast because of the alignment
+ * requirement of the jumbo payload option.
+ */
+#if 0
+ *plenp = ntohl(*(u_int32_t *)(opt + 2));
+#else
+ bcopy(opt + 2, plenp, sizeof(*plenp));
+ *plenp = htonl(*plenp);
+#endif
+ if (*plenp <= IPV6_MAXPACKET) {
+ /*
+ * jumbo payload length must be larger
+ * than 65535
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ sizeof(struct ip6_hdr) +
+ sizeof(struct ip6_hbh) +
+ opt + 2 - opthead);
+ return(-1);
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (ip6->ip6_plen) {
+ /*
+ * IPv6 packets that have non 0 payload length
+ * must not contain a jumbo paylod option.
+ */
+ ip6stat.ip6s_badoptions++;
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ sizeof(struct ip6_hdr) +
+ sizeof(struct ip6_hbh) +
+ opt - opthead);
+ return(-1);
+ }
+ break;
+ default: /* unknown option */
+ if (hbhlen < IP6OPT_MINLEN) {
+ ip6stat.ip6s_toosmall++;
+ goto bad;
+ }
+ if ((optlen = ip6_unknown_opt(opt, m,
+ sizeof(struct ip6_hdr) +
+ sizeof(struct ip6_hbh) +
+ opt - opthead)) == -1)
+ return(-1);
+ optlen += 2;
+ break;
+ }
+ }
+
+ 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 routine will be called from upper layer handlers like tcp6_input().
+ * Thus the routine assumes that the caller (tcp6_input) have already
+ * called IP6_EXTHDR_CHECK() and all the extension headers are located in the
+ * very first mbuf on the mbuf chain.
+ * We may want to add some infinite loop prevention or sanity checks for safety.
+ * (This applies only when you are using KAME mbuf chain restriction, i.e.
+ * you are using IP6_EXTHDR_CHECK() not m_pulldown())
+ */
+void
+ip6_savecontrol(in6p, mp, ip6, m)
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(HAVE_NRL_INPCB)
+ register struct inpcb *in6p;
+#else
+ register struct in6pcb *in6p;
+#endif
+ register struct mbuf **mp;
+ register struct ip6_hdr *ip6;
+ register struct mbuf *m;
+{
+#ifdef HAVE_NRL_INPCB
+# define in6p_flags inp_flags
+#endif
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ struct proc *p = curproc; /* XXX */
+#endif
+#ifdef __bsdi__
+# define sbcreatecontrol so_cmsg
+#endif
+ int privileged;
+
+ privileged = 0;
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (p && !suser(p->p_ucred, &p->p_acflag))
+ privileged++;
+#else
+#ifdef HAVE_NRL_INPCB
+ if ((in6p->inp_socket->so_state & SS_PRIV) != 0)
+ privileged++;
+#else
+ if ((in6p->in6p_socket->so_state & SS_PRIV) != 0)
+ privileged++;
+#endif
+#endif
+
+#ifdef SO_TIMESTAMP
+ if (in6p->in6p_socket->so_options & SO_TIMESTAMP) {
+ struct timeval tv;
+
+ microtime(&tv);
+ *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
+ SCM_TIMESTAMP, SOL_SOCKET);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#endif
+ if (in6p->in6p_flags & IN6P_RECVDSTADDR) {
+ *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst,
+ sizeof(struct in6_addr), IPV6_RECVDSTADDR,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+
+#ifdef noyet
+ /* options were tossed above */
+ if (in6p->in6p_flags & IN6P_RECVOPTS)
+ /* broken */
+ /* ip6_srcroute doesn't do what we want here, need to fix */
+ if (in6p->in6p_flags & IPV6P_RECVRETOPTS)
+ /* broken */
+#endif
+
+ /* RFC 2292 sec. 5 */
+ if (in6p->in6p_flags & IN6P_PKTINFO) {
+ 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) {
+ int hlim = ip6->ip6_hlim & 0xff;
+ *mp = sbcreatecontrol((caddr_t) &hlim,
+ sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ /* IN6P_NEXTHOP - for outgoing packet only */
+
+ /*
+ * IPV6_HOPOPTS socket option. We require super-user privilege
+ * 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) && 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;
+
+#ifndef PULLDOWN_TEST
+ hbh = (struct ip6_hbh *)(ip6 + 1);
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+#else
+ IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m,
+ sizeof(struct ip6_hdr), sizeof(struct ip6_hbh));
+ if (hbh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+ hbhlen = (hbh->ip6h_len + 1) << 3;
+ IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m,
+ sizeof(struct ip6_hdr), hbhlen);
+ if (hbh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+#endif
+
+ /*
+ * XXX: We copy whole the header even if a jumbo
+ * payload option is included, which option is to
+ * be removed before returning in the RFC 2292.
+ * But it's too painful operation...
+ */
+ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen,
+ IPV6_HOPOPTS, IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ }
+
+ /* IPV6_DSTOPTS and IPV6_RTHDR socket options */
+ if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) {
+ 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;
+ int elen;
+
+#ifndef PULLDOWN_TEST
+ ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
+ if (nxt == IPPROTO_AH)
+ elen = (ip6e->ip6e_len + 2) << 2;
+ else
+ elen = (ip6e->ip6e_len + 1) << 3;
+#else
+ IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off,
+ sizeof(struct ip6_ext));
+ if (ip6e == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+ if (nxt == IPPROTO_AH)
+ elen = (ip6e->ip6e_len + 2) << 2;
+ else
+ elen = (ip6e->ip6e_len + 1) << 3;
+ IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen);
+ if (ip6e == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return;
+ }
+#endif
+
+ switch(nxt) {
+ case IPPROTO_DSTOPTS:
+ if (!in6p->in6p_flags & IN6P_DSTOPTS)
+ break;
+
+ /*
+ * We also require super-user privilege for
+ * the option.
+ * See the comments on IN6_HOPOPTS.
+ */
+ if (!privileged)
+ break;
+
+ *mp = sbcreatecontrol((caddr_t)ip6e, elen,
+ IPV6_DSTOPTS,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
+
+ case IPPROTO_ROUTING:
+ if (!in6p->in6p_flags & IN6P_RTHDR)
+ break;
+
+ *mp = sbcreatecontrol((caddr_t)ip6e, elen,
+ IPV6_RTHDR,
+ IPPROTO_IPV6);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ break;
+
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_ICMPV6:
+ default:
+ /*
+ * stop search if we encounter an upper
+ * layer protocol headers.
+ */
+ goto loopend;
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_AH: /* is it possible? */
+ break;
+ }
+
+ /* proceed with the next header. */
+ off += elen;
+ nxt = ip6e->ip6e_nxt;
+ }
+ loopend:
+ }
+ if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) {
+ /* to be done */
+ }
+ if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) {
+ /* to be done */
+ }
+ /* IN6P_RTHDR - to be done */
+
+#ifdef __bsdi__
+# undef sbcreatecontrol
+#endif
+#ifdef __OpenBSD__
+# undef in6p_flags
+#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;
+ }
+}
+
+/*
+ * 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
+};
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+int
+ip6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return ENOTDIR;
+
+ switch (name[0]) {
+
+ case IPV6CTL_FORWARDING:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_forwarding);
+ case IPV6CTL_SENDREDIRECTS:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_sendredirects);
+ case IPV6CTL_DEFHLIM:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_defhlim);
+ case IPV6CTL_MAXFRAGPACKETS:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_maxfragpackets);
+ case IPV6CTL_ACCEPT_RTADV:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_accept_rtadv);
+ case IPV6CTL_KEEPFAITH:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_keepfaith);
+ case IPV6CTL_LOG_INTERVAL:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_log_interval);
+ case IPV6CTL_HDRNESTLIMIT:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_hdrnestlimit);
+ case IPV6CTL_DAD_COUNT:
+ return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_dad_count);
+ case IPV6CTL_AUTO_FLOWLABEL:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_auto_flowlabel);
+ case IPV6CTL_DEFMCASTHLIM:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_defmcasthlim);
+ case IPV6CTL_GIF_HLIM:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_gif_hlim);
+ case IPV6CTL_KAME_VERSION:
+ return sysctl_rdstring(oldp, oldlenp, newp, __KAME_VERSION);
+ case IPV6CTL_USE_DEPRECATED:
+ return sysctl_int(oldp, oldlenp, newp, newlen,
+ &ip6_use_deprecated);
+ default:
+ return EOPNOTSUPP;
+ }
+ /* NOTREACHED */
+}
+#endif /* __NetBSD__ || __OpenBSD__ */
+
+#ifdef __bsdi__
+int *ip6_sysvars[] = IPV6CTL_VARS;
+
+int
+ip6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ if (name[0] >= IPV6CTL_MAXID)
+ return (EOPNOTSUPP);
+
+ switch (name[0]) {
+ case IPV6CTL_STATS:
+ return sysctl_rdtrunc(oldp, oldlenp, newp, &ip6stat,
+ sizeof(ip6stat));
+ case IPV6CTL_KAME_VERSION:
+ return sysctl_rdstring(oldp, oldlenp, newp, __KAME_VERSION);
+ default:
+ return (sysctl_int_arr(ip6_sysvars, name, namelen,
+ oldp, oldlenp, newp, newlen));
+ }
+}
+#endif /* __bsdi__ */
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
new file mode 100644
index 00000000000..7790c0aff9d
--- /dev/null
+++ b/sys/netinet6/ip6_mroute.c
@@ -0,0 +1,1865 @@
+/* $OpenBSD: ip6_mroute.c,v 1.1 1999/12/08 06:50:21 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
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#ifndef _KERNEL
+# ifdef KERNEL
+# define _KERNEL
+# endif
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/malloc.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6_mroute.h>
+#include <netinet6/pim6.h>
+#include <netinet6/pim6_var.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static MALLOC_DEFINE(M_MRTABLE, "mf6c", "multicast forwarding cache entry");
+#endif
+
+#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 *));
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+static int get_pim6 __P((struct mbuf *));
+#endif
+static int socket_send __P((struct socket *, struct mbuf *,
+ struct sockaddr_in6 *));
+static int register_send __P((struct ip6_hdr *, struct mif6 *,
+ struct mbuf *));
+
+/*
+ * Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static,
+ * except for netstat or debugging purposes.
+ */
+struct socket *ip6_mrouter = NULL;
+int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */
+struct mrt6stat mrt6stat;
+
+#define NO_RTE_FOUND 0x1
+#define RTE_FOUND 0x2
+
+struct mf6c *mf6ctable[MF6CTBLSIZ];
+u_char nexpire[MF6CTBLSIZ];
+static struct mif6 mif6table[MAXMIFS];
+#ifdef MRT6DEBUG
+u_int mrt6debug = 0; /* debug level */
+#define DEBUG_MFC 0x02
+#define DEBUG_FORWARD 0x04
+#define DEBUG_EXPIRE 0x08
+#define DEBUG_XMIT 0x10
+#define DEBUG_REG 0x20
+#define DEBUG_PIM 0x40
+#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;
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static struct callout_handle expire_upcalls_ch;
+#endif
+
+/*
+ * one-back cache used by ipip_input to locate a tunnel's mif
+ * given a datagram's src ip address.
+ */
+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) { \
+ register 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++; \
+ } \
+}
+
+/*
+ * Macros to compute elapsed time efficiently
+ * Borrowed from Van Jacobson's scheduling code
+ */
+#define TV_DELTA(a, b, delta) { \
+ register 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); \
+ } \
+ } \
+}
+
+#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 *));
+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 *));
+
+/*
+ * Handle MRT setsockopt commands to modify the multicast routing tables.
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+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)) /* XXX */
+ return (error);
+ if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ return (error);
+
+ switch (sopt->sopt_name) {
+ case MRT6_INIT:
+ error = ip6_mrouter_init(so, m);
+ 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);
+}
+#else
+int
+ip6_mrouter_set(cmd, so, m)
+ int cmd;
+ struct socket *so;
+ struct mbuf *m;
+{
+ if (cmd != MRT6_INIT && so != ip6_mrouter)
+ return EACCES;
+
+ switch (cmd) {
+ case MRT6_INIT: return ip6_mrouter_init(so, m);
+ case MRT6_DONE: return ip6_mrouter_done();
+ case MRT6_ADD_MIF: return add_m6if(mtod(m, struct mif6ctl *));
+ case MRT6_DEL_MIF: return del_m6if(mtod(m, mifi_t *));
+ case MRT6_ADD_MFC: return add_m6fc(mtod(m, struct mf6cctl *));
+ case MRT6_DEL_MFC: return del_m6fc(mtod(m, struct mf6cctl *));
+ case MRT6_PIM: return set_pim6(mtod(m, int *));
+ default: return EOPNOTSUPP;
+ }
+}
+#endif
+
+/*
+ * Handle MRT getsockopt commands
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+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);
+}
+#else
+int
+ip6_mrouter_get(cmd, so, m)
+ int cmd;
+ struct socket *so;
+ struct mbuf **m;
+{
+ struct mbuf *mb;
+
+ if (so != ip6_mrouter) return EACCES;
+
+ *m = mb = m_get(M_WAIT, MT_SOOPTS);
+
+ switch (cmd) {
+ case MRT6_PIM: return get_pim6(mb);
+ default:
+ m_free(mb);
+ return EOPNOTSUPP;
+ }
+}
+#endif
+
+/*
+ * 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)
+ register struct sioc_sg_req6 *req;
+{
+ register struct mf6c *rt;
+ int s;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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)
+ register struct sioc_mif_req6 *req;
+{
+ register 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;
+}
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >=3)
+/*
+ * Get PIM processiong global
+ */
+static int
+get_pim6(m)
+ struct mbuf *m;
+{
+ int *i;
+
+ i = mtod(m, int *);
+
+ *i = pim6;
+
+ return 0;
+}
+#endif
+
+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)
+ struct socket *so;
+ struct mbuf *m;
+{
+ 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;
+
+ bzero((caddr_t)mf6ctable, sizeof(mf6ctable));
+ bzero((caddr_t)nexpire, sizeof(nexpire));
+
+ pim6 = 0;/* used for stubbing out/in pim stuff */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ expire_upcalls_ch =
+#endif
+ timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT);
+
+#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;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+
+ /*
+ * 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 */
+
+ untimeout(expire_upcalls, (caddr_t)NULL
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ , expire_upcalls_ch
+#endif
+ );
+
+ /*
+ * 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;
+
+ 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)
+ register struct mif6ctl *mifcp;
+{
+ register struct mif6 *mifp;
+ struct ifnet *ifp;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ struct in6_ifreq ifr;
+#endif
+ 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 = ifindex2ifnet[mifcp->mif6c_pifi];
+
+ if (mifcp->mif6c_flags & MIFF_REGISTER) {
+ if (reg_mif_num == (mifi_t)-1) {
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ strcpy(multicast_register_if.if_xname,
+ "register_mif"); /* XXX */
+#else
+ multicast_register_if.if_name = "register_mif";
+#endif
+ 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;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ error = if_allmulti(ifp, 1);
+#else
+ /*
+ * Enable promiscuous reception of all IPv6 multicasts
+ * from the interface.
+ */
+ ifr.ifr_addr.sin6_family = AF_INET6;
+ ifr.ifr_addr.sin6_addr = in6addr_any;
+ error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
+#endif
+ splx(s);
+ if (error)
+ return error;
+ }
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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;
+{
+ register struct mif6 *mifp = mif6table + *mifip;
+ register mifi_t mifi;
+ struct ifnet *ifp;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ struct in6_ifreq ifr;
+#endif
+ int s;
+
+ if (*mifip >= nummifs)
+ return EINVAL;
+ if (mifp->m6_ifp == NULL)
+ return EINVAL;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+
+ if (!(mifp->m6_flags & MIFF_REGISTER)) {
+ /*
+ * XXX: what if there is yet IPv4 multicast daemon
+ * using the interface?
+ */
+ ifp = mifp->m6_ifp;
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if_allmulti(ifp, 0);
+#else
+ ifr.ifr_addr.sin6_family = AF_INET6;
+ ifr.ifr_addr.sin6_addr = in6addr_any;
+ (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr);
+#endif
+ }
+
+#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;
+ register 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 update 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
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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
+ */
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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 */
+ nexpire[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;
+ /* 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)
+ nexpire[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;
+ /* 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)
+ register struct timeval *t;
+{
+ register u_long d;
+ register struct timeval tp;
+ register 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
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+
+ 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)
+ register struct ip6_hdr *ip6;
+ struct ifnet *ifp;
+ struct mbuf *m;
+{
+ register struct mf6c *rt;
+ register struct mif6 *mifp;
+ register 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--;
+
+ /*
+ * Determine forwarding mifs from the forwarding cache table
+ */
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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
+ */
+
+ register struct mbuf *mb0;
+ register struct rtdetq *rte;
+ register u_long hash;
+/* register 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;
+
+ /* 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 = mtod(mm, struct mrt6msg *);
+ im->im6_msgtype = MRT6MSG_NOCACHE;
+ im->im6_mbz = 0;
+
+#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++)
+ ;
+
+ im->im6_mif = mifi;
+
+ 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;
+ nexpire[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;
+ register 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;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ for (i = 0; i < MF6CTBLSIZ; i++) {
+ if (nexpire[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++;
+ nexpire[i]--;
+
+ *nptr = mfc->mf6c_next;
+ free(mfc, M_MRTABLE);
+ } else {
+ nptr = &mfc->mf6c_next;
+ }
+ }
+ }
+ splx(s);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ expire_upcalls_ch =
+#endif
+ timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT);
+}
+
+/*
+ * Packet forwarding routine once entry in the cache is made
+ */
+static int
+ip6_mdq(m, ifp, rt)
+ register struct mbuf *m;
+ register struct ifnet *ifp;
+ register struct mf6c *rt;
+{
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register mifi_t mifi, iif;
+ register struct mif6 *mifp;
+ register int plen = m->m_pkthdr.len;
+
+/*
+ * 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
+ * seperate.
+ */
+
+#define MC6_SEND(ip6,mifp,m) { \
+ if ((mifp)->m6_flags & MIFF_REGISTER) \
+ register_send((ip6), (mifp), (m)); \
+ else \
+ phyint_send((ip6), (mifp), (m)); \
+}
+
+ /*
+ * 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.
+ */
+ if(mifi < nummifs) /* have to make sure this is a valid mif */
+ if(mif6table[mifi].m6_ifp)
+
+ if (pim6 && (m->m_flags & M_LOOP) == 0) {
+ /*
+ * Check the M_LOOP flag to avoid an
+ * unnecessary PIM assert.
+ * XXX: M_LOOP is an ad-hoc hack...
+ */
+ static struct sockaddr_in6 sin6 =
+ { sizeof(sin6), AF_INET6 };
+
+ register struct mbuf *mm;
+ struct mrt6msg *im;
+
+ mm = m_copy(m, 0,
+ sizeof(struct ip6_hdr));
+ if (mm &&
+ (M_HASCL(mm) ||
+ mm->m_len < sizeof(struct ip6_hdr)))
+ mm = m_pullup(mm, sizeof(struct ip6_hdr));
+ if (mm == NULL)
+ return ENOBUFS;
+
+ im = mtod(mm, struct mrt6msg *);
+ im->im6_msgtype = MRT6MSG_WRONGMIF;
+ im->im6_mbz = 0;
+
+ for (mifp = mif6table, iif = 0;
+ iif < nummifs && mifp &&
+ mifp->m6_ifp != ifp;
+ mifp++, iif++);
+
+ im->im6_mif = iif;
+
+ sin6.sin6_addr = im->im6_src;
+
+ 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)) {
+ 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;
+{
+ register struct mbuf *mb_copy;
+ struct ifnet *ifp = mifp->m6_ifp;
+ int error = 0;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+ static struct route_in6 ro6;
+ struct in6_multi *in6m;
+
+ /*
+ * 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)
+ 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, &ro6,
+ IPV6_FORWARDING, &im6o, 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.
+ */
+ IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+ if (in6m != NULL) {
+ ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro6.ro_dst.sin6_family = AF_INET6;
+ ro6.ro_dst.sin6_addr = ip6->ip6_dst;
+ ip6_mloopback(ifp, m, &ro6.ro_dst);
+ }
+ /*
+ * Put the packet into the sending queue of the outgoing interface
+ * if it would fit in the MTU of the interface.
+ */
+ if (mb_copy->m_pkthdr.len < ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) {
+ ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
+ ro6.ro_dst.sin6_family = AF_INET6;
+ ro6.ro_dst.sin6_addr = ip6->ip6_dst;
+ /*
+ * We just call if_output instead of nd6_output here, since
+ * we need no ND for a multicast forwarded packet...right?
+ */
+ error = (*ifp->if_output)(ifp, mb_copy,
+ (struct sockaddr *)&ro6.ro_dst,
+ NULL);
+#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);
+ return;
+#else
+#ifdef MRT6DEBUG
+#ifdef __NetBSD__
+ if (mrt6debug & DEBUG_DEBUG_XMIT)
+ log(LOG_DEBUG,
+ "phyint_send: packet too big on %s o %s g %s"
+ " size %d(discarded)\n",
+ ifp->if_xname,
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ mb_copy->m_pkthdr.len);
+#else
+ if (mrt6debug & DEBUG_XMIT)
+ log(LOG_DEBUG,
+ "phyint_send: packet too big on %s%u o %s g %s"
+ " size %d(discarded)\n",
+ ifp->if_name, ifp->if_unit,
+ ip6_sprintf(&ip6->ip6_src),
+ ip6_sprintf(&ip6->ip6_dst),
+ mb_copy->m_pkthdr.len);
+#endif /* __NetBSD__ */
+#endif /* MRT6DEBUG */
+ m_freem(mb_copy); /* simply discard the packet */
+ return;
+#endif
+ }
+}
+
+static int
+register_send(ip6, mif, m)
+ register struct ip6_hdr *ip6;
+ struct mif6 *mif;
+ register struct mbuf *m;
+{
+ register struct mbuf *mm;
+ register 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_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){
+ m_freem(mm);
+ 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: ip_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;
+{
+ register struct pim *pim; /* pointer to a pim struct */
+ register struct ip6_hdr *ip6;
+ register 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 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
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ rc = if_simloop(mif6table[reg_mif_num].m6_ifp, m,
+ (struct sockaddr *) &dst, NULL);
+#else
+ rc = looutput(mif6table[reg_mif_num].m6_ifp, m,
+ (struct sockaddr *) &dst,
+ (struct rtentry *) NULL);
+#endif
+
+ /* 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 00000000000..d577d290231
--- /dev/null
+++ b/sys/netinet6/ip6_mroute.h
@@ -0,0 +1,254 @@
+/* $OpenBSD: ip6_mroute.h,v 1.1 1999/12/08 06:50:21 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.h,v 2.5 1996/10/11 16:01:48 pjd Exp */
+
+/*
+ * Definitions for IP multicast forwarding.
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ * Modified by Ajit Thyagarajan, PARC, August 1993.
+ * Modified by Ajit Thyagarajan, PARC, August 1994.
+ * Modified by Ahmed Helmy, USC, September 1996.
+ *
+ * MROUTING Revision: 1.2
+ */
+
+#ifndef _NETINET6_IP6_MROUTE_H_
+#define _NETINET6_IP6_MROUTE_H_
+
+/*
+ * Multicast Routing set/getsockopt commands.
+ */
+#define MRT6_INIT 100 /* initialize forwarder */
+#define MRT6_DONE 101 /* shut down forwarder */
+#define MRT6_ADD_MIF 102 /* add multicast interface */
+#define MRT6_DEL_MIF 103 /* delete multicast interface */
+#define MRT6_ADD_MFC 104 /* insert forwarding cache entry */
+#define MRT6_DEL_MFC 105 /* delete forwarding cache entry */
+#define MRT6_PIM 107 /* enable pim code */
+
+#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 long if_mask;
+#define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */
+
+#ifndef howmany
+#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+#endif
+
+typedef struct if_set {
+ fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)];
+} if_set;
+
+#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS)))
+#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS)))
+#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS)))
+#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f)))
+#define IF_ZERO(p) bzero(p, sizeof(*(p)))
+
+/*
+ * Argument structure for MRT6_ADD_IF.
+ */
+struct mif6ctl {
+ mifi_t mif6c_mifi; /* the index of the mif to be added */
+ u_char mif6c_flags; /* MIFF_ flags defined below */
+ u_short mif6c_pifi; /* the index of the physical IF */
+#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 */
+};
+
+/*
+ * Struct used to communicate from kernel to multicast router
+ * note the convenient similarity to an IPv6 header.
+ */
+struct mrt6msg {
+ u_long unused1;
+ u_char im6_msgtype; /* what type of message */
+#define MRT6MSG_NOCACHE 1
+#define MRT6MSG_WRONGMIF 2
+#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/
+ u_char im6_mbz; /* must be zero */
+ u_char im6_mif; /* mif rec'd on */
+ u_char unused2;
+ struct in6_addr im6_src, im6_dst;
+};
+
+/*
+ * Argument structure used by multicast routing daemon to get src-grp
+ * packet counts
+ */
+struct sioc_sg_req6 {
+ struct sockaddr_in6 src;
+ struct sockaddr_in6 grp;
+ u_quad_t pktcnt;
+ u_quad_t bytecnt;
+ u_quad_t wrong_if;
+};
+
+/*
+ * Argument structure used by mrouted to get mif pkt counts
+ */
+struct sioc_mif_req6 {
+ mifi_t mifi; /* mif number */
+ u_quad_t icount; /* Input packet count on mif */
+ u_quad_t ocount; /* Output packet count on mif */
+ u_quad_t ibytes; /* Input byte count on mif */
+ u_quad_t obytes; /* Output byte count on mif */
+};
+
+#if defined(_KERNEL) || defined(KERNEL)
+/*
+ * The kernel's multicast-interface structure.
+ */
+struct mif6 {
+ u_char m6_flags; /* MIFF_ flags defined above */
+ u_int m6_rate_limit; /* max rate */
+#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 */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt));
+int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt));
+#else
+int ip6_mrouter_set __P((int, struct socket *, struct mbuf *));
+int ip6_mrouter_get __P((int, struct socket *, struct mbuf **));
+#endif
+int ip6_mrouter_done __P((void));
+int mrt6_ioctl __P((int, caddr_t));
+#endif /* _KERNEL */
+
+#endif /* !_NETINET6_IP6_MROUTE_H_ */
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
new file mode 100644
index 00000000000..c1784604fad
--- /dev/null
+++ b/sys/netinet6/ip6_output.c
@@ -0,0 +1,2534 @@
+/* $OpenBSD: ip6_output.c,v 1.1 1999/12/08 06:50:21 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, 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
+ */
+
+#ifdef __FreeBSD__
+#include "opt_ip6fw.h"
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/systm.h>
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/kernel.h>
+#endif
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+#include <machine/pcpu.h>
+#endif
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+#include <netinet6/ip6.h>
+#include <netinet6/icmp6.h>
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet/in_pcb.h>
+#else
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
+#undef IPSEC
+#endif
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif /* IPSEC */
+
+#ifndef __bsdi__
+#include "loop.h"
+#endif
+
+#include <net/net_osdep.h>
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
+#endif
+
+struct ip6_exthdrs {
+ struct mbuf *ip6e_ip6;
+ struct mbuf *ip6e_hbh;
+ struct mbuf *ip6e_dest1;
+ struct mbuf *ip6e_rthdr;
+ struct mbuf *ip6e_dest2;
+};
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
+ struct socket *, struct sockopt *sopt));
+#else
+static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
+ struct socket *));
+#endif
+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 *));
+#ifdef __bsdi__
+#if _BSDI_VERSION < 199802
+extern struct ifnet loif;
+#else
+extern struct ifnet *loifp;
+#endif
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+extern struct ifnet loif[NLOOP];
+#endif
+
+/*
+ * 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.
+ */
+int
+ip6_output(m0, opt, ro, flags, im6o, ifpp)
+ 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 ip6_hdr *ip6, *mhip6;
+ struct ifnet *ifp;
+ struct mbuf *m = m0;
+ int hlen, tlen, len, off;
+ struct route_in6 ip6route;
+ struct sockaddr_in6 *dst;
+ int error = 0;
+ struct in6_ifaddr *ia;
+ 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;
+#if defined(__bsdi__) && _BSDI_VERSION < 199802
+ struct ifnet *loifp = &loif;
+#endif
+#ifdef IPSEC
+ int needipsectun = 0;
+ struct socket *so;
+ struct secpolicy *sp = NULL;
+
+ /* for AH processing. stupid to have "socket" variable in IP layer... */
+ so = (struct socket *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ip6 = mtod(m, struct ip6_hdr *);
+#endif /* IPSEC */
+
+#define MAKE_EXTHDR(hp,mp) \
+ { \
+ 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; \
+ } \
+ }
+
+ 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 (so == NULL)
+ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+ else
+ sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error);
+
+ if (sp == NULL) {
+ ipsec6stat.out_inval++;
+ goto bad;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ goto bad;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* no need to do IPsec. */
+ needipsec = 0;
+ break;
+
+ case IPSEC_POLICY_IPSEC:
+ if (sp->req == NULL) {
+ /* XXX should be panic ? */
+ printf("ip6_output: No IPsec request specified.\n");
+ error = EINVAL;
+ goto bad;
+ }
+ needipsec = 1;
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ printf("ip6_output: Invalid policy found. %d\n", sp->policy);
+ }
+#endif /* 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.
+ *
+ * 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)\
+ {\
+ 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);\
+ }\
+ }
+ /*
+ * 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);
+
+#ifdef 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 ||
+ !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 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 /*IPESC*/
+
+ 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) {
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ /*
+ * NetBSD/OpenBSD always clones routes, if parent is
+ * PRF_CLONING.
+ */
+ rtalloc((struct route *)ro);
+#else
+ if (ro == &ip6route) /* xxx kazu */
+ rtalloc((struct route *)ro);
+ else
+ rtcalloc((struct route *)ro);
+#endif
+ }
+ 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 there is the outgoing interface conflicts with
+ * the interface specified by ifi6_ifindex(if specified).
+ * Note that loopback interface is always okay.
+ * (this happens when we are sending packet toward my
+ * interface)
+ */
+ 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 = ifindex2ifnet[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 {
+#if defined(__bsdi__)
+ ifp = loifp;
+#else
+ ifp = &loif[0];
+#endif
+ }
+ }
+
+ 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
+#ifdef __FreeBSD__
+ , 0UL
+#endif
+ );
+ }
+ 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) != NULL) {
+ 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;
+
+#ifdef __FreeBSD__
+ rtcalloc((struct route *)ro_pmtu);
+#else
+ rtalloc((struct route *)ro_pmtu);
+#endif
+ }
+ }
+ 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) {
+ /*
+ * 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).
+ */
+ 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;
+ }
+
+ /*
+ * Fake link-local scope-class addresses
+ */
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1] = 0;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] = 0;
+ }
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* 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;
+ }
+ }
+#endif
+
+ /*
+ * 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 */
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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
+ )
+ {
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ ia->ia_ifa.ifa_data.ifad_outbytes +=
+ m->m_pkthdr.len;
+ }
+ }
+#endif
+#ifdef OLDIP6OUTPUT
+ error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
+ ro->ro_rt);
+#else
+ error = nd6_output(ifp, m, dst, ro->ro_rt);
+#endif
+ 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_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) {
+#if defined(__NetBSD__) && defined(IFA_STATS)
+ if (IFA_STATS) {
+ struct in6_ifaddr *ia6;
+ ip6 = mtod(m, struct ip6_hdr *);
+ ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
+ if (ia6) {
+ ia->ia_ifa.ifa_data.ifad_outbytes +=
+ m->m_pkthdr.len;
+ }
+ }
+#endif
+#ifdef OLDIP6OUTPUT
+ error = (*ifp->if_output)(ifp, m,
+ (struct sockaddr *)dst,
+ ro->ro_rt);
+#else
+ error = nd6_output(ifp, m, dst, ro->ro_rt);
+#endif
+ }
+ 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 */
+
+ 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;
+
+#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) {
+ caddr_t oldoptp = mtod(mopt, caddr_t);
+ int oldoptlen = mopt->m_len;
+
+ if (mopt->m_flags & M_EXT)
+ return(ENOBUFS); /* XXX */
+ MCLGET(mopt, M_DONTWAIT);
+ if ((mopt->m_flags & M_EXT) == 0)
+ return(ENOBUFS);
+
+ bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen);
+ optbuf = mtod(mopt, caddr_t) + oldoptlen;
+ mopt->m_len = oldoptlen + JUMBOOPTLEN;
+ }
+ 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;
+ *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN);
+
+ /* 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.
+ */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+int
+ip6_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+#else
+int
+ip6_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+#endif
+{
+ int privileged;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ register struct inpcb *in6p = sotoinpcb(so);
+ int error, optval;
+ int level, op, optname;
+ int optlen;
+ struct proc *p;
+
+ if (sopt) {
+ level = sopt->sopt_level;
+ op = sopt->sopt_dir;
+ optname = sopt->sopt_name;
+ optlen = sopt->sopt_valsize;
+ p = sopt->sopt_p;
+ } else {
+ panic("ip6_ctloutput: arg soopt is NULL");
+ }
+#else
+#ifdef HAVE_NRL_INPCB
+ register struct inpcb *inp = sotoinpcb(so);
+#else
+ register struct in6pcb *in6p = sotoin6pcb(so);
+#endif
+ register struct mbuf *m = *mp;
+ int error, optval;
+ int optlen;
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ struct proc *p = curproc; /* XXX */
+#endif
+
+ optlen = m ? m->m_len : 0;
+#endif
+ error = optval = 0;
+
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1;
+#else
+#ifdef HAVE_NRL_INPCB
+ privileged = (inp->inp_socket->so_state & SS_PRIV);
+#else
+ privileged = (in6p->in6p_socket->so_state & SS_PRIV);
+#endif
+#endif
+
+ if (level == IPPROTO_IPV6) {
+ switch (op) {
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case SOPT_SET:
+#else
+ case PRCO_SETOPT:
+#endif
+ switch (optname) {
+ case IPV6_PKTOPTIONS:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ {
+ struct mbuf *m;
+
+ error = soopt_getm(sopt, &m); /* XXX */
+ if (error != NULL)
+ break;
+ error = soopt_mcopyin(sopt, m); /* XXX */
+ if (error != NULL)
+ break;
+ return (ip6_pcbopts(&in6p->in6p_outputopts,
+ m, so, sopt));
+ }
+#else
+#ifdef HAVE_NRL_INPCB
+ return(ip6_pcbopts(&inp->inp_outputopts6,
+ m, so));
+#else
+ return(ip6_pcbopts(&in6p->in6p_outputopts,
+ m, so));
+#endif
+#endif
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* fall through */
+ case IPV6_UNICAST_HOPS:
+ case IPV6_RECVOPTS:
+ case IPV6_RECVRETOPTS:
+ case IPV6_RECVDSTADDR:
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_RTHDR:
+ case IPV6_CHECKSUM:
+ case IPV6_FAITH:
+#ifdef MAPPED_ADDR_ENABLED
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case IPV6_BINDV6ONLY:
+#endif
+#endif /* MAPPED_ADDR_ENABLED */
+ if (optlen != sizeof(int))
+ error = EINVAL;
+ else {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ error = sooptcopyin(sopt, &optval,
+ sizeof optval, sizeof optval);
+ if (error)
+ break;
+#else
+ optval = *mtod(m, int *);
+#endif
+ switch (optname) {
+
+ case IPV6_UNICAST_HOPS:
+ if (optval < -1 || optval >= 256)
+ error = EINVAL;
+ else {
+ /* -1 = kernel default */
+#ifdef HAVE_NRL_INPCB
+ inp->inp_hops = optval;
+#else
+ in6p->in6p_hops = optval;
+
+#if defined(MAPPED_ADDR_ENABLED)
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if ((in6p->in6p_vflag &
+ INP_IPV4) != 0)
+ in6p->inp_ip_ttl = optval;
+#endif
+#endif
+#endif
+ }
+ break;
+#ifdef HAVE_NRL_INPCB
+#define OPTSET(bit) \
+ if (optval) \
+ inp->inp_flags |= bit; \
+ else \
+ inp->inp_flags &= ~bit;
+#else
+#define OPTSET(bit) \
+ if (optval) \
+ in6p->in6p_flags |= bit; \
+ else \
+ in6p->in6p_flags &= ~bit;
+#endif
+
+ case IPV6_RECVOPTS:
+ OPTSET(IN6P_RECVOPTS);
+ break;
+
+ case IPV6_RECVRETOPTS:
+ OPTSET(IN6P_RECVRETOPTS);
+ break;
+
+ case IPV6_RECVDSTADDR:
+ OPTSET(IN6P_RECVDSTADDR);
+ break;
+
+ case IPV6_PKTINFO:
+ OPTSET(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ OPTSET(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_HOPOPTS:
+ OPTSET(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_DSTOPTS:
+ OPTSET(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RTHDR:
+ OPTSET(IN6P_RTHDR);
+ break;
+
+ case IPV6_CHECKSUM:
+#ifdef HAVE_NRL_INPCB
+ inp->inp_csumoffset = optval;
+#else
+ in6p->in6p_cksum = optval;
+#endif
+ break;
+
+ case IPV6_FAITH:
+ OPTSET(IN6P_FAITH);
+ break;
+
+#ifdef MAPPED_ADDR_ENABLED
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case IPV6_BINDV6ONLY:
+ OPTSET(IN6P_BINDV6ONLY);
+ break;
+#endif
+#endif /* MAPPED_ADDR_ENABLED */
+ }
+ }
+ break;
+#undef OPTSET
+
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ {
+ struct mbuf *m;
+ if (sopt->sopt_valsize > MLEN) {
+ error = EMSGSIZE;
+ break;
+ }
+ /* XXX */
+ MGET(m, sopt->sopt_p ? M_WAIT : 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);
+ }
+#else
+#ifdef HAVE_NRL_INPCB
+ error = ip6_setmoptions(optname,
+ &inp->inp_moptions6, m);
+#else
+ error = ip6_setmoptions(optname,
+ &in6p->in6p_moptions, m);
+#endif
+#endif
+ break;
+
+#ifndef __bsdi__
+ case IPV6_PORTRANGE:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ break;
+#else
+ optval = *mtod(m, int *);
+#endif
+
+#ifdef HAVE_NRL_INPCB
+# define in6p inp
+# define in6p_flags inp_flags
+#endif
+ 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;
+ }
+#ifdef HAVE_NRL_INPCB
+# undef in6p
+# undef in6p_flags
+#endif
+ break;
+#endif
+
+#ifdef IPSEC
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct mbuf *m;
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (error = soopt_getm(sopt, &m)) /* XXX */
+ break;
+ if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ break;
+#endif
+ if (m != 0)
+ req = mtod(m, caddr_t);
+ error = ipsec6_set_policy(in6p, optname, req,
+ privileged);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ m_freem(m);
+#endif
+ }
+ break;
+#endif /* IPSEC */
+
+#ifdef IPV6FIREWALL
+ case IPV6_FW_ADD:
+ case IPV6_FW_DEL:
+ case IPV6_FW_FLUSH:
+ case IPV6_FW_ZERO:
+ {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (ip6_fw_ctl_ptr == NULL)
+ return EINVAL;
+ if (error = soopt_getm(sopt, &m)) /* XXX */
+ break;
+ if (error = soopt_mcopyin(sopt, m)) /* XXX */
+ break;
+#else
+ if (ip6_fw_ctl_ptr == NULL) {
+ if (m) (void)m_free(m);
+ return EINVAL;
+ }
+#endif
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+ m = *mp;
+ }
+ break;
+#endif
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (m)
+ (void)m_free(m);
+#endif
+ break;
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case SOPT_GET:
+#else
+ case PRCO_GETOPT:
+#endif
+ switch (optname) {
+
+ case IPV6_OPTIONS:
+ case IPV6_RETOPTS:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#if 0
+ if (in6p->in6p_options) {
+ error = sooptcopyout(sopt,
+ mtod(in6p->in6p_options,
+ char *),
+ in6p->in6p_options->m_len);
+ } else
+ sopt->sopt_valsize = 0;
+ break;
+#else
+ error = ENOPROTOOPT;
+ break;
+#endif
+#else
+#if 0
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ if (in6p->in6p_options) {
+ m->m_len = in6p->in6p_options->m_len;
+ bcopy(mtod(in6p->in6p_options, caddr_t),
+ mtod(m, caddr_t),
+ (unsigned)m->m_len);
+ } else
+ m->m_len = 0;
+ break;
+#else
+ error = ENOPROTOOPT;
+ break;
+#endif
+#endif
+
+ case IPV6_PKTOPTIONS:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (in6p->in6p_options) {
+ error = soopt_mcopyout(sopt,
+ in6p->in6p_options);
+ } else
+ sopt->sopt_valsize = 0;
+#elif defined(HAVE_NRL_INPCB)
+ if (inp->inp_options) {
+ *mp = m_copym(inp->inp_options, 0,
+ M_COPYALL, M_WAIT);
+ } else {
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+ (*mp)->m_len = 0;
+ }
+#else
+ if (in6p->in6p_options) {
+ *mp = m_copym(in6p->in6p_options, 0,
+ M_COPYALL, M_WAIT);
+ } else {
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+ (*mp)->m_len = 0;
+ }
+#endif
+ break;
+
+ case IPV6_HOPOPTS:
+ case IPV6_DSTOPTS:
+ if (!privileged) {
+ error = EPERM;
+ break;
+ }
+ /* fall through */
+ case IPV6_UNICAST_HOPS:
+ case IPV6_RECVOPTS:
+ case IPV6_RECVRETOPTS:
+ case IPV6_RECVDSTADDR:
+ case IPV6_PKTINFO:
+ case IPV6_HOPLIMIT:
+ case IPV6_RTHDR:
+ case IPV6_CHECKSUM:
+ case IPV6_FAITH:
+#ifdef MAPPED_ADDR_ENABLED
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case IPV6_BINDV6ONLY:
+#endif
+#endif /* MAPPED_ADDR_ENABLED */
+ switch (optname) {
+
+ case IPV6_UNICAST_HOPS:
+#ifdef HAVE_NRL_INPCB
+ optval = inp->inp_hops;
+#else
+ optval = in6p->in6p_hops;
+#endif
+ break;
+
+#ifdef HAVE_NRL_INPCB
+#define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0)
+#else
+#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0)
+#endif
+
+ case IPV6_RECVOPTS:
+ optval = OPTBIT(IN6P_RECVOPTS);
+ break;
+
+ case IPV6_RECVRETOPTS:
+ optval = OPTBIT(IN6P_RECVRETOPTS);
+ break;
+
+ case IPV6_RECVDSTADDR:
+ optval = OPTBIT(IN6P_RECVDSTADDR);
+ break;
+
+ case IPV6_PKTINFO:
+ optval = OPTBIT(IN6P_PKTINFO);
+ break;
+
+ case IPV6_HOPLIMIT:
+ optval = OPTBIT(IN6P_HOPLIMIT);
+ break;
+
+ case IPV6_HOPOPTS:
+ optval = OPTBIT(IN6P_HOPOPTS);
+ break;
+
+ case IPV6_DSTOPTS:
+ optval = OPTBIT(IN6P_DSTOPTS);
+ break;
+
+ case IPV6_RTHDR:
+ optval = OPTBIT(IN6P_RTHDR);
+ break;
+
+ case IPV6_CHECKSUM:
+#ifdef HAVE_NRL_INPCB
+ optval = inp->inp_csumoffset;
+#else
+ optval = in6p->in6p_cksum;
+#endif
+ break;
+
+ case IPV6_FAITH:
+ optval = OPTBIT(IN6P_FAITH);
+ break;
+
+#ifdef MAPPED_ADDR_ENABLED
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ case IPV6_BINDV6ONLY:
+ optval = OPTBIT(IN6P_BINDV6ONLY);
+ break;
+#endif
+#endif /* MAPPED_ADDR_ENABLED */
+
+#ifndef __bsdi__
+ case IPV6_PORTRANGE:
+ {
+ int flags;
+#ifdef HAVE_NRL_INPCB
+ flags = inp->inp_flags;
+#else
+ flags = in6p->in6p_flags;
+#endif
+ if (flags & IN6P_HIGHPORT)
+ optval = IPV6_PORTRANGE_HIGH;
+ else if (flags & IN6P_LOWPORT)
+ optval = IPV6_PORTRANGE_LOW;
+ else
+ optval = 0;
+ break;
+ }
+#endif
+ }
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ error = sooptcopyout(sopt, &optval,
+ sizeof optval);
+#else
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof(int);
+ *mtod(m, int *) = optval;
+#endif
+ break;
+
+ case IPV6_MULTICAST_IF:
+ case IPV6_MULTICAST_HOPS:
+ case IPV6_MULTICAST_LOOP:
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ {
+ 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);
+ }
+#elif defined(HAVE_NRL_INPCB)
+ error = ip6_getmoptions(optname, inp->inp_moptions6, mp);
+#else
+ error = ip6_getmoptions(optname, in6p->in6p_moptions, mp);
+#endif
+ break;
+
+#ifdef IPSEC
+ case IPV6_IPSEC_POLICY:
+ {
+ caddr_t req = NULL;
+ int len = 0;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+#endif
+ if (m != 0) {
+ req = mtod(m, caddr_t);
+ len = m->m_len;
+ }
+ error = ipsec6_get_policy(in6p, req, mp);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /*XXX*/
+ m_freem(m);
+#endif
+ break;
+ }
+#endif /* IPSEC */
+
+#ifdef IPV6FIREWALL
+ case IPV6_FW_GET:
+ {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct mbuf *m;
+ struct mbuf **mp = &m;
+#endif
+
+ if (ip6_fw_ctl_ptr == NULL)
+ {
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (m)
+ (void)m_free(m);
+#endif
+ return EINVAL;
+ }
+ error = (*ip6_fw_ctl_ptr)(optname, mp);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ if (error == 0)
+ error = soopt_mcopyout(sopt, m); /* XXX */
+ if (m)
+ m_freem(m);
+#endif
+ }
+ break;
+#endif
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ } else {
+ error = EINVAL;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ if (op == PRCO_SETOPT && *mp)
+ (void)m_free(*mp);
+#endif
+ }
+ return(error);
+}
+
+/*
+ * Set up IP6 options in pcb for insertion in output packets.
+ * Store in mbuf with pointer in pcbopt, adding pseudo-option
+ * with destination address if source routed.
+ */
+static int
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ip6_pcbopts(pktopt, m, so, sopt)
+#else
+ip6_pcbopts(pktopt, m, so)
+#endif
+ struct ip6_pktopts **pktopt;
+ register struct mbuf *m;
+ struct socket *so;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct sockopt *sopt;
+#endif
+{
+ register struct ip6_pktopts *opt = *pktopt;
+ int error = 0;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct proc *p = sopt->sopt_p;
+#elif defined(__bsdi__) && _BSDI_VERSION >= 199802
+ struct proc *p = PCPU(curproc); /* XXX */
+#else
+ struct proc *p = curproc; /* XXX */
+#endif
+ int priv = 0;
+
+ /* turn off any old options. */
+ if (opt) {
+ if (opt->ip6po_m)
+ (void)m_free(opt->ip6po_m);
+ }
+ else
+ opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
+ *pktopt = 0;
+
+ if (!m || m->m_len == 0) {
+ /*
+ * Only turning off any previous options.
+ */
+ if (opt)
+ free(opt, M_IP6OPT);
+ if (m)
+ (void)m_free(m);
+ return(0);
+ }
+
+ /* set options specified by user. */
+ if (p && !suser(p->p_ucred, &p->p_acflag))
+ priv = 1;
+ if ((error = ip6_setpktoptions(m, opt, priv)) != 0) {
+ (void)m_free(m);
+ return(error);
+ }
+ *pktopt = opt;
+ return(0);
+}
+
+/*
+ * 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;
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+ struct proc *p = PCPU(curproc); /* XXX */
+#else
+ struct proc *p = curproc; /* XXX */
+#endif
+#if defined(__bsdi__) && _BSDI_VERSION < 199802
+ struct ifnet *loifp = &loif;
+#endif
+
+ 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;
+ }
+ ifindex = *(mtod(m, u_int *));
+ if (ifindex < 0 || if_index < ifindex) {
+ error = ENXIO; /* XXX EINVAL? */
+ break;
+ }
+ ifp = ifindex2ifnet[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;
+ }
+ optval = *(mtod(m, u_int *));
+ 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) ||
+ (loop = *(mtod(m, u_int *))) > 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(p->p_ucred, &p->p_acflag)) {
+ 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)) {
+#if defined(__bsdi__)
+ ifp = loifp;
+#else
+ ifp = &loif[0];
+#endif
+ }
+ 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 = ifindex2ifnet[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(p->p_ucred, &p->p_acflag)) {
+ 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 = ifindex2ifnet[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;
+ register struct ip6_moptions *im6o;
+ register struct mbuf **mp;
+{
+ u_int *hlim, *loop, *ifindex;
+
+#ifdef __FreeBSD__
+ *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/
+#else
+ *mp = m_get(M_WAIT, MT_SOOPTS);
+#endif
+
+ 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)
+ register 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)
+ struct mbuf *control;
+ struct ip6_pktopts *opt;
+ int priv;
+{
+ register struct cmsghdr *cm = 0;
+
+ if (control == 0 || opt == 0)
+ return(EINVAL);
+
+ bzero(opt, sizeof(*opt));
+ opt->ip6po_hlim = -1; /* -1 means to use default hop limit */
+
+ /*
+ * XXX: Currently, we assume all the optional information is stored
+ * in a single mbuf.
+ */
+ if (control->m_next)
+ return(EINVAL);
+
+ opt->ip6po_m = control;
+
+ for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
+ control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
+ cm = mtod(control, struct cmsghdr *);
+ if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
+ return(EINVAL);
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+
+ switch(cm->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo)))
+ return(EINVAL);
+ opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm);
+ if (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);
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) {
+ struct ifaddr *ia;
+ 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;
+ ia = ifa_ifwithaddr(sin6tosa(&sin6));
+ if (ia == NULL ||
+ (opt->ip6po_pktinfo->ipi6_ifindex &&
+ (ia->ifa_ifp->if_index !=
+ opt->ip6po_pktinfo->ipi6_ifindex))) {
+ return(EADDRNOTAVAIL);
+ }
+ /*
+ * Check if the requested source address is
+ * indeed a unicast address assigned to the
+ * node.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr))
+ 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) ||
+ cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm)))
+ return(EINVAL);
+
+ opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm);
+
+ break;
+
+ case IPV6_HOPOPTS:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh)))
+ return(EINVAL);
+ opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3))
+ return(EINVAL);
+ break;
+
+ case IPV6_DSTOPTS:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest)))
+ return(EINVAL);
+
+ /*
+ * If there is no routing header yet, the destination
+ * options header should be put on the 1st part.
+ * Otherwise, the header should be on the 2nd part.
+ * (See RFC 2460, section 4.1)
+ */
+ if (opt->ip6po_rthdr == NULL) {
+ opt->ip6po_dest1 =
+ (struct ip6_dest *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1)
+ << 3))
+ return(EINVAL);
+ }
+ else {
+ opt->ip6po_dest2 =
+ (struct ip6_dest *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1)
+ << 3))
+ return(EINVAL);
+ }
+ break;
+
+ case IPV6_RTHDR:
+ if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr)))
+ return(EINVAL);
+ opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm);
+ if (cm->cmsg_len !=
+ CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3))
+ return(EINVAL);
+ switch(opt->ip6po_rthdr->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ if (opt->ip6po_rthdr->ip6r_segleft == 0)
+ return(EINVAL);
+ break;
+ default:
+ return(EINVAL);
+ }
+ 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;
+ register struct mbuf *m;
+ register struct sockaddr_in6 *dst;
+{
+ struct mbuf *copym;
+
+ copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL) {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ (void)if_simloop(ifp, copym, (struct sockaddr *)dst, NULL);
+#else
+ (void)looutput(ifp, copym, (struct sockaddr *)dst, NULL);
+#endif
+ }
+}
+
+/*
+ * 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_COPY_PKTHDR(mh, m);
+ MH_ALIGN(mh, sizeof(*ip6));
+ m->m_flags &= ~M_PKTHDR;
+ 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.
+ */
+#ifdef HAVE_NRL_INPCB
+# define in6pcb inpcb
+# define in6p_outputopts inp_outputopts6
+#endif
+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);
+ 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
+}
+#ifdef HAVE_NRL_INPCB
+# undef in6pcb
+# undef in6p_outputopts
+#endif
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
new file mode 100644
index 00000000000..6bacf3ccbda
--- /dev/null
+++ b/sys/netinet6/ip6_var.h
@@ -0,0 +1,289 @@
+/* $OpenBSD: ip6_var.h,v 1.1 1999/12/08 06:50:21 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_long ip6q_head;
+ u_short ip6q_len;
+ u_char ip6q_nxt;
+ u_char ip6q_hlim;
+ struct ip6asfrag *ip6q_down;
+ struct ip6asfrag *ip6q_up;
+ u_long ip6q_ident;
+ u_char ip6q_arrive;
+ u_char ip6q_ttl;
+ struct in6_addr ip6q_src, ip6q_dst;
+ struct ip6q *ip6q_next;
+ struct ip6q *ip6q_prev;
+ int ip6q_unfrglen;
+#ifdef notyet
+ u_char *ip6q_nxtp;
+#endif
+};
+
+struct ip6asfrag {
+ u_long ip6af_head;
+ u_short ip6af_len;
+ u_char ip6af_nxt;
+ u_char ip6af_hlim;
+ /* must not override the above members during reassembling */
+ struct ip6asfrag *ip6af_down;
+ struct ip6asfrag *ip6af_up;
+ u_short ip6af_mff;
+ u_short ip6af_off;
+ struct mbuf *ip6af_m;
+ u_long ip6af_offset; /* offset where next header starts */
+ u_short ip6af_frglen; /* fragmentable part length */
+ u_char ip6af_x1[10];
+};
+
+#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 */
+ struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */
+ struct sockaddr *ip6po_nexthop; /* Next-hop address */
+ struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */
+ struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */
+ struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */
+ struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */
+};
+
+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 */
+ /* XXX the following two items are not really AF_INET6 thing */
+ u_quad_t ip6s_pulldown; /* # of calls to m_pulldown */
+ u_quad_t ip6s_pulldown_copy; /* # of mbuf copies in m_pulldown */
+ u_quad_t ip6s_pulldown_alloc; /* # of mbuf allocs in m_pulldown */
+};
+
+#ifdef _KERNEL
+/* flags passed to ip6_output as last parameter */
+#define IPV6_DADOUTPUT 0x01 /* DAD */
+#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */
+
+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. */
+#ifdef MAPPED_ADDR_ENABLED
+extern int ip6_mapped_addr_on;
+#endif /* MAPPED_ADDR_ENABLED */
+
+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;
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+struct in6pcb;
+#endif
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+extern struct pr_usrreqs rip6_usrreqs;
+struct sockopt;
+#endif
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+struct inpcb;
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt));
+#else
+int icmp6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+#endif
+
+void ip6_init __P((void));
+void ip6intr __P((void));
+void ip6_input __P((struct mbuf *));
+void ip6_freemoptions __P((struct ip6_moptions *));
+int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int));
+char * ip6_get_prevhdr __P((struct mbuf *, int));
+int ip6_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 *));
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *,
+ struct mbuf *));
+#else
+void ip6_savecontrol __P((struct in6pcb *, struct mbuf **, struct ip6_hdr *,
+ struct mbuf *));
+#endif
+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 **));
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+int ip6_ctloutput __P((struct socket *, struct sockopt *sopt));
+#else
+int ip6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+#endif
+int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int));
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
+int ip6_optlen __P((struct inpcb *));
+#else
+int ip6_optlen __P((struct in6pcb *));
+#endif
+
+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));
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt));
+#else
+int rip6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+#endif
+int rip6_output __P((struct mbuf *, ...));
+int rip6_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
+
+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 00000000000..c6cf818c8a4
--- /dev/null
+++ b/sys/netinet6/ip6protosw.h
@@ -0,0 +1,151 @@
+/* $OpenBSD: ip6protosw.h,v 1.1 1999/12/08 06:50: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.
+ *
+ */
+
+/* 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_
+
+/*
+ * Protocol switch table for IPv6.
+ * All other definitions should refer to sys/protosw.h
+ */
+
+struct mbuf;
+struct sockaddr;
+struct socket;
+struct domain;
+struct proc;
+struct ip6_hdr;
+#ifdef __FreeBSD__
+struct pr_usrreqs;
+#endif
+
+/*
+ * argument type for the last arg of pr_ctlinput().
+ * should be consulted only with AF_INET6 family.
+ */
+struct ip6ctlparam {
+ struct mbuf *ip6c_m; /* start of mbuf chain */
+ struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */
+ int ip6c_off; /* offset of the target proto header */
+};
+
+struct ip6protosw {
+#if (defined(__FreeBSD__) && __FreeBSD__ < 3) || defined(__OpenBSD__)
+ short pr_type; /* socket type used for */
+#else
+ int pr_type; /* socket type used for */
+#endif
+ 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));
+#ifdef __bsdi__
+ int (*pr_output)(); /* output to protocol (from above) */
+#else
+ int (*pr_output) /* output to protocol (from above) */
+ __P((struct mbuf *, ...));
+#endif
+ void (*pr_ctlinput) /* control input (from below) */
+ __P((int, struct sockaddr *, void *));
+ int (*pr_ctloutput) /* control output (from above) */
+ __P((int, struct socket *, int, int, struct mbuf **));
+
+/* user-protocol hook */
+#if !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !defined(__bsdi__)
+ int (*pr_usrreq) /* user request: see list below */
+ __P((struct socket *, int, struct mbuf *,
+ struct mbuf *, struct mbuf *, struct proc *));
+#else
+ int (*pr_usrreq) /* user request: see list below */
+ __P((struct socket *, int, struct mbuf *,
+ struct mbuf *, struct mbuf *));
+#endif
+
+/* 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));
+#ifdef __FreeBSD__
+ struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */
+#else
+ int (*pr_sysctl) /* sysctl for protocol */
+ __P((int *, u_int, void *, size_t *, void *, size_t));
+#endif
+};
+
+#endif /* !_NETINET6_IP6PROTOSW_H_ */
diff --git a/sys/netinet6/ipv6.h b/sys/netinet6/ipv6.h
deleted file mode 100644
index 311231a0e7d..00000000000
--- a/sys/netinet6/ipv6.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-#ifndef _NETINET6_IPV6_H
-#define _NETINET6_IPV6_H 1
-
-#define IPV6VERSION 6
-
-/*
- * Header structures.
- */
-
-struct ipv6
-{
- uint32_t ipv6_versfl; /* Version and flow label word. */
-
- uint16_t ipv6_length; /* Datagram length (not including the length
- of this header). */
- uint8_t ipv6_nexthdr; /* Next header type. */
- uint8_t ipv6_hoplimit; /* Hop limit. */
-
- struct in6_addr ipv6_src; /* Source address. */
- struct in6_addr ipv6_dst; /* Destination address. */
-};
-
-#if __linux__
-#include <endian.h>
-#else /* __linux__ */
-#include <machine/endian.h>
-#endif /* __linux__ */
-
-struct ipv6hdr {
-#if BYTE_ORDER == LITTLE_ENDIAN
- uint8_t ipv6_priority:4; /* going away? */
- uint8_t ipv6_version:4;
- uint32_t ipv6_flowid:24;
-#elif BYTE_ORDER == BIG_ENDIAN
- uint32_t ipv6_flowid:24;
- uint8_t ipv6_priority:4; /* going away? */
- uint8_t ipv6_version:4;
-#else
-#error "Don't know what endian to use."
-#endif
- uint16_t ipv6_len;
- uint8_t ipv6_nextheader;
- uint8_t ipv6_hoplimit;
- struct in6_addr ipv6_src; /* source address */
- struct in6_addr ipv6_dst; /* destination address */
-};
-
-/*
- * Macros and defines for header fields, and values thereof.
- * Assume things are in host order for these three macros.
- */
-
-#define IPV6_VERSION(h) ((h)->ipv6_versfl >> 28)
-#define IPV6_PRIORITY(h) (((h)->ipv6_versfl & 0x0f000000) >> 24)
-#define IPV6_FLOWID(h) ((h)->ipv6_versfl & 0x00ffffff)
-
-#define MAXHOPLIMIT 64
-#define IPV6_MINMTU 576
-
-/*
- * Other IPv6 header definitions.
- */
-
-/* Fragmentation header & macros for it. NOTE: Host order assumption. */
-
-struct ipv6_fraghdr
-{
- uint8_t frag_nexthdr; /* Next header type. */
- uint8_t frag_reserved;
- uint16_t frag_bitsoffset; /* More bit and fragment offset. */
- uint32_t frag_id; /* Fragment identifier. */
-};
-
-#define FRAG_MOREMASK 0x1
-#define FRAG_OFFMASK 0xFFF8
-#define FRAG_MORE_BIT(fh) ((fh)->frag_bitsoffset & FRAG_MOREMASK)
-#define FRAG_OFFSET(fh) ((fh)->frag_bitsoffset & FRAG_OFFMASK)
-
-/* Source routing header. Host order assumption for macros. */
-
-struct ipv6_srcroute0
-{
- uint8_t i6sr_nexthdr; /* Next header type. */
- uint8_t i6sr_len; /* RH len in 8-byte addrs, !incl this structure */
- uint8_t i6sr_type; /* Routing type, should be 0 */
- uint8_t i6sr_left; /* Segments left */
- uint32_t i6sr_reserved; /* 8 bits of reserved padding. */
-};
-
-#define I6SR_BITMASK(i6sr) ((i6sr)->i6sr_reserved & 0xffffff)
-
-/* Options header. For "ignoreable" options. */
-
-struct ipv6_opthdr
-{
- uint8_t oh_nexthdr; /* Next header type. */
- uint8_t oh_extlen; /* Header extension length. */
- uint8_t oh_data[6]; /* Option data, may be reserved for
- alignment purposes. */
-};
-
-#define OPT_PAD1 0
-#define OPT_PADN 1
-#define OPT_JUMBO 194
-
-struct ipv6_option
-{
- uint8_t opt_type; /* Option type. */
- uint8_t opt_datalen; /* Option data length. */
- uint8_t opt_data[1]; /* Option data. */
-};
-#endif /* _NETINET6_IPV6_H */
diff --git a/sys/netinet6/ipv6_addrconf.c b/sys/netinet6/ipv6_addrconf.c
deleted file mode 100644
index 19b868d5af3..00000000000
--- a/sys/netinet6/ipv6_addrconf.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#include <sys/param.h>
-#include <sys/systm.h>
-/*#include <sys/ioctl.h> */
-#include <sys/errno.h>
-#include <sys/malloc.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/time.h>
-#include <sys/kernel.h>
-#include <sys/mbuf.h>
-
-#include <net/if.h>
-#include <net/if_types.h>
-#include <net/if_dl.h>
-#include <net/route.h>
-
-#include <netinet/in.h>
-
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-#include <netinet6/ipv6_addrconf.h>
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * External Globals
- */
-
-extern struct in6_ifaddr *in6_ifaddr;
-extern u_long v6d_retranstime;
-extern int ipv6rsolicit;
-
-void send_nsolicit __P((struct rtentry *, struct ifnet *, struct in6_addr *, int));
-struct mbuf *get_discov_cluster __P((void));
-
-static void send_rsolicit __P((struct ifnet *));
-
-/*----------------------------------------------------------------------
- * Initialize addrconf.
- ----------------------------------------------------------------------*/
-
-void
-addrconf_init()
-{
- timeout(addrconf_timer,NULL,v6d_retranstime * hz);
-}
-
-/*----------------------------------------------------------------------
- * Send a router solicitation out a certain interface.
- ----------------------------------------------------------------------*/
-
-static void
-send_rsolicit(ifp)
- struct ifnet *ifp;
-{
- struct mbuf *solicit = NULL;
- struct ipv6 *header;
- struct ipv6_icmp *icmp;
- struct ipv6_moptions i6mo,*i6mop = NULL;
- struct in6_ifaddr *i6a;
-
- if ((solicit = get_discov_cluster()) == NULL)
- {
- DPRINTF(IDL_ERROR, ("Can't allocate mbuf in send_gsolicit().\n"));
- return;
- }
- header = mtod(solicit,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(header + 1);/* I want the bytes after the hdr. */
-
- bzero(&i6mo,sizeof(struct ipv6_moptions));
- i6mo.i6mo_multicast_ifp = ifp;
- i6mo.i6mo_multicast_ttl = 255;
- i6mop = &i6mo;
- /* Find source link-local or use unspec. */
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp && (i6a->i6a_addrflags & I6AF_LINKLOC) &&
- !(i6a->i6a_addrflags & I6AF_NOTSURE))
- break;
-
- if (i6a == NULL)
- header->ipv6_src = in6addr_any;
- else
- header->ipv6_src = i6a->i6a_addr.sin6_addr;
-
- header->ipv6_dst.s6_addr32[0] = htonl(0xff020000);
- header->ipv6_dst.s6_addr32[1] = 0;
- header->ipv6_dst.s6_addr32[2] = 0;
- header->ipv6_dst.s6_addr32[3] = htonl(2);
-
- header->ipv6_versfl = htonl(0x6f000000);
- header->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
- 255. */
- header->ipv6_nexthdr = IPPROTO_ICMPV6;
- header->ipv6_length = ICMPV6_RSOLMINLEN; /* For now. */
- icmp->icmp_type = ICMPV6_ROUTERSOL;
- icmp->icmp_code = 0;
- icmp->icmp_unused = 0;
- icmp->icmp_cksum = 0;
-
- /*
- * For now, just let ND run its normal course and don't include the link
- * extension.
- */
-
- solicit->m_len = solicit->m_pkthdr.len =
- header->ipv6_length + sizeof(*header);
- icmp->icmp_cksum = in6_cksum(solicit,IPPROTO_ICMPV6,header->ipv6_length,
- sizeof(struct ipv6));
- /*
- * NOTE: The solicit mbuf chain will have a NULL instead of a valid
- * socket ptr. When ipv6_output() calls ipsec_output_policy(),
- * this socket ptr will STILL be NULL. Sooo, the security
- * policy on outbound packets from here will == system security
- * level (set in ipsec_init() of netinet6/ipsec.c). If your
- * system security level is paranoid, then you won't move packets
- * unless you have _preloaded_ keys for at least the ND addresses.
- * - danmcd rja
- */
- ipv6_output(solicit, NULL, IPV6_RAWOUTPUT, i6mop, NULL, NULL);
-}
-
-/*----------------------------------------------------------------------
- * Scan list if in6_ifaddrs and see if any are expired (or can go to
- * being unique).
- ----------------------------------------------------------------------*/
-/* XXX - This function doesn't appear to ever actually remove addresses... */
-void
-addrconf_timer(whocares)
- void *whocares;
-{
- struct in6_ifaddr *i6a = in6_ifaddr;
- int s = splnet();
-
- while (i6a != NULL)
- {
- /*
- * Scan address list for all sorts of neat stuff. Also, in6_ifaddr
- * may be a "prefix list" as well. This will be difficult when
- * an router advert. advertises an on-link prefix, but I don't have
- * (for whatever reason) an address on that link.
- */
-#ifdef __FreeBSD__
- if (i6a->i6a_preferred && i6a->i6a_preferred <= time_second)
-#else /* __FreeBSD__ */
- if (i6a->i6a_preferred && i6a->i6a_preferred <= time.tv_sec)
-#endif /* __FreeBSD__ */
- {
- i6a->i6a_addrflags |= I6AF_DEPRECATED;
- DPRINTF(IDL_EVENT,("Address has been deprecated.\n"));
- }
-#ifdef __FreeBSD__
- if (i6a->i6a_expire && i6a->i6a_expire <= time_second) {
-#else /* __FreeBSD__ */
- if (i6a->i6a_expire && i6a->i6a_expire <= time.tv_sec) {
-#endif /* __FreeBSD__ */
- if (i6a->i6a_addrflags & I6AF_NOTSURE) {
- DPRINTF(IDL_FINISHED, ("Address appears to be unique.\n"));
- i6a->i6a_addrflags &= ~I6AF_NOTSURE;
- /*
- * From what I can tell, addrs that survive DAD are
- * permanent. I won't mark as permanent, but I will zero
- * expiration for now.
- *
- * If this is a link-local address, it may be a good idea to
- * send a router solicit. (But only if I'm a host.)
- */
- if (ipv6rsolicit && IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
- send_rsolicit(i6a->i6a_ifp);
-
- i6a->i6a_expire = 0;
- i6a->i6a_preferred = 0;
- } else {
- /*
- * Do address deletion, and nuke any routes, pcb's, etc.
- * that use this address.
- *
- * As an implementation note, it's probably more likely than
- * not that addresses that get deprecated (see above) will be
- * moved off the master list, as that keeps them away from
- * some things. This is something we couldn't implement in time,
- * however.
- */
- }
- }
-
- i6a = i6a->i6a_next;
- }
- timeout(addrconf_timer,NULL,v6d_retranstime * hz);
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * Send multicast solicit for this address from all 0's. Set timer such
- * that if address is still in in6_ifaddr list, it's good.
- ----------------------------------------------------------------------*/
-
-void
-addrconf_dad(i6a)
- struct in6_ifaddr *i6a;
-{
- int s;
- struct rtentry dummy;
-
- DPRINTF(IDL_GROSS_EVENT,("Sending DAD solicit!\n"));
- s = splnet();
- rt_key(&dummy) = i6a->i6a_ifa.ifa_addr;
- dummy.rt_gateway = NULL;
- /*
- * Set i6a flags and expirations such that it is NOT SURE about uniqueness.
- *
- * What about random delay?
- */
- i6a->i6a_addrflags |= I6AF_NOTSURE; /* Might be done already. */
- i6a->i6a_preferred = 0;
-#ifdef __FreeBSD__
- i6a->i6a_expire = time_second + v6d_retranstime;
-#else /* __FreeBSD__ */
- i6a->i6a_expire = time.tv_sec + v6d_retranstime;
-#endif /* __FreeBSD__ */
- splx(s);
- /*
- * It would be nice if I delayed a random amount of time here.
- */
- send_nsolicit(&dummy, i6a->i6a_ifp, (struct in6_addr *)&in6addr_any, 1);
-}
diff --git a/sys/netinet6/ipv6_addrconf.h b/sys/netinet6/ipv6_addrconf.h
deleted file mode 100644
index 26f6bf76034..00000000000
--- a/sys/netinet6/ipv6_addrconf.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-#ifndef _NETINET6_IPV6_ADDRCONF_H
-#define _NETINET6_IPV6_ADDRCONF_H 1
-
-/*
- * Function definitions.
- */
-void addrconf_init __P((void));
-void addrconf_timer __P((void *));
-void addrconf_dad __P((struct in6_ifaddr *));
-
-#endif /* _NETINET6_IPV6_ADDRCONF_H */
diff --git a/sys/netinet6/ipv6_discovery.c b/sys/netinet6/ipv6_discovery.c
deleted file mode 100644
index a38b97a4712..00000000000
--- a/sys/netinet6/ipv6_discovery.c
+++ /dev/null
@@ -1,3295 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/proc.h>
-#include <sys/malloc.h>
-#include <sys/mbuf.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/time.h>
-#include <sys/kernel.h>
-#include <sys/ioctl.h>
-#include <sys/errno.h>
-#include <sys/syslog.h>
-
-#include <net/if.h>
-#include <net/if_types.h>
-#include <net/if_dl.h>
-#include <net/radix.h>
-#include <net/route.h>
-
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-
-#if __OpenBSD__
-#ifdef IPSEC
-#undef IPSEC
-#endif /* IPSEC */
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#ifdef IPSEC
-/* #include <netsec/ipsec.h> */
-#include <net/netproc.h>
-#include <net/netproc_var.h>
-#endif /* IPSEC */
-
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
-#include <machine/pcpu.h>
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-#ifdef IPSEC
-extern struct netproc_security fixedencrypt;
-extern struct netproc_auth fixedauth;
-#endif /* IPSEC */
-
-/*
- * Globals (and forward function declarations).
- */
-
-struct discq dqhead;
-struct v6router defrtr,nondefrtr;
-struct sockaddr_in6 in6_allones;
-
-void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
-void ipv6_uni_nsolicit __P((struct rtentry *));
-
-/*
- * These should be sysctl-tweakable. See ipv6_var.h for units.
- * They share the common prefix v6d_.
- */
-
-u_long v6d_maxinitrai = MAX_INITIAL_RTR_ADVERT_INTERVAL;
-u_long v6d_maxinitra = MAX_INITIAL_RTR_ADVERTISEMENTS;
-u_long v6d_maxrtrdel = MAX_RTR_RESPONSE_DELAY;
-
-u_long v6d_maxrtsoldel = MAX_RTR_SOLICITATION_DELAY;
-u_long v6d_rtsolint = RTR_SOLICITATION_INTERVAL;
-u_long v6d_maxrtsol = MAX_RTR_SOLICITATIONS;
-
-u_long v6d_maxmcastsol = MAX_MULTICAST_SOLICIT;
-u_long v6d_maxucastsol = MAX_UNICAST_SOLICIT;
-u_long v6d_maxacastdel = MAX_ANYCAST_DELAY_TIME;
-u_long v6d_maxnadv = MAX_NEIGHBOR_ADVERTISEMENTS;
-u_long v6d_minnadvint = MIN_NEIGHBOR_ADVERT_INTERVAL;
-u_long v6d_reachtime = REACHABLE_TIME;
-u_long v6d_retranstime = RETRANS_TIMER;
-u_long v6d_delfirstprobe= DELAY_FIRST_PROBE_TIME;
-/* Need to somehow define random factors. */
-
-u_long v6d_nexthopclean = NEXTHOP_CLEAN_INTERVAL;
-u_long v6d_toolong = NEXTHOP_CLEAN_INTERVAL >> 1; /* Half the cleaning
- interval. */
-
-u_long v6d_down = REJECT_TIMER; /* Dead node routes are marked REJECT
- for this amt. */
-
-/*
- * External globals.
- */
-
-extern struct in6_ifaddr *in6_ifaddr;
-extern struct in6_ifnet *in6_ifnet;
-
-extern int ipv6forwarding;
-extern int ipv6rsolicit;
-extern int ipv6_defhoplmt;
-
-#if __NetBSD__ || __OpenBSD__
-#define if_name if_xname
-#endif /* __NetBSD__ || __OpenBSD__ */
-
-/*
- * General notes:
- *
- * Currently this module does not support encryption/authentication
- * of ND messages. That support is probably needed in some environments.
- * NRL intends to add it in a later release.
- *
- * Please keep this in mind.
- * danmcd rja
- */
-int ipv6_enabled __P((struct ifnet *));
-
-void ipv6_neighbor_timer __P((void *));
-int ipv6_clean_nexthop __P((struct radix_node *, void *));
-void ipv6_nexthop_timer __P((void *));
-void ipv6_discovery_init __P((void));
-struct mbuf *get_discov_cluster __P((void));
-void send_nsolicit __P((struct rtentry *, struct ifnet *, struct in6_addr *, int));
-struct mbuf *ipv6_discov_pullup __P((struct mbuf *, int));
-struct v6router *ipv6_add_defrouter_advert __P((struct in6_addr *, int lifetime, struct ifnet *));
-struct v6router *ipv6_add_defrouter_rtrequest __P((struct rtentry *));
-void ipv6_nadvert __P((struct in6_ifaddr *, struct ifnet *, struct rtentry *, uint32_t));
-static int update_defrouter __P((struct v6router *, struct ipv6_icmp *));
-int ipv6_delete_defrouter __P((struct v6router *));
-static void prefix_concat __P((struct in6_addr *, struct in6_addr *, struct in6_addr *));
-struct rtentry *ipv6_new_neighbor __P((struct sockaddr_in6 *, struct ifnet *));
-int ipv6_discov_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *));
-void tunnel_parent __P((struct rtentry *));
-void tunnel_parent_clean __P((struct rtentry *));
-void tunnel_child __P((struct rtentry *));
-void tunnel_child_clean __P((struct rtentry *));
-
-/*
- * Functions and macros.
- */
-
-/*----------------------------------------------------------------------
- void padto(u_char *ptr, u_long mask, int alignto)
-
- Examples: padto(foo, 0x3, 0) will get 4-byte alignment
- padto(foo, 0x7, 6) will get to 2 bytes before an 8-byte
- boundary.
-
- Padding generated will be most efficient.
-
- This is now deprecated for discovery, but it still has
- uses in generating IPv6 options (the option bags).
-
-----------------------------------------------------------------------*/
-#define padto(ptr,mask,alignto)\
-{\
- int difference = 0;\
- \
- if (((u_long)(ptr) & (mask)) != (alignto))\
- {\
- difference = (((u_long)(ptr) & ~(mask)) + (alignto)) - (long)(ptr);\
- if (difference < 0)\
- difference += ((mask) +1);\
- if (difference == 1)\
- (ptr)[0] = EXT_PAD1;\
- else\
- {\
- (ptr)[0] = EXT_PADN;\
- (ptr)[1] = (difference) - 2; /* difference >= 2 always here. */\
- bzero((ptr)+2,(ptr)[1]);\
- }\
- (ptr) += difference;\
- }\
-}
-
-
-/*----------------------------------------------------------------------
- * ipv6_neighbor_timer():
- * Scan neighbor list and resend (1) mcast ND solicits for all
- * neighbors in INCOMPLETE state and (2) unicast ND solicits
- * for all neighbors in PROBE state
- ----------------------------------------------------------------------*/
-void
-ipv6_neighbor_timer(whocares)
- void *whocares;
-{
- struct discq *dq;
- int s = splnet();
- struct rtentry *rt;
-
- /*
- * 1. Scan all neighbors (go down dq list) and retransmit those which
- * are still in INCOMPLETE.
- * 2. Also, those in PROBE state should have unicast solicits sent.
- * 3. ...
- */
-
- dq = dqhead.dq_next;
- while (dq != &dqhead)
- {
- struct discq *current = dq;
-
- dq = dq->dq_next; /* Before any rtrequest */
-
- rt = current->dq_rt;
-#ifdef __FreeBSD__
- if (rt->rt_rmx.rmx_expire <= time_second)
-#else /* __FreeBSD__ */
- if (rt->rt_rmx.rmx_expire <= time.tv_sec)
-#endif /* __FreeBSD__ */
- {
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
-
- /*
- * Gotta do SOMETHING...
- */
- if (sdl->sdl_alen == 0) {
- /* If RTF_REJECT, then delete... */
- if (rt->rt_flags & RTF_REJECT)
- {
- DPRINTF(GROSSEVENT,
- ("neighbor_timer() deleting rejected route\n"));
- rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
- continue;
- }
-
- /* Am in INCOMPLETE mode... */
-
- if (current->dq_unanswered >= 0) { /* If not newly created... */
- if (current->dq_unanswered >= v6d_maxmcastsol)
- {
- /* Dead node. For now, just delete.
- May want to do an RTF_REJECT entry.
- May also want to send ICMP errors on enqueued packet(s).
- May also want to do routing cleanup if a defult router.
- XXX */
- DPRINTF(GROSSEVENT,("Deleting dead node... "));
- rt->rt_flags |= RTF_REJECT;
- /*
- * If there's no ifa, add an aribitrary one.
- * (May be unnecessary if rtfree() ifa != NULL check
- * is in place.)
- *
- * It turns out that adding a dummy ifa causes
- * problems for subsequent packets that successfully
- * rtalloc-ed this route (since rtalloc1 doesn't
- * check if a route is marked RTF_REJECT... the
- * check is done at a further down the stack, e.g.,
- * ether_output(). This causes ipv6_output to
- * call ipv6_nsolicit (with an incomplete route that
- * has an ifa but no ifp). ipv6_nsolicit then uses
- * the address of the dummy ifa as the source address
- * to be used in send_nsolicit which may panic
- * if the address turns out to not have a link-local
- * address (e.g. loopback).
- *
- * The solution, for now, is to check for RTF_REJECT
- * in ipv6_nsolicit. This may change in the future.
- */
- if (rt->rt_ifa == NULL)
- {
- rt->rt_ifa = (struct ifaddr *)in6_ifaddr;
- rt->rt_ifa->ifa_refcnt++;
- }
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second + v6d_down;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec + v6d_down;
-#endif /* __FreeBSD__ */
-
- /* ICMPv6 error part. */
- while (current->dq_queue != NULL)
- {
- struct mbuf *badpack = current->dq_queue;
-
- current->dq_queue = badpack->m_nextpkt;
- /* Set rcvif for icmp_error, is this good? */
- badpack->m_pkthdr.rcvif = current->dq_rt->rt_ifp;
- ipv6_icmp_error(badpack,ICMPV6_UNREACH,
- ICMPV6_UNREACH_ADDRESS,0);
- }
- }
- else
- {
- /* Send another solicit. The ipv6_nsolicit call will
- ensure the rate-limitation. Send the queued packet
- to ipv6_nsolicit for source address determination. */
- ipv6_nsolicit(rt->rt_ifp,NULL,rt);
- }
- }
- }
- else
- /*
- * Am in either PROBE mode or REACHABLE mode.
- *
- * Either way, that will (in the case of REACHABLE->PROBE)
- * or might (in the case of PROBE->INCOMPLETE) change.
- */
- if (current->dq_unanswered >= 0) { /* PROBE */
- if (current->dq_unanswered >= v6d_maxucastsol) {
- /* PROBE -> INCOMPLETE */
- if (rt->rt_refcnt > 0) {
- /* The code below is an optimization for the case
- * when someone is using this route :
- * - defer actual deletion until mcast solicits fail.
- *
- * Q: Do we still want to do this?
- */
- sdl->sdl_alen = 0;
- current->dq_unanswered = 0;
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
-#endif /* __FreeBSD__ */
- } else {
- /*
- * Unicast probes failed and no one is using this route.
- * Delete and let address resolution take its course.
- *
- * At this point, I have an ifa, so I don't need to add
- * one.
- */
- rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
- }
- } else {
- /* Retry PROBE */
- ipv6_uni_nsolicit(rt);
- }
- } else {
- /*
- * Do nothing if REACHABLE expires. Only on output,
- * BUT... some of these might hang around for too long.
- * See ipv6_clean_nexthop for details on a solution.
- *
- * I am now in the STALE state.
- */
- }
- }
- }
-
- timeout(ipv6_neighbor_timer,NULL,v6d_retranstime * hz);
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_clean_nexthop:
- * Delete requested route, if either the route is both GATEWAY and HOST
- * and not held (or the caller REALLY requests it).
- ----------------------------------------------------------------------*/
-int
-ipv6_clean_nexthop(rn, arg)
- struct radix_node *rn;
- void *arg;
-{
- struct rtentry *rt = (struct rtentry *)rn;
-#ifdef __alpha__
- long hardcore = (long)arg;
-#else
- int hardcore = (int)arg;
-#endif
- /* If hardcore is not zero, then the caller REALLY insists that we
- delete the route. A drain function would call this with hardcore != 0. */
-
- DPRINTF(GROSSEVENT,("Entering ipv6_clean_nexthop... "));
- if (!(rt->rt_flags & RTF_HOST) ||
- !(rt->rt_flags & (RTF_LLINFO|RTF_TUNNEL|RTF_GATEWAY)) ||
- /* Keep the static host routes; unless told to delete? */
- ( (rt->rt_flags & (RTF_STATIC)) && !hardcore) ||
- (!hardcore && rt->rt_refcnt > 0))
- {
- /*
- * Unless asked (i.e. a hardcore clean :), don't delete held routes.
- * Only delete host routes (that aren't my own addresses) this way.
- */
-
- DPRINTF(GROSSEVENT,("not deleting.\n"));
- return 0;
- }
-
- if ((rt->rt_flags & RTF_LLINFO) && rt->rt_refcnt == 0)
- {
- struct discq *dq = (struct discq *)rt->rt_llinfo;
- /*
- * This is a neighbor cache entry. Delete only if not held, and
- * if in STALE state (i.e. pre-PROBE) for too long.
- */
-#ifdef __FreeBSD__
- if (rt->rt_rmx.rmx_expire + v6d_toolong >= time_second &&
-#else /* __FreeBSD__ */
- if (rt->rt_rmx.rmx_expire + v6d_toolong >= time.tv_sec &&
-#endif /* __FreeBSD__ */
- dq->dq_unanswered <= 0)
- return 0;
- /*
- * In case clean_nexthop catches one of these non-determinate
- * neighbor entries...
- * (May be unnecessary if rtfree() ifa != NULL check is in place.)
- */
- if (rt->rt_ifa == NULL)
- {
- rt->rt_ifa = (struct ifaddr *)in6_ifaddr;
- rt->rt_ifa->ifa_refcnt++;
- }
- }
-
- /*
- * At this point, the route is RTF_HOST, and is either a
- * force or a legitimate node to be cleaned. Call RTM_DELETE...
- */
- DPRINTF(GROSSEVENT,("deleting.\n"));
-
- return rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_nexthop_timer():
- * Keeps routing table from getting too filled up with off-net host
- * routes.
- *
- * NOTES:
- * Later might want to put some intelligence in here like __FreeBSD__ does,
- * but for now just do an rnh->rnh_walktree() every so often.
- *
- * A smarter function might:
- *
- * - Keep a count of how many off-net host routes we have.
- * - Maintain upper bounds on said count.
- * - Avoid deletion if said count is low.
- * - Etc.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_nexthop_timer(whocares)
- void *whocares;
-{
- struct radix_node_head *rnh = rt_tables[AF_INET6];
- int s, rc;
-
- DPRINTF(IDL_EVENT,("Entering ipv6_nexthop_timer().\n"));
- s = splnet();
- rc = rnh->rnh_walktree(rnh, ipv6_clean_nexthop, (void *)0);
- splx(s);
-
- DDO(IDL_ERROR,if (rc != 0)\
- printf("walktree rc is %d in nexthop_timer.\n", rc));
-
- timeout(ipv6_nexthop_timer,NULL,v6d_nexthopclean * hz);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_discovery_init():
- * Initializes ND data structures.
- ----------------------------------------------------------------------*/
-void
-ipv6_discovery_init()
-{
- dqhead.dq_next = dqhead.dq_prev = &dqhead;
- defrtr.v6r_next = defrtr.v6r_prev = &defrtr;
- nondefrtr.v6r_next = nondefrtr.v6r_prev = &nondefrtr;
-
- in6_allones.sin6_family = AF_INET6;
- in6_allones.sin6_len = sizeof(in6_allones);
-
- /* Other fields are bzeroed. */
- in6_allones.sin6_addr.s6_addr32[0] = 0xffffffff;
- in6_allones.sin6_addr.s6_addr32[1] = 0xffffffff;
- in6_allones.sin6_addr.s6_addr32[2] = 0xffffffff;
- in6_allones.sin6_addr.s6_addr32[3] = 0xffffffff;
-
- timeout(ipv6_nexthop_timer,NULL,hz);
- timeout(ipv6_neighbor_timer,NULL,hz);
-}
-
-/*----------------------------------------------------------------------
- * get_discov_cluster():
- * Allocates a single-cluster mbuf and sets it up for use by ND.
- *
- ----------------------------------------------------------------------*/
-
-struct mbuf *
-get_discov_cluster()
-{
- struct mbuf *rc;
-
- MGET(rc, M_DONTWAIT, MT_HEADER);
- if (rc != NULL)
- {
- MCLGET(rc,M_DONTWAIT);
- if ((rc->m_flags & M_EXT) == 0)
- {
- m_free(rc);
- rc = NULL;
- }
- }
-
- /* Make it a pkthdr appropriately. */
- if (rc)
- {
- rc->m_flags |= M_PKTHDR;
- rc->m_pkthdr.rcvif = NULL;
- rc->m_pkthdr.len = 0;
- rc->m_len = 0;
- }
- return rc;
-}
-
-/*----------------------------------------------------------------------
- * send_nsolicit():
- * Send a neighbor solicit for destination in route entry rt,
- * across interface pointed by ifp. Use source address in src (see below),
- * and either unicast or multicast depending on the mcast flag.
- *
- * NOTES: The entry pointed to by rt MUST exist.
- * If the caller has to, it may cruft up a dummy rtentry with at least
- * a valid rt_key() for the neighbor's IPv6 address, and maybe
- * a dummy rt_gateway.
- *
- * If src points to 0::0 address, set M_DAD flag so ipv6_output() doesn't
- * try and fill in a source address.
- ----------------------------------------------------------------------*/
-void
-send_nsolicit(rt,ifp,src,mcast)
- struct rtentry *rt;
- struct ifnet *ifp;
- struct in6_addr *src; /* Source address of invoking packet... */
- int mcast;
-{
- struct mbuf *solicit = NULL;
- struct ipv6 *header;
- struct ipv6_icmp *icmp;
- struct ipv6_moptions i6mo,*i6mop = NULL;
- struct in6_ifaddr *i6a;
- struct sockaddr_in6 *neighbor = (struct sockaddr_in6 *)rt_key(rt);
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
-
- DPRINTF(IDL_EVENT,("Entering send_nsolicit with\n"));
- DPRINTF(IDL_EVENT,("rt=0x%lx\n",(unsigned long)rt));
- DPRINTF(IDL_EVENT,("ifp:\n"));
- DDO(IDL_EVENT,dump_ifp(ifp));
- DPRINTF(IDL_EVENT,("src:\n"));
- DDO(IDL_EVENT,dump_in6_addr(src));
- DPRINTF(IDL_EVENT,("mcast=%d\n",mcast));
-
- if ((solicit = get_discov_cluster()) == NULL)
- {
- DPRINTF(IDL_ERROR, ("Can't allocate mbuf in send_gsolicit().\n"));
- return;
- }
-
- header = mtod(solicit,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(header + 1);/* I want the bytes after the hdr. */
-
- if (mcast)
- {
- bzero(&i6mo,sizeof(struct ipv6_moptions));
- i6mo.i6mo_multicast_ifp = ifp;
- i6mo.i6mo_multicast_ttl = 255; /* Must set, hoplimit is otherwise
- overridden! */
- i6mop = &i6mo;
-
- header->ipv6_dst.s6_addr32[0] = htonl(0xff020000);
- header->ipv6_dst.s6_addr32[1] = 0;
- header->ipv6_dst.s6_addr32[2] = htonl(1);
- header->ipv6_dst.s6_addr32[3] = neighbor->sin6_addr.s6_addr32[3] | htonl(0xff000000);
- }
- else
- {
- if (sdl == NULL || sdl->sdl_alen == 0)
- {
- DPRINTF(IDL_ERROR, ("Can't unicast if I don't know address.\n"));
- m_freem(solicit);
- return;
- }
- header->ipv6_dst = neighbor->sin6_addr;
- /* If rmx_expire is not already set, set it to avoid chicken-egg loop. */
- }
-
- header->ipv6_versfl = htonl(0x6f000000);
- header->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
- 255. */
- header->ipv6_nexthdr = IPPROTO_ICMPV6;
- header->ipv6_length = ICMPV6_NSOLMINLEN; /* For now. */
-
- /*
- * Now find source address for solicit packet.
- *
- * Rules on src address selection:
- * if NULL find link-local for i/f.
- * If no link-local, then use unspec source.
- * (If unspec source and !mcast, fail)
- * if UNSPEC src
- * if not mcast, fail.
- * if a real address, sanity check to see if it's indeed my address.
- * Additionally check to see if it matches the
- * outbound ifp requested.
- */
-
- if (src == NULL)
- {
- /* Find source link-local or use unspec. */
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp && (i6a->i6a_addrflags & I6AF_LINKLOC) &&
- !(i6a->i6a_addrflags & I6AF_NOTSURE))
- break;
- if (i6a == NULL) {
- if (mcast)
- header->ipv6_src = in6addr_any;
- else {
- DPRINTF(IDL_ERROR, ("send_nsolicit: Unicast solicit w/o any known source address.\n"));
- m_freem(solicit);
- return;
- }
- } else
- header->ipv6_src = i6a->i6a_addr.sin6_addr;
- }
- else
- if (IN6_IS_ADDR_UNSPECIFIED(src)) {
- if (!mcast) {
- DPRINTF(IDL_ERROR, ("send_nsolicit: Unicast DAD solicit.\n"));
- m_freem(solicit);
- return;
- } else {
- DPRINTF(GROSSEVENT, ("Sending DAD solicit.\n"));
- solicit->m_flags |= M_DAD;
- header->ipv6_src = in6addr_any;
- }
- } else {
- struct in6_ifaddr *llsave = NULL;
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- {
- /*
- * Might want to put llsave code where it's actually needed.
- * (i.e. take it out of this loop and put it in i6a == NULL case.)
- */
- if (i6a->i6a_ifp == ifp && IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
- llsave = i6a;
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, src))
- break;
- }
- if (i6a == NULL)
- {
- /*
- * This path is entered by a router forwarding packets to a yet
- * undiscovered neighbor.
- */
-#ifdef DIAGNOSTIC
- if (llsave == NULL)
- panic("No link-local for this address.");
-#endif /* DIAGNOSTIC */
- header->ipv6_src = llsave->i6a_addr.sin6_addr;
- }
- else if (i6a->i6a_ifp != ifp)
- {
- /*
- * Q: Is this a reason to be panicking?
- * A: For now, no.
- */
- DDO(IDL_ERROR,\
- printf("WARNING: Src addr fubar Addr, ifp, i6a: ");\
- dump_in6_addr(src);dump_ifp(ifp);\
- dump_ifa((struct ifaddr *)i6a));
-#ifdef DIAGNOSTIC
- if (llsave == NULL)
- panic("No link-local for this address.");
-#endif /* DIAGNOSTIC */
- header->ipv6_src = llsave->i6a_addr.sin6_addr;
- }
- else header->ipv6_src = *src;
- }
-
- /* Have source address, now create outbound packet */
- icmp->icmp_type = ICMPV6_NEIGHBORSOL;
- icmp->icmp_code = 0;
- icmp->icmp_unused = 0;
- icmp->icmp_cksum = 0;
- icmp->icmp_nsoltarg = neighbor->sin6_addr;
-
- /*
- * Put ND extensions here, if any.
- * This next code fragment could be its own function if there
- * were enough callers of the fragment to make that sensible.
- *
- * This might also want to be its own function because of variations in
- * link (ifp) type.
- */
- if (ifp->if_addrlen != 0 && !IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src))
- {
- struct icmp_exthdr *ext = (struct icmp_exthdr *)&(icmp->icmp_nsolext);
- struct ifaddr *ifa;
- struct sockaddr_dl *lsdl;
-
- ext->ext_id = EXT_SOURCELINK;
-
- switch (ifp->if_type)
- {
- case IFT_ETHER:
- ext->ext_length = 1 + ((ifp->if_addrlen + 1) >> 3);
-#ifdef __FreeBSD__
- for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if (ifa->ifa_addr->sa_family == AF_LINK)
- break;
- if (ifa == NULL)
- {
- DPRINTF(IDL_ERROR, ("Can't find link addr. in nsolicit().\n"));
- m_freem(solicit);
- return;
- }
- lsdl = (struct sockaddr_dl *)ifa->ifa_addr;
- bzero(ext->ext_data,ext->ext_length * 8 - 2);
- bcopy(LLADDR(lsdl),ext->ext_data,ifp->if_addrlen);
- break;
- default:
- DPRINTF(IDL_ERROR,("DANGER: unk. link type for n. sol.\n"));
- break;
- }
-
- header->ipv6_length += ext->ext_length <<3;
- }
-
- solicit->m_len = solicit->m_pkthdr.len =
- header->ipv6_length + sizeof(*header);
- icmp->icmp_cksum = in6_cksum(solicit,IPPROTO_ICMPV6,header->ipv6_length,
- sizeof(struct ipv6));
-
- /*
- * NOTE: We pass in a NULL instead of a valid
- * socket ptr. When ipv6_output() calls ipsec_output_policy(),
- * this socket ptr will STILL be NULL. Sooo, the security
- * policy on outbound packets from here will == system security
- * level (set in ipsec_init()). If your
- * system security level is paranoid, then you won't move packets
- * unless you have _preloaded_ keys for at least the ND addresses.
- * - danmcd rja
- */
-
- ipv6_output(solicit, NULL, IPV6_RAWOUTPUT, i6mop, NULL, NULL);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_nsolicit:
- * Send an IPv6 Neighbor Solicit message
- *
- * NOTES:
- * State checking is needed so that a neighbor can be declared unreachable.
- *
- * newrt == NULL iff this is a virgin packet with no known i/f,
- * otherwise valid newrt MUST be passed in.
- *
- * If ifp == NULL, ipv6_nsolicit() executes potentially multihomed code.
- * (For now, I guess I should pick an arbitrary interface. For single-homed
- * nodes, this is the optimal behavior.)
- * This is called when a neighbor is in INCOMPLETE state.
- *
- * The NUD INCOMPLETE and new-neighbor detection state belongs here.
- *
- * Not yet clear how will implement duplicate address detection.
- * (See send_nsolicit.)
- * Should I splnet() inside here?
- ----------------------------------------------------------------------*/
-void
-ipv6_nsolicit(outifp,outgoing,newrt)
- struct ifnet *outifp;
- struct mbuf *outgoing;
- struct rtentry *newrt;
-{
- struct discq *dq;
- struct ipv6 *ipv6 = (outgoing == NULL) ? NULL :
- mtod(outgoing,struct ipv6 *);
-
- /*
- * ASSERT: The header is pulled up, and has either the
- * unspecified address or one of MY valid ipv6 addresses.
- */
-#if __NetBSD__ || __FreeBSD__ || __OpenBSD__
- struct ifnet *ifp = (outifp == NULL)?ifnet.tqh_first:outifp;
-#else /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
- struct ifnet *ifp = (outifp == NULL)?ifnet:outifp;
-#endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
-
- DPRINTF(IDL_EVENT,("Entering IPV6_NSOLICIT.\n"));
- DPRINTF(IDL_EVENT,("outifp:\n"));
- DDO(IDL_EVENT,dump_ifp(outifp));
- DPRINTF(IDL_EVENT,("outgoing=0x%lx\n",(unsigned long)outgoing));
- DPRINTF(IDL_EVENT,("newrt:\n"));
- DDO(IDL_EVENT,dump_rtentry(newrt));
- DPRINTF(IDL_EVENT,("ifp:\n"));
- DDO(IDL_EVENT,dump_ifp(ifp));
-
- if (newrt == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("ipv6_nsolicit() called with newrt == NULL.\n"));
- m_freem(outgoing);
- return;
- }
-
- /*
- * Route with RTF_REJECT has dummy ifa assigned to it in
- * ipv6_neighbor_timer(). We shouldn't need to send out
- * another solicit if the route is already marked REJECT.
- */
- if (newrt->rt_flags & RTF_REJECT)
- {
- DPRINTF(ERROR, ("ipv6_nsolicit passed a RTF_REJECT route\n"));
- m_freem(outgoing);
- return;
- }
-
- dq = (struct discq *)newrt->rt_llinfo;
- DPRINTF(GROSSEVENT,("dq:\n"));
- DDO(GROSSEVENT,dump_discq(dq));
- DDO(IDL_ERROR,if (dq == NULL) \
- panic("dq == NULL in nsolicit()."); \
- if (dq->dq_rt != newrt)\
- panic("dq <-> rt mismatch in nsolicit.");\
- {\
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)\
- newrt->rt_gateway;\
- \
- if (sdl->sdl_alen != 0)\
- panic("sdl_alen not 0 in nsolicit!");
- });
- DDO(GROSSEVENT,if (outifp == NULL)
- printf("nsolicit passed in outifp of NULL.\n"));
-
- /*
- * If new entry, set up certain variables.
- */
- if (newrt->rt_rmx.rmx_expire == 0)
- dq->dq_unanswered = 0;
-
- /*
- * Currently queue the last packet sent. May want to
- * queue up last n packets.
- */
-
- if (outgoing != NULL)
- {
- if (dq->dq_queue != NULL)
- m_freem(dq->dq_queue);
- dq->dq_queue = outgoing;
- }
-
- /*
- * We want to rate-limit these, so only send out if the time has
- * expired.
- */
-#ifdef __FreeBSD__
- if (newrt->rt_rmx.rmx_expire < time_second)
-#else /* __FreeBSD__ */
- if (newrt->rt_rmx.rmx_expire < time.tv_sec)
-#endif /* __FreeBSD__ */
- {
- DPRINTF(IDL_EVENT,
- ("ipv6_nsolict: solicit time expired -- send another one!\n"));
-#ifdef __FreeBSD__
- newrt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
-#else /* __FreeBSD__ */
- newrt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
-#endif /* __FreeBSD__ */
- dq->dq_unanswered++; /* Overload for "number of m-cast probes sent". */
- do
- {
- if (ifp->if_type != IFT_LOOP && ipv6_enabled(ifp))
- {
- if (ipv6 == NULL && dq->dq_queue != NULL) {
- ipv6 = mtod(dq->dq_queue,struct ipv6 *);
- DPRINTF(IDL_EVENT,("v6_solicit: grabbing src from dq!\n"));
- }
- DPRINTF(IDL_EVENT,("dq b/f calling send_nsolicit:\n"));
- DDO(IDL_EVENT,dump_discq(dq));
- DPRINTF(IDL_EVENT,("ipv6 hdr b/f calling send_nsolicit:\n"));
- DDO(IDL_EVENT,dump_ipv6(ipv6));
- send_nsolicit(newrt,ifp,(ipv6 == NULL) ? NULL : &ipv6->ipv6_src,
- 1);
- }
-
- if (outifp == NULL)
-#ifdef __FreeBSD__
- ifp = ifp->if_link.tqe_next;
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- ifp = ifp->if_list.tqe_next;
-#else /* __NetBSD__ || __OpenBSD__ */
- ifp = ifp->if_next;
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- else ifp = NULL;
- }
- while (ifp != NULL);
- }
-}
-
-/*----------------------------------------------------------------------
- * ipv6_onlink_query():
- * If I have no route, and now assume that a destination is on-link,
- * then I should create a route entry (I'll have to do it using raw
- * radix code, because I don't know which interface the destination is
- * for yet.)
- *
- * I should probably optimize this for single-homed hosts, such that
- * the route lookup after doing this will return with an rt_ifa.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_onlink_query(dst)
- struct sockaddr_in6 *dst; /* Dest. sockaddr_in6 */
-{
- struct rtentry *rt;
- struct sockaddr_dl *sdl;
- struct in6_ifnet *i6ifp = in6_ifnet;
- struct ifnet *ifp;
- int s = splnet();
-
- /*
- * Pick an interface, trick ipv6_new_neighbor() into adding a route,
- * then blank out the interface-specific stuff.
- */
-
- while (i6ifp != NULL && (i6ifp->i6ifp_ifp->if_flags & IFF_LOOPBACK))
- i6ifp = i6ifp->i6ifp_next;
-
- if (i6ifp == NULL)
- {
- DPRINTF(IDL_ERROR,("Oooh boy. No non-loopback i6ifp.\n"));
- splx(s);
- return;
- }
- ifp = i6ifp->i6ifp_ifp;
-
- rt = ipv6_new_neighbor(dst,ifp);
- if (rt == NULL)
- {
- DPRINTF(IDL_ERROR,("ipv6_new_neighbor failed in onlink_query.\n"));
- splx(s);
- return;
- }
-
- if (rt->rt_gateway->sa_family != AF_LINK) {
- DPRINTF(IDL_ERROR,("onlink_query returns route with non AF_LINK gateway.\n"));
- splx(s);
- return;
- }
-
- sdl = (struct sockaddr_dl *)rt->rt_gateway;
- sdl->sdl_index = 0;
- sdl->sdl_nlen = 0;
- sdl->sdl_alen = 0;
- sdl->sdl_slen = 0;
-
- rt->rt_ifp = NULL;
- rt->rt_ifa->ifa_refcnt--;
- rt->rt_ifa = NULL;
- rt->rt_rmx.rmx_mtu = 0;
-
- /*
- * I think I'm cool, now. So send a multicast nsolicit.
- */
- ipv6_nsolicit(NULL,NULL,rt);
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_verify_onlink():
- * Verify sockaddr_in6 dst has been determined to be a neighbor
- * or not. Will only work on an output/route lookup caused by
- * a process that's trapped in the kernel. I do a tsleep of the
- * current process while neighbor discovery runs its course.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_verify_onlink(dst)
- struct sockaddr_in6 *dst;
-{
- struct rtentry *rt = NULL;
-#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
- struct proc *p = curproc; /* XXX */
-#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
- struct proc *p = PCPU(curproc); /* XXX */
-#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
-
- if (!p) /* i.e. if I'm an interrupt-caused output... */
- return 1;
-
- do
- {
- if (rt != NULL)
- rt->rt_refcnt--;
- /*
- * Sleep this process, then lookup neighbor route to see if it's
- * been updated.
- */
- switch (tsleep((caddr_t)&rt, PCATCH , "mhomed", hz))
- {
- case 0:
- DPRINTF(IDL_ERROR,("How did I awaken?\n"));
- /* Fallthrough to... */
- case EWOULDBLOCK:
- break;
- case EINTR:
- case ERESTART:
- return EINTR;
- }
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)dst,0, 0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)dst,0);
-#endif /* __FreeBSD__ */
- }
- while (rt && rt->rt_ifa == NULL);
-
- DPRINTF(FINISHED, ("verify_onlink came up with rt:\n"));
- DDO(FINISHED, dump_rtentry(rt));
- if (rt == NULL)
- return -1;
- rt->rt_refcnt--;
- return 0;
-}
-
-/*----------------------------------------------------------------------
- * ipv6_uni_nsolicit():
- * Send a unicast neighbor solicit to neighbor specified in rt.
- * Also update the next-probe time.
- *
- * NOTES:
- * This is so small that it might be better inlined.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_uni_nsolicit(rt)
- struct rtentry *rt;
-{
- struct discq *dq = (struct discq *)rt->rt_llinfo;
-
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second + v6d_retranstime;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec + v6d_retranstime;
-#endif /* __FreeBSD__ */
- /* If ifa->ifa_addr doesn't work, revert to NULL. */
- send_nsolicit(rt,rt->rt_ifp,&(I6A_SIN(rt->rt_ifa)->sin6_addr),0);
- dq->dq_unanswered ++;
-}
-
-
-/*----------------------------------------------------------------------
- *ipv6_nadvert():
- * Construct an IPv6 neighbor advertisement,
- * and send it out via either unicast or multicast.
- *
- * NOTES:
- * Might later add a proxy advertisement bit or anycast bit to the flags
- * parameter.
- ----------------------------------------------------------------------*/
-void
-ipv6_nadvert(i6a,ifp,dstrt,flags)
- struct in6_ifaddr *i6a;
- struct ifnet *ifp;
- struct rtentry *dstrt; /* If null, send m-cast neighbor advert. */
- uint32_t flags;
-{
- struct ipv6_moptions i6mo,*i6mop = NULL;
- struct mbuf *advert;
- struct ipv6 *ipv6;
- struct ipv6_icmp *icmp;
-
- advert = get_discov_cluster();
- if (advert == NULL)
- return;
-
- ipv6 = mtod(advert,struct ipv6 *);
- icmp = (struct ipv6_icmp *)&(advert->m_data[sizeof(struct ipv6)]);
-
- ipv6->ipv6_src = i6a->i6a_addr.sin6_addr; /* May be different for
- proxy. */
- ipv6->ipv6_versfl = htonl(0x6f000000);
- ipv6->ipv6_hoplimit = 255; /* Guaranteed to be intra-link if arrives with
- 255. */
- ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
- ipv6->ipv6_length = ICMPV6_NADVMINLEN; /* For now */
- icmp->icmp_type = ICMPV6_NEIGHBORADV;
- icmp->icmp_code = 0;
- icmp->icmp_cksum = 0;
- icmp->icmp_nadvbits = flags;
-
- /* Flags in ipv6_icmp.h are in endian-specific #ifdefs, so
- there is no need to HTONL(icmp->icmp_nadvbits); */
-
- /* If proxy advert, set proxy bits. */
- icmp->icmp_nadvaddr = i6a->i6a_addr.sin6_addr;
-
- if (dstrt == NULL)
- {
- struct in6_addr addr = IN6ADDR_ALLNODES_INIT;
-
- i6mo.i6mo_multicast_ifp = ifp;
- i6mo.i6mo_multicast_ttl = 255; /* Must set. */
- i6mop = &i6mo;
-
- ipv6->ipv6_dst = addr;
- }
- else ipv6->ipv6_dst = ((struct sockaddr_in6 *)rt_key(dstrt))->sin6_addr;
-
- /*
- * Set up extensions (if any)
- */
-
- /*
- * Perhaps create a separate function to look through interface's address
- * list to find my data link address, but if this would really be called
- * enough other places...
- */
- if (i6a->i6a_ifp->if_addrlen != 0)
- {
- struct icmp_exthdr *ext = (struct icmp_exthdr *)&(icmp->icmp_nadvext);
- struct ifaddr *ifa;
- struct sockaddr_dl *lsdl; /* Target's Local Link-layer Sockaddr */
-
- ext->ext_id = EXT_TARGETLINK;
- switch (i6a->i6a_ifp->if_type)
- {
- case IFT_ETHER:
- ext->ext_length = 1 + ((ifp->if_addrlen +1) >> 3);
-#ifdef __FreeBSD__
- for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if (ifa->ifa_addr->sa_family == AF_LINK)
- break;
- if (ifa == NULL)
- {
- DPRINTF(IDL_ERROR, ("Can't find link addr. in nadvert().\n"));
- m_freem(advert);
- return;
- }
- lsdl = (struct sockaddr_dl *)ifa->ifa_addr;
- bzero(ext->ext_data,ext->ext_length*8 - 2);
- bcopy(LLADDR(lsdl),ext->ext_data,ifp->if_addrlen);
- break;
- default:
- DPRINTF(IDL_ERROR,("DANGER: sending n. adv on unk. link type.\n"));
- break;
- }
- ipv6->ipv6_length += ext->ext_length <<3;
- }
-
- advert->m_len = advert->m_pkthdr.len = ipv6->ipv6_length + sizeof(*ipv6);
- icmp->icmp_cksum = in6_cksum(advert,IPPROTO_ICMPV6,ipv6->ipv6_length,
- sizeof(struct ipv6));
- ipv6_output(advert,NULL,IPV6_RAWOUTPUT,i6mop, NULL, NULL);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_routersol_input():
- * Handle reception of Router Solicit messages.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_routersol_input(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- /*
- * Return and let user-level process deal with it.
- */
-}
-
-/* Add a default router in response to a route request (RTM_ADD/RTM_RESOLVE) */
-
-struct v6router *ipv6_add_defrouter_rtrequest(struct rtentry *inrt)
-{
-#if 0
- struct rtentry *rt, *defrt=NULL;
- struct v6router *v6r;
- struct sockaddr_in6 sin6;
-#endif /* 0 */
-
- DPRINTF(GROSSEVENT, ("ipv6_add_defrouter_rtrequest(inrt=%08x)\n", OSDEP_PCAST(inrt)));
- DDO(GROSSEVENT, dump_rtentry(inrt));
-
-#if 0
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
-
- if (!(rt = inrt->rt_gwroute))
-#ifdef __FreeBSD__
- if (!(rt = inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1, 0UL)))
-#else /* __FreeBSD__ */
- if (!(rt = inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1)))
-#endif /* __FreeBSD__ */
- return NULL;
-
- if (!(v6r = malloc(sizeof(*v6r),M_DISCQ,M_NOWAIT))) {
- DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_rtrequest: Can't allocate router list entry.\n"));
- /* Q: Do I want to delete the neighbor entry? */
- /*rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
- return NULL;
- };
-
- /*
- * Assertion:
- * Tunnel default routes are added only by manual
- * (i.e. user-level) adds and deletes.
- * If that's true, then the route in the v6r structure is "held" by
- * the default route. Oooh, I'd better make sure that default router
- * entries hold their... wait a minute, when I delete neighbors, the
- * default router neigbors are searched in the list (and nuked...).
- */
-
- bzero(v6r,sizeof(*v6r));
- v6r->v6r_rt = rt;
- v6r->v6r_children.v6c_next = v6r->v6r_children.v6c_prev = &v6r->v6r_children;
-
- if (!inrt->rt_gwroute) {
- /*
- * First default router added, and not a manual add. Add
- * default route.
- */
- struct sockaddr_in6 mask;
-
- bzero(&mask,sizeof(mask));
- mask.sin6_family = AF_INET6;
- mask.sin6_len = sizeof(struct sockaddr_in6);
-
- sin6.sin6_addr = in6addr_any;
-
- DDO(IDL_EVENT,printf("------(Before rtrequest)----------\n");\
- dump_smart_sockaddr((struct sockaddr *)&sin6);\
- printf("----------------\n"));
-
- insque(v6r,&defrtr); /* To prevent double-adds. */
- if ((rc = rtrequest(RTM_ADD,(struct sockaddr *)&sin6,rt_key(rt),
- (struct sockaddr *)&mask,RTF_DEFAULT|RTF_GATEWAY,
- &defrt))) {
- remque(v6r);
- DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_rtrequest: Default route add failed (%d).\n",rc));
- free(v6r, M_DISCQ);
- /* Q: Do I want do delete the neighbor entry? */
- /* rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
- return NULL;
- }
- defrt->rt_refcnt--;
- /*defrt->rt_flags |= RTF_CLONING;*/
- } else
- insque(v6r,&defrtr); /* If double-add prevention not needed. */
-
- return v6r;
-#else /* 0 */
-
- if (!inrt->rt_gwroute)
-#if __FreeBSD__
- if (!(inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1, 0)))
-#else /* __FreeBSD__ */
- if (!(inrt->rt_gwroute = rtalloc1(inrt->rt_gateway, 1)))
-#endif /* __FreeBSD__ */
- return NULL;
-
- return &defrtr;
-#endif /* 0 */
-};
-
-/* Add a default router in response to a router advertisement.
- ipv6_add_defrouter_rtrequest ends up getting called underneath because of
- the rtalloc1(). */
-
-struct v6router *ipv6_add_defrouter_advert(struct in6_addr *addr, int lifetime, struct ifnet *ifp)
-{
- struct rtentry *rt, *defrt=NULL;
- struct v6router *v6r;
- struct sockaddr_in6 sin6;
- int rc;
-
- DPRINTF(IDL_EVENT, ("ipv6_add_defrouter_advert(addr=%08x, lifetime=%d, ifp=%08x)\n", OSDEP_PCAST(addr), lifetime, OSDEP_PCAST(ifp)));
-
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
- sin6.sin6_addr = *addr;
-
- /*
- * Find it if it already exists. Router adverts come from
- * link-local addresses, so doing rtalloc1() will be safe.
- * If this function is called because of a manual add, inrt should
- * be non-NULL, so this codepath won't be executed.
- */
-#if __FreeBSD__
- rt = rtalloc1((struct sockaddr *)&sin6, 0, 0);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)&sin6, 0);
-#endif /* __FreeBSD__ */
- if (rt == NULL || !(rt->rt_flags & RTF_LLINFO)) {
- if (rt != NULL)
- RTFREE(rt);
- rt = ipv6_new_neighbor(&sin6,ifp);
- } else
- rt->rt_refcnt--;
-
- DDO(GROSSEVENT,printf("After new_neighbor:\n"); dump_smart_sockaddr((struct sockaddr *)&sin6));
- if (rt == NULL) {
- DPRINTF(IDL_ERROR, ("ipv6_add_defrouter's new neighbor failed.\n"));
- return NULL;
- };
- rt->rt_flags |= RTF_DEFAULT|RTF_ISAROUTER;
-
- if (!(v6r = malloc(sizeof(struct v6router), M_DISCQ, M_NOWAIT))) {
- DPRINTF(IDL_ERROR,("ipv6_add_defrouter_advert: Can't allocate router list entry.\n"));
- /* Q: Do I want to delete the neighbor entry? */
- /*rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
- return NULL;
- };
-
- /*
- * Assertion:
- * Tunnel default routes are added only by manual
- * (i.e. user-level) adds and deletes.
- * If that's true, then the route in the v6r structure is "held" by
- * the default route. Oooh, I'd better make sure that default router
- * entries hold their... wait a minute, when I delete neighbors, the
- * default router neigbors are searched in the list (and nuked...).
- */
-
- bzero(v6r,sizeof(*v6r));
- v6r->v6r_rt = rt;
- v6r->v6r_children.v6c_next = v6r->v6r_children.v6c_prev = &v6r->v6r_children;
-#ifdef __FreeBSD__
- v6r->v6r_expire = time_second + lifetime;
-#else /* __FreeBSD__ */
- v6r->v6r_expire = time.tv_sec + lifetime;
-#endif /* __FreeBSD__ */
-
- if (defrtr.v6r_next == &defrtr) {
- /*
- * First default router added, and not a manual add. Add
- * default route.
- */
- struct sockaddr_in6 mask;
-
- bzero(&mask,sizeof(mask));
- mask.sin6_family = AF_INET6;
- mask.sin6_len = sizeof(struct sockaddr_in6);
-
- sin6.sin6_addr = in6addr_any;
-
- DDO(GROSSEVENT,printf("------(Before rtrequest)----------\n");\
- dump_smart_sockaddr((struct sockaddr *)&sin6);\
- printf("----------------\n"));
-
- insque(v6r, &defrtr); /* To prevent double-adds. */
- if ((rc = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, rt_key(rt),
- (struct sockaddr *)&mask, RTF_DEFAULT|RTF_GATEWAY,
- &defrt))) {
- remque(v6r);
- DPRINTF(IDL_ERROR, ("ipv6_add_defrouter_advert: Default route add failed (%d).\n", rc));
- free(v6r, M_DISCQ);
- /* Q: Do I want do delete the neighbor entry? */
- /* rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);*/
- return NULL;
- };
- defrt->rt_refcnt--;
- /*defrt->rt_flags |= RTF_CLONING;*/
- } else
- insque(v6r, &defrtr); /* If double-add prevention not needed. */
-
- return v6r;
-}
-
-/*----------------------------------------------------------------------
- * Update a default router entry.
- ----------------------------------------------------------------------*/
-
-static int
-update_defrouter(v6r,icmp)
- struct v6router *v6r;
- struct ipv6_icmp *icmp;
-{
- /* Is this it? What if someone deleted the default route? */
-#ifdef __FreeBSD__
- v6r->v6r_expire = time_second + ntohs(icmp->icmp_radvlifetime);
-#else /* __FreeBSD__ */
- v6r->v6r_expire = time.tv_sec + ntohs(icmp->icmp_radvlifetime);
-#endif /* __FreeBSD__ */
- return 0;
-}
-
-/*----------------------------------------------------------------------
- * Delete a default router entry. This function may even delete the
- * default route itself.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_delete_defrouter(v6r)
- struct v6router *v6r;
-{
- struct sockaddr_in6 sin6,mask;
- struct rtentry *rt = v6r->v6r_rt;
- u_long flags = rt->rt_flags;
-
- DPRINTF(FINISHED, ("In ipv6_delete_defrouter, v6r = 0x%lx\n", (unsigned long)v6r));
-
- remque(v6r);
- rt->rt_flags |= RTF_UP; /* To prevent double-freeing of the route. */
- while (v6r->v6r_children.v6c_next != &v6r->v6r_children)
- {
- DDO(IDL_ERROR,printf("Deleting route (0x%lx):\n",\
- (unsigned long)v6r->v6r_children.v6c_next->v6c_route);\
- dump_rtentry(v6r->v6r_children.v6c_next->v6c_route));
- rtrequest(RTM_DELETE,rt_key(v6r->v6r_children.v6c_next->v6c_route),NULL,
- NULL,0,NULL);
- }
- rt->rt_flags = flags;
- /*
- * Do I RTFREE() or do I rtrequest(RTM_DELETE) or do neither?
- * For now, neither, because this is called from either a r. advert.
- * advertising 0, or from an RTM_DELETE request for the neighbor.
- */
- /*RTFREE(v6r->v6r_rt);*/
- free(v6r,M_DISCQ);
-
- /*
- * What if I'm the last router in the default router list? If so, then
- * delete the default route.
- */
-
- if (defrtr.v6r_next == &defrtr)
- {
- DPRINTF(IDL_ERROR,\
- ("Last router on list deleted. Deleting default route"));
- bzero(&mask,sizeof(mask));
- bzero(&sin6,sizeof(sin6));
- mask.sin6_family = AF_INET6;
- mask.sin6_len = sizeof(struct sockaddr_in6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(struct sockaddr_in6);
- rt->rt_flags |= RTF_UP; /* To prevent double-freeing. */
- rtrequest(RTM_DELETE,(struct sockaddr *)&sin6,NULL,
- (struct sockaddr *)&mask,0,NULL);
- rt->rt_flags = flags;
- }
-
- return 1;
-}
-
-/*----------------------------------------------------------------------
- * Given an advertised prefix, a mask, and a link-local, create the
- * new address. Write result into "prefix" argument space.
- * (Should inline this.)
- ----------------------------------------------------------------------*/
-
-static void
-prefix_concat(prefix,linkloc,mask)
- struct in6_addr *prefix,*linkloc,*mask;
-{
- prefix->s6_addr32[0] = (prefix->s6_addr32[0] & mask->s6_addr32[0]) | (linkloc->s6_addr32[0] & ~mask->s6_addr32[0]);
- prefix->s6_addr32[1] = (prefix->s6_addr32[1] & mask->s6_addr32[1]) | (linkloc->s6_addr32[1] & ~mask->s6_addr32[1]);
- prefix->s6_addr32[2] = (prefix->s6_addr32[2] & mask->s6_addr32[2]) | (linkloc->s6_addr32[2] & ~mask->s6_addr32[2]);
- prefix->s6_addr32[3] = (prefix->s6_addr32[3] & mask->s6_addr32[3]) | (linkloc->s6_addr32[3] & ~mask->s6_addr32[3]);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_routeradv_input():
- * Handle reception of Router Advertisement messages.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_routeradv_input(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *ipv6;
- struct ipv6_icmp *icmp;
- struct v6router *v6r;
- struct icmp_exthdr *ext;
- int howbig;
-
- /*
- * Hmmmm, do want to handle some things down here in the kernel, but
- * what about other things, like addrconf?
- */
-
- DPRINTF(IDL_EVENT, ("OK, got a router advert.\n"));
- if (!ipv6rsolicit) /* If I'm not soliciting routers, ignore this */
- return;
-
- /* Verify that length looks OK */
- howbig = incoming->m_pkthdr.len - extra;
- if (howbig < ICMPV6_RADVMINLEN)
- return;
-
- /* XXX - Assumes that the entire packet fits within MCLBYTES. */
- if (incoming->m_len < incoming->m_pkthdr.len)
- if ((incoming = m_pullup2(incoming, incoming->m_pkthdr.len)))
- return;
-
- ipv6 = mtod(incoming,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
-
- /* Check to see hop count is 255. */
- if (ipv6->ipv6_hoplimit != 255)
- {
- /*
- * Keep stats on this!
- */
- DPRINTF(IDL_ERROR, ("Received Router Advertisement with hoplimit != 255.\n"));
- return;
- }
-
- /* Verify source address is link-local */
- if (!IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src))
- {
- DPRINTF(IDL_ERROR, ("Received Router Advertisement with non link-local source address.\n"));
- return;
- }
-
- /*
- * I now have a router advertisement.
- */
-
- /*
- * First, find the entry, if available.
- */
- for (v6r = defrtr.v6r_next; v6r != &defrtr; v6r = v6r->v6r_next)
- if (IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &V6R_SIN6(v6r)->sin6_addr))
- break;
-
- if (v6r == &defrtr) {
- /* Not found. */
- if (icmp->icmp_radvlifetime) {
- if (!(v6r = ipv6_add_defrouter_advert(&ipv6->ipv6_src, ntohs(icmp->icmp_radvlifetime), incoming->m_pkthdr.rcvif))) {
- printf("Problems adding to default router list.\n");
- v6r = &defrtr;
- }
- }
- /* else fallthrough if no lifetime, and not found. */
- } else {
- /* Found. */
- if (icmp->icmp_radvlifetime) {
- /*
- * Perhaps do some reality checking here. Was the radv snarfed
- * off the same interface? Is it the same link address? Is it
- * marked M_AUTHENTIC or M_DECRYPTED?
- */
-
- if (incoming->m_pkthdr.rcvif != v6r->v6r_rt->rt_ifp) {
- printf("WARNING: radv for router off different interface.\n");
- v6r = &defrtr;
- } else
- if (update_defrouter(v6r,icmp)) {
- printf("update_defrouter failed on radv_input.\n");
- v6r = &defrtr;
- }
- } else {
- if (ipv6_delete_defrouter(v6r)) /* XXX */
- printf("ipv6_delete_defrouter failed on radv_input.\n");
- v6r = &defrtr;
- }
- };
-
- if (icmp->icmp_radvhop)
- ipv6_defhoplmt = icmp->icmp_radvhop;
-
- if (icmp->icmp_radvbits)
- {
- /*
- * Kick DHCP in the pants to do the right thing(s).
- */
- }
-
- if (icmp->icmp_radvreach)
- v6d_reachtime = max(1,ntohl(icmp->icmp_radvreach)/1000);
-
- if (icmp->icmp_radvretrans)
- v6d_retranstime = max(1,ntohl(icmp->icmp_radvretrans)/1000);
-
- /*
- * Handle extensions/options.
- */
- ext = (struct icmp_exthdr *)icmp->icmp_radvext;
-
- DPRINTF(IDL_EVENT,\
- ("Parsing exensions. ext = 0x%lx, icmp = 0x%lx, howbig = %d.\n",\
- (unsigned long)ext, (unsigned long)icmp,howbig));
-
- while ((u_long)ext - (u_long)icmp < howbig)
- {
- struct ext_prefinfo *pre = (struct ext_prefinfo *)ext;
-
- DPRINTF(GROSSEVENT,("In loop, ext_id = %d.\n",ext->ext_id));
-
- switch (ext->ext_id)
- {
- case EXT_SOURCELINK:
- /*
- * We already have a v6r that may point to the neighbor. If so,
- * fill in the sockaddr_dl part of it, and set the neighbor state
- * STALE.
- */
- if (v6r != &defrtr)
- {
- /*
- * i.e. if I have a neighbor cache entry...
- */
- struct rtentry *rt = v6r->v6r_rt;
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)rt->rt_gateway;
- struct discq *dq = (struct discq *)rt->rt_llinfo;
-
- switch (rt->rt_ifp->if_type)
- {
- case IFT_ETHER:
- if (sdl->sdl_alen == 0)
- {
- sdl->sdl_alen = 6;
- bcopy(ext->ext_data,LLADDR(sdl),6);
- }
- else if (bcmp(LLADDR(sdl),ext->ext_data,6))
- {
- DPRINTF(IDL_ERROR,\
- ("WARNING: R. adv is saying lladdr is new.\n"));
- }
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec;
-#endif /* __FreeBSD__ */
- dq->dq_unanswered = -1; /* in STALE state.
- (STALE => expired,
- but unans = -1. */
- break;
- default:
- DPRINTF(IDL_ERROR,\
- ("I haven't a clue what if_type this is.\n"));
- break;
- }
- }
- break;
- case EXT_PREFIX:
- {
- /*
- * NOTE: We really should handle this section of code off in
- * ipv6_addrconf.c, but I didn't have time to move it
- * over.
- *
- * 1. If L bit is set, and A bit is not, then simply add an on-link
- * route for this prefix, and be done with it. (If the on-link
- * isn't already there, of course!)
- *
- * But what if I have an i6a that matches this?
- *
- * 2. Okay, if A bit is set, then do the following:
- * a. If prefix is link-local, bail.
- * b. Sanity check that preferred <= valid. If not, bail.
- * c. Check in6_ifaddr list, see if prefix is present. If so,
- * check if autoconf. (i.e. lifetime != 0). If so, update
- * lifetimes. (then bail.)
- * d. Okay, so it's not present this means we have to:
- * i. If L bit is set:
- * - If route present (exact prefix match), delete it.
- * - Call in6_control, just like ifconfig(8) does.
- * - Look up i6a and set expiration times.
- * ELSE (no L-bit)
- * - Set up i6a manually (gag me).
- * - See if there exists an exact prefix match, if so,
- * then change its ifa to the new i6a.
- */
- struct inet6_aliasreq ifra;
- struct sockaddr_in6 *dst = &ifra.ifra_addr,*mask = &ifra.ifra_mask;
-
-#if 0
- u_char *cp,*cp2,*cp3;
-#else /* 0 */
- u_char *cp;
-#endif /* 0 */
- int i;
-
- /* Sanity check prefix length and lifetimes. */
- if (pre->pre_prefixsize == 0 || pre->pre_prefixsize >= 128)
- {
- DPRINTF(IDL_ERROR,("Prefix size failed sanity check.\n"));
- break;
- }
- if (ntohl(pre->pre_valid) < ntohl(pre->pre_preferred))
- {
- DPRINTF(IDL_ERROR,("Lifetimes failed sanity check.\n"));
- break;
- }
-
- /*
- * Set up dst and mask.
- */
- bzero(&ifra,sizeof(ifra));
- dst->sin6_family = AF_INET6;
- dst->sin6_len = sizeof(*dst);
- dst->sin6_addr = pre->pre_prefix;
- mask->sin6_len = sizeof(*mask) - sizeof(struct in6_addr) +
- ((pre->pre_prefixsize - 1) / 8) + 1;
- cp = (u_char *)&mask->sin6_addr;
- for (i = 0; i <= ((pre->pre_prefixsize - 1) / 8) ; i ++)
- cp[i] = 0xff;
- cp[--i] <<= (8 - (pre->pre_prefixsize % 8)) % 8;
-
- DDO(GROSSEVENT,printf("mask and prefix are:\n");\
- dump_sockaddr_in6(dst);dump_sockaddr_in6(mask));
-
- DP(GROSSEVENT, pre->pre_bits, d);
-
- if (pre->pre_bits & ICMPV6_PREFIX_AUTO)
- {
- struct in6_ifaddr *i6a;
-
- if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) {
- DPRINTF(IDL_ERROR, ("ipv6_routeradv_input: Received router advertisement for link-local prefix\n"));
- break; /* Bail. */
- };
-
- DPRINTF(GROSSEVENT, ("ipv6_routeradv_input: Scanning interface addresses for the received prefix\n"));
-
-#if 0
- for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
- if (bcmp(&i6a->i6a_sockmask.sin6_addr,&mask->sin6_addr,
- sizeof(struct in6_addr)) == 0)
- {
- cp = (u_char *)&i6a->i6a_addr.sin6_addr;
- cp2 = (u_char *)&i6a->i6a_sockmask.sin6_addr;
- cp3 = (u_char *)&dst->sin6_addr;
- for (i = 0; (i >=0 && i < sizeof(struct in6_addr)); i++)
- if ((cp[i] ^ cp3[i]) & cp2[i])
- i = -2;
-
- if (i >= 0 && i6a->i6a_expire != 0 &&
- ((i6a->i6a_addrflags & I6AF_NOTSURE) == 0))
-#else /* 0 */
- for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next) {
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_sockmask.sin6_addr, &mask->sin6_addr))
- continue;
- if (
-(i6a->i6a_addr.sin6_addr.s6_addr32[0] ^ dst->sin6_addr.s6_addr32[0]) & i6a->i6a_sockmask.sin6_addr.s6_addr32[0] ||
-(i6a->i6a_addr.sin6_addr.s6_addr32[1] ^ dst->sin6_addr.s6_addr32[1]) & i6a->i6a_sockmask.sin6_addr.s6_addr32[1] ||
-(i6a->i6a_addr.sin6_addr.s6_addr32[2] ^ dst->sin6_addr.s6_addr32[2]) & i6a->i6a_sockmask.sin6_addr.s6_addr32[2] ||
-(i6a->i6a_addr.sin6_addr.s6_addr32[3] ^ dst->sin6_addr.s6_addr32[3]) & i6a->i6a_sockmask.sin6_addr.s6_addr32[3])
- continue;
-
- if (i6a->i6a_expire && !(i6a->i6a_addrflags & I6AF_NOTSURE))
-#endif /* 0 */
- {
- /*
- * Found an autoconfigured i6a.
- *
- * WARNING: For now, this path will not attempt to
- * do route table repair/updating.
- * This means if the link bit was off
- * before, but on now, you are in trouble.
- */
- DDO(FINISHED, printf("Found i6a of:\n");\
- dump_ifa((struct ifaddr *)i6a));
-
- if (ntohl(pre->pre_preferred) == 0xffffffff)
- i6a->i6a_preferred = ~0;
-#ifdef __FreeBSD__
- else i6a->i6a_preferred = time_second +
-#else /* __FreeBSD__ */
- else i6a->i6a_preferred = time.tv_sec +
-#endif /* __FreeBSD__ */
- ntohl(pre->pre_preferred);
-
- if (ntohl(pre->pre_valid) == 0xffffffff)
- i6a->i6a_expire = ~0;
-#ifdef __FreeBSD__
- else i6a->i6a_expire = time_second +
-#else /* __FreeBSD__ */
- else i6a->i6a_expire = time.tv_sec +
-#endif /* __FreeBSD__ */
- ntohl(pre->pre_valid);
- break;
- }
- }
-
- DP(GROSSEVENT, OSDEP_PCAST(i6a), 08x);
-
- if (i6a == NULL)
- {
- struct rtentry *rt;
- struct socket so;
-#if __NetBSD__ || __FreeBSD__
- struct proc proc;
- struct pcred pcred;
- struct ucred ucred;
-#endif /* __NetBSD__ || __FreeBSD__ */
- /*
- * Need to create new one.
- */
- DPRINTF(GROSSEVENT,("Creating new i6a.\n"));
-
-#if __NetBSD__ || __FreeBSD__
- ucred.cr_uid = 0;
- proc.p_cred = &pcred;
- proc.p_ucred = &ucred;
-#else /* __NetBSD__ || __FreeBSD__ */
- /*
- * Do in6_control, like ifconfig does. If L bit is not
- * set, delete on-link route.
- */
- so.so_state = SS_PRIV;
-#endif /* __NetBSD__ || __FreeBSD__ */
-
- /*
- * Construct address.
- */
-
- for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == incoming->m_pkthdr.rcvif &&
- IN6_IS_ADDR_LINKLOCAL(&i6a->i6a_addr.sin6_addr))
- break;
- if (i6a == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("Can't find link-local for this if!\n"));
- break;
- }
-
- if (i6a->i6a_preflen != pre->pre_prefixsize
- || i6a->i6a_preflen == 0)
- {
- DPRINTF(IDL_ERROR,\
- ("Prefix size problems, i6a_preflen = %d, adv. size = %d.\n",\
- i6a->i6a_preflen,pre->pre_prefixsize));
- break;
- }
-
- prefix_concat(&dst->sin6_addr,&i6a->i6a_addr.sin6_addr,
- &mask->sin6_addr);
-
- if (pre->pre_bits & ICMPV6_PREFIX_ONLINK)
- {
- /*
- * If route already exists, delete it.
- */
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)dst,0,0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)dst,0);
-#endif /* __FreeBSD__ */
- if (rt != NULL &&
- !(rt->rt_flags & (RTF_GATEWAY|RTF_TUNNEL)))
- if ((rt_mask(rt) && bcmp(&mask->sin6_addr,
- &((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr,
- sizeof(struct in6_addr)) == 0) ||
- (!rt_mask(rt) && (bcmp(&mask->sin6_addr, &in6_allones,
- sizeof(mask->sin6_addr)) == 0)) )
- {
- /*
- * Making sure the route is THIS ONE is as
- * simple as checking the masks' equality.
- */
- DPRINTF(IDL_EVENT, ("Deleting existing on-link route before autoconfiguring new interface.\n"));
- rt->rt_refcnt--;
- rtrequest(RTM_DELETE,rt_key(rt),NULL,NULL,0,NULL);
- }
- if (rt != NULL)
- rt->rt_refcnt--;
- }
-
-#if __NetBSD__ || __FreeBSD__
- if (in6_control(&so,SIOCAIFADDR_INET6,(caddr_t)&ifra, incoming->m_pkthdr.rcvif,1, &proc))
-#else /* __NetBSD__ || __FreeBSD__ */
- if (in6_control(&so,SIOCAIFADDR_INET6,(caddr_t)&ifra, incoming->m_pkthdr.rcvif,1))
-#endif /* __NetBSD__ || __FreeBSD__ */
- {
- DPRINTF(IDL_ERROR,
- ("DANGER: in6_control failed.\n"));
- }
- else if (!(pre->pre_bits & ICMPV6_PREFIX_ONLINK))
- {
- /*
- * Router advert didn't specify the prefix as
- * being all on-link, so nuke the route.
- */
- rtrequest(RTM_DELETE,(struct sockaddr *)dst,NULL,
- (struct sockaddr *)mask,0,NULL);
- }
- for(i6a = in6_ifaddr;i6a != NULL; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &dst->sin6_addr))
- break;
- if (i6a != NULL)
- {
- if (ntohl(pre->pre_preferred) == 0xffffffff)
- i6a->i6a_preferred = 0xffffffff;
- else i6a->i6a_preferred =
-#ifdef __FreeBSD__
- time_second + ntohl(pre->pre_preferred);
-#else /* __FreeBSD__ */
- time.tv_sec + ntohl(pre->pre_preferred);
-#endif /* __FreeBSD__ */
- if (ntohl(pre->pre_valid) == 0xffffffff)
- i6a->i6a_expire = 0xffffffff;
- else i6a->i6a_expire =
-#ifdef __FreeBSD__
- time_second + ntohl(pre->pre_valid);
-#else /* __FreeBSD__ */
- time.tv_sec + ntohl(pre->pre_valid);
-#endif /* __FreeBSD__ */
- }
- }
- }
- else if (pre->pre_bits & ICMPV6_PREFIX_ONLINK)
- {
- /*
- * Construct on-link prefix route and add.
- *
- * WARNING: According to the discovery document, prefixes
- * should be kept in a list, though I only do that
- * if I have an address on that list.
- * The falllout from this is that prefixes don't have
- * their lifetimes enforced.
- *
- * Also, what if I have an i6a for this already?
- */
- struct sockaddr_dl sdl;
-
- bzero(&sdl,sizeof(sdl));
- sdl.sdl_family = AF_LINK;
- sdl.sdl_len = sizeof(sdl);
- sdl.sdl_index = incoming->m_pkthdr.rcvif->if_index;
- rtrequest(RTM_ADD,(struct sockaddr *)dst,
- (struct sockaddr *)&sdl, (struct sockaddr *)mask,
- 0,NULL);
- }
- }
- break;
- case EXT_MTU:
- /*
- * I'm going to ignore for now.
- *
- * Processing would include:
- *
- * 1. Possibly change the ifp->if_mtu
- * 2. Traversing all IPv6 routes with this MTU and updating.
- * (This could cause TCP pcb's to be updated too.)
- */
- break;
- default:
- /*
- * And I quote:
- *
- * Future version of this protocol may define new option types.
- * Receivers MUST silently ignore any options they do not recognize
- * and continue processing the message.
- */
- break;
- }
- (u_long)ext += (ext->ext_length << 3);
- }
-}
-
-/*----------------------------------------------------------------------
- * ipv6_new_neighbor():
- * Return a new neighbor-cache entry for address sin6 on interface ifp.
- * A front-end for rtrequest(RTM_ADD, ...). This returns NULL
- * if there is a problem.
- *
- * NOTES:
- * May want to handle case of ifp == NULL.
- * ipv6_discov_rtrequest() will handle ancillary structure setup.
- ----------------------------------------------------------------------*/
-
-struct rtentry *
-ipv6_new_neighbor(sin6, ifp)
- struct sockaddr_in6 *sin6; /* Neighbor's IPv6 address. */
- struct ifnet *ifp; /* Interface neighbor lies off this. */
-{
- struct sockaddr_dl lsdl; /* Target's Link-local Address sockaddr */
- struct sockaddr *dst = (struct sockaddr *)sin6;
- struct sockaddr *gateway = (struct sockaddr *)&lsdl;
- struct sockaddr *netmask = (struct sockaddr *)&in6_allones;
- struct rtentry *newrt = NULL;
- struct ifaddr *ifa;
- int flags = RTF_HOST;
-
-#ifdef __FreeBSD__
- for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if (ifa->ifa_addr->sa_family == AF_LINK)
- break;
- if (ifa == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("ipv6_new_neighbor() can't find AF_LINK for ifp.\n"));
- return NULL;
- }
-
- bcopy(ifa->ifa_addr,&lsdl,min(sizeof(lsdl),ifa->ifa_addr->sa_len));
- lsdl.sdl_alen = 0;
- lsdl.sdl_nlen = 0;
- lsdl.sdl_slen = 0;
-
-#ifdef DIAGNOSTIC
- if (lsdl.sdl_index == 0)
- panic("sdl_index is 0 in ipv6_new_neighbor().");
-#endif
-
- /* ASSUMES: there is enough room for the link address shoved in here */
- if (rtrequest(RTM_ADD,dst,gateway,netmask,flags,&newrt) == EEXIST) {
- DPRINTF(FINISHED,("Can't add neighbor that already exists?\n"));
- DDO(FINISHED, dump_smart_sockaddr(dst));
- DDO(FINISHED, dump_smart_sockaddr(gateway));
- DDO(FINISHED, dump_smart_sockaddr(netmask));
- DP(FINISHED, flags, d);
- }
-
- if (newrt != NULL)
- {
- /* Fill in any other goodies, especially MTU. */
- DDO(IDL_EVENT, printf("New route okay, before MTU setup...\n");\
- dump_rtentry(newrt));
- newrt->rt_rmx.rmx_mtu = newrt->rt_ifp->if_mtu;
- newrt->rt_refcnt = 0; /* XXX - should decrement instead? */
- DDO(IDL_EVENT,printf("New route okay, after MTU setup...\n");\
- dump_rtentry(newrt));
- }
- return newrt;
-}
-
-/*----------------------------------------------------------------------
- * ipv6_neighborsol_input():
- * Handle input processing for Neighbor Solicit messages.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_neighborsol_input(incoming,extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *ipv6;
- struct ipv6_icmp *icmp;
- struct rtentry *rt;
- struct discq *dq;
- struct sockaddr_in6 sin6;
- struct sockaddr_dl *sdl;
- struct in6_ifaddr *i6a;
- struct icmp_exthdr *ext = NULL;
-
- /* Thanks to ipv6_icmp.h, ICMP_NEIGHBORADV_* are already in network order */
- uint32_t flags = (ipv6forwarding) ? ICMPV6_NEIGHBORADV_RTR : 0;
-
- if (incoming->m_flags & M_DAD) /* Incoming DAD solicit from me. Ignore. */
- return;
-
- /* Verify that length looks OK */
- if (incoming->m_pkthdr.len - extra < ICMPV6_NSOLMINLEN)
- return;
-
- /* XXX - Assumes that the entire packet fits within MCLBYTES. */
- if (incoming->m_len < incoming->m_pkthdr.len)
- if ((incoming = m_pullup2(incoming, incoming->m_pkthdr.len)))
- return;
-
- ipv6 = mtod(incoming,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
-
- /* Check to see hop count is 255. */
- if (ipv6->ipv6_hoplimit != 255)
- {
- /*
- * Keep stats on this!
- */
- DPRINTF(IDL_ERROR,
- ("Received Neighbor Solicit with hoplimit != 255.\n"));
- return;
- }
-
- if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_nsoltarg))
- {
- DPRINTF(IDL_EVENT, ("Received multicast address in solicit!\n"));
- return;
- }
- /*
- * Have a Neighbor Solicit message.
- */
-
- /* Verify this is for me. */
- /* Eventually proxy & anycast checking will go here. */
- for (i6a = in6_ifaddr ; i6a ; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &icmp->icmp_nsoltarg))
- break;
- if (i6a == NULL)
- {
- /* Not for me. */
- return;
- }
-
- if (i6a->i6a_ifp != incoming->m_pkthdr.rcvif)
- {
- DPRINTF(IDL_ERROR,\
- ("Received off-link Neighbor Solicit for self\n"));
- return;
- }
-
- /*
- * Can't process solicits for addresses in DAD phase.
- * Furthermore, if solicit comes from all 0's, (and if I made it this far,
- * it's not from me), then there's a duplicate.
- */
- if (i6a->i6a_addrflags & I6AF_NOTSURE)
- {
- if (IN6_IS_ADDR_UNSPECIFIED(&ipv6->ipv6_src))
- {
- struct socket so;
- struct inet6_aliasreq ifra;
-#if __NetBSD__ || __FreeBSD__
- struct proc proc;
- struct pcred pcred;
- struct ucred ucred;
-#endif /* __NetBSD__ || __FreeBSD__ */
- printf("Duplicate address detected.\n"); /* NEED to print this. */
- /*
- * Delete in6_ifaddr.
- */
-#if __NetBSD__ || __FreeBSD__
- ucred.cr_uid = 0;
- proc.p_cred = &pcred;
- proc.p_ucred = &ucred;
-#else /* __NetBSD__ || __FreeBSD__ */
- so.so_state = SS_PRIV;
-#endif /* __NetBSD__ || __FreeBSD__ */
- strncpy(ifra.ifra_name,i6a->i6a_ifp->if_name,IFNAMSIZ);
- ifra.ifra_addr = i6a->i6a_addr;
- ifra.ifra_dstaddr = i6a->i6a_dstaddr;
- ifra.ifra_mask = i6a->i6a_sockmask;
-#if __NetBSD__ || __FreeBSD__
- in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1, &proc);
-#else /* __NetBSD__ || __FreeBSD__ */
- in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1);
-#endif /* __NetBSD__ || __FreeBSD__ */
- }
- return;
- }
-
- /*
- * Create neighbor cache entry for neighbor to send back advertisement.
- */
-
- if (!IN6_IS_ADDR_UNSPECIFIED(&ipv6->ipv6_src))
- {
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
- sin6.sin6_addr = ipv6->ipv6_src;
-
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
-#else /* __FreeBSD */
- rt = rtalloc1((struct sockaddr *)&sin6, 0);
-#endif /* __FreeBSD */
-
- /*
- * I just did a passive route lookup. I'll either get:
- *
- * 1. No route, meaning I have to create one.
- * 2. A host neighbor (RTF_LLINFO) route, meaning I might update it.
- * 3. A host off-link (RTF_GATEWAY) route, meaning I possibly missed a
- * redirect.
- * 4. An on-link prefix (RTF_CLONING, no RTF_GATEWAY) route, meaning
- * I have to create one, like no route.
- * 5. The default route (RTF_DEFAULT), meaning the same as no route.
- * 6. A network route, meaning either a subset of that prefix is on-link,
- * or my routing table is bogus. I'll create one.
- *
- * In any case I actually get one, I should decrement the rt_refcnt.
- *
- * Future support for RTF_TUNNEL needed here.
- */
-
- DPRINTF(GROSSEVENT,("After rtalloc1().\n"));
- if (rt == NULL || !(rt->rt_flags & RTF_HOST) )
- {
- /*
- * No available host route, create a new entry.
- */
- if (rt != NULL)
- rt->rt_refcnt--;
-
- DPRINTF(GROSSEVENT,("Creating new neighbor.\n"));
- rt = ipv6_new_neighbor(&sin6,incoming->m_pkthdr.rcvif);
- if (rt == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("Can't allocate soliciting neighbor route.\n"));
- return;
- }
- }
- else if (rt->rt_flags & RTF_LLINFO)
- {
- rt->rt_refcnt--;
- if (rt->rt_gateway->sa_family != AF_LINK) {
- DPRINTF(IDL_ERROR, ("LLINFO but gateway != AF_LINK."));
- return;
- };
- }
- else
- {
- /*
- * Received Neighbor Solicit from an address which I have
- * an off-net host route for. For now, bail.
- */
- DPRINTF(FINISHED,
- ("Received Neighbor Solicit from unknown target.\n"));
- return;
- }
-
- /*
- * If new, or inactive, set to probe.
- */
-
- /*
- * All of this data will fit in one mbuf as long as the upper limit
- * for ICMP message size <= 576 <= MCLBYTES.
- */
- if (incoming->m_pkthdr.len > extra + ICMPV6_NSOLMINLEN)
- {
- u_char *data = (u_char *)&icmp->icmp_nsolext;
- u_char *bounds = data + incoming->m_pkthdr.len - extra -
- ICMPV6_NSOLMINLEN;
-
- /* Only possible extension (so far) in a neighbor advert
- is a source link-layer address, but be careful anyway. */
-
- ext = (struct icmp_exthdr *)data;
- while (ext->ext_id != EXT_SOURCELINK && (data<bounds))
- {
- DPRINTF(FINISHED,("Got extension other than source link.\n"));
- data += ext->ext_length<<3;
- ext = (struct icmp_exthdr *)data;
- }
- if (data >= bounds) {
- DPRINTF(IDL_ERROR, ("couldn't find SOURCELINK"));
- return;
- };
- }
-
- sdl = (struct sockaddr_dl *)rt->rt_gateway;
- rt->rt_flags &= ~RTF_REJECT;
- if (sdl->sdl_alen == 0) /* New or inactive */
- {
- if (rt->rt_ifa == NULL)
- {
- /*
- * If multihomed and not sure of interface, take ifp and ifa
- * for "My destination," where it does not have to be link-
- * local.
- */
- rt->rt_ifa = (struct ifaddr *)i6a;
- rt->rt_ifp = i6a->i6a_ifp;
- rt->rt_ifa->ifa_refcnt++;
- sdl->sdl_index = rt->rt_ifp->if_index;
- sdl->sdl_type = rt->rt_ifp->if_type;
- sdl->sdl_nlen = strlen(rt->rt_ifp->if_name);
- bcopy(rt->rt_ifp->if_name,sdl->sdl_data,sdl->sdl_nlen);
- rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
- }
-
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec;
-#endif /* __FreeBSD__ */
- dq = (struct discq *)rt->rt_llinfo;
- dq->dq_unanswered = -1; /* In STALE state */
- /*
- * Checks for non-broadcast multiple-access (NBMA) links
- * such as PPP, Frame Relay, and ATM are probably needed here.
- */
- if (ext != NULL)
- switch (rt->rt_ifp->if_type)
- {
- case IFT_ETHER:
- sdl->sdl_alen = 6;
- bcopy(ext->ext_data,LLADDR(sdl),sdl->sdl_alen);
- break;
- default:
- DPRINTF(IDL_ERROR,("DANGER: Non-ethernet n. adv.\n"));
- break;
- }
- }
- else
- {
- /*
- * For now, ignore if I already have somewhat valid entry.
- * Only adverts can affect changes on PROBE or STALE entries.
- * (And then, only maybe.)
- */
- }
-
- /*
- * Neighbor Cache is now updated.
- * Now, send out my unicast advertisement.
- *
- * NB: ICMPV6_* symbol was already htonl()'d in the header file.
- */
- flags |= ICMPV6_NEIGHBORADV_SOL|ICMPV6_NEIGHBORADV_OVERRIDE;
- ipv6_nadvert(i6a,i6a->i6a_ifp,rt,flags);
- }
- else
- {
- /*
- * Send multicast advertisement rather than unicast advertisement
- * because the solicit contained the unspecified address.
- *
- * DAD code may need to be executed in here as well.
- */
- ipv6_nadvert(i6a,incoming->m_pkthdr.rcvif,NULL,flags);
- }
-}
-
-
-/*----------------------------------------------------------------------
- * ipv6_neighboradv_input():
- * Handle reception of a Neighbor Advertisement message.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_neighboradv_input(incoming,extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *ipv6;
- struct ipv6_icmp *icmp;
- struct rtentry *rt;
- struct sockaddr_in6 sin6;
- struct in6_ifaddr *i6a;
- int s;
-
- /* Verify that incoming length looks plausible */
- if (incoming->m_pkthdr.len - extra < ICMPV6_NADVMINLEN)
- return;
-
- /* XXX - Assumes that the entire packet fits within MCLBYTES. */
- if (incoming->m_len < incoming->m_pkthdr.len)
- if ((incoming = m_pullup2(incoming, incoming->m_pkthdr.len)))
- return;
-
- ipv6 = mtod(incoming,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
-
- /* Verify hoplimit == 255 */
- if (ipv6->ipv6_hoplimit != 255)
- {
- /*
- * Keep stats on this!
- */
- DPRINTF(IDL_ERROR,
- ("Received Neighbor Solicit with hoplimit != 255.\n"));
- return;
- }
-
-#if 0
- /* Verify quickly src/node matching. Causes way too many false alarms. */
- if (!IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &icmp->icmp_nadvaddr))
- {
- DPRINTF(IDL_ERROR, ("WARNING: Possible proxy, addrs are unequal.\n"));
- DPRINTF(IDL_ERROR, ("src=%08x %08x %08x %08x\n", ntohl(ipv6->ipv6_src.in6a_u.words[0]), ntohl(ipv6->ipv6_src.in6a_u.words[1]), ntohl(ipv6->ipv6_src.in6a_u.words[2]), ntohl(ipv6->ipv6_src.in6a_u.words[3])));
- DPRINTF(IDL_ERROR, ("nadvaddr=%08x %08x %08x %08x\n", ntohl(icmp->icmp_nadvaddr.in6a_u.words[0]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[1]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[2]), ntohl(icmp->icmp_nadvaddr.in6a_u.words[3])));
- }
-#endif /* 0 */
-
- if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_nadvaddr))
- {
- DPRINTF(ERROR, ("Received Neighbor Advert with multicast address.\n"));
- return; /* For now... */
- }
-
- /*
- * Have a Neighbor Advertisement.
- */
-
- s = splnet();
-
- /* Look to see if it's for one of my addresses. */
- for (i6a = in6_ifaddr ; i6a ; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &icmp->icmp_nadvaddr))
- break;
- if (i6a != NULL)
- {
- if (i6a->i6a_addrflags & I6AF_NOTSURE)
- {
- struct socket so;
- struct inet6_aliasreq ifra;
-#if __NetBSD__ || __FreeBSD__
- struct proc proc;
- struct pcred pcred;
- struct ucred ucred;
-#endif /* __NetBSD__ || __FreeBSD__ */
- printf("Duplicate address detected.\n"); /* NEED to print this. */
- /*
- * Delete in6_ifaddr.
- */
-#if __NetBSD__ || __FreeBSD__
- ucred.cr_uid = 0;
- proc.p_cred = &pcred;
- proc.p_ucred = &ucred;
-#else /* __NetBSD__ || __FreeBSD__ */
- so.so_state = SS_PRIV;
-#endif /* __NetBSD__ || __FreeBSD__ */
- strncpy(ifra.ifra_name,i6a->i6a_ifp->if_name,IFNAMSIZ);
- ifra.ifra_addr = i6a->i6a_addr;
- ifra.ifra_dstaddr = i6a->i6a_dstaddr;
- ifra.ifra_mask = i6a->i6a_sockmask;
-#if __NetBSD__ || __FreeBSD__
- in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1, &proc);
-#else /* __NetBSD__ || __FreeBSD__ */
- in6_control(&so,SIOCDIFADDR_INET6,(caddr_t)&ifra,i6a->i6a_ifp,1);
-#endif /* __NetBSD__ || __FreeBSD__ */
- }
- else
- {
- /* For now, ignore advert which is for me. */
- }
-
- splx(s);
- return;
- }
-
- /* Lookup and see if I have something waiting for it... */
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr = icmp->icmp_nadvaddr;
-
- /* Next 2 lines might not be strictly needed since this is an rtalloc,
- but they're included to be safe. */
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
-
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)&sin6,0,0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)&sin6,0);
-#endif /* __FreeBSD__ */
-
- if (rt != NULL)
- rt->rt_refcnt--;
-
- /*
- * After passive route lookup, I have either:
- *
- * 1. No route, drop the advert.
- * 2. Route with no RTF_HOST, drop the advert.
- * 3. Route with no RTF_LLINFO, for now, drop the advert, this could be
- * a redirect weirdness?
- * 4. Route with RTF_LLINFO, try and update.
- */
-
- if (rt == NULL || !(rt->rt_flags & RTF_HOST))
- {
- /* Cases 1 and 2. */
- splx(s);
- return;
- }
-
- if (rt->rt_flags & RTF_LLINFO)
- {
- /* Case 4. */
- struct sockaddr_dl *sdl;
- struct icmp_exthdr *liext = NULL;
-
- /*
- * Possibly update the link-layer address, and maybe change to
- * REACHABLE state.
- */
- rt->rt_flags &= ~RTF_REJECT; /* That neighbor talked to me! */
-
- if (incoming->m_pkthdr.len - extra > ICMPV6_NADVMINLEN)
- {
- u_char *data = (u_char *)&icmp->icmp_nadvext;
- u_char *bounds = data + incoming->m_pkthdr.len - extra -
- ICMPV6_NADVMINLEN;
- struct icmp_exthdr *ext = (struct icmp_exthdr *)data;
-
- /* Only possible extension (so far) in a neighbor advert is a
- source link-layer address, but be careful anyway. */
-
- while (ext->ext_id != EXT_TARGETLINK && (data < bounds))
- {
- DPRINTF(IDL_EVENT,("Got extension other than source link.\n"));
- data += ext->ext_length<<3;
- ext = (struct icmp_exthdr *)data;
- }
- if (data >= bounds) {
- DPRINTF(IDL_ERROR,("Received neighbor advertisement with no source link layer address.\n"));
- splx(s);
- return;
- }
- liext = ext;
- }
-
- sdl = (struct sockaddr_dl *)rt->rt_gateway;
-
- if (liext != NULL)
- {
- if (rt->rt_ifa == NULL)
- {
- struct in6_ifaddr *i6a;
-
- /*
- * ifa and ifp for this address are null, because it's an
- * on-link brute force discovery. Use link-local as source.
- */
- rt->rt_ifp = incoming->m_pkthdr.rcvif;
- for (i6a = in6_ifaddr ; i6a != NULL; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == rt->rt_ifp &&
- (i6a->i6a_flags & I6AF_LINKLOC))
- break;
-
- if (i6a == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("Got advert from interface with no link-local.\n"));
- splx(s);
- return;
- }
- rt->rt_ifa = (struct ifaddr *)i6a;
- rt->rt_ifa->ifa_refcnt++;
- sdl->sdl_index = rt->rt_ifp->if_index;
- sdl->sdl_type = rt->rt_ifp->if_type;
- sdl->sdl_nlen = strlen(rt->rt_ifp->if_name);
- bcopy(rt->rt_ifp->if_name,sdl->sdl_data,sdl->sdl_nlen);
- rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
- }
-
- /*
- * Update neighbor cache link address.
- * Should make this code more general than just for Ethernet,
- * so we switch off the interface type assigned to the route.
- */
- switch (rt->rt_ifp->if_type)
- {
- case IFT_ETHER:
- if (sdl->sdl_alen == 0 ||
- (icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_OVERRIDE))
- {
- sdl->sdl_alen = 6;
- bcopy(&liext->ext_data,LLADDR(sdl),sdl->sdl_alen);
- }
- else if (bcmp(LLADDR(sdl),&liext->ext_data,6))
- {
- /*
- * Override bit not set, and have link address already.
- * Discard.
- */
- DPRINTF(IDL_ERROR,("Danger, got non-override with different link-local address.\n"));
- splx(s);
- return;
- }
- break;
- }
- /* The ICMP_NEIGHBORADV_* bits are already machine-specific.
- So no HTONLs/NTOHLs need to be done here. */
-
- /* Now in REACHABLE or STALE state, depending on
- ICMP_NEIGHBORADV_SOL bit. */
- {
- struct discq *dq = (struct discq *)rt->rt_llinfo;
-
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second +
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec +
-#endif /* __FreeBSD__ */
- ((icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_SOL)?v6d_reachtime:0);
- dq->dq_unanswered = -1;
- if (dq->dq_queue != NULL)
- {
- rt->rt_ifp->if_output(rt->rt_ifp,dq->dq_queue, rt_key(rt),
- rt);
- dq->dq_queue = NULL;
- }
- }
- }
-
- /*
- * Check for routers becoming hosts, and vice-versa.
- */
- if (icmp->icmp_nadvbits & ICMPV6_NEIGHBORADV_RTR)
- rt->rt_flags |= RTF_ISAROUTER;
- else if (rt->rt_flags & RTF_ISAROUTER)
- {
- /*
- * Deal with router becoming host.
- */
- }
- }
- else
- {
- /* Case 3. */
- /* Should consider adding a counter for these */
- DPRINTF(ERROR,
- ("Received Neighbor Advert for off-link host.\n"));
- }
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * ipv6_redirect_input():
- * Handle reception of a Redirect message.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_redirect_input(incoming,extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *ipv6;
- struct ipv6_icmp *icmp;
- struct rtentry *rt;
- struct sockaddr_in6 dst6, gate6, src6;
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- extern int icmp_redirtimeout;
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
-
- /* XXX - Assumes that the entire packet fits within MCLBYTES. */
- if (incoming->m_len < incoming->m_pkthdr.len)
- if ((incoming = m_pullup2(incoming, incoming->m_pkthdr.len)))
- return;
-
- ipv6 = mtod(incoming,struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
-
- /* Verify source address is link-local */
- if (!IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src)) {
- DPRINTF(IDL_ERROR, ("Received Redirect with non link-local source address.\n"));
- return;
- };
-
- /* Verify hoplimit == 255 */
- if (ipv6->ipv6_hoplimit != 255) {
- /*
- * Keep stats on this!
- */
- DPRINTF(IDL_ERROR, ("Received IPv6 Redirect with hoplimit != 255.\n"));
- return;
- };
-
- /* Verify that incoming length looks plausible */
- if (incoming->m_pkthdr.len - extra < ICMPV6_REDIRMINLEN) {
- DPRINTF(IDL_ERROR, ("Received IPv6 Redirect without enough data.\n"));
- return;
- };
-
- /* Verify current next hop == IPv6 src addr */
- bzero(&dst6, sizeof(struct sockaddr_in6));
- dst6.sin6_len = sizeof(struct sockaddr_in6);
- dst6.sin6_family = AF_INET6;
- dst6.sin6_addr = icmp->icmp_redirdest;
-
-#ifdef __FreeBSD__
- if (!(rt = rtalloc1((struct sockaddr *)&dst6, 0, 0UL))) {
-#else /* __FreeBSD__ */
- if (!(rt = rtalloc1((struct sockaddr *)&dst6, 0))) {
-#endif /* __FreeBSD__ */
- DPRINTF(IDL_ERROR, ("Received IPv6 Redirect for unreachable host.\n"));
- return;
- };
-
- if ((rt->rt_gateway->sa_family != AF_INET6) || !IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_src, &((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr)) {
- DPRINTF(IDL_ERROR, ("Received IPv6 Redirect from wrong source.\n"));
- return;
- };
-
- /* No redirects for multicast packets! */
- if (IN6_IS_ADDR_MULTICAST(&icmp->icmp_redirdest)) {
- DPRINTF(IDL_ERROR, ("Received Redirect with multicast address.\n"));
- return; /* For now... (?) */
- };
-
- /* Target must be link local, or same as destination */
- if (!IN6_IS_ADDR_LINKLOCAL(&icmp->icmp_redirtarg) && !IN6_ARE_ADDR_EQUAL(&icmp->icmp_redirtarg, &icmp->icmp_redirdest)) {
- DPRINTF(IDL_ERROR, ("Received Redirect with non link-local target addr != dest addr.\n"));
- return;
- };
-
- /*
- * We have a valid Redirect.
- */
-
- bzero(&gate6, sizeof(struct sockaddr_in6));
- gate6.sin6_len = sizeof(struct sockaddr_in6);
- gate6.sin6_family = AF_INET6;
- gate6.sin6_addr = icmp->icmp_redirtarg;
-
- bzero(&src6, sizeof(struct sockaddr_in6));
- src6.sin6_len = sizeof(struct sockaddr_in6);
- src6.sin6_family = AF_INET6;
- src6.sin6_addr = ipv6->ipv6_src;
-
- rtredirect((struct sockaddr *)&dst6, (struct sockaddr *)&gate6,
- NULL, RTF_DONE|RTF_GATEWAY|RTF_HOST, (struct sockaddr *)&src6,
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- icmp_redirtimeout);
-#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- (struct rtentry **)0);
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- return;
-};
-
-/*----------------------------------------------------------------------
- * ipv6_discov_resolve():
- * Called by LAN output routine. Will either queue outgoing packet
- * on the discovery structure that hangs off the route and return 0,
- * OR copies LAN MAC address into edst and returns 1.
- *
- * NOTES:
- * Neighbor Unreachable Detection (NUD) should be done here.
- * LAN output routine should handle IPv6 multicast -> link multicast
- * mapping, unlike arp_resolve, this function doesn't handle multicast.
- ----------------------------------------------------------------------*/
-
-int ipv6_discov_resolve(ifp, rt, outgoing, dst, edst)
- struct ifnet *ifp;
- struct rtentry *rt;
- struct mbuf *outgoing;
- struct sockaddr *dst;
- u_char *edst;
-{
- struct discq *dq = NULL;
- struct sockaddr_dl *sdl;
- struct ipv6 *ipv6;
-
- DPRINTF(IDL_EVENT,("Entering ipv6_discov_resolve().\n"));
- if (rt)
- dq = (struct discq *)rt->rt_llinfo;
- else
- {
-#ifdef __FreeBSD__
- rt = rtalloc1(dst, 0, 0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1(dst, 0);
-#endif /* __FreeBSD__ */
-
- /*
- * I just did a passive route lookup. I'll either get:
- *
- * 1. No route, meaning I have to create one.
- * 2. A host neighbor (RTF_LLINFO) route, meaning I'm good to go.
- * 3. A host off-link route, meaning I possibly missed a redirect.
- * 4. An on-link prefix (RTF_CLONING, no RTF_GATEWAY) route, meaning
- * I have to create one, like no route.
- * 5. The default route (RTF_DEFAULT), meaning the same as no route.
- * 6. A network route, meaning either a subset of that prefix is on-link,
- * or my routing table is invalid. I'll create a new route entry.
- */
-
- if (rt == NULL || !(rt->rt_flags & RTF_HOST))
- {
- /*
- * No available host route, create a new entry.
- * (Cases 1, 4, 5, 6.)
- */
- if (rt != NULL)
- rt->rt_refcnt--;
-
- rt = ipv6_new_neighbor((struct sockaddr_in6 *)dst,ifp);
- if (rt == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("Can't allocate soliciting neighbor route.\n"));
- m_freem(outgoing);
- return 0;
- }
- }
- else if (rt->rt_flags & RTF_LLINFO)
- {
- /* (Case 2) */
- rt->rt_refcnt--;
- if (rt->rt_gateway->sa_family != AF_LINK) {
- DPRINTF(IDL_ERROR, ("LLINFO but gateway != AF_LINK."));
- m_freem(outgoing);
- return 0;
- }
- }
- else
- {
- /*
- * I just got a neighbor solicit from an address which I have
- * an off-net host route for. For now, bail. (Case 3.)
- */
- DPRINTF(ERROR,
- ("Received Neighbor Solicit from unknown target.\n"));
- return 0;
- }
-
- dq = (struct discq *)rt->rt_llinfo;
- }
-
- if (dq == NULL)
- {
- DPRINTF(IDL_ERROR, ("No discq structure hanging off route.\n"));
- m_freem(outgoing);
- return 0;
- }
- if (dq->dq_rt != rt) {
- DPRINTF(IDL_ERROR, ("discov_resolve route passed in (rt) != dq->dq_rt\n"));
- m_freem(outgoing);
- return 0;
- };
-
- sdl = (struct sockaddr_dl *)rt->rt_gateway;
- if (sdl->sdl_family != AF_LINK) {
- DPRINTF(IDL_ERROR, ("ipv6_discov_resolve called with rt->rt_gateway->sa_family == %d.\n", sdl->sdl_family));
- m_freem(outgoing);
- return 0;
- };
-
- if (sdl->sdl_alen == 0)
- {
- /*
- * I'm in INCOMPLETE mode or a new entry.
- *
- * Also, if this is a LINK-LOCAL address, (or there is some other
- * reason that it isn't clear which interface the address is on)
- * I might want to send the solicit out all interfaces.
- */
-
- rt->rt_flags &= ~RTF_REJECT; /* Clear RTF_REJECT in case LAN output
- routine caught expiration before
- the timer did. */
- ipv6_nsolicit(ifp,outgoing,rt);
- return 0;
- }
-
-#ifdef __FreeBSD__
- if (dq->dq_unanswered < 0 && time_second >= rt->rt_rmx.rmx_expire)
-#else /* __FreeBSD__ */
- if (dq->dq_unanswered < 0 && time.tv_sec >= rt->rt_rmx.rmx_expire)
-#endif /* __FreeBSD__ */
- {
- /*
- * Timeout on REACHABLE entry. Process accordingly.
- * Change the timed out REACHABLE entry into PROBE state.
- * ( REACHABLE -> PROBE )
- * PROBE state handling is the job of ipv6_discovery_timer().
- */
-#ifdef __FreeBSD__
- rt->rt_rmx.rmx_expire = time_second + v6d_delfirstprobe;
-#else /* __FreeBSD__ */
- rt->rt_rmx.rmx_expire = time.tv_sec + v6d_delfirstprobe;
-#endif /* __FreeBSD__ */
- dq->dq_unanswered = 0;
- ipv6 = mtod(outgoing,struct ipv6 *);
- }
-
- DPRINTF(GROSSEVENT,("ipv6_discov_resolve() returning 1.\n"));
- /*
- * Right now, just trust sdl is set up right. May need to change this
- * later.
- */
- bcopy(LLADDR(sdl),edst, sdl->sdl_alen);
- return 1;
-}
-
-
-/*----------------------------------------------------------------------
- * tunnel_parent():
- * Set up tunnel state for a network (cloning?) tunnel route.
- * Right now, there is no tunnel state unless an IPv6 secure tunnel
- * (rt->rt_gateway->sa_family == AF_INET6 && (RTF_CRYPT || RTF_AUTH))
- ----------------------------------------------------------------------*/
-
-void tunnel_parent(rt)
- register struct rtentry *rt;
-{
- struct rtentry *chaser = rt;
-
- DPRINTF(GROSSEVENT,("ipv6_tunnel_parent():0000-Starting.\n"));
- DDO(GROSSEVENT,printf(" rt_flags = 0x%x\n",(unsigned int)rt->rt_flags));
-
- /*
- * For now, set up master tunnel MTU. Chase rt_gwroute until no more, and
- * see if there is either rmx_mtu or ifp->mtu to transfer. This should
- * work on both cloning and non-cloning tunnel routes.
- *
- * Q: Do I want to chase it all the way? Or just to the next one?
- * A: For now go to the next one. Change the following "if" to a
- * "while" if you want to switch.
- *
- * Q2: For non-gateway tunnels (i.e. node-to-host tunnels), I may
- * need to undo some braindamage. How?
- */
-
- /* Change "if" to "while" for all-the-way chasing. */
- while (chaser->rt_gwroute != NULL)
- chaser = chaser->rt_gwroute;
-
- DDO(GROSSEVENT,printf("Last route in gwroute chain is:\n");\
- dump_rtentry(rt));
-
- if (chaser == rt)
- {
- /*
- * If non-gateway tunnel, find a route for the gateway address.
- */
-#ifdef __FreeBSD__
- chaser = rtalloc1(rt->rt_gateway,0,0UL);
-#else /* __FreeBSD__ */
- chaser = rtalloc1(rt->rt_gateway,0);
-#endif /* __FreeBSD__ */
- if (chaser == NULL)
- /*
- * Oooh boy, you're on your own, kid!
- */
- chaser = rt;
- else
- {
- chaser->rt_refcnt--;
- /* else do I want to do that while loop again? */
- }
- }
-
- if (chaser->rt_rmx.rmx_mtu != 0)
- {
- DPRINTF(GROSSEVENT,("Chaser's route MTU (%d) is set.\n",\
- (int)chaser->rt_rmx.rmx_mtu));
- rt->rt_rmx.rmx_mtu = chaser->rt_rmx.rmx_mtu;
- }
- else
- {
- DPRINTF(GROSSEVENT,("Chaser's route MTU is not set. "));
- DPRINTF(GROSSEVENT,("Attempting ifp check.\n"));
- if (chaser->rt_ifp == NULL)
- {
- DPRINTF(IDL_ERROR,\
- ("Can't find ifp. Using IPV6_MINMTU (%d).\n",IPV6_MINMTU));
- rt->rt_rmx.rmx_mtu = IPV6_MINMTU;
- }
- else
- {
- DPRINTF(FINISHED,("Found ifp with mtu of (%d).\n",\
- (int)chaser->rt_ifp->if_mtu));
- rt->rt_rmx.rmx_mtu = chaser->rt_ifp->if_mtu;
- }
- }
-
- if (chaser->rt_ifp != rt->rt_ifp)
- {
- /*
- * Somewhere along the way, things got messed up.
- * (IPv4 tends to confuse ifa_ifwithroute(), and loopback happens.)
- *
- * For tunnels, set the rt_ifp to the interface the chaser finds.
- * Hopefully ipv6_setrtifa() will do the right thing with the ifa.
- */
- rt->rt_ifp = chaser->rt_ifp;
- }
-
- /*
- * Adjust based on any known encapsulations:
- */
-
- /* Use the rt->rt_gateway->sa_family to determine kind of tunnel */
- if (rt->rt_gateway->sa_family == AF_INET) /* v6 in v4 tunnel */
- {
- /* WARNING: v6-in-v4 tunnel may also have secure bits set. */
- rt->rt_rmx.rmx_mtu -= sizeof(struct ip);
- }
- else if (rt->rt_gateway->sa_family == AF_INET6)
- {
- /* Perhaps need to ensure that the rt_gateway is a neighbor. */
- rt->rt_rmx.rmx_mtu -= sizeof(struct ipv6);
- }
-
-#ifdef IPSEC
- if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
- {
- struct socket *so;
-
- DPRINTF(GROSSEVENT,("tunnel_parent: Setting up secure tunnel state.\n"));
-
- if (!(rt->rt_netproc = malloc(sizeof(*so), M_SOCKET, M_NOWAIT))) {
- DPRINTF(IDL_ERROR, ("tunnel_parent: can't allocate fake socket\n"));
- return;
- }
-
- so = (struct socket *)rt->rt_netproc;
- bzero(so, sizeof(*so));
- so->so_oobmark = 1; /* really the refcount */
-
- if (netproc_alloc(so)) {
- DPRINTF(IDL_ERROR, ("tunnel_parent: can't allocate netproc state\n"));
- return;
- }
-
- {
- struct netproc_requestandstate *r =
- &((struct netproc_socketdata *)so->so_netproc)->requests[0];
- void *p;
-
- r->requestlen = ((rt->rt_flags & RTF_AUTH) ? sizeof(fixedauth) : 0)
- + ((rt->rt_flags & RTF_CRYPT) ? sizeof(fixedencrypt) : 0);
-
- if (!(r->request = malloc(r->requestlen, M_SOOPTS, M_NOWAIT))) {
- DPRINTF(IDL_ERROR,("tunnel_child: can't allocate netproc request\n"));
- r->requestlen = 0;
- return;
- }
-
- p = r->request;
-
- /* XXX - should be determined at runtime */
- if (rt->rt_flags & RTF_AUTH) {
- memcpy(p, &fixedauth, sizeof(fixedauth));
- p += sizeof(fixedauth);
- rt->rt_rmx.rmx_mtu -= IPSEC_AH_WORSTPREOVERHEAD +
- IPSEC_AH_WORSTPOSTOVERHEAD;
- }
-
- if (rt->rt_flags & RTF_CRYPT) {
- memcpy(p, &fixedencrypt, sizeof(fixedencrypt));
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPREOVERHEAD;
- if (rt->rt_flags & RTF_AUTH)
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_PLAIN;
- else
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_COMBINED;
- }
- }
- }
-#endif /* IPSEC */
- DPRINTF(GROSSEVENT,("ipv6_tunnel_parent():1000-Done.\n"));
-}
-
-/*----------------------------------------------------------------------
- * tunnel_parent_clean():
- * Frees dummy socket of secure tunnel.
- * Any other tunnel state should also be cleaned up here for the parent.
- * NOTE: Children won't be cleaned up.
- ----------------------------------------------------------------------*/
-void tunnel_parent_clean(rt)
- register struct rtentry *rt;
-{
- DPRINTF(GROSSEVENT,("tunnel_parent_clean():0000-Starting.\n"));
-#ifdef IPSEC
- if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
- {
- struct socket *so = (struct socket *)rt->rt_netproc;
-
- DPRINTF(GROSSEVENT,("tunnel_parent_clean():0500-It's RTF_CRYPT|RTF_AUTH; now removing.\n"));
- if (so == NULL)
- {
- DPRINTF(IDL_ERROR,("WARNING: Secure tunnel w/o dummy socket.\n"));
- return;
- }
-
- if (so->so_netproc)
- if (--so->so_oobmark) /* Refcnt != 0 */
- return;
- else
- netproc_free(so);
-
- free(so, M_SOCKET);
- rt->rt_netproc = NULL;
- }
-#endif /* IPSEC */
- DPRINTF(GROSSEVENT,("tunnel_parent_clean():1000-Finished.\n"));
-}
-
-/*----------------------------------------------------------------------
- * tunnel_child():
- * Set up tunnel state for a host or cloned tunnel route.
- * Right now, there is no tunnel state except for secure tunnels
- * ((RTF_CRYPT || RTF_AUTH))
- ----------------------------------------------------------------------*/
-
-void tunnel_child(rt)
- register struct rtentry *rt;
-{
- struct sockaddr_in6 *dst = (struct sockaddr_in6 *)rt_key(rt);
- struct sockaddr_in *sin;
-
- DPRINTF(GROSSEVENT,("tunnel_child():0000-Starting.\n"));
-
- /* Turn off STATIC flag only if cloned. */
- if (rt->rt_parent != NULL)
- rt->rt_flags &= ~RTF_STATIC;
-
- /*
- * If additional tunnel state were needed, it could be hung off rt_llinfo.
- * August 14, 1996: Actually, we're now using rt_tunsec to hold the
- * tunnel security state information. If we need to
- * carry more state information, we'll have to rework
- * this.
- */
-
- if (!(rt->rt_flags & RTF_GATEWAY) && rt->rt_parent != NULL)
- {
- /*
- * If not a gateway route, and cloned, I'll have to do some transforms.
- */
- switch (rt->rt_gateway->sa_family)
- {
- case AF_INET:
- if (IN6_IS_ADDR_V4COMPAT(&dst->sin6_addr))
- {
- /* Create new self-tunneling IPv6 over IPv4 route. */
- /* DANGER: If original setgate doesn't work properly, this
- could be trouble. */
- sin = (struct sockaddr_in *)rt->rt_gateway;
- sin->sin_addr.s_addr = dst->sin6_addr.s6_addr32[3];
- if (rt_setgate(rt,rt_key(rt),rt->rt_gateway))
- {
- DPRINTF(GROSSEVENT,
- ("rt_setgate failed in tunnel_child().\n"));
- rt->rt_rmx.rmx_mtu = 0;
- /* More should probably be done here. */
- }
- }
- /* else we're in BIG trouble. */
- break;
- }
- }
-
-#ifdef IPSEC
- if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
- {
- u_long flagsformtu;
-
- DPRINTF(GROSSEVENT,("tunnel_child():0300-Setting up secure host tunnel route.\n"));
- if (rt->rt_parent == NULL)
- {
- struct socket *so;
-
- DPRINTF(GROSSEVENT,("tunnel_child():400-Need to create a fresh socket.\n"));
-
- if (!(rt->rt_netproc = malloc(sizeof(*so), M_SOCKET, M_NOWAIT))) {
- DPRINTF(IDL_ERROR, ("tunnel_child: can't allocate fake socket\n"));
- return;
- }
-
- so = (struct socket *)rt->rt_netproc;
- bzero(so, sizeof(*so));
- so->so_oobmark = 1; /* really the refcount */
-
- if (netproc_alloc(so)) {
- DPRINTF(IDL_ERROR, ("tunnel_child: can't allocate netproc state\n"));
- return;
- }
-
- {
- struct netproc_requestandstate *r =
- &((struct netproc_socketdata *)so->so_netproc)->requests[0];
- void *p;
-
- r->requestlen = ((rt->rt_flags & RTF_AUTH) ? sizeof(fixedauth) : 0)
- + ((rt->rt_flags & RTF_CRYPT) ? sizeof(fixedencrypt) : 0);
-
- if (!(r->request = malloc(r->requestlen, M_SOOPTS, M_NOWAIT))) {
- DPRINTF(IDL_ERROR,("tunnel_child: can't allocate netproc request\n"));
- r->requestlen = 0;
- return;
- }
-
- p = r->request;
-
- if (rt->rt_flags & RTF_AUTH) {
- memcpy(p, &fixedauth, sizeof(fixedauth));
- p += sizeof(fixedauth);
- }
-
- if (rt->rt_flags & RTF_CRYPT)
- memcpy(p, &fixedencrypt, sizeof(fixedencrypt));
- }
-
- flagsformtu = rt->rt_flags;
- } else {
- struct socket *so = (struct socket *)rt->rt_parent->rt_netproc;
-
- DPRINTF(GROSSEVENT,("tunnel_child: Using parent's socket.\n"));
- if (!so) {
- DPRINTF(ERROR, ("tunnel_child: No socket in parent!\n"));
- return;
- }
-
- rt->rt_netproc = so;
-
- /* XXX - Bump refcount. This will break if you have more than about
- four million children, but you'll have bigger problems way before
- that ever happens - cmetz */
- so->so_oobmark++;
-
- flagsformtu = rt->rt_parent->rt_flags;
- }
-
- /* XXX - This block of code was a reasonable hack at the time, but isn't
- a good idea anymore. - cmetz */
- /* Security Level should be more configurable, possibly using
- a sysctl or by calling the IPsec security policy engine */
- if (flagsformtu & RTF_AUTH)
- rt->rt_rmx.rmx_mtu -= IPSEC_AH_WORSTPREOVERHEAD +
- IPSEC_AH_WORSTPOSTOVERHEAD;
- if (flagsformtu & RTF_CRYPT) {
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPREOVERHEAD;
- if (rt->rt_flags & RTF_AUTH)
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_PLAIN;
- else
- rt->rt_rmx.rmx_mtu -= IPSEC_ESP_WORSTPOSTOVERHEAD_COMBINED;
- }
- }
-#endif /* IPSEC */
-
- if (rt->rt_gwroute != NULL)
- {
- if (rt->rt_gwroute->rt_ifp != rt->rt_ifp)
- rt->rt_ifp = rt->rt_gwroute->rt_ifp;
- /* Should also handle Path MTU increases. */
- }
- /* else assume ifp is good to go. */
- DPRINTF(GROSSEVENT,("tunnel_child():1000-Finished.\n"));
-}
-
-/*----------------------------------------------------------------------
- * tunnel_child_clean():
- * probably does something like tunnel_parent_clean...
- ----------------------------------------------------------------------*/
-void tunnel_child_clean(rt)
- register struct rtentry *rt;
-{
- DPRINTF(GROSSEVENT,("tunnel_child_clean():0000-Starting.\n"));
-
-#ifdef IPSEC
- if (rt->rt_flags & (RTF_CRYPT|RTF_AUTH))
- {
- struct socket *so = (struct socket *)rt->rt_netproc;
-
- DPRINTF(GROSSEVENT,("tunnel_child_clean():-Removing security state.\n"));
- if (so == NULL)
- {
- DPRINTF(IDL_ERROR,("WARNING: tunnel_child_clean() Secure tunnel w/o dummy socket.\n"));
- return;
- }
-
- if (so->so_netproc)
- if (--so->so_oobmark) /* Refcnt != 0 */
- return;
- else
- netproc_free(so);
-
- free(so, M_SOCKET);
- rt->rt_netproc = NULL;
- }
-#endif /* IPSEC */
- DPRINTF(GROSSEVENT,("tunnel_child_clean():1000-Finished.\n"));
-}
diff --git a/sys/netinet6/ipv6_icmp.c b/sys/netinet6/ipv6_icmp.c
deleted file mode 100644
index 01424305243..00000000000
--- a/sys/netinet6/ipv6_icmp.c
+++ /dev/null
@@ -1,1086 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#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/time.h>
-#include <sys/kernel.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_pcb.h>
-
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-#include <netinet6/icmpv6_var.h>
-
-#if __OpenBSD__
-#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#ifdef IPSEC
-#include <sys/osdep.h>
-#include <net/netproc.h>
-#include <net/netproc_var.h>
-
-extern struct netproc_security fixedencrypt;
-extern struct netproc_auth fixedauth;
-#endif /* IPSEC */
-
-#if __FreeBSD__
-#include <sys/sysctl.h>
-#endif /* __FreeBSD__ */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * Globals.
- */
-
-static struct sockaddr_in6 icmpsrc = { sizeof (struct sockaddr_in6),
- AF_INET6 };
-static struct sockaddr_in6 icmpdst = { sizeof (struct sockaddr_in6),
- AF_INET6 };
-struct icmpv6stat icmpv6stat;
-
-/*
- * External globals.
- */
-
-extern struct in6_ifaddr *in6_ifaddr;
-extern u_char ipv6_protox[];
-extern struct protosw inet6sw[];
-
-/*
- * Functions and macros (and one global that needs to be near the function).
- */
-
-uint8_t ipv6_saved_routing[384]; /* If a routing header has been processed,
- then it will have been bcopy()d into
- this buffer. If not, the first byte
- will be set to zero (which would be
- a nexthdr of hop-by-hop, and is not
- valid). */
-
-struct mbuf *ipv6_srcrt __P((void));
-void ipv6_icmp_reflect __P((struct mbuf *, int));
-void update_pathmtu __P((struct in6_addr *, uint32_t));
-
-/* This is broken as of getting rid of hdrindex and the mbuf structure it
- creates. While this needs to be fixed, it's a Real Shame that source
- route reflection couldn't be reimplemented for this release... - cmetz */
-#if 0
-/*----------------------------------------------------------------------
- * Reverse a saved IPv6 source route, for possible use on replies.
- ----------------------------------------------------------------------*/
-
-struct mbuf *ipv6_srcrt()
-{
- struct ipv6_srcroute0 *sr, *osr = (struct ipv6_srcroute0 *)ipv6_saved_routing;
- struct in6_addr *sra, *osra;
- struct mbuf *srm;
- int i, j;
-
- if (!osr->i6sr_nexthdr)
- return NULL;
- if (osr->i6sr_type)
- return NULL;
- if (!(srm = m_get(M_DONTWAIT, MT_DATA)))
- return NULL;
-
- sr = mtod(srm, struct ipv6_srcroute0 *);
- bzero(sr, sizeof(struct ipv6_srcroute0));
- sr->i6sr_nexthdr = IPPROTO_ICMPV6;
- sr->i6sr_len = osr->i6sr_len;
- j = sr->i6sr_left = sr->i6sr_len/2;
-/* We probably should reverse the bit mask, but it's painful, and defaulting
- to loose source routing might be preferable anyway. */
- sra = (struct in6_addr *)((caddr_t)sr + sizeof(struct ipv6_srcroute0));
- osra = (struct in6_addr *)((caddr_t)osr + sizeof(struct ipv6_srcroute0));
- srm->m_len = sizeof(struct ipv6_srcroute0) + sizeof(struct in6_addr) * j;
- for (i = 0; i < sr->i6sr_len/2; sra[i++] = osra[j--]);
- return srm;
-}
-#endif /* 0 */
-
-/*----------------------------------------------------------------------
- * Reflect an IPv6 ICMP packet back to the source.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_icmp_reflect(m, extra)
- struct mbuf *m;
- int extra;
-{
- struct in6_addr tmp;
- struct ipv6 *ipv6;
- struct in6_ifaddr *i6a;
- struct ipv6_icmp *icmp;
-#if 0
- struct mbuf *routing = NULL;
-#endif /* 0 */
-#ifdef IPSEC
- struct socket *socket, fake;
-#endif /* IPSEC */
-
- /*
- * Hmmm, we potentially have authentication, routing, and hop-by-hop
- * headers behind this. OUCH. For now, however, assume only IPv6
- * header, followed by ICMP.
- */
-
- DP(FINISHED, extra, d);
-
- ipv6 = mtod(m, struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(m, caddr_t) + extra);
-
- tmp = ipv6->ipv6_dst;
- ipv6->ipv6_dst = ipv6->ipv6_src;
-
- /*
- * If the incoming packet was addressed directly to us,
- * use dst as the src for the reply. Otherwise (multicast
- * or anonymous), use the address which corresponds
- * to the incoming interface.
- */
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- {
- /* Find first (non-local if possible) address for
- source usage. If multiple locals, use last one found. */
-
- if (IN6_ARE_ADDR_EQUAL(&tmp, &I6A_SIN(i6a)->sin6_addr))
- break;
- }
- icmpdst.sin6_addr = tmp;
-
- if (i6a == NULL && m->m_pkthdr.rcvif != NULL)
- i6a = (struct in6_ifaddr *)ifaof_ifpforaddr((struct sockaddr *)&icmpdst,
- m->m_pkthdr.rcvif);
-
- if (i6a == NULL)
- {
- /* Want globally-routable if I can help it. */
- i6a = in6_ifaddr;
- }
-
- ipv6->ipv6_src = I6A_SIN(i6a)->sin6_addr;
-
- ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
-
- if (extra > sizeof(struct ipv6)) {
- DP(FINISHED, extra, 08x);
- DP(FINISHED, m->m_pkthdr.len, 08x);
- ipv6_stripoptions(m, extra);
- DP(FINISHED, m->m_pkthdr.len, 08x);
- extra = sizeof(struct ipv6);
-#if 0
- if ((routing = ipv6_srcrt())) {
- ipv6->ipv6_nexthdr = IPPROTO_ROUTING;
- mtod(routing, struct ipv6_srcroute0 *)->i6sr_nexthdr = IPPROTO_ICMPV6;
- routing->m_next = m->m_next;
- m->m_next = routing;
- extra += routing->m_len;
- } else
- DPRINTF(IDL_ERROR, ("icmp_reflect() got options but can't strip them\n"));
-#endif /* 0 */
- }
-
- m->m_flags &= ~(M_BCAST|M_MCAST);
-
- DP(FINISHED, m->m_pkthdr.len, d);
-
- /* For errors, anything over the 576 byte mark we discard. */
- if (!(ICMPV6_INFOTYPE(icmp->icmp_type)))
- if (m->m_pkthdr.len > ICMPV6_MAXLEN)
- m_adj(m, -(m->m_pkthdr.len - ICMPV6_MAXLEN));
-
- DP(FINISHED, m->m_pkthdr.len - extra, d);
-
- icmp->icmp_cksum = 0;
- DPRINTF(IDL_EVENT,("ipv6_icmp_reflect() calling in6_cksum().\n"));
- icmp->icmp_cksum = in6_cksum(m,IPPROTO_ICMPV6, m->m_pkthdr.len - extra,
- extra);
- DP(FINISHED, icmp->icmp_cksum, 04x);
-
- ipv6->ipv6_hoplimit = 255;
- /* Version 6, priority 12 (info) or 15 (error), flow zero */
- ipv6->ipv6_versfl = htonl(((6) << 28) |
- ((ICMPV6_INFOTYPE(icmp->icmp_type) ? 12 : 15) << 24));
-
-#if 0 /* def IPSEC */
- /*
- * Packet sent should be authenticated/encrypted if responding to
- * received packet that was authenticated/encrypted.
- */
- if (m->m_flags & (M_AUTHENTIC | M_DECRYPTED)) {
- struct netproc_requestandstate *r;
-
- bzero(&fake, sizeof(struct socket));
-
- if (netproc_alloc(&fake)) {
- DPRINTF(ERROR, ("icmp_send: netproc_alloc failed\n"));
- m_freem(m);
- return;
- }
-
- r = &((struct netproc_socketdata *)fake.so_netproc)->requests[0];
- r->requestlen = ((m->m_flags & M_AUTHENTIC) ?
- sizeof(struct netproc_auth) : 0) +
- ((m->m_flags & M_DECRYPTED) ?
- sizeof(struct netproc_security) : 0);
- if (!(r->request = OSDEP_MALLOC(r->requestlen))) {
- DPRINTF(ERROR, ("icmp_send: malloc(%d) failed\n",
- r->requestlen));
- netproc_free(&fake);
- m_freem(m);
- return;
- }
-
- {
- void *p = r->request;
-
- if (m->m_flags & M_AUTHENTIC) {
- memcpy(p, &fixedauth, sizeof(struct netproc_auth));
- p += sizeof(fixedauth);
- }
- if (m->m_flags & M_DECRYPTED)
- memcpy(p, &fixedencrypt, sizeof(struct netproc_security));
- }
-
- socket = &fake;
- } else
- socket = NULL;
-#endif /* IPSEC */
-
- icmpv6stat.icps_outhist[icmp->icmp_type]++;
-
-#ifdef IPSEC
- ipv6_output(m, NULL, IPV6_RAWOUTPUT, NULL, NULL, socket);
-
- if (socket)
- netproc_free(socket);
-#else /* IPSEC */
- ipv6_output(m, NULL, IPV6_RAWOUTPUT, NULL, NULL, NULL);
-#endif /* IPSEC */
-}
-
-/*----------------------------------------------------------------------
- * Given a bad packet (badpack), generate an ICMP error packet in response.
- * We assume that ipv6_preparse() has been run over badpack.
- *
- * Add rate-limiting code to this function, on a timer basis.
- * (i.e. if t(current) - t(lastsent) < limit, then don't send a message.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_icmp_error(badpack, type, code, paramptr)
- struct mbuf *badpack;
- int type,code;
- uint32_t paramptr;
-{
- struct ipv6 *oipv6;
- int divpoint = sizeof(struct ipv6);
- struct ipv6_icmp *icmp;
- struct mbuf *outgoing;
-
- if ((badpack->m_flags & M_MCAST) /*&& type != ICMPV6_TOOBIG */) {
- m_freem(badpack);
- return;
- }
-
- /*if (type != ICMPV6_REDIRECT)*/
- icmpv6stat.icps_error++;
-
- /*
- * Since MTU and max ICMP packet size is less than a cluster (so far...)
- * pull the offending packet into a single cluster.
- *
- * If option-stripping becomes required, here might be the place to do it.
- * (The current design decision is to not strip options. Besides, one of
- * the callers of this function is ipv6_hop(), which does hop-by-hop
- * option processing.)
- */
-
- oipv6 = mtod(badpack,struct ipv6 *);
-
- DDO(GROSSEVENT,printf("oipv6 (0x%lx) is:\n",(unsigned long)oipv6);dump_ipv6(oipv6));
-
- DP(FINISHED, badpack->m_pkthdr.len, d);
-
- /*
- * Get a new cluster mbuf for ICMP error message. Since IPv6 ICMP messages
- * have a length limit that should be less than MCLBYTES, one cluster should
- * work nicely.
- */
-
- if (!(outgoing = m_gethdr(M_DONTWAIT,MT_HEADER))) {
- m_freem(badpack);
- return;
- };
-
- MCLGET(outgoing, M_DONTWAIT);
- if (!(outgoing->m_flags & M_EXT)) {
- m_freem(badpack);
- m_free(outgoing);
- return;
- };
-
- outgoing->m_len = sizeof(struct ipv6) + ICMPV6_MINLEN;
- bcopy(oipv6, mtod(outgoing, caddr_t), sizeof(struct ipv6));
- icmp = (struct ipv6_icmp *)(mtod(outgoing, caddr_t) + sizeof(struct ipv6));
-
- {
- int i = badpack->m_pkthdr.len;
-
- if (i > ICMPV6_MAXLEN - sizeof(struct ipv6) - sizeof(struct icmpv6hdr))
- i = ICMPV6_MAXLEN - sizeof(struct ipv6) - sizeof(struct icmpv6hdr);
-
- outgoing->m_len += i;
- /* Copies are expensive, but linear buffers are nice. Luckily, the data
- is bounded, short, and ICMP errors aren't that performance critical. */
- m_copydata(badpack, 0, i, mtod(outgoing, caddr_t) +
- sizeof(struct ipv6) + sizeof(struct icmpv6hdr));
- }
-
- /* Need rcvif to do source address selection later */
- outgoing->m_pkthdr.rcvif = badpack->m_pkthdr.rcvif;
-
- outgoing->m_pkthdr.len = outgoing->m_len;
-
-#if 0 /* defined(IPSEC) || defined(NRL_IPSEC) */
- /*
- * Copy over the DECRYPTED and AUTHENTIC flag.
- * NB: If the inbound packet was sent to us in an authenticated
- * or encrypted tunnel, these flags are cleared when we get here.
- * We don't have a way to preserve tunnel state at the moment.
- * This is a hole we need to fix soon.
- */
- outgoing->m_flags |= badpack->m_flags & (M_DECRYPTED | M_AUTHENTIC);
- DDO(IDL_ERROR,if (outgoing->m_flags & M_AUTHENTIC) printf("icmpv6_error processing authentic pkt\n"));
- DDO(IDL_ERROR,if (outgoing->m_flags & M_DECRYPTED) printf("icmpv6_error processing encrypted pkt\n"));
-#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
-
- m_freem(badpack);
-
- icmp->icmp_type = type;
- icmp->icmp_code = code;
- icmp->icmp_unused = 0;
- if (type == ICMPV6_PARAMPROB || type == ICMPV6_TOOBIG)
- icmp->icmp_paramptr = htonl(paramptr);
-
- ipv6_icmp_reflect(outgoing, divpoint);
-}
-
-/*----------------------------------------------------------------------
- * Update path MTU for an IPv6 destination. This function may have to go
- * scan for TCP control blocks and give them hints to scale down.
- * There is a small denial-of-service attack if MTU messages are
- * unauthenticated. I can lower MTU to 576.
- ----------------------------------------------------------------------*/
-
-void
-update_pathmtu(dst, newmtu)
- struct in6_addr *dst;
- uint32_t newmtu; /* ntohl()'ed by caller. */
-{
- int s = splnet();
- struct rtentry *rt;
- struct sockaddr_in6 sin6;
-
- DDO(IDL_EVENT,printf("Entering update_pathmtu with:\n");\
- dump_in6_addr(dst);printf("And newmtu of %d.\n",(unsigned int)newmtu));
-
- if (IN6_IS_ADDR_MULTICAST(dst))
- {
- /* Multicast MTU Discovery not yet implemented */
- DPRINTF(IDL_ERROR, ("Multicast MTU too big message.\n"));
- splx(s);
- return;
- }
-
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(struct sockaddr_in6);
- sin6.sin6_addr = *dst;
-
- /*
- * Since I'm doing a rtalloc, no need to zero-out the port and flowlabel.
- */
-#ifdef __FreeBSD__
- if ((rt = rtalloc1((struct sockaddr *)&sin6,0,0UL)) != NULL)
-#else /* __FreeBSD__ */
- if ((rt = rtalloc1((struct sockaddr *)&sin6,0)) != NULL)
-#endif /* __FreeBSD__ */
- {
- if (rt->rt_flags & RTF_HOST) {
- if (rt->rt_rmx.rmx_mtu < newmtu) {
- DPRINTF(IDL_ERROR,
- ("DANGER: New MTU message is LARGER than current MTU.\n"));
- };
-
- rt->rt_rmx.rmx_mtu = newmtu; /* This should be enough for HLP's to
- know MTU has changed, IMHO. */
- rt->rt_refcnt--;
- } else {
- DPRINTF(IDL_ERROR,
- ("Got path MTU message for non-cloned destination route.\n"));
- }
- /*
- * Find all active tcp connections, and indicate they need path MTU
- * updating as well.
- *
- * Also, find RTF_TUNNEL routes that point to this updated route,
- * because they need their path MTU lowered. Perhaps decapsulating
- * the message, and sending TOOBIG messages back.
- */
- }
-
- splx(s);
- return;
-}
-
-/*----------------------------------------------------------------------
- * ICMPv6 input routine. Handles inbound ICMPv6 packets, including
- * direct handling of some packets.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_icmp_input(incoming, extra)
- register struct mbuf *incoming;
- int extra;
-{
- struct ipv6_icmp *icmp;
- struct ipv6 *ipv6;
- int icmplen,code;
- void (*ctlfunc) __P((int, struct sockaddr *, struct ipv6 *, struct mbuf *));
-
- /*
- * Q: Any address validity checks beyond those in ipv6_input()?
- */
-
- DPRINTF(FINISHED, ("ipv6_icmp_input -- pkthdr.len = %d, extra = %d\n",
- incoming->m_pkthdr.len, extra));
-
- DDO(FINISHED, dump_mbuf_tcpdump(incoming));
-
- icmplen = incoming->m_pkthdr.len - extra;
- if (icmplen < ICMPV6_MINLEN)
- {
- /* Not enough for full ICMP packet. */
- icmpv6stat.icps_tooshort++;
- m_freem(incoming);
- return;
- }
-
- if (incoming->m_len < extra + ICMPV6_MINLEN)
- if (!(incoming = m_pullup2(incoming, extra + ICMPV6_MINLEN)))
- return;
-
- DDO(FINISHED, dump_mbuf_tcpdump(incoming));
-
- ipv6 = mtod(incoming, struct ipv6 *);
- icmp = (struct ipv6_icmp *)(mtod(incoming, caddr_t) + extra);
-
- /*
- * Verify checksum with IPv6 header at the top of this chain.
- */
-
- DPRINTF(IDL_EVENT,("ipv6_icmp_input() calling in6_cksum().\n"));
- DPRINTF(IDL_EVENT,("icmplen = %d\n", icmplen));
- {
- unsigned int cksum;
-
- if ((cksum = in6_cksum(incoming, IPPROTO_ICMPV6, icmplen, extra)))
- {
- DPRINTF(IDL_ERROR,("ipv6_icmp_input() -- checksum returned %08x.\n", cksum));
- m_freem(incoming);
- icmpv6stat.icps_checksum++;
- return;
- }
- }
-
-#ifdef IPSEC
- /* Perform input-side policy check. Drop packet if policy says to drop it. */
- {
- struct sockaddr_in6 srcsa, dstsa;
-
- bzero(&srcsa, sizeof(struct sockaddr_in6));
- srcsa.sin6_family = AF_INET6;
- srcsa.sin6_len = sizeof(struct sockaddr_in6);
- srcsa.sin6_addr = ipv6->ipv6_src;
-
- bzero(&dstsa, sizeof(struct sockaddr_in6));
- dstsa.sin6_family = AF_INET6;
- dstsa.sin6_len = sizeof(struct sockaddr_in6);
- dstsa.sin6_addr = ipv6->ipv6_dst;
-
- /* XXX - state arg should NOT be NULL, it should be the netproc state
- carried up the stack - cmetz */
- if (netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
- (struct sockaddr *)&dstsa, IPPROTO_ICMPV6,
- incoming, NULL, NULL)) {
- m_freem(incoming);
- return;
- }
- }
-#endif /* IPSEC */
-
- code = icmp->icmp_code;
- DPRINTF(IDL_EVENT, ("icmp->icmp_type = %d\n", icmp->icmp_type));
-
- if (icmp->icmp_type < ICMPV6_MAXTYPE + 1)
- {
- icmpv6stat.icps_inhist[icmp->icmp_type]++;
-
- /*
- * Deal with the appropriate ICMPv6 message type/code.
- */
- switch(icmp->icmp_type)
- {
- case ICMPV6_ECHO:
- icmp->icmp_type = ICMPV6_ECHOREPLY;
- icmpv6stat.icps_reflect++;
- ipv6_icmp_reflect(incoming, extra);
- return;
- case ICMPV6_UNREACH:
- /*
- * The pair of <type,code> should map into a PRC_*
- * value such that I don't have to rewrite in_pcb.c.
- */
- switch (code)
- {
- case ICMPV6_UNREACH_NOROUTE:
- code = PRC_UNREACH_NET;
- break;
- case ICMPV6_UNREACH_ADMIN:
- /* Subject to change */
- code = PRC_UNREACH_HOST;
- break;
- case ICMPV6_UNREACH_NOTNEIGHBOR:
- /* Subject to change */
- code = PRC_UNREACH_HOST;
- break;
- case ICMPV6_UNREACH_ADDRESS:
- code = PRC_HOSTDEAD;
- break;
- case ICMPV6_UNREACH_PORT:
- code = PRC_UNREACH_PORT;
- break;
- default:
- goto badcode;
- }
- goto deliver;
- break;
- case ICMPV6_TIMXCEED:
- if (code >1)
- goto badcode;
- code += PRC_TIMXCEED_INTRANS;
- goto deliver;
-
- case ICMPV6_PARAMPROB:
- if (code >2)
- goto badcode;
- code = PRC_PARAMPROB;
-
- case ICMPV6_TOOBIG:
- deliver:
- /*
- * Problem with datagram, advice HLP's.
- */
- DPRINTF(IDL_EVENT, ("delivering\n"));
- if (icmplen < ICMPV6_HLPMINLEN)
- {
- icmpv6stat.icps_badlen++;
- m_freem(incoming);
- return;
- }
-
- /* May want to pullup more than this */
- if (!(incoming = m_pullup2(incoming, extra + ICMPV6_HLPMINLEN)))
- return;
-
- /*
- * If cannot determine HLP, discard packet.
- *
- * For now, I assume that ICMP messages will be generated such that
- * the enclosed header contains only IPv6+<HLP header>. This is not
- * a good assumption to make in light of all sorts of options between
- * IPv6 and the relevant place to deliver this message.
- */
- {
- struct ipv6 *ipv6 = (struct ipv6 *)(mtod(incoming, caddr_t) + extra + ICMPV6_MINLEN);
- icmpsrc.sin6_addr = ipv6->ipv6_dst;
-
- if (icmp->icmp_type == ICMPV6_TOOBIG)
- {
- update_pathmtu(&ipv6->ipv6_dst,htonl(icmp->icmp_nexthopmtu));
- /* If I want to deliver to HLP, remove the break, and
- set code accordingly. */
- break;
- }
- DPRINTF(IDL_EVENT, ("Finding control function for %d\n",
- ipv6->ipv6_nexthdr));
- if ((ctlfunc = (void *)inet6sw[ipv6_protox[ipv6->ipv6_nexthdr]].pr_ctlinput)) {
- DPRINTF(IDL_EVENT, ("Calling control function for %d\n",
- ipv6->ipv6_nexthdr));
- (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, ipv6,incoming);
- }
- }
- break;
-
- badcode:
- DPRINTF(IDL_EVENT, ("Bad code!\n"));
- icmpv6stat.icps_badcode++;
- break;
-
- /*
- * IPv6 multicast group messages.
- */
- case ICMPV6_GRPQUERY:
- case ICMPV6_GRPREPORT:
- case ICMPV6_GRPTERM:
- break;
-
- /*
- * IPv6 discovery messages.
- */
- case ICMPV6_ROUTERSOL:
- ipv6_routersol_input(incoming, extra);
- break;
- case ICMPV6_ROUTERADV:
- ipv6_routeradv_input(incoming, extra);
- break;
- case ICMPV6_NEIGHBORSOL:
- ipv6_neighborsol_input(incoming, extra);
- break;
- case ICMPV6_NEIGHBORADV:
- ipv6_neighboradv_input(incoming, extra);
- break;
- case ICMPV6_REDIRECT:
- ipv6_redirect_input(incoming, extra);
- break;
- default:
- /* Allow delivery to raw socket. */
- break;
- }
- }
- DPRINTF(IDL_EVENT, ("Delivering ICMPv6 to raw socket\n"));
- DP(FINISHED, incoming->m_pkthdr.len, d);
-
- ripv6_input(incoming, extra); /* Deliver to raw socket. */
-}
-
-
-/*
- * The following functions attempt to address ICMP deficiencies in IPv6.
- * Mostly these are part of a hack to keep the user-level program from
- * computing checksums. :-P
- */
-
-/*----------------------------------------------------------------------
- * This function should never be called.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_icmp_output(m, so, dst)
- struct mbuf *m;
- struct socket *so;
- struct in6_addr *dst;
-{
- DPRINTF(IDL_ERROR,
- ("ipv6_icmp_output() was called and shouldn't have been.\n"));
-
- return ripv6_output(m,so,dst,NULL);
-}
-
-#if !__FreeBSD__
-/*----------------------------------------------------------------------
- * Prepend IPv6 header, and compute IPv6 checksum for PRU_SEND, otherwise,
- * redirect call to ripv6_usrreq().
- ----------------------------------------------------------------------*/
-int
-#if __NetBSD__
-ipv6_icmp_usrreq(so, req, m, nam, control, p)
-#else /* __NetBSD__ */
-ipv6_icmp_usrreq(so, req, m, nam, control)
-#endif /* __NetBSD__ */
- struct socket *so;
- int req;
- struct mbuf *m, *nam, *control;
-#if __NetBSD__
- struct proc *p;
-#endif /* __NetBSD__ */
-{
- register struct inpcb *inp = sotoinpcb(so);
-
- DPRINTF(IDL_EVENT,("Entering ipv6_icmp_usrreq(), req == %d\n",req));
-
- /*
- * If not sending, or sending with the header included (which IMHO means
- * the user filled in the src & dest on his/her own), do normal raw
- * IPv6 user request.
- */
-
- DPRINTF(IDL_EVENT,("Before check for ripv6_usrreq().\n"));
- if (req != PRU_SEND || inp->inp_flags & INP_HDRINCL)
-#if __NetBSD__
- return ripv6_usrreq(so,req,m,nam,control,p);
-#else /* __NetBSD__ */
- return ripv6_usrreq(so,req,m,nam,control);
-#endif /* __NetBSD__ */
-
- {
- struct sockaddr *sa;
-
- if (nam)
- sa = mtod(nam, struct sockaddr *);
- else
- sa = NULL;
-
-#if __NetBSD__
- return ipv6_icmp_send(so, req, m, sa, control, p);
-#else /* __NetBSD__ */
- return ipv6_icmp_send(so, req, m, sa, control, NULL);
-#endif /* __NetBSD__ */
- };
-}
-#endif /* !__FreeBSD__ */
-
-int ipv6_icmp_send(struct socket *so, int req, struct mbuf *m,
- struct sockaddr *addr, struct mbuf *control,
- struct proc *p)
-{
- register struct inpcb *inp = sotoinpcb(so);
- register struct ipv6 *ipv6;
- register struct ipv6_icmp *icp;
- struct in6_addr *dst;
- int tflags = 0, len, rc;
- struct in6_ifaddr *i6a;
-
- /*
- * redundant check, but necessary since we don't know if we are coming from
- * icmp_usrreq or not.
- */
- if (inp->inp_flags & INP_HDRINCL)
-#if __NetBSD__ || __FreeBSD__
- return ripv6_usrreq_send(so, req, m, addr, control, p);
-#else /* __NetBSD__ || __FreeBSD__ */
- return ripv6_usrreq_send(so, req, m, addr, control);
-#endif /* __NetBSD__ || __FreeBSD__ */
-
- if (in6_ifaddr == NULL)
- {
- m_freem(m);
- return EADDRNOTAVAIL;
- }
- len = m->m_pkthdr.len;
-
- /*
- * If we get here, req == PRU_SEND and flags do not have INP_HDRINCL set.
- * What that means in English is that a user process is sending an ICMPv6
- * datagram without constructing an IPv6 header.
- * We will construct an IPv6 header, fill it in completely, THEN COMPUTE
- * THE ICMPv6 CHECKSUM and tell ipv6_output() that we are raw.
- */
-
- if (so->so_state & SS_ISCONNECTED)
- {
- if (addr)
- {
- m_freem(m);
- return EISCONN;
- }
- dst = &(inp->inp_faddr6);
- i6a = (struct in6_ifaddr *)inp->inp_route6.ro_rt->rt_ifa;
- }
- else /* Not connected */
- {
- if (addr == NULL)
- {
- m_freem(m);
- return ENOTCONN;
- }
- DPRINTF(GROSSEVENT,("Sockaddr in nam is:\n"));
- DDO(GROSSEVENT,dump_smart_sockaddr(addr));
- dst = &(((struct sockaddr_in6 *) addr)->sin6_addr);
- inp->inp_route6.ro_dst = *((struct sockaddr_in6 *)addr);
- DPRINTF(EVENT,("In icmpv6_usrreq, Route is:\n"));
- DDO(EVENT, dump_rtentry(inp->inp_route6.ro_rt));
- if (so->so_options & SO_DONTROUTE)
- {
-#define ifatoi6a(x) ((struct in6_ifaddr *)x)
- if ((i6a =
- ifatoi6a(ifa_ifwithdstaddr((struct sockaddr *)addr))) == 0
- &&
- (i6a =
- ifatoi6a(ifa_ifwithnet((struct sockaddr *)addr))) == 0)
- {
- m_freem(m);
- return ENETUNREACH;
- }
- inp->inp_route.ro_rt = NULL;
- }
- else
- {
- struct route *ro = &inp->inp_route;
-
- /*
- * If there is no route, consider sending out a neighbor advert
- * across all the nodes. This is like the ipv6_onlink_query()
- * call in ipv6_output.c.
- */
- if (ro->ro_rt == NULL)
- rtalloc(ro);
- if (ro->ro_rt == NULL)
- {
- /*
- * No route of any kind, so spray neighbor solicits out all
- * interfaces, unless it's a multicast address.
- */
- DPRINTF(IDL_FINISHED,("Icmpv6 spraying neigbor solicits.\n"));
- if (IN6_IS_ADDR_MULTICAST(dst))
- {
- /*
- * Select source address for multicast, for now
- * return an error.
- */
- m_freem(m);
- return ENETUNREACH;
- }
- ipv6_onlink_query((struct sockaddr_in6 *)&ro->ro_dst);
- rtalloc(ro);
- }
- if (ro->ro_rt == NULL)
- {
- m_freem(m);
- return ENETUNREACH;
- }
- DPRINTF(IDL_EVENT,("Route after ipv6_onlink_query:\n"));
- DDO(IDL_EVENT,dump_route(ro));
- DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
- if (ro->ro_rt->rt_ifa == NULL)
- {
- /*
- * We have a route where we don't quite know which interface
- * the neighbor belongs to yet. If I get here, I know that this
- * route is not pre-allocated (such as done by in6_pcbconnect()),
- * because those pre-allocators will do the same
- * ipv6_onlink_query() and ipv6_verify_onlink() in advance.
- *
- * I can therefore free the route, and get it again.
- */
- int error;
-
- DPRINTF(IDL_EVENT,("Okay...rt_ifa==NULL!\n"));
- RTFREE(ro->ro_rt);
- ro->ro_rt = NULL;
- switch (error = ipv6_verify_onlink((struct sockaddr_in6 *)&ro->ro_dst))
- {
- case 0:
- break;
- case -1:
- error = ENETUNREACH;
- default:
- m_freem(m);
- return error;
- }
- rtalloc((struct route *)ro);
- if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
- panic("Oops3, I'm forgetting something after verify_onlink().");
- DPRINTF(IDL_EVENT,("Route after ipv6_verify_onlink:\n"));
- DDO(IDL_EVENT,dump_route(ro));
- DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
- }
-
- i6a = (struct in6_ifaddr *)ro->ro_rt->rt_ifa;
- DPRINTF(IDL_EVENT,("Okay we got an interface for ipv6_icmp:\n"));
- DDO(IDL_EVENT,dump_ifa((struct ifaddr *)i6a));
- }
-
- if (IN6_IS_ADDR_MULTICAST(dst) && inp->inp_moptions6 &&
- inp->inp_moptions6->i6mo_multicast_ifp &&
- inp->inp_moptions6->i6mo_multicast_ifp != i6a->i6a_ifp)
- {
- struct ifaddr *ifa;
-
-#if __FreeBSD__
- for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrhead.tqh_first;
- ifa != NULL; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrlist.tqh_first;
- ifa != NULL; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = inp->inp_moptions6->i6mo_multicast_ifp->if_addrlist;
- ifa != NULL; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if (ifa->ifa_addr->sa_family == AF_INET6)
- {
- i6a = (struct in6_ifaddr *)ifa;
- break;
- }
- if (ifa == NULL)
- {
- DPRINTF(IDL_ERROR,("mcast inconsitency in icmp PRU_SEND.\n"));
- }
- }
- }
-
- /*
- * If PCB has options hanging off of it, insert them here.
- */
-
- DPRINTF(GROSSEVENT,("ipv6_icmp_usrreq(): dst is "));
- DDO(GROSSEVENT,dump_in6_addr(dst));
-
- M_PREPEND(m,sizeof(struct ipv6),M_WAIT);
- if (m == NULL)
- panic("M_PREPEND died in ipv6_icmp_usrreq().");
-
- DPRINTF(EVENT,("Before m_pullup() for %d bytes.\n",\
- sizeof(struct ipv6) + ICMPV6_MINLEN));
- if ((m = m_pullup(m,sizeof(struct ipv6) + ICMPV6_MINLEN)) == NULL)
- {
- DPRINTF(IDL_ERROR,("m_pullup in ipv6_icmp_usrreq() failed.\n"));
- return ENOBUFS; /* Any better ideas? */
- }
-
- ipv6 = mtod(m,struct ipv6 *);
- ipv6->ipv6_length = len;
- ipv6->ipv6_nexthdr = IPPROTO_ICMPV6;
- ipv6->ipv6_hoplimit = 255;
- ipv6->ipv6_dst = *dst;
- ipv6->ipv6_versfl = htonl(0x60000000); /* Plus flow label stuff. */
- /*
- * i6a pointer should be checked here.
- */
- ipv6->ipv6_src = i6a->i6a_addr.sin6_addr;
-
- icp = (struct ipv6_icmp *)(m->m_data + sizeof(struct ipv6));
- if (!(sotoinpcb(so)->inp_csumoffset))
- sotoinpcb(so)->inp_csumoffset = 2;
-
- DPRINTF(GROSSEVENT,("ipv6_icmp_usrreq(): Headers are\n"));
- DDO(GROSSEVENT,dump_ipv6(ipv6));
- DDO(GROSSEVENT,dump_ipv6_icmp(icp));
-
- /*
- * After this comment block you'd probably insert options,
- * and adjust lengths accordingly.
- */
-
- /*
- * Temporarily tweak INP_HDRINCL to fool ripv6_output(). I still don't
- * know how a user who sets INP_HDRINCL for real will prepare ICMP packets.
- * Also, set up data structures for callback routine in ipv6_output().
- */
-
- if (!(sotoinpcb(so)->inp_flags & INP_HDRINCL))
- {
- sotoinpcb(so)->inp_flags |= INP_HDRINCL;
- tflags = 1;
- }
- rc = ripv6_output(m,so,dst,control);
- if (!(so->so_state & SS_ISCONNECTED) && !(so->so_options & SO_DONTROUTE))
- {
- RTFREE(inp->inp_route.ro_rt);
- inp->inp_route.ro_rt = NULL;
- }
- if (tflags)
- sotoinpcb(so)->inp_flags &= ~INP_HDRINCL;
-
- return rc;
-}
-
-#if __FreeBSD__
-#if __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__
-#define MAYBEINLINE __inline__
-#else /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
-#define MAYBEINLINE
-#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
-
-#if 0
-MAYBEINLINE int ripv6_usrreq_attach(struct socket *, int , struct proc *);
-MAYBEINLINE int ripv6_usrreq_detach(struct socket *);
-MAYBEINLINE int ripv6_usrreq_abort(struct socket *);
-MAYBEINLINE int ripv6_usrreq_bind(struct socket *, struct sockaddr *, struct proc *);
-MAYBEINLINE int ripv6_usrreq_connect(struct socket *, struct sockaddr *, struct proc *);
-MAYBEINLINE int ripv6_usrreq_shutdown(struct socket *so);
-MAYBEINLINE int ripv6_usrreq_control(struct socket *, u_long, caddr_t,
- struct ifnet *, struct proc *);
-MAYBEINLINE int ripv6_usrreq_sense(struct socket *, struct stat *);
-MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *, struct sockaddr **);
-MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *, struct sockaddr **);
-#endif /* 0 */
-
-struct pr_usrreqs ipv6_icmp_usrreqs = {
- ripv6_usrreq_abort, pru_accept_notsupp, ripv6_usrreq_attach,
- ripv6_usrreq_bind, ripv6_usrreq_connect, pru_connect2_notsupp,
- ripv6_usrreq_control, ripv6_usrreq_detach, ripv6_usrreq_detach,
- pru_listen_notsupp, ripv6_usrreq_peeraddr, pru_rcvd_notsupp,
- pru_rcvoob_notsupp, ipv6_icmp_send, ripv6_usrreq_sense,
- ripv6_usrreq_shutdown, ripv6_usrreq_sockaddr, sosend, soreceive, sopoll
-};
-#endif /* __FreeBSD__ */
-
-int *icmpv6_sysvars[] = ICMPV6CTL_VARS;
-
-#if __FreeBSD__
-SYSCTL_STRUCT(_net_inet_icmpv6, ICMPV6CTL_STATS, icmpv6stat, CTLFLAG_RD, &icmpv6stat, icmpv6stat, "");
-#else /* __FreeBSD__ */
-int
-ipv6_icmp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
- int *name;
- u_int namelen;
- void *oldp;
- size_t *oldlenp;
- void *newp;
- size_t newlen;
-{
- if (name[0] >= ICMPV6CTL_MAXID)
- return (EOPNOTSUPP);
- switch (name[0]) {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- case ICMPV6CTL_STATS:
- return sysctl_rdtrunc(oldp, oldlenp, newp, &icmpv6stat, sizeof(icmpv6stat));
- default:
- return (sysctl_int_arr(icmpv6_sysvars, name, namelen, oldp, oldlenp, newp, newlen));
-#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- default:
- return EOPNOTSUPP;
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- }
-};
-#endif /* __FreeBSD__ */
diff --git a/sys/netinet6/ipv6_icmp.h b/sys/netinet6/ipv6_icmp.h
deleted file mode 100644
index 906cdd49395..00000000000
--- a/sys/netinet6/ipv6_icmp.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-#ifndef _NETINET6_IPV6_ICMP_H
-#define _NETINET6_IPV6_ICMP_H 1
-
-/*
- * ICMPv6 header.
- */
-
-struct ipv6_icmp
-{
- uint8_t icmp_type;
- uint8_t icmp_code;
- uint16_t icmp_cksum;
- union
- {
- uint32_t ih_reserved;
- struct
- {
- uint16_t ihs_id;
- uint16_t ihs_seq;
- } ih_idseq;
- struct
- {
- uint8_t ihr_hoplimit;
- uint8_t ihr_bits;
- uint16_t ihr_lifetime;
- } ih_radv;
- } icmp_hun;
-#define icmp_unused icmp_hun.ih_reserved
-#define icmp_nexthopmtu icmp_hun.ih_reserved
-#define icmp_paramptr icmp_hun.ih_reserved
-#define icmp_echoid icmp_hun.ih_idseq.ihs_id
-#define icmp_echoseq icmp_hun.ih_idseq.ihs_seq
-#define icmp_grpdelay icmp_hun.ih_idseq.ihs_id
-#define icmp_grpunused icmp_hun.ih_idseq.ihs_seq
-#define icmp_nadvbits icmp_hun.ih_reserved
-#define icmp_radvhop icmp_hun.ih_radv.ihr_hoplimit
-#define icmp_radvbits icmp_hun.ih_radv.ihr_bits
-#define icmp_radvlifetime icmp_hun.ih_radv.ihr_lifetime
- union
- {
- struct
- {
- struct ipv6 ido_ipv6;
- uint8_t ido_remaining[1];
- } id_offending;
- uint8_t id_data[1];
- struct
- {
- struct in6_addr idn_addr;
- uint8_t idn_ext[1];
- } id_neighbor;
- struct
- {
- struct in6_addr idr_addr1;
- struct in6_addr idr_addr2;
- uint8_t idr_ext[1];
- } id_redirect;
- struct
- {
- uint32_t ida_reachable;
- uint32_t ida_retrans;
- uint8_t ida_opt[1];
- } id_radv;
- } icmp_dun;
-#define icmp_offending icmp_dun.id_offending
-#define icmp_ipv6 icmp_dun.id_offending.ido_ipv6
-
-#define icmp_echodata icmp_dun.id_data
-
-#define icmp_grpaddr icmp_dun.id_neighbor.idn_addr
-
-#define icmp_radvreach icmp_dun.id_radv.ida_reachable
-#define icmp_radvretrans icmp_dun.id_radv.ida_retrans
-#define icmp_radvext icmp_dun.id_radv.ida_opt
-
-#define icmp_nsoltarg icmp_dun.id_neighbor.idn_addr
-#define icmp_nsolext icmp_dun.id_neighbor.idn_ext
-#define icmp_nadvaddr icmp_dun.id_neighbor.idn_addr
-#define icmp_nadvext icmp_dun.id_neighbor.idn_ext
-
-#define icmp_redirtarg icmp_dun.id_redirect.idr_addr1
-#define icmp_redirdest icmp_dun.id_redirect.idr_addr2
-#define icmp_redirext icmp_dun.id_redirect.idr_ext
-};
-
-/*
- * ICMPv6 extension constants.
- */
-
-#define EXT_SOURCELINK 1
-#define EXT_TARGETLINK 2
-#define EXT_PREFIX 3
-#define EXT_REDIR 4
-#define EXT_MTU 5
-
-/*
- * Extension structures for IPv6 discovery messages.
- */
-
-struct icmp_exthdr /* Generic extension */
-{
- uint8_t ext_id;
- uint8_t ext_length; /* Length is 8 * this field, 0 is invalid. */
- uint8_t ext_data[6]; /* Padded to 8 bytes. */
-};
-
-struct ext_prefinfo /* Prefix information */
-{
- uint8_t pre_extid;
- uint8_t pre_length;
-
- uint8_t pre_prefixsize;
- uint8_t pre_bits;
-
- uint32_t pre_valid;
- uint32_t pre_preferred;
- uint32_t pre_reserved;
-
- struct in6_addr pre_prefix;
-};
-
-/*
- * Values for pre_bits
- */
-#define ICMPV6_PREFIX_ONLINK 0x80
-#define ICMPV6_PREFIX_AUTO 0x40
-
-struct ext_redir /* Redirected header */
-{
- uint8_t rd_extid;
- uint8_t rd_length;
- uint8_t rd_reserved[6];
- struct ipv6 rd_header;
-};
-
-struct ext_mtu /* Recommended link MTU. */
-{
- uint8_t mtu_extid;
- uint8_t mtu_length;
- uint16_t mtu_reserved;
- uint32_t mtu_mtu;
-};
-
-/*
- * Constants
- */
-
-/*
- * Lower bounds on packet lengths for various types.
- * For the error advice packets must first insure that the
- * packet is large enought to contain the returned ip header.
- * Only then can we do the check to see if enough bits of packet
- * data have been returned, since we need to check the returned
- * ipv6 header length.
- */
-#define ICMPV6_MINLEN 8 /* abs minimum */
-#define ICMPV6_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
-#define ICMPV6_NADVMINLEN 24 /* min neighbor advertisement */
-#define ICMPV6_NSOLMINLEN 24 /* min neighbor solicit */
-#define ICMPV6_RADVMINLEN 16 /* min router advertisement */
-#define ICMPV6_RSOLMINLEN 8 /* min router solicit */
-#define ICMPV6_REDIRMINLEN 40 /* min redirect */
-#define ICMPV6_HLPMINLEN (8 + sizeof(struct ipv6) + 8) /* HLP demux len. */
-#define ICMPV6_MAXLEN 576 /* This should be whatever IPV6_MINMTU
- will be. I take this to be the WHOLE
- packet, including IPv6 header, and any
- IPv6 options before the ICMP message. */
-
-/*
- * Definition of type and code field values.
- * ICMPv6 fixes things so that info messages are >= 128.
- */
-
-/* Error messages and codes. */
-
-#define ICMPV6_UNREACH 1 /* dest unreachable, codes: */
-#define ICMPV6_UNREACH_NOROUTE 0 /* No route to dest. */
-#define ICMPV6_UNREACH_ADMIN 1 /* Admin. prohibited */
-#define ICMPV6_UNREACH_NOTNEIGHBOR 2 /* For strict source
- routing. */
-#define ICMPV6_UNREACH_ADDRESS 3 /* Address unreach. */
-#define ICMPV6_UNREACH_PORT 4 /* Port unreachable */
-#define ICMPV6_TOOBIG 2 /* Packet too big. */
-#define ICMPV6_TIMXCEED 3 /* time exceeded, code: */
-#define ICMPV6_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
-#define ICMPV6_TIMXCEED_REASS 1 /* Reassembly t.o. */
-#define ICMPV6_PARAMPROB 4 /* ip header bad */
-#define ICMPV6_PARAMPROB_PROB 0 /* Actual incorrect
- parameter. */
-#define ICMPV6_PARAMPROB_NEXTHDR 1 /* Bad next hdr. */
-#define ICMPV6_PARAMPROB_BADOPT 2 /* Unrec. option */
-
-/* Info messages. */
-
-#define ICMPV6_ECHO 128 /* echo service */
-#define ICMPV6_ECHOREPLY 129 /* echo reply */
-#define ICMPV6_GRPQUERY 130 /* Query group membership. */
-#define ICMPV6_GRPREPORT 131 /* Join mcast group. */
-#define ICMPV6_GRPTERM 132 /* Leave mcast group. */
-
-#define ICMPV6_ROUTERSOL 133 /* Router solicit. */
-#define ICMPV6_ROUTERADV 134 /* Router advertisement. */
-#define ICMPV6_NEIGHBORSOL 135 /* Neighbor solicit. */
-#define ICMPV6_NEIGHBORADV 136 /* Neighbor advertisement. */
-
-#define ICMPV6_REDIRECT 137 /* ICMPv6 redirect. */
-
-/* Defined this way to save some HTONL cycles on little-endian boxes. */
-#if BYTE_ORDER == BIG_ENDIAN
-#define ICMPV6_NEIGHBORADV_RTR 0x80000000 /* Router flag. */
-#define ICMPV6_NEIGHBORADV_SOL 0x40000000 /* Solicited flag. */
-#define ICMPV6_NEIGHBORADV_OVERRIDE 0x20000000 /* Override flag. */
-#else /* BYTE_ORDER == LITTLE_ENDIAN */
-#define ICMPV6_NEIGHBORADV_RTR 0x80 /* Router flag. */
-#define ICMPV6_NEIGHBORADV_SOL 0x40 /* Solicited flag. */
-#define ICMPV6_NEIGHBORADV_OVERRIDE 0x20 /* Override flag. */
-#endif
-
-#define ICMPV6_MAXTYPE 137
-
-#define ICMPV6_INFOTYPE(type) ((type) >= 128)
-
-#if defined(_KERNEL) || defined(KERNEL)
-#include <netinet6/ipv6_var.h>
-
-/* Function prototypes */
-void ipv6_icmp_error(struct mbuf *, int, int, uint32_t);
-void ipv6_icmp_input(struct mbuf *, int);
-void ipv6_gsolicit(struct ifnet *, struct mbuf *, struct rtentry *);
-void ipv6_rtrequest(int, struct rtentry *, struct sockaddr *);
-int ipv6_icmp_output(struct mbuf *,struct socket *, struct in6_addr *);
-int ipv6_icmp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
-#if __NetBSD__ || __FreeBSD__
-int ipv6_icmp_usrreq(struct socket *,int, struct mbuf *,struct mbuf *, struct mbuf *, struct proc *);
-#else /* __NetBSD__ || __FreeBSD__ */
-int ipv6_icmp_usrreq(struct socket *,int, struct mbuf *,struct mbuf *, struct mbuf *);
-#endif /* __NetBSD__ || __FreeBSD__ */
-
-void ipv6_routersol_input(struct mbuf *, int);
-void ipv6_routeradv_input(struct mbuf *, int);
-void ipv6_neighborsol_input(struct mbuf *, int);
-void ipv6_neighboradv_input(struct mbuf *, int);
-void ipv6_redirect_input(struct mbuf *, int);
-#endif /* defined(_KERNEL) || defined(KERNEL) */
-
-#endif /* _NETINET6_IPV6_ICMP_H */
diff --git a/sys/netinet6/ipv6_input.c b/sys/netinet6/ipv6_input.c
deleted file mode 100644
index a0e01faf481..00000000000
--- a/sys/netinet6/ipv6_input.c
+++ /dev/null
@@ -1,1449 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#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>
-#ifdef __FreeBSD__
-#include <net/netisr.h>
-#endif /* __FreeBSD__ */
-
-#include <netinet/in.h>
-
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-#include <netinet6/ipv6_addrconf.h>
-
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
-
-#if __OpenBSD__
-#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#ifdef IPSEC
-#include <sys/osdep.h>
-#include <net/netproc.h>
-#include <net/netproc_var.h>
-
-#include <netsec/ipsec.h>
-#endif /* IPSEC */
-
-#if __FreeBSD__
-#include <sys/sysctl.h>
-#endif /* __FreeBSD__ */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * Globals
- */
-
-u_char ipv6_protox[IPPROTO_MAX]; /* For easy demuxing to HLP's. */
-struct ipv6stat ipv6stat; /* Stats. */
-struct in6_ifaddr *in6_ifaddr = NULL; /* List of IPv6 addresses. */
-struct in6_ifnet *in6_ifnet = NULL; /* List of IPv6 interfaces. */
-struct ipv6_fragment *ipv6_fragmentq = NULL; /* Fragment reassembly queue */
-struct ifqueue ipv6intrq;
-struct route6 ipv6forward_rt;
-
-/*
- * External globals
- */
-
-extern int ipv6forwarding; /* See in6_proto.c */
-extern int ipv6rsolicit; /* See in6_proto.c */
-extern int ipv6_defhoplmt; /* See in6_proto.c */
-extern int ipv6qmaxlen; /* See in6_proto.c */
-extern struct protosw inet6sw[]; /* See in6_proto.c */
-extern struct domain inet6domain; /* See in6_proto.c */
-extern struct discq dqhead; /* See ipv6_discovery.c */
-
-/*
- * Funct. prototypes.
- */
-
-void ipv6_forward __P((struct mbuf *));
-void ipv6_discovery_init __P((void));
-extern int ipv6_clean_nexthop __P((struct radix_node *,void *));
-#if !__FreeBSD__
-int sysctl_int __P((void *, size_t *, void *, size_t, int *));
-/* For reasons somewhat unknown, <sys/mbuf.h> doesn't prototype this */
-struct mbuf *m_split __P((register struct mbuf *, int, int));
-#endif /* !__FreeBSD__ */
-
-void ipv6intr __P((void));
-int ipv6_enabled __P((struct ifnet *));
-
-static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level);
-#if 0
-static struct mbuf *ipv6_savebag(struct mbuf *m, int level);
-#endif /* 0 */
-
-/*----------------------------------------------------------------------
- * IPv6 initialization function.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_init()
-{
- register struct protosw *pr;
- register int i;
-
- DPRINTF(GROSSEVENT,("IPv6 initializing..."));
-
- bzero(&ipv6stat,sizeof(struct ipv6stat));
-
- pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
- if (pr == 0)
- panic("ipv6_init");
-
- /*
- * To call the right IPv6 next header function off next header type, have
- * next header numbers index the protocol switch, like protocols in IP
- * (default the switch for this). Otherwise, just switch off into normal
- * (TCP,UDP) stuff.
- *
- * Initialize ipv6_protox[].
- */
-
- for (i = 0; i < IPPROTO_MAX; i++)
- ipv6_protox[i] = pr - inet6sw;
- for (pr = inet6domain.dom_protosw;
- pr < inet6domain.dom_protoswNPROTOSW; pr++)
- if (pr->pr_domain->dom_family == PF_INET6
- && pr->pr_protocol != IPPROTO_RAW )
- ipv6_protox[pr->pr_protocol] = pr - inet6sw;
-
- /*
- * Initialize discovery stuff.
- */
-
- ipv6_discovery_init();
-
- /*
- * Initialize addrconf stuff.
- */
-
- addrconf_init();
-
- /*
- * Initialize IPv6 i/f queue.
- */
-
- ipv6intrq.ifq_maxlen = ipv6qmaxlen;
-
-#if 0 /* defined(INET6) && defined(IPSEC) */
- /*
- * Initialise IPsec
- */
- ipsec_init();
-#endif /* defined(INET6) && defined(IPSEC) */
-
- DPRINTF(GROSSEVENT,("...done\n"));
-}
-
-/*----------------------------------------------------------------------
- * IPv6 input queue interrupt handler.
- ----------------------------------------------------------------------*/
-
-void
-ipv6intr()
-{
- struct mbuf *m;
- int s;
-
- while (1) /* Keep yanking off packets until I hit... */
- {
- s = splimp();
- IF_DEQUEUE(&ipv6intrq, m);
- splx(s);
-
- if (m == NULL)
- {
- return; /* ...HERE. THIS is how I exit this endless loop. */
- }
-
-#if 0 /* def IPSEC */
- m->m_flags &= ~(M_AUTHENTIC | M_DECRYPTED);
-#endif /* IPSEC */
-
- ipv6_input(m, 0);
- }
-}
-
-#if __FreeBSD__
-NETISR_SET(NETISR_IPV6, ipv6intr);
-#endif /* __FreeBSD__ */
-
-/*----------------------------------------------------------------------
- * Actual inbound (up the protocol graph from the device to the user)
- * IPv6 processing.
- ----------------------------------------------------------------------*/
-
-#if __OpenBSD__
-void
-#if __STDC__
-ipv6_input(struct mbuf *incoming, ...)
-#else /* __STDC__ */
-ipv6_input(incoming, va_alist)
- struct mbuf *m;
- va_dcl
-#endif /* __STDC__ */
-#else /* __OpenBSD__ */
-void
-ipv6_input(incoming, extra)
- struct mbuf *incoming;
- int extra;
-#endif /* __OpenBSD__ */
-{
- struct ipv6 *header;
- struct in6_ifaddr *i6a = NULL;
- struct in6_multi *in6m = NULL;
- int jumbogram = 0;
- uint8_t nexthdr;
- int payload_len;
-#if __OpenBSD__
- va_list ap;
- int extra;
-#endif /* __OpenBSD__ */
-
- DPRINTF(GROSSEVENT,("ipv6_input(struct mbuf *incoming=%08lx, int extra=%x)\n", (unsigned long)incoming, extra));
- DPRINTF(IDL_FINISHED,("incoming->m_data = %08lx, & 3 = %lx\n", (unsigned long)incoming->m_data, (unsigned long)incoming->m_data & 3));
-
-#if __OpenBSD__
- va_start(ap, incoming);
- extra = va_arg(ap, int);
- va_end(ap);
-#endif /* __OpenBSD__ */
-
- /*
- * Can't do anything until at least an interface is marked as
- * ready for IPv6.
- */
-
- if (in6_ifaddr == NULL)
- {
- m_freem(incoming);
- return;
- }
-
- ipv6stat.ips_total++;
-
- /*
- * IPv6 inside something else. Discard encapsulating header(s) and
- * (maybe try to) make it look like virgin IPv6 packet.
- *
- * As with other stupid mbuf tricks, there probably is a better way.
- */
-
- if (extra) {
- struct mbuf *newpacket;
-
- DP(FINISHED, extra, d);
-
-#if IPSEC
- /* Perform input-side policy check. Drop packet if policy says to drop
- it.
-
- Note that the gist of this check is that every decapsulation
- requires a trip to input policy. For packets that end up locally,
- this is probably bad. For packets that go off-host, this is probably
- good.
-
- Right now, we err on the side of security and always check. -cmetz*/
- {
- struct sockaddr_in6 srcsa, dstsa;
-
- bzero(&srcsa, sizeof(struct sockaddr_in6));
- srcsa.sin6_family = AF_INET6;
- srcsa.sin6_len = sizeof(struct sockaddr_in6);
- srcsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_src;
-
- bzero(&dstsa, sizeof(struct sockaddr_in6));
- dstsa.sin6_family = AF_INET6;
- dstsa.sin6_len = sizeof(struct sockaddr_in6);
- dstsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_dst;
-
- /* XXX - state arg should NOT be NULL, it should be the netproc state
- carried up the stack - cmetz */
- if (netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
- (struct sockaddr *)&dstsa, IPPROTO_IPV6,
- incoming, NULL, NULL)) {
- m_freem(incoming);
- return;
- }
- }
-#endif /* IPSEC */
-
- /*
- * Split packet into what I need, and what was encapsulating what I
- * need. Discard the encapsulating portion.
- */
-
- if (!(newpacket = m_split(incoming, extra, M_DONTWAIT))) {
- printf("ipv6_input() couldn't trim extra off.\n");
- m_freem(incoming);
- return;
- }
- newpacket->m_flags |= incoming->m_flags;
- m_freem(incoming);
- incoming = newpacket;
- extra = 0;
- }
-
- /*
- * Even before preparsing (see vitriolic comments later),
- * I need to have the whole IPv6 header at my disposal.
- */
-
-
- if (incoming->m_len < sizeof (struct ipv6) &&
- (incoming = m_pullup(incoming, sizeof(struct ipv6))) == NULL)
- {
- ipv6stat.ips_toosmall++;
- return;
- }
-
- /*
- * Check version bits immediately.
- */
-
- if ((incoming->m_data[0] >> 4) != IPV6VERSION)
- {
- ipv6stat.ips_badvers++;
- m_freem(incoming);
- return;
- }
-
- header = mtod(incoming, struct ipv6 *);
-
- DDO(IDL_FINISHED,dump_ipv6(header));
-
- /*
- * Save off payload_len because of munging later...
- */
- payload_len = ntohs(header->ipv6_length);
-
- /*
- * 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 (incoming->m_pkthdr.len < payload_len + sizeof(struct ipv6))
- {
- ipv6stat.ips_tooshort++;
- m_freem(incoming);
- return;
- }
-
- if (incoming->m_pkthdr.len > payload_len + sizeof(struct ipv6)) {
- if (!payload_len) {
- jumbogram = 1; /* We might have a jumbogram here! */
- } else {
- if (incoming->m_len == incoming->m_pkthdr.len) {
- incoming->m_len = payload_len + sizeof(struct ipv6);
- incoming->m_pkthdr.len = payload_len + sizeof(struct ipv6);
- } else {
- m_adj(incoming,
- (payload_len + sizeof(struct ipv6)) - incoming->m_pkthdr.len );
- }
- }
- }
-
- /*
- * See if it's for me by checking list of i6a's. I may want to convert
- * this into a routing lookup and see if rt->rt_ifp is loopback.
- * (Might be quicker. :)
- */
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &header->ipv6_dst))
- break;
- /*
- * Check for inbound multicast datagram is for us.
- */
-
- if (i6a == NULL && IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- {
-#ifdef MROUTING
- /*
- * Do IPv6 mcast routing stuff here. This means even if I'm in the
- * m-cast group, I may have to forward the packet too. Hence, I put
- * the m-cast routing stuff HERE.
- */
-#endif
- DPRINTF(IDL_EVENT,("In multicast case. Looking up..."));
- IN6_LOOKUP_MULTI(&header->ipv6_dst,incoming->m_pkthdr.rcvif,in6m);
- if (!in6m) {
- DPRINTF(IDL_EVENT,("mcast lookup failed.\n"));
- DDO(IDL_EVENT, dump_ipv6(header));
- ipv6stat.ips_cantforward++;
- m_freem(incoming);
- return;
- }
- DPRINTF(IDL_EVENT,("succeeded.\n"));
- }
-
- /* If this datagram is for me, I'll either have one of my addresses in
- i6a, or one of my multicast groups in in6m. From the code given
- above, the condition (in6m && i6a) is always false. */
-
- if (i6a || in6m)
- {
- if (i6a) {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- /* Don't increment stats for encapsulated pkt */
- if (!extra) {
- i6a->i6a_ifa.ifa_ipackets++;
- i6a->i6a_ifa.ifa_ibytes += incoming->m_pkthdr.len;
- };
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
-
- if (i6a->i6a_addrflags & I6AF_NOTSURE) {
- /*
- * Don't want to unicast receive packets for me unless I know this
- * is a verified unique address.
- *
- * Multicast packets will still come in and be handled.
- */
- m_freem(incoming);
- return;
- }
- };
-
- nexthdr = (mtod(incoming, struct ipv6 *))->ipv6_nexthdr;
-
- DPRINTF(IDL_EVENT, ("nexthdr = %d.\n", nexthdr));
-
- DPRINTF(IDL_EVENT, ("Calling protoswitch for %d\n", nexthdr));
-
- ipv6stat.ips_delivered++;
- (*inet6sw[ipv6_protox[nexthdr]].pr_input)
- (incoming, extra + sizeof(struct ipv6));
-
- DPRINTF(IDL_EVENT, ("Back from protoswitch for %d\n", nexthdr));
- return;
- }
-
- DDO(IDL_EVENT,printf("Might forward for dest: ");\
- dump_in6_addr(&header->ipv6_dst););
-
- if (ipv6forwarding == 0)
- {
- DPRINTF(IDL_ERROR,("ipv6_input: Would call ipv6_forward if on.\n"));
- ipv6stat.ips_cantforward++;
- m_freem(incoming);
- return;
- }
- else
- {
- /*
- * Perform hop-by-hop options, if present. Dest opt. bags, and source
- * routes SHOULD be handled by previous protosw call.
- */
- DPRINTF(IDL_FINISHED,("Calling ipv6_forward.\n"));
- ipv6_forward(incoming);
- }
-}
-
-/*----------------------------------------------------------------------
- * IPv6 reassembly code. Returns an mbuf chain if the fragment completes
- * a message, otherwise it returns NULL.
- *
- * Assumptions:
- * * The IPv6 header is at the beginning of the incoming structure
- * and has already been pulled up. I don't expect this to be
- * a problem.
- * * Fragments don't overlap (or, if they do, we can discard them).
- * They can be duplicates (which we drop), but they cannot overlap.
- * If we have IPv4->IPv6 header translation, this assumption could
- * be incorrect because IPv4 has intermediate fragmentation (which
- * you have to have in order for fragments to overlap). This is yet
- * another reason why header translation is a Bad Thing.
- * * The slowtimeo()-based routine that frees fragments will not
- * get called during the middle of this routine. This assumption
- * seems to be made in the IPv4 reassembly code. How this is so
- * actually so I have yet to discover. Requiring splnet() or some
- * sort of resource arbitration to handle this assumption being
- * incorrect could be a major hassle.
- *
- * Other comments:
- *
- * * m_split(), a valuable call that gets no press, does much magic here.
- * * Security against denial-of-service attacks on reassembly cannot be
- * provided.
- * * frag_id is more or less used as a 32-bit cookie. We don't need to
- * htonl() it, because it's either equal or it's not, no matter what
- * byte order you use.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_reasm(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6_fraghdr *fraghdr;
- struct ipv6_fragment *fragment, **pfragment;
-
- if (incoming->m_len < extra + sizeof(struct ipv6_fraghdr))
- if (!(incoming = m_pullup(incoming, extra + sizeof(struct ipv6_fraghdr))))
- return;
-
- DP(FINISHED, OSDEP_PCAST(incoming), 08x);
-
- fraghdr = (struct ipv6_fraghdr *)(mtod(incoming, caddr_t) + extra);
- fraghdr->frag_bitsoffset = htons(fraghdr->frag_bitsoffset);
-
- /*
- * Locate reassembly queue for incoming fragment.
- */
- {
- struct ipv6 *ipv6i, *ipv6f;
-
- ipv6i = mtod(incoming, struct ipv6 *);
-
- for (pfragment = &ipv6_fragmentq; *pfragment;
- pfragment = &((*pfragment)->next)) {
-
- ipv6f = mtod((*pfragment)->prefix, struct ipv6 *);
-
- if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_src, &ipv6f->ipv6_src)) {
- if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_dst, &ipv6f->ipv6_dst)) {
- register struct ipv6_fraghdr *fhf = mtod((*pfragment)->data,
- struct ipv6_fraghdr *);
-
- if ((fraghdr->frag_id == fhf->frag_id) &&
- (fraghdr->frag_nexthdr == fhf->frag_nexthdr)) {
- break;
- }
- }
- }
- }
- }
-
- fragment = *pfragment;
-
- if (!fragment) {
- /*
- * Create a new fragment queue entry - this one looks new.
- *
- * Notice that the order of insertion is such that the newest queues
- * are at the head of the one-way list. The entry aging code takes
- * advantage of this.
- */
- MALLOC(fragment, struct ipv6_fragment *, sizeof(*fragment), M_FRAGQ,
- M_NOWAIT);
- if (!fragment) {
- DPRINTF(IDL_ERROR, ("MALLOC fragment queue entry failed.\n"));
- goto reasm_cleanup;
- };
- fragment->ttl = 60; /* 30 seconds */
- if (!(fragment->data = m_split(incoming, extra, M_DONTWAIT))) {
- free(fragment, M_FRAGQ);
- goto reasm_cleanup;
- }
-
- fragment->prefix = incoming;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
-
- fragment->flags = (~fraghdr->frag_bitsoffset) & 1;
-
- fragment->next = ipv6_fragmentq;
- ipv6_fragmentq = fragment;
- return;
- }
-
- /*
- * If two packets have claimed to be the beginning or the end, we don't
- * know which is right. The easiest solution is to drop this packet.
- */
-
- if (!(fraghdr->frag_bitsoffset & 1)) {
- if (fragment->flags & 1) /* i.e. we already have the end... */
- {
- /*
- * Duplicate (ending) fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
- else
- fragment->flags |= 1;
- }
-
- if (!(fraghdr->frag_bitsoffset & 0xFFF8)) {
- /*
- * We want to end up with the part before the frag header for the packet
- * at offset zero. (RFC1123?)
- */
- if (!(mtod(fragment->data, struct ipv6_fraghdr *)->frag_bitsoffset & 0xFFF8)) {
- /*
- * Duplicate (initial) packet.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- } else {
- struct mbuf *mbuf;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
-
- m_freem(fragment->prefix);
-
- /* Save everything before the frag header */
- if (!(mbuf = m_split(incoming, extra, M_DONTWAIT))) {
- /* should probably toss whole fragment queue */
- m_freem(incoming);
- return;
- }
-
- fragment->prefix = incoming;
- incoming = mbuf;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
- }
- } else
- m_adj(incoming, extra); /* Discard everything before the frag header */
-
- {
- struct mbuf *hm[3];
- int i;
-
- {
- struct mbuf *m, *pm;
-
- /*
- * Find where this fragment fits in.
- */
- for (pm = NULL, m = fragment->data; m; pm = m, m = m->m_nextpkt) {
- if (((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset & 0xFFF8) >
- (fraghdr->frag_bitsoffset & 0xFFF8)) {
- break;
- }
-
- if ((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset ==
- fraghdr->frag_bitsoffset) {
- /*
- * Duplicate fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
- }
-
- /*
- * Right here, pm will contain the preceeding fragment to the incoming
- * one, and m will contain the succeeding fragment to the incoming one.
- *
- * This is somewhat non-obvious. hm[] is a vector of pointers to the
- * mbufs containing ipv6_fraghdrs and dm[] is a vector of pointers to
- * the mbufs at the head of each of their associated data lists. [0]
- * is the mbuf in the main chain to the immediate left of where our
- * new data should go, [1] is our new data, and [2] is the mbuf in
- * the main chain to the immediate right of where our new data should
- * go. One of [0] or [2] may be NULL.
- *
- * Each dm[n]->m_nextpkt will have that fragment's length stored.
- *
- * The reason why we do this is so we can bubble together the [0] and
- * [1] elements if possible, make the [1] = [0] if we do, then we can
- * bubble the [1] and the [2] together and it'll do the right thing.
- * We could theoretically do this for the rest of the list except that
- * it is made deliberately unecessary (we bubble on insertion until we
- * have a known-done big bubble so we don't have to do a O(N/2)
- * rescan of the list every time just to figure out whether or not
- * we're done.
- *
- * This seems really ugly, but it does the job and it may even be
- * somewhat efficient.
- */
-
- hm[0] = pm;
- hm[1] = incoming;
- hm[2] = m;
-
- if (!pm) {
- incoming->m_nextpkt = fragment->data;
- fragment->data = incoming;
- } else {
- pm->m_nextpkt = incoming;
- incoming->m_nextpkt = m;
- }
- }
-
- for (i = 0; i < 2; i++) {
- if (!hm[i] || !hm[i+1]) {
- DP(FINISHED, i, d);
- continue;
- }
-
- if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) >
- ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8)) {
- /*
- * Overlapping fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
-
- if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) ==
- ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8)) {
- /*
- * If the fragments are contiguous, bubble them together:
- * Combine the data chains and increase the appropriate
- * chain data length. The second fragment header is now
- * unnecessary.
- */
- if (!((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset & 1)){
- (mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset &= (~1);
- }
-
- DDO(FINISHED, dump_mchain(hm[i]));
- DDO(FINISHED, dump_mchain(hm[i+1]));
-
- /* Trim the second fragment header */
- m_adj(hm[i+1], sizeof(struct ipv6_fraghdr));
- /* Append the second fragment's data */
- m_cat(hm[i], hm[i+1]);
- /* And update the first fragment's length */
- hm[i]->m_pkthdr.len += hm[i+1]->m_pkthdr.len;
-
- hm[i]->m_nextpkt = hm[i+1]->m_nextpkt;
-
- DDO(FINISHED, dump_mchain(hm[i]));
-
- /* Hack to make the bubble happen with the m found above if we
- just bubbled with the pm found above */
- if (!i)
- hm[i+1] = hm[i];
- }
- }
- }
-
- /*
- * Now, the moment of truth. Do we have a complete packet?
- * To be done, we have to have only one packet left in the queue now that
- * we've bubbled together (i.e., one complete packet), have it at offset
- * zero (i.e., there's nothing before it), and not have its more bit set
- * (i.e., there's nothing after it). If we meet these conditions, we are
- * DONE!
- *
- * Remember when we htons()ed frag_bitsoffset? If we're done, it contains
- * a zero. I don't know of any architecture in which a zero in network
- * byte order isn't a zero in host byte order, do you?
- */
-
- if (!fragment->data->m_nextpkt &&
- !(mtod(fragment->data, struct ipv6_fraghdr *))->frag_bitsoffset) {
- uint8_t nexthdr = mtod(fragment->data, struct ipv6_fraghdr *)->frag_nexthdr;
-
- if (*pfragment)
- *pfragment = fragment->next;
-
- /*
- * Pain time.
- *
- * The fragmentation header must be removed. This requires me to rescan
- * prefix, going through each header until I figure out where the last
- * header before the fragmentation header is. Then I set that header's
- * next header to the fragmentation header's next header.
- *
- * N.B. that this means people adding new random header processing code
- * to this IPv6 implementation need to make the appropriate mods below.
- * Failure to do so will really hose you if your header appears before
- * a fragment header. It is a Good Thing to mod the code below even if
- * you don't *think* it will ever appear before a fragment header, just
- * because it *could*.
- *
- * This is really annoying and somewhat expensive. On the other hand,
- * it might prove itself to be yet another reason for higher level
- * protocols to work at avoiding fragmentation where possible.
- */
-
- /*
- Since we start out by doing a m_pullup() of extra + sizeof(struct
- ipv6_fraghdr, we can treat this as a straight linear buffer.
-
- This could easily be implemented as a fast and slow path, but
- reassembly is an inherently slow path anyway. -cmetz
- */
-
- DDO(FINISHED, dump_mchain(fragment->prefix));
- DDO(FINISHED, dump_mchain(fragment->data));
-
- {
- caddr_t data;
- uint8_t *type;
-
- DDO(FINISHED, dump_ipv6(mtod(fragment->prefix, struct ipv6 *)));
-
- data = mtod(fragment->prefix, caddr_t) + sizeof(struct ipv6);
- type = &(mtod(fragment->prefix, struct ipv6 *)->ipv6_nexthdr);
-
- while(*type != IPPROTO_FRAGMENT) {
- switch(*type) {
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- {
- struct ipv6_opthdr *ipv6_opthdr = (struct ipv6_opthdr *)data;
-
- type = &(ipv6_opthdr->oh_nexthdr);
- data += sizeof(struct ipv6_opthdr) +
- ipv6_opthdr->oh_extlen * sizeof(uint64_t);
- };
- break;
- case IPPROTO_ROUTING:
- {
- struct ipv6_srcroute0 *ipv6_srcroute0 =
- (struct ipv6_srcroute0 *)data;
-
- type = &(ipv6_srcroute0->i6sr_nexthdr);
- data += sizeof(struct ipv6_srcroute0) +
- ipv6_srcroute0->i6sr_len * sizeof(uint64_t);
- };
- break;
- default:
- DPRINTF(ERROR, ("ipv6_reasm: Received a header (%d) that isn't allowed before a fragment header", *type));
-
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
-
- m_freem(fragment->data);
- m_freem(fragment->prefix);
- FREE(fragment, M_FRAGQ);
-
- return;
- }
- }
- *type = nexthdr;
- }
-
- extra = fragment->prefix->m_pkthdr.len;
-
- incoming = fragment->prefix;
- m_adj(fragment->data, sizeof(struct ipv6_fraghdr));
- m_cat(incoming, fragment->data);
- incoming->m_pkthdr.len += fragment->data->m_pkthdr.len;
-
- FREE(fragment, M_FRAGQ);
-
- /* Can't reassemble into a jumbogram */
- if (incoming->m_pkthdr.len > 0xffff) {
- m_freem(incoming);
- return; /* no other cleanup needed */
- }
-
- /* Dummy up length */
- (mtod(incoming, struct ipv6 *))->ipv6_length =
- htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
-
- DDO(FINISHED, dump_mchain(incoming));
-
- (*inet6sw[ipv6_protox[nexthdr]].pr_input)(incoming, extra);
- }
- return;
-
-reasm_cleanup:
- if (incoming)
- m_freem(incoming);
-
- return;
-};
-
-/*----------------------------------------------------------------------
- * IPv6 forwarding engine.
- *
- * Assumes IPv6 header is already pulled up and ready-to-read.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_forward(outbound)
- struct mbuf *outbound;
-{
- struct ipv6 *ipv6 = mtod(outbound, struct ipv6 *);
- struct sockaddr_in6 *sin6;
- struct rtentry *rt;
- struct mbuf *ocopy;
- int type = 0, code = 0, pptr = 0, error;
-
- /*
- * Check hop limit.
- */
-
- if (ipv6->ipv6_hoplimit <= 1)
- {
- ipv6_icmp_error(outbound,ICMPV6_TIMXCEED,ICMPV6_TIMXCEED_INTRANS,0);
- return;
- }
-
- /*
- * Check link-local nature of source and dest. (Thanks to rja!)
- */
- if (IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src) || IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_dst))
- {
- printf("Can't forward packet with link-locals in it!\n");
- m_freem(outbound);
- return;
- }
- ipv6->ipv6_hoplimit--;
- sin6 = &ipv6forward_rt.ro_dst;
- if ((rt = ipv6forward_rt.ro_rt) == NULL ||
- !IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_dst, &sin6->sin6_addr))
- {
- if (ipv6forward_rt.ro_rt != NULL)
- {
- RTFREE(ipv6forward_rt.ro_rt);
- ipv6forward_rt.ro_rt = NULL;
- }
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_addr = ipv6->ipv6_dst;
-
- rtalloc_noclone((struct route *)&ipv6forward_rt,ONNET_CLONING);
- if (ipv6forward_rt.ro_rt == NULL)
- {
- ipv6_icmp_error(outbound,ICMPV6_UNREACH, ICMPV6_UNREACH_NOROUTE, 0);
- return;
- }
- rt = ipv6forward_rt.ro_rt;
- }
-
- /*
- * Save 576 bytes of the packet in case we need to generate and ICMPv6
- * message to the sender.
- */
-
- ocopy = m_copy(outbound,0, min(ntohs(ipv6->ipv6_length),IPV6_MINMTU));
-
- /*
- * ip_forward() keeps some statistics here if GATEWAY is defined. We
- * skip that for now.
- */
-
- if (rt->rt_ifp == outbound->m_pkthdr.rcvif && rt_mask(rt)
- && rt_mask(rt)->sa_len
- && (rt->rt_flags & (RTF_TUNNEL|RTF_DYNAMIC|RTF_MODIFIED)) == 0 )
- {
- DPRINTF(IDL_EVENT, ("WARNING: May be cause for a redirect in ipv6_forward().\n"));
- }
-
- /*
- * Perform hop-by-hop options since we're ready to go!
- */
-
- if (!ipv6->ipv6_nexthdr)
- {
- /*
- * If bad hop-by-hop, return.
- */
- printf("Hop-by-hop options present in packet to be forwarded!\n");
- }
-
- error = ipv6_output(outbound, &ipv6forward_rt, IPV6_FORWARDING, NULL, NULL, NULL);
-
- if (error)
- ipv6stat.ips_cantforward++;
- else
- {
- /*
- * Check for redirect flags that should've been set in the redirect
- * code, otherwise...
- */
- m_freem(ocopy);
- return;
- }
-
- switch (error)
- {
- case 0: /* type and code should've been set by redirect code. */
- break;
-
- case ENETUNREACH: /* These two should've been checked above. */
- case EHOSTUNREACH:
- type = ICMPV6_UNREACH;
- code = ICMPV6_UNREACH_NOROUTE;
- break;
-
- case EHOSTDOWN:
- type = ICMPV6_UNREACH;
- code = ICMPV6_UNREACH_ADDRESS;
- break;
-
- case EMSGSIZE:
- type = ICMPV6_TOOBIG;
- code = 0;
- ipv6stat.ips_cantfrag++;
- pptr = rt->rt_rmx.rmx_mtu;
- break;
- }
-
- ipv6_icmp_error(ocopy, type, code, pptr);
-}
-
-/*----------------------------------------------------------------------
- * IPv6 hop-by-hop option handler.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_hop(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *header;
- struct ipv6_opthdr *hopopt;
- struct ipv6_option *option;
- uint8_t *tmp;
-
- if (incoming->m_len < extra + sizeof(struct ipv6_opthdr))
- if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr))))
- return;
-
- hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
-
- if (incoming->m_len < extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t))) {
- if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t)))))
- return;
-
- hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
- }
-
- header = mtod(incoming, struct ipv6 *);
-
- tmp = hopopt->oh_data;
- /*
- * Slide the char pointer tmp along, parsing each option in the "bag" of
- * hop-by-hop options.
- */
- while (tmp < hopopt->oh_data + (hopopt->oh_extlen << 2))
- {
- option = (struct ipv6_option *)tmp;
- switch (option->opt_type)
- {
- case OPT_PAD1:
- tmp++;
- break;
- case OPT_PADN:
- tmp += option->opt_datalen + 2;
- break;
- case OPT_JUMBO:
- tmp += 2;
- /*
- * Confirm that the packet header field matches the jumbogram size.
- */
- if (incoming->m_pkthdr.len != ntohl(*(uint32_t *)tmp) + sizeof(*header))
- {
- /*
- * For now, bail. Add instrumenting code here, too.
- */
- m_freem(incoming);
- return;
- }
- break;
- default:
- /*
- * Handle unknown option by taking appropriate action based on
- * high bit values. With this code, this first non-skipping
- * unknown option will cause the packet to be dropped.
- */
- switch (option->opt_type & 0xC0)
- {
- case 0: /* Skip over */
- tmp += option->opt_datalen + 2;
- break;
- case 0xC0: /* Only if not multicast... */
- if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- /* FALLTHROUGH */
- case 0x80: /* Send ICMP Unrecognized type. */
- {
- /* Issue ICMP parameter problem. */
- ipv6_icmp_error(incoming,ICMPV6_PARAMPROB,
- ICMPV6_PARAMPROB_BADOPT,
-#ifdef __alpha__
- (u_long)option - (u_long)hopopt->oh_data +
-#else
- (uint32_t)option - (uint32_t)hopopt->oh_data +
-#endif
- sizeof(struct ipv6));
- }
- return; /* incoming has already been freed. */
-
- case 0x40: /* Discard packet */
- m_freem(incoming);
- return;
- }
- }
- }
-
- DPRINTF(GROSSEVENT, ("ipv6_hop calling protoswitch for %d\n", \
- hopopt->oh_nexthdr));
-
- (*inet6sw[ipv6_protox[hopopt->oh_nexthdr]].pr_input)
- (incoming, extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t)));
-
- DPRINTF(GROSSEVENT, ("Leaving ipv6_hop\n"));
-}
-
-
-/*----------------------------------------------------------------------
- * If timers expires on reassembly queues, discard them.
- * Also update any discovery queues.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_slowtimo()
-{
- struct ipv6_fragment *fragment, *fragmentprev;
- int s = splnet();
-
- /*
- * Age reasssembly fragments.
- *
- * (Since fragments are inserted at the beginning of the queue, once we've
- * found the first timed-out fragment, we know that everything beyond is
- * also timed-out since it must be older.)
- *
- */
- for (fragmentprev = NULL, fragment = ipv6_fragmentq; fragment;
- fragmentprev = fragment, fragment = fragment->next)
- if (fragment->ttl <= 1)
- break;
- else
- fragment->ttl--;
-
- if (fragment) {
- struct mbuf *m, *m2;
-
- if (fragmentprev)
- fragmentprev->next = NULL;
- else
- ipv6_fragmentq = NULL;
-
- /*
- * This loop does most of the work and doesn't require splnet()...?
- */
- splx(s);
-
- while(fragment) {
- /*
- * We "should" (says the spec) send an ICMP time exceeded here.
- * However, among other headaches, we may not actually have a copy
- * of the real packet sent to us (if we bubbled, we now have a frag
- * header that never really came from the sender). The solution taken
- * for now is to continue the BSD tradition of not bothering to send
- * these messages.
- */
- m = fragment->data;
- ipv6stat.ips_fragtimeout++;
- while(m) {
- m2 = m;
- m = m->m_nextpkt;
- m_freem(m2);
- }
- m_freem(fragment->prefix);
- fragmentprev = fragment;
- fragment = fragment->next;
- FREE(fragmentprev, M_FRAGQ);
- }
- } else
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * Drain all fragments & possibly discovery structures.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_drain()
-{
- struct ipv6_fragment *totrash;
- struct mbuf *m,*m2;
- struct radix_node_head *rnh = rt_tables[AF_INET6];
-
- while (ipv6_fragmentq != NULL)
- {
- ipv6stat.ips_fragdropped++;
- totrash = ipv6_fragmentq;
- ipv6_fragmentq = totrash->next;
- m = totrash->data;
- while (m)
- {
- m2 = m;
- m = m->m_nextpkt;
- m_freem(m2);
- }
- if (totrash->prefix)
- m_freem(totrash->prefix);
- FREE(totrash, M_FRAGQ);
- }
-
- /*
- * Might want to delete all off-net host routes,
- * and maybe even on-net host routes.
- *
- * For now, do only the off-net host routes.
- */
- (void) rnh->rnh_walktree(rnh, ipv6_clean_nexthop, (void *)1);
-}
-
-/*----------------------------------------------------------------------
- * sysctl(2) handler for IPv6. Not yet implemented.
- ----------------------------------------------------------------------*/
-int *ipv6_sysvars[] = IPV6CTL_VARS;
-
-#if __FreeBSD__
-SYSCTL_STRUCT(_net_inet_ipv6, IPV6CTL_STATS, ipv6stats, CTLFLAG_RD, &ipv6stat, ipv6stat, "");
-#else /* __FreeBSD__ */
-int
-ipv6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
- int *name;
- uint namelen;
- void *oldp;
- size_t *oldlenp;
- void *newp;
- size_t newlen;
-{
- if (namelen != 1)
- return ENOTDIR;
-
- switch (name[0])
- {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- case IPV6CTL_STATS:
- return sysctl_rdtrunc(oldp, oldlenp, newp, &ipv6stat, sizeof(ipv6stat));
- default:
- return (sysctl_int_arr(ipv6_sysvars, name, namelen, oldp, oldlenp, newp, newlen));
-#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- default:
- return EOPNOTSUPP;
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- }
-}
-#endif __FreeBSD__
-
-/*----------------------------------------------------------------------
- * Should be a macro, this function determines if IPv6 is running on a
- * given interface.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_enabled(ifp)
- struct ifnet *ifp;
-{
- struct in6_ifaddr *i6a;
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp)
- return 1;
-
- return 0;
-}
-
-/*----------------------------------------------------------------------
- * Strips IPv6 options for TCP or UDP.
- *
- * This function assumes that the input chain (incoming) has been
- * munged by ipv6_preparse() and starts with an IPv6 header.
- * The header index is invalid after this call.
- * The IPv6 header is not updated EXCEPT for the length, which must be
- * in HOST order.
- * Note that the API for this function is NOT THE SAME as its IPv4
- * counterpart.
- *
- * Often called like:
- *
- * ipv6_stripoptions(incoming, extra, nexthdr);
- *
- * Where ihi and ihioff are the header index arrays, passed up after
- * ipv6_preparse(). If preparse is eliminated or altered, this code will
- * be too.
- ----------------------------------------------------------------------*/
-
-void ipv6_stripoptions(incoming, extra)
-register struct mbuf *incoming;
-int extra;
-{
- struct mbuf *optm, *m;
- int optlen;
-
- if (extra == sizeof(struct ipv6)) {
- /* i.e. If there are no options... */
- optm = NULL;
- optlen = 0;
- return;
- }
-
- if (!(optm = m_split(incoming, sizeof(struct ipv6), M_DONTWAIT)))
- return;
-
- if (!(m = m_split(optm, extra - sizeof(struct ipv6), M_DONTWAIT))) {
- m_cat(incoming, optm);
- return;
- }
-
- m_cat(incoming, m);
- incoming->m_pkthdr.len -= (extra - sizeof(struct ipv6));
-
- (mtod(incoming, struct ipv6 *))->ipv6_length =
- (incoming->m_pkthdr.len - sizeof(struct ipv6) > 0xffff) ? 0 :
- htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
-
- /*
- * XXX - We should be saving these stripped options somewhere...
- */
- m_freem(optm);
-}
-
-static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level)
-{
- register struct cmsghdr *cp;
- struct mbuf *m;
-
- if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
- return ((struct mbuf *) NULL);
- cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
- bcopy(p, CMSG_DATA(cp), size);
- size += sizeof(*cp);
- m->m_len = size;
- cp->cmsg_len = size;
- cp->cmsg_level = level;
- cp->cmsg_type = type;
- return m;
-}
-
-#if 0
-static struct mbuf *ipv6_savebag(struct mbuf *m, int level)
-{
- uint8_t *p = mtod(m, uint8_t *) + 1;
- int len = (*(p++) << 3) + 6;
- struct mbuf *opts = NULL, **mp = &opts;
-
- while(len > 0) {
- if (!*p) {
- p++; len--;
- continue;
- }
-
- if (len <= 1)
- return opts;
-
- if (*p != 1)
- if ((*mp = ipv6_saveopt((caddr_t)(p + 2), *(p + 1), *p, level)))
- mp = &(*mp)->m_next;
-
- len -= *(p + 1) + 2;
- p += *(p + 1) + 2;
- };
-
- return opts;
-};
-#endif /* 0 */
-
-struct mbuf *ipv6_headertocontrol(struct mbuf *m, int extra, int inp_flags)
-{
- struct mbuf *opts = NULL, **mp = &opts;
-
- if (inp_flags & INP_RXINFO) {
- struct in6_pktinfo pktinfo;
-
- if (m->m_pkthdr.rcvif) {
- pktinfo.ipi6_ifindex = m->m_pkthdr.rcvif->if_index;
- } else {
- DPRINTF(IDL_ERROR, ("ipv6_controldata: m->m_pkthdr.rcvif = NULL!\n"));
- pktinfo.ipi6_ifindex = 0;
- }
-
- bcopy(&(mtod(m, struct ipv6 *)->ipv6_dst), &pktinfo.ipi6_addr, sizeof(struct in6_addr));
- if ((*mp = ipv6_saveopt((caddr_t)&pktinfo, sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6)))
- mp = &(*mp)->m_next;
- };
-
- if (inp_flags & INP_HOPLIMIT) {
- int hoplimit = mtod(m, struct ipv6 *)->ipv6_hoplimit;
- if ((*mp = ipv6_saveopt((caddr_t)&hoplimit, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6)))
- mp = &(*mp)->m_next;
- };
-
-#if 0
- /* Since there's not any immediate need for these options anyway, we'll
- worry about reimplementing them later. - cmetz */
-
- if (inp_flags & INP_RXHOPOPTS) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_HOPOPTS) {
- if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_HOPOPTS)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-
- if (inp_flags & INP_RXDSTOPTS) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_DSTOPTS) {
- if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_DSTOPTS)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-
- if (inp_flags & INP_RXSRCRT) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_ROUTING) {
- if ((*mp = ipv6_saveopt((caddr_t)(mtod(ihi[i].ihi_mbuf, uint8_t *) + 3), (*mtod(ihi[i].ihi_mbuf, uint8_t *) << 3) + 5, *(mtod(ihi[i].ihi_mbuf, uint8_t *) + 2), IPPROTO_ROUTING)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-#endif /* 0 */
-
- return opts;
-};
diff --git a/sys/netinet6/ipv6_output.c b/sys/netinet6/ipv6_output.c
deleted file mode 100644
index fca449a4215..00000000000
--- a/sys/netinet6/ipv6_output.c
+++ /dev/null
@@ -1,2107 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#include <sys/param.h>
-#include <sys/protosw.h>
-#include <sys/domain.h>
-#include <sys/mbuf.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/systm.h>
-
-#include <net/if.h>
-#include <net/route.h>
-
-#include <netinet/in.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
-#include <netinet/tcp.h>
-#include <netinet/tcp_timer.h>
-#include <netinet/ip_var.h>
-#include <netinet/tcpip.h>
-#include <netinet/tcp_var.h>
-
-#if __OpenBSD__
-#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#if IPSEC
-#include <sys/osdep.h>
-#include <net/netproc.h>
-#include <net/netproc_var.h>
-#include <sys/nbuf.h>
-#endif /* IPSEC */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * Globals and function definitions.
- */
-
-uint32_t outfragid = 0; /* Outbound fragment groups have unique id's. */
-struct mbuf *ipv6_fragment __P((struct mbuf *,int));
-
-/*
- * External globals.
- */
-
-extern struct ipv6stat ipv6stat;
-extern struct in6_ifaddr *in6_ifaddr;
-extern struct in6_ifnet *in6_ifnet;
-extern struct ifnet *mcastdefault;
-extern int ipv6_defhoplmt;
-
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
-extern struct ifnet *loifp;
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
-
-void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
-int ipv6_tunnel_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
-
-int ipv6_setmoptions __P((int, struct inpcb *, void *, size_t));
-int ipv6_getmoptions __P((int, struct ipv6_moptions *, int *));
-void ipv6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *));
-int ipv6_finddivpoint __P((struct mbuf *, uint8_t *));
-
-/*----------------------------------------------------------------------
- * IPv6 output routine. The mbuf chain contains a near-complete IPv6 header,
- * and an already-inserted list of options. (I figure it's something for
- * the code with PCB access to handle.) The options should have their
- * fields in network order. The header should have its fields in host order.
- * (Save the addresses, which IMHO are always in network order. Weird.)
- ----------------------------------------------------------------------*/
-
-int
-#ifdef __OpenBSD__
-ipv6_output(struct mbuf *outgoing, ...)
-#else /* __OpenBSD__ */
-ipv6_output(outgoing,ro,flags,i6mo, forceifp, socket)
- struct mbuf *outgoing;
- struct route6 *ro;
- int flags;
- struct ipv6_moptions *i6mo;
- struct ifnet *forceifp;
- struct socket *socket;
-#endif /* __OpenBSD__ */
-{
- struct ipv6 *header;
- struct route6 ipv6route;
- struct sockaddr_in6 *dst;
- struct in6_ifaddr *i6a = NULL;
- struct ifnet *ifp = NULL;
- int error=0;
- uint32_t outmtu = 0;
-#if __OpenBSD__
- va_list ap;
- struct route6 *ro;
- int flags;
- struct ipv6_moptions *i6mo;
- struct ifnet *forceifp;
- struct socket *socket;
-
- va_start(ap, outgoing);
- ro = va_arg(ap, struct route6 *);
- flags = va_arg(ap, int);
- i6mo = va_arg(ap, struct ipv6_moptions *);
- forceifp = va_arg(ap, struct ifnet *);
- socket = va_arg(ap, struct socket *);
- va_end(ap);
-#endif /* __OpenBSD__ */
-
-#ifdef DIAGNOSTIC
- if ((outgoing->m_flags & M_PKTHDR) == 0)
- panic("ipv6_output() no HDR");
-#endif
-
- /*
- * Assume the IPv6 header is already contiguous.
- */
- header = mtod(outgoing, struct ipv6 *);
-
- DDO(IDL_FINISHED,printf("ipv6_output:\n");dump_ipv6(header));
- DPRINTF(IDL_FINISHED,("\n"));
-
- /*
- * Fill in v6 header. Assume flow id/version field is in network order,
- * and that the high 4 bits are 0's.
- */
-
- if ((flags & (IPV6_FORWARDING|IPV6_RAWOUTPUT)) == 0)
- {
- header->ipv6_versfl = htonl(0x60000000) |
- (header->ipv6_versfl & htonl(0x0fffffff));
- ipv6stat.ips_localout++;
- }
-
- /*
- * Determine interface and physical destination to send datagram out
- * towards. Do this by looking up a route, or using the route we were
- * passed.
- */
-
- DPRINTF(IDL_FINISHED,("route passed to ipv6_output is:\n"));
- DDO(IDL_FINISHED,if (ro) dump_rtentry(ro->ro_rt));
- if (ro == 0)
- {
- ro = &ipv6route;
- bzero((caddr_t)ro,sizeof(struct route6));
- }
- dst = &ro->ro_dst;
-
- if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
- !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &header->ipv6_dst)))
- {
- RTFREE(ro->ro_rt);
- ro->ro_rt = NULL;
- }
-
- if (ro->ro_rt == NULL)
- {
- dst->sin6_family = AF_INET6;
- dst->sin6_len = sizeof(struct sockaddr_in6);
- dst->sin6_addr = header->ipv6_dst;
- dst->sin6_port = 0;
- dst->sin6_flowinfo = 0;
- }
-
-#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa))
-#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
-
- if (flags & IPV6_ROUTETOIF)
- {
- /*
- * Check for route to interface only. (i.e. the user doesn't want
- * to rely on the routing tables, so send it out an interface).
- */
- if ((i6a = ifatoi6a(ifa_ifwithdstaddr(sin6tosa(dst)))) == 0 &&
- (i6a = ifatoi6a(ifa_ifwithnet(sin6tosa(dst)))) == 0 )
- {
- /*
- * Q: Do we want to assume that if a user specifies this option,
- * the user doesn't want ANYTHING to do with the routing tables?
- */
-
- ipv6stat.ips_noroute++;
- error = ENETUNREACH;
- goto bad;
- }
- ifp = i6a->i6a_ifp;
- header->ipv6_hoplimit = 1;
- outmtu = ifp->if_mtu;
- }
- else
- {
- /*
- * Do normal next-hop determination with the help of the routing tree.
- */
- if (ro->ro_rt == 0)
- rtalloc((struct route *)ro); /* Initial route lookup. */
-
- if (ro->ro_rt == 0)
- {
- /*
- * No route of any kind, so spray neighbor solicits out all
- * interfaces, unless it's a multicast address.
- */
- if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- goto mcast;
- DPRINTF(IDL_FINISHED, ("v6_output doesn't have a route...calling onlink_query!\n"));
- ipv6_onlink_query(dst);
- rtalloc((struct route *)ro);
- }
- if (ro->ro_rt == NULL)
- {
- /*
- * ipv6_onlink_query() should've added a route. Probably
- * failed.
- */
- DPRINTF(IDL_GROSS_EVENT, ("v6_output: onlink_query didn't add route!\n"));
- ipv6stat.ips_noroute++;
- error = ENETUNREACH;
- goto bad;
- }
-
- if (ro->ro_rt->rt_ifa == NULL)
- {
- /*
- * We have a route where we don't quite know which interface
- * the neighbor belongs to yet. If I get here, I know that this
- * route is not pre-allocated (such as done by in6_pcbconnect()),
- * because those pre-allocators will do the same ipv6_onlink_query()
- * and ipv6_verify_onlink() in advance.
- *
- * I can therefore free the route, and get it again.
- * Multicast dgrams should NEVER be in this code path.
- */
-
- RTFREE(ro->ro_rt);
- ro->ro_rt = NULL;
- DPRINTF(IDL_FINISHED,("v6_output calling ipv6_verify_onlink\n"));
- if ((error = ipv6_verify_onlink(dst)) != 0)
- {
- if (error == -1)
- {
- DPRINTF(IDL_ERROR,("verify_onlink() failed in v6_out.\n"));
- error = ENETUNREACH;
- }
- ipv6stat.ips_noroute++; /* Better stat needed, because
- error might not be
- E{NET,HOST}UNREACH. */
-
- goto bad;
- }
- rtalloc((struct route *)ro);
- if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL)
- panic("Oops, I'm forgetting something after verify_onlink().");
- }
-
- /*
- * Exploit properties of route.
- */
-
- ifp = ro->ro_rt->rt_ifp; /* Q: Is this REALLY the ifp
- for the route?
-
- A: Maybe. If multi-homed,
- and we attempt to
- intelligently figure out
- where link-locals are
- destined, then we're
- in trouble. */
- /*
- * On-net route exists, but no destination as of yet. This can
- * be snipped if an ifp is just selected. (Depends on multihomed
- * experience.)
- *
- * Currently, this code never executes, because we guarantee rt_ifp is
- * set. This may, however, change in later versions of this code as
- * we gain multihomed experience.
- */
- if (ifp == NULL && ro->ro_rt->rt_gateway->sa_family == AF_LINK)
- {
- DPRINTF(IDL_EVENT,\
- ("ipv6_output() calling ipv6_nsolicit(2)\n"));
- ipv6_nsolicit(NULL, outgoing, ro->ro_rt);
- DPRINTF(IDL_EVENT,\
- ("ipv6_output() attempted to send neighbor solicit(2), returning.\n"));
- goto done;
- }
-
- /*
- * Q: What if address has expired? Perhaps I should use ifp to
- * obtain optimal i6a value. There's also the question of using
- * link-local source addresses for off-link communication. (or for
- * that matter, on-link, but not link-local communication.
- *
- * Q2: Perhaps use this time to reset the route's ifa?
- * Q3: Perhaps it is better to use the ipv6_rtrequest()?
- *
- * Regardless, i6a's only use in this function is to determine the
- * source address of the packet.
- *
- * Currently, ipv6_rtrequest() attempts to store a decent in6_ifaddr
- * in rt_ifa. This also may change with experience.
- */
-
- i6a = ifatoi6a(ro->ro_rt->rt_ifa);
- if (i6a->i6a_addrflags & I6AF_NOTSURE) {
- if (!(outgoing->m_flags & M_DAD))
- {
- /*
- * 1. Think of a better error.
- *
- * 2. Keep some sort of statistic.
- */
- DPRINTF(IDL_ERROR,("Using NOTSURE source address.\n"));
- error = EADDRNOTAVAIL;
- goto bad;
- }
- else i6a = NULL;
- }
-
- /*
- * More source address selection goes here.
- */
-
- ro->ro_rt->rt_use++;
- /*
- * Path MTU comes from the route entry.
- */
- outmtu = ro->ro_rt->rt_rmx.rmx_mtu;
-
- if (ro->ro_rt->rt_flags & RTF_GATEWAY) /* Gateway/router/whatever. */
- dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
- }
-
- if (forceifp) {
- DPRINTF(IDL_EVENT, ("ipv6_output: in forceifp case\n"));
- ifp = forceifp;
- if (outmtu > ifp->if_mtu)
- outmtu = ifp->if_mtu;
- };
-
- /*
- * Handle case of a multicast destination.
- */
- mcast:
- if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- {
- struct in6_multi *in6m;
-
- outgoing->m_flags |= M_MCAST;
-
- dst = &ro->ro_dst;
-
- if (i6mo != NULL)
- {
- /*
- * As we gain more multicast experience, use i6mo fields to alter
- * properties of outgoing packet. (Including, quite possibly,
- * the source address.)
- */
- if (i6mo->i6mo_multicast_ifp != NULL)
- ifp = i6mo->i6mo_multicast_ifp;
- header->ipv6_hoplimit = i6mo->i6mo_multicast_ttl;
- }
- else
- {
- /*
- * Use default values, since there are no multicast options to
- * use.
- */
- if (ifp == NULL)
- ifp = mcastdefault;
- header->ipv6_hoplimit = IPV6_DEFAULT_MCAST_HOPS;
- }
-
- if (outmtu == 0) /* But what about mcast Path MTU? */
- outmtu = ifp->if_mtu;
-
- if ((ifp->if_flags & IFF_MULTICAST) == 0)
- {
- ipv6stat.ips_noroute++;
- error = ENETUNREACH;
- goto bad;
- }
-
- if ((IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && !(outgoing->m_flags & M_DAD)) ||
- (IN6_IS_ADDR_LINKLOCAL(&header->ipv6_src) &&
- IN6_MSCOPE(&header->ipv6_dst) > IN6_LINK_LOCAL))
- {
- register struct in6_ifaddr *i6a;
-
- /*
- * Source address selection for multicast datagrams.
- * If link-local source, get in here too, because you don't want
- * link-local addresses going on non-local multicast.
- *
- * Eventually should fix this to perform best source address
- * selection. Probably should separate this out into a function.
- */
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp)
- {
- header->ipv6_src = I6A_SIN(i6a)->sin6_addr;
- break;
- }
- }
-
- IN6_LOOKUP_MULTI(&header->ipv6_dst, ifp, in6m);
- DPRINTF(IDL_GROSS_EVENT,("in6m == 0x%lx, i6mo == 0x%lx\n", (unsigned long)in6m, (unsigned long)i6mo));
- if (in6m != NULL &&
- (i6mo == NULL || i6mo->i6mo_multicast_loop))
- {
-
- /*
- * See ipv6_mloopback for details, but that function will tag
- * the packet with the actual interface the multicast is
- * supposed to go out. This makes duplicate address detection
- * harder to implement, because the inbound mbuf SHOULD be tagged
- * as coming from me for the case of solicits. (Perhaps burning
- * another flag...)
- */
- DPRINTF(IDL_GROSS_EVENT,("Calling ipv6_mloopback().\n"));
- ipv6_mloopback(ifp, outgoing, dst);
- }
-
-#ifdef MROUTING
- /*
- * Do m-cast routing even if I can't find it in my m-cast list.
- */
-#endif
-
- /*
- * If intra-node scope. I've already hit it with ipv6_mloopback above.
- */
-
- if (IN6_MSCOPE(&header->ipv6_dst) == IN6_NODE_LOCAL ||
- (ifp->if_flags & IFF_LOOPBACK))
- goto bad; /* Not really bad, y'know, just getting out of here. */
- }
-
- if (ro->ro_rt == NULL && outmtu == 0)
- panic("ipv6_output: How did I get here without a route or MTU?");
-
- /*
- * Specify source address. Use i6a, for now.
- */
-
- if (IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && i6a != NULL &&
- !(outgoing->m_flags & M_DAD))
- header->ipv6_src = I6A_SIN(i6a)->sin6_addr;
-
- DPRINTF(IDL_FINISHED,("header & chain before security check are:\n"));
- DDO(IDL_FINISHED,dump_ipv6(header));
- DDO(IDL_FINISHED,dump_mchain(outgoing));
-
-#ifdef IPSEC
- if (!(flags & IPV6_FORWARDING)) {
- size_t preoverhead, postoverhead;
- void *state;
-
- /* NB: If there exists a configured secure tunnel, then
- the packet being tunneled will have been encapsulated
- inside an IP packet with (src=me, dest=tunnel-end-point)
- PRIOR to ip_output() being called, so the above
- check doesn't preclude secure tunnelling. rja */
- /*
- * I would like to just pass in &ia->ia_addr, but there is a small
- * chance that the source address doesn't match ia->ia_addr.
- *
- * Also, if you need a dest. port, fill in ro->ro_dst with it.
- */
- {
- struct sockaddr_in6 srcsa, dstsa;
-
- bzero(&srcsa, sizeof(struct sockaddr_in6));
- srcsa.sin6_family = AF_INET6;
- srcsa.sin6_len = sizeof(struct sockaddr_in6);
- /* XXX - port */
- srcsa.sin6_addr = header->ipv6_src;
-
- bzero(&dstsa, sizeof(struct sockaddr_in6));
- dstsa.sin6_family = AF_INET6;
- dstsa.sin6_len = sizeof(struct sockaddr_in6);
- /* XXX - port */
- dstsa.sin6_addr = header->ipv6_dst;
-
- /* XXX - get the ULP protocol number */
- if (error = netproc_outputpolicy(socket, (struct sockaddr *)&srcsa,
- (struct sockaddr *)&dstsa, header->ipv6_nexthdr, &preoverhead,
- &postoverhead, &state)) {
- if (error == EACCES) /* XXX - means fail silently */
- error = 0;
- goto bad;
- }
- }
-
- if (state) {
- struct nbuf *nbuf;
-
- DP(FINISHED, preoverhead, d);
- DP(FINISHED, postoverhead, d);
-
- header->ipv6_length = htons(outgoing->m_pkthdr.len -
- sizeof(struct ipv6));
-
- if (!(nbuf = mton(outgoing, preoverhead, postoverhead))) {
- netproc_outputfree(state);
- error = ENOMEM;
- goto bad;
- }
-
- outgoing = NULL;
-
- if (error = netproc_output(state, nbuf)) {
- if (error == EACCES)
- error = 0;
- }
-
- /* If successful, netproc_output actually does the output.
- Either way, it frees the nbuf. */
- goto done;
- }
- }
-#endif /* defined(IPSEC) || defined(NRL_IPSEC) */
-
- /*
- * Assume above three return a contiguous and UPDATED IPv6 header.
- */
- header = mtod(outgoing,struct ipv6 *);
-
- /*
- * Determine the outbound i6a to record statistics.
- */
- if (flags & IPV6_FORWARDING)
- i6a = NULL;
- else if (i6a == NULL ||
- !IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src))
- {
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp &&
- IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src))
- break;
- }
-
- /*
- * If small enough for path MTU, send, otherwise, fragment.
- */
-
- DPRINTF(IDL_FINISHED,("Before output, path mtu = %d, header is:\n",\
- (int)outmtu));
- DDO(IDL_FINISHED,dump_ipv6(header));
- DDO(IDL_FINISHED,printf("Chain is:\n");dump_mchain(outgoing));
-
-#if 0
-/* DEBUG tunnel */
- DPRINTF(IDL_EVENT,("ROUTE passed to ipv6_output is:\n"));
- DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt));
- if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL))
- DPRINTF(IDL_FINISHED,("HEY !! I see the tunnel!!!\n"));
- else {
- DPRINTF(IDL_FINISHED,("HEY !! I can't see the tunnel!!!\n"));
- if (ro->ro_rt == NULL)
- DPRINTF(IDL_FINISHED,("ro->ro_rt is null!!\n"));
- else
- {
- DPRINTF(IDL_FINISHED,("ro->ro_rt is not null!!\n"));
- if (ro->ro_rt->rt_flags & RTF_TUNNEL)
- DPRINTF(IDL_FINISHED,("HEY, I can see RTFTUNNEL!\n"));
- else
- DPRINTF(IDL_FINISHED,("HEY, I can't see RTFTUNNEL!\n"));
- }
- }
-/* END OF DEBUG tunnel */
-#endif /* 0 */
-
- if (outgoing->m_pkthdr.len <= outmtu)
- {
-DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Not entering fragmenting engine.\n"));
- header->ipv6_length = htons(outgoing->m_pkthdr.len -
- sizeof(struct ipv6));
-
- /*
- * If there is a route, and its TUNNEL bit is turned on, do not send
- * out the interface, but send through a tunneling routine, which will,
- * given information from the route, encapsulate the packet accordingly.
- *
- * Keith Sklower suggested a "rt_output() method" which would save
- * the checking here.
- */
- if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) {
- DPRINTF(IDL_EVENT,("ipv6_output():-Sending out IPV6 in IPV4/6 tunnel.\n"));
- error = ipv6_tunnel_output(outgoing, dst, ro->ro_rt);
- } else {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- if (i6a) {
- i6a->i6a_ifa.ifa_opackets++;
- i6a->i6a_ifa.ifa_obytes += outgoing->m_pkthdr.len;
- }
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- error = (*ifp->if_output)(ifp, outgoing, (struct sockaddr *)dst, ro->ro_rt);
- }
- DPRINTF(IDL_FINISHED,("Lone IPv6 went out if (error = %d).\n",error));
- goto done;
- }
-
-
- /*
- * If I make it here, then the packet is too big for the path MTU, and must
- * be fragmented.
- */
-
- DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Entering fragmenting engine.\n"));
-
- if (flags & IPV6_FORWARDING)
- {
- error = EMSGSIZE;
- goto bad;
- }
-
- if (outgoing->m_pkthdr.len > 0xffff) {
- DPRINTF(IDL_ERROR,("Jumbogram needs fragmentation, something that can't be done\n"));
- ipv6stat.ips_odropped++; /* ?!? */
- error = EINVAL;
- goto bad;
- }
-
- /*
- * The following check should never really take place.
- */
-#ifdef DIAGNOSTIC
- if (outmtu < IPV6_MINMTU)
- {
- DPRINTF(IDL_ERROR,("Outbound MTU is less than IPV6_MINMTU (%d).\n",\
- IPV6_MINMTU));
- error = ENETUNREACH; /* Can you think of a better idea? */
- goto bad;
- }
-#endif
-
- /*
- * ipv6_fragment returns a chain of outgoing packets. It returns NULL
- * if something went wrong.
- */
- outgoing = ipv6_fragment(outgoing,outmtu);
- if (outgoing == NULL)
- error = ENOBUFS; /* Can you think of a better idea? */
-
- DPRINTF(IDL_FINISHED,\
- ("ipv6_fragment() returned, attempting to send fragments out.\n"));
-
- /*
- * Walk through chain of fragments, and send them out.
- */
- while (outgoing != NULL)
- {
- struct mbuf *current = outgoing;
-
- DPRINTF(IDL_FINISHED,("In fragment-sending loop, error == %d.\n",\
- error));
- outgoing = current->m_nextpkt;
- current->m_nextpkt = NULL;
-
- DDO(IDL_FINISHED,printf("Current (0x%lx) 1st mbuf is:\n", (unsigned long)current);\
- dump_mbuf(current));
-
- if (error != 0)
- m_freem(current);
- else
- if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) {
- DPRINTF(IDL_EVENT,("Sending fragments out tunnel.\n"));
- error = ipv6_tunnel_output(current, dst, ro->ro_rt);
- } else {
- DPRINTF(IDL_EVENT,("After if_output(), error == %d.\n",error));
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- if (i6a) {
- i6a->i6a_ifa.ifa_opackets++;
- i6a->i6a_ifa.ifa_obytes += current->m_pkthdr.len;
- }
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- error = (*ifp->if_output)(ifp, current,(struct sockaddr *)dst, ro->ro_rt);
- }
- }
-
- if (error == 0)
- ipv6stat.ips_fragmented++;
-
-done:
- if (ro == &ipv6route && (flags & IPV6_ROUTETOIF) == 0 && ro->ro_rt)
- RTFREE(ro->ro_rt);
- return (error);
-
-bad:
- if (outgoing != NULL)
- m_freem(outgoing);
- goto done;
-}
-
-#define INDEX_TO_IFP(index, ifp)\
-{\
- struct in6_ifnet *i6ifp; \
- for (i6ifp = in6_ifnet; i6ifp; i6ifp = i6ifp->i6ifp_next) \
- if (i6ifp->i6ifp_ifp->if_index == index) { \
- (ifp) = i6ifp->i6ifp_ifp; \
- break; \
- }; \
-} \
-
-/*----------------------------------------------------------------------
- * Set IPv6 multicast options.
- ----------------------------------------------------------------------*/
-int ipv6_setmoptions(int optname, struct inpcb *inp, void *p, size_t len)
-{
- register int error = 0;
- register int i;
- register struct ipv6_mreq *mreq;
- register struct ifnet *ifp = NULL;
- register struct ipv6_moptions *imo = inp->inp_moptions6;
- struct route6 ro;
-
- if (imo == NULL)
- {
- imo = (struct ipv6_moptions *)malloc(sizeof(*imo), M_IPMOPTS,M_WAITOK);
- if (imo == NULL)
- return ENOBUFS;
- inp->inp_moptions6 = imo;
- inp->inp_flags |= INP_IPV6_MCAST;
- imo->i6mo_multicast_ifp = NULL;
- imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS;
- imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP;
- imo->i6mo_num_memberships = 0;
- }
- else /* Only if points to v6 moptions can I set them! */
- if (!(inp->inp_flags & INP_IPV6_MCAST))
- return EEXIST;
-
- switch (optname)
- {
- case IPV6_MULTICAST_IF:
- {
- unsigned int index;
- if (!p || (len != sizeof(unsigned int))) {
- error = EINVAL;
- break;
- }
-
- index = *((int *)p);
-
- if (!index) {
- imo->i6mo_multicast_ifp = NULL;
- break;
- }
-
- INDEX_TO_IFP(index, ifp);
- if (!ifp || !(ifp->if_flags & IFF_MULTICAST))
- error = EADDRNOTAVAIL;
- else
- imo->i6mo_multicast_ifp = ifp;
- };
- break;
-
- case IPV6_MULTICAST_HOPS:
- /*
- * Set the IPv6 hop limit for outgoing multicast packets.
- */
- if (!p || (len != sizeof(int))) {
- error = EINVAL;
- break;
- }
- if (*((int *)p) == -1)
- imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS;
- else
- if ((*((int *)p) > -1) && (*((int *)p) < 256))
- imo->i6mo_multicast_ttl = *((int *)p);
- else
- error = EINVAL;
- break;
-
- case IPV6_MULTICAST_LOOP:
- /*
- * Set the loopback flag for outgoing multicast packets.
- * Must be zero or one.
- */
- if (!p || (len != sizeof(int))) {
- error = EINVAL;
- break;
- }
- switch(*((int *)p)) {
- case 0:
- case 1:
- imo->i6mo_multicast_loop = *((int *)p);
- break;
- case -1:
- imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP;
- break;
- default:
- error = EINVAL;
- break;
- };
- break;
- case IPV6_ADD_MEMBERSHIP:
- /*
- * Add a multicast group membership.
- * Group must be a valid IP multicast address.
- */
- if (!p || (len != sizeof(struct ipv6_mreq))) {
- error = EINVAL;
- break;
- }
- mreq = (struct ipv6_mreq *)p;
- if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
- /*
- * If no interface address was provided, use the interface of
- * the route to the given multicast address.
- */
- if (!mreq->ipv6mr_interface) {
- ro.ro_rt = NULL;
- ro.ro_dst.sin6_family = AF_INET6;
- ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
- ro.ro_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 {
- INDEX_TO_IFP(mreq->ipv6mr_interface, ifp);
- }
- /*
- * See if we found an interface, and confirm that it
- * supports multicast.
- */
- if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
- error = EADDRNOTAVAIL;
- break;
- }
- /*
- * See if the membership already exists or if all the
- * membership slots are full.
- */
- for (i = 0; i < imo->i6mo_num_memberships; ++i) {
- if (imo->i6mo_membership[i]->in6m_ifp == ifp &&
- IN6_ARE_ADDR_EQUAL(&mreq->ipv6mr_multiaddr,
- &imo->i6mo_membership[i]->in6m_addr))
- break;
- }
- if (i < imo->i6mo_num_memberships) {
- error = EADDRINUSE;
- break;
- }
- if (i == IN6_MAX_MEMBERSHIPS) {
- error = ETOOMANYREFS;
- break;
- }
- /*
- * Everything looks good; add a new record to the multicast
- * address list for the given interface.
- */
- if ((imo->i6mo_membership[i] = in6_addmulti(&mreq->ipv6mr_multiaddr, ifp))
- == NULL)
- {
- error = ENOBUFS;
- break;
- }
- ++imo->i6mo_num_memberships;
- break;
-
- case IPV6_DROP_MEMBERSHIP:
- /*
- * Drop a multicast group membership.
- * Group must be a valid IP multicast address.
- */
- if (!p || (len != sizeof(struct ipv6_mreq))) {
- error = EINVAL;
- break;
- }
- mreq = (struct ipv6_mreq *)p;
- if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
- error = EINVAL;
- break;
- }
-
- /*
- * If an interface index was specified, get a pointer to its ifnet
- * structure.
- */
- if (!mreq->ipv6mr_interface)
- ifp = NULL;
- else {
- INDEX_TO_IFP(mreq->ipv6mr_interface, ifp);
- if (ifp == NULL) {
- error = EADDRNOTAVAIL;
- break;
- }
- }
-
- /*
- * Find the membership in the membership array.
- */
- for (i = 0; i < imo->i6mo_num_memberships; ++i) {
- if ((ifp == NULL ||
- imo->i6mo_membership[i]->in6m_ifp == ifp) &&
- IN6_ARE_ADDR_EQUAL(&imo->i6mo_membership[i]->in6m_addr,
- &mreq->ipv6mr_multiaddr))
- break;
- }
-
- if (i == imo->i6mo_num_memberships) {
- error = EADDRNOTAVAIL;
- break;
- }
- /*
- * Give up the multicast address record to which the
- * membership points.
- */
- in6_delmulti(imo->i6mo_membership[i]);
- /*
- * Remove the gap in the membership array.
- */
- for (++i; i < imo->i6mo_num_memberships; ++i)
- imo->i6mo_membership[i-1] = imo->i6mo_membership[i];
-
- --imo->i6mo_num_memberships;
-
- break;
- default:
- error = EOPNOTSUPP;
- break;
- }
-
- if (imo->i6mo_multicast_ifp == NULL &&
- imo->i6mo_multicast_ttl == IPV6_DEFAULT_MCAST_HOPS &&
- imo->i6mo_multicast_loop == IPV6_DEFAULT_MCAST_LOOP &&
- imo->i6mo_num_memberships == 0) {
- free(inp->inp_moptions6, M_IPMOPTS);
- inp->inp_moptions6 = NULL;
- inp->inp_flags &= ~INP_IPV6_MCAST;
- }
-
- return (error);
-}
-
-#define IFP_TO_INDEX(ifp, index) \
-{\
- (index) = ifp->if_index; \
-}
-
-/*----------------------------------------------------------------------
- * Get IPv6 multicast options.
- ----------------------------------------------------------------------*/
-/* ... now assumes all returned values are ints... */
-int ipv6_getmoptions(int optname, struct ipv6_moptions *i6mo, int *mp)
-{
- switch (optname)
- {
- case IPV6_MULTICAST_IF:
- if (!i6mo == NULL || !i6mo->i6mo_multicast_ifp)
- *mp = 0;
- else {
- IFP_TO_INDEX(i6mo->i6mo_multicast_ifp, *(unsigned int *)mp);
- }
- return (0);
-
- case IPV6_MULTICAST_HOPS:
- *mp = i6mo ? IPV6_DEFAULT_MCAST_HOPS : i6mo->i6mo_multicast_ttl;
- return (0);
-
- case IPV6_MULTICAST_LOOP:
- *mp = i6mo ? IPV6_DEFAULT_MCAST_LOOP : i6mo->i6mo_multicast_loop;
- return (0);
-
- default:
- return (EOPNOTSUPP);
- }
-}
-
-/*----------------------------------------------------------------------
- * Free IPv6 multicast options.
- ----------------------------------------------------------------------*/
-void
-ipv6_freemoptions(i6mo)
- register struct ipv6_moptions *i6mo;
-{
- register int i;
-
- if (i6mo != NULL)
- {
- for (i = 0 ; i < i6mo->i6mo_num_memberships ; i++)
- in6_delmulti(i6mo->i6mo_membership[i]);
- free(i6mo, M_IPMOPTS);
- }
-}
-
-/*----------------------------------------------------------------------
- * Handler for IPV6 [gs]etsockopt() calls. One problem arises when an
- * AF_INET6 socket actually wants to set IPv4 options.
- *
- * The decision to call this or to call ip_ctloutput() is best left in
- * the hands of TCP/UDP/etc., which have information about which IP is
- * in use.
- *
- ----------------------------------------------------------------------*/
-
-#if __FreeBSD__
-int ipv6_ctloutput(struct socket *so, struct sockopt *sopt)
-{
- register struct inpcb *inp;
- int op;
- int level;
- int optname;
- int optval;
- int error = 0;
-
- DPRINTF(FINISHED, ("ipv6_ctloutput(so=%08x, sopt=%08x)\n", OSDEP_PCAST(so),
- OSDEP_PCAST(sopt)));
-
- inp = sotoinpcb(so);
-
- switch(sopt->sopt_dir) {
- case SOPT_GET:
- op = PRCO_GETOPT;
- break;
- case SOPT_SET:
- op = PRCO_SETOPT;
- break;
- default:
- return EINVAL;
- };
-
- level = sopt->sopt_level;
- optname = sopt->sopt_name;
-
- DS();
-#else /* __FreeBSD__ */
-int
-ipv6_ctloutput (op, so, level, optname, mp)
- int op;
- struct socket *so;
- int level;
- int optname;
- struct mbuf **mp;
-{
- register struct inpcb *inp = sotoinpcb(so);
- struct mbuf *m = *mp;
- int error = 0;
-
- DPRINTF(IDL_EVENT, ("ipv6_ctloutput(op=%x,so=%08lx,level=%x,optname=%x,mp)\n", op, (unsigned long)so, level, optname));
-#endif /* __FreeBSD__ */
-
- if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ROUTING) && (level != IPPROTO_ICMPV6)) {
-#if !__FreeBSD__
- if (op == PRCO_SETOPT && *mp)
- m_free(*mp);
-#endif /* !__FreeBSD__ */
- return EINVAL;
- }
-
- DS();
- switch (op) {
- case PRCO_SETOPT:
- switch(optname) {
- case IPV6_UNICAST_HOPS:
- DPRINTF(IDL_GROSS_EVENT, ("ipv6_ctloutput: Reached IPV6_UNICAST_HOPS\n"));
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int))
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int))
-#endif /* __FreeBSD__ */
- error = EINVAL;
- else {
- struct tcpcb *tp;
-#if __FreeBSD__
- int i;
-
- if (error = sooptcopyin(sopt, &i, sizeof(int), sizeof(int)))
- break;
-#else /* __FreeBSD__ */
- int i = *mtod(m, int *);
-#endif /* __FreeBSD__ */
-
- if (i == -1)
- i = ipv6_defhoplmt;
-
- if ((i < 0) || (i > 255)) {
- error = EINVAL;
- break;
- };
-
- inp->inp_ipv6.ipv6_hoplimit = i;
-
- /*
- * Minor optimization for TCP. We change the hoplimit
- * in the template here so we don't have to do the extra
- * load before the ipv6_output() call in tcp_output() for
- * every single packet (as is the case for IPv4).
- */
- DS();
-#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
- if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp)) &&
- tp->t_template)
-#if __FreeBSD__
- (mtod(tp->t_template, struct ipv6 *))->ipv6_hoplimit = i;
-#else /* __FreeBSD__ */
- ((struct ipv6 *)(tp->t_template))->ipv6_hoplimit = i;
-#endif /* __FreeBSD__ */
-#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
- if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp)))
- ((struct ipv6 *)(tp->t_tcpiphdr))->ipv6_hoplimit = i;
-#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
- }
- break;
- case IPV6_MULTICAST_IF:
- case IPV6_MULTICAST_HOPS:
- case IPV6_MULTICAST_LOOP:
- case IPV6_DROP_MEMBERSHIP:
- case IPV6_ADD_MEMBERSHIP:
-#if __FreeBSD__
- {
- void *val;
-
- if (!(val = OSDEP_MALLOC(sopt->sopt_valsize))) {
- error = ENOMEM;
- break;
- };
-
- if (error = sooptcopyin(sopt, val, sopt->sopt_valsize,
- sopt->sopt_valsize)) {
- OSDEP_FREE(val);
- break;
- };
-
- error = ipv6_setmoptions(optname, inp, val, sopt->sopt_valsize);
- OSDEP_FREE(val);
- };
-#else /* __FreeBSD__ */
- error = ipv6_setmoptions(optname, inp, mtod(m, void *), m->m_len);
-#endif /* __FreeBSD__ */
- break;
- case IPV6_ADDRFORM:
- {
- int newpf;
- int oldpf = sotopf(inp->inp_socket);
- union inpaddru new_faddru;
- union inpaddru new_laddru;
- int new_flags;
- struct protosw *new_proto;
- int s;
-
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
- DPRINTF(IDL_ERROR, ("addrform: valsize = %d\n",
- sopt->sopt_valsize));
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
- DPRINTF(IDL_ERROR, ("addrform: m->m_len = %d\n", m->m_len));
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &newpf, sizeof(int), sizeof(int)))
- break;
-#else /* __FreeBSD__ */
- newpf = *(mtod(m, int *));
-#endif /* __FreeBSD__ */
-
- DPRINTF(IDL_ERROR, ("newpf = %d, oldpf = %d", newpf, oldpf));
-
- if (((newpf != AF_INET) && (newpf != AF_INET6)) ||
- ((oldpf != AF_INET) && (oldpf != AF_INET6)))
- return EINVAL;
-
- DP(ERROR, __LINE__, d);
-
- if (newpf == oldpf)
- return 0;
-
- DP(ERROR, inp->inp_flags, 08x);
-
- if (newpf == AF_INET6)
- if (!(inp->inp_flags & INP_IPV6_UNDEC) &&
- !(inp->inp_flags & INP_IPV6_MAPPED))
- return EINVAL;
-
- DP(ERROR, __LINE__, d);
-
- if (!(new_proto = pffindproto(newpf,
- so->so_proto->pr_protocol,
- so->so_proto->pr_type)))
- return EINVAL;
-
- DP(ERROR, new_proto->pr_domain->dom_family, d);
-
- new_flags = inp->inp_flags;
- new_faddru = inp->inp_faddru;
- new_laddru = inp->inp_laddru;
-
- if (newpf == AF_INET) {
- if (new_flags & INP_IPV6_UNDEC) {
- new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC);
- new_laddru.iau_a4u.inaddr.s_addr = INADDR_ANY;
- new_faddru.iau_a4u.inaddr.s_addr = INADDR_ANY;
- } else {
- new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED);
- }
- } else {
- new_faddru.iau_addr6.s6_addr32[0] = 0;
- new_faddru.iau_addr6.s6_addr32[1] = 0;
- new_laddru.iau_addr6.s6_addr32[0] = 0;
- new_laddru.iau_addr6.s6_addr32[1] = 0;
-
- if (new_laddru.iau_a4u.inaddr.s_addr == INADDR_ANY) {
- new_flags |= (INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC);
- new_faddru.iau_addr6.s6_addr32[2] = 0;
- new_laddru.iau_addr6.s6_addr32[2] = 0;
- } else {
- new_flags |= (INP_IPV6 | INP_IPV6_MAPPED);
- new_faddru.iau_addr6.s6_addr32[2] = htonl(0xffff);
- new_faddru.iau_addr6.s6_addr32[2] = htonl(0xffff);
- }
- }
-
- s = splnet();
-
- inp->inp_flags = new_flags;
- inp->inp_faddru = new_faddru;
- inp->inp_laddru = new_laddru;
- so->so_proto = new_proto;
-
- splx(s);
- }
- break;
- case IPV6_PKTINFO:
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
- break;
-
- if (optval)
-#else /* __FreeBSD__ */
- if (*(mtod(m, int *)))
-#endif /* __FreeBSD__ */
- inp->inp_flags |= INP_RXINFO;
- else
- inp->inp_flags &= ~INP_RXINFO;
- break;
- case IPV6_HOPOPTS:
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
- break;
-
- if (optval)
-#else /* __FreeBSD__ */
- if (*(mtod(m, int *)))
-#endif /* __FreeBSD__ */
- inp->inp_flags |= INP_RXHOPOPTS;
- else
- inp->inp_flags &= ~INP_RXHOPOPTS;
- break;
- case IPV6_DSTOPTS:
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
- break;
-
- if (optval)
-#else /* __FreeBSD__ */
- if (*(mtod(m, int *)))
-#endif /* __FreeBSD__ */
- inp->inp_flags |= INP_RXDSTOPTS;
- else
- inp->inp_flags &= ~INP_RXDSTOPTS;
- break;
- case IPV6_RTHDR:
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
- break;
-
- if (optval)
-#else /* __FreeBSD__ */
- if (*(mtod(m, int *)))
-#endif /* __FreeBSD__ */
- inp->inp_flags |= INP_RXSRCRT;
- else
- inp->inp_flags &= ~INP_RXSRCRT;
- break;
- case IPV6_HOPLIMIT:
-#if __FreeBSD__
- if (sopt->sopt_valsize != sizeof(int)) {
-#else /* __FreeBSD__ */
- if (m->m_len != sizeof(int)) {
-#endif /* __FreeBSD__ */
- error = EINVAL;
- break;
- };
-
-#if __FreeBSD__
- if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int)))
- break;
-
- if (optval)
-#else /* __FreeBSD__ */
- if (*(mtod(m, int *)))
-#endif /* __FreeBSD__ */
- inp->inp_flags |= INP_HOPLIMIT;
- else
- inp->inp_flags &= ~INP_HOPLIMIT;
- break;
- default:
- error = ENOPROTOOPT;
- break;
- }
-#if !__FreeBSD__
- if (m)
- m_free(m);
-#endif /* !__FreeBSD__ */
- break;
- case PRCO_GETOPT:
- switch(optname)
- {
- case IPV6_ADDRFORM:
- {
- int pf = sotopf(inp->inp_socket);
- DP(ERROR, pf, d);
- if ((pf != PF_INET) && (pf != PF_INET6))
- return EINVAL;
- DP(ERROR, __LINE__, d);
-#if __FreeBSD__
- error = sooptcopyout(sopt, &pf, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- *mp = m;
- m->m_len = sizeof(int);
- *mtod(m, int *) = pf;
-#endif /* __FreeBSD__ */
- }
- break;
- case IPV6_UNICAST_HOPS:
- DPRINTF(IDL_GROSS_EVENT,("ipv6_ctloutput(): Reached IPV6_UNICAST_HOPS:\n"));
-#if __FreeBSD__
- error = sooptcopyout(sopt, &inp->inp_ipv6.ipv6_hoplimit,
- sizeof(int));
-#else /* __FreeBSD__ */
- if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- *mp = m;
- m->m_len = sizeof(int);
- *mtod(m, int *) = inp->inp_ipv6.ipv6_hoplimit;
-#endif /* __FreeBSD__ */
- break;
- case IPV6_MULTICAST_IF:
- case IPV6_MULTICAST_HOPS:
- case IPV6_MULTICAST_LOOP:
- case IPV6_DROP_MEMBERSHIP:
- case IPV6_ADD_MEMBERSHIP:
-#if __FreeBSD__
- error = ipv6_getmoptions(optname, inp->inp_moptions6, &optval);
- if (error)
- break;
-
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(*mp = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
-
- error = ipv6_getmoptions(optname, inp->inp_moptions6,
- mtod(*mp, int *));
-#endif /* __FreeBSD__ */
- break;
- case IPV6_PKTINFO:
-#if __FreeBSD__
- optval = (inp->inp_flags & INP_RXINFO) ? 1 : 0;
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- *mp = m;
- m->m_len = sizeof(int);
- *mtod(m, int *) = (inp->inp_flags & INP_RXINFO) ? 1 : 0;
-#endif /* __FreeBSD__ */
- break;
- case IPV6_HOPOPTS:
-#if __FreeBSD__
- optval = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0;
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- m->m_len = sizeof(int);
- *mtod(m, int *) = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0;
-#endif /* __FreeBSD__ */
- break;
- case IPV6_DSTOPTS:
-#if __FreeBSD__
- optval = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0;
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- m->m_len = sizeof(int);
- *mtod(m, int *) = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0;
-#endif /* __FreeBSD__ */
- break;
- case IPV6_RTHDR:
-#if __FreeBSD__
- optval = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0;
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- m->m_len = sizeof(int);
- *mtod(m, int *) = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0;
-#endif /* __FreeBSD__ */
- break;
- case IPV6_HOPLIMIT:
-#if __FreeBSD__
- optval = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0;
- error = sooptcopyout(sopt, &optval, sizeof(int));
-#else /* __FreeBSD__ */
- if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) {
- error = ENOBUFS;
- break;
- };
- m->m_len = sizeof(int);
- *mtod(m, int *) = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0;
-#endif /* __FreeBSD__ */
- break;
- default:
- error = ENOPROTOOPT;
- break;
- }
- break;
- default:
- error = ENOPROTOOPT;
- break;
- }
-
- return error;
-}
-
-/*----------------------------------------------------------------------
- * Loops back multicast packets to groups of which I'm a member.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_mloopback(ifp, m, dst)
- struct ifnet *ifp;
- register struct mbuf *m;
- register struct sockaddr_in6 *dst;
-{
- struct mbuf *copym;
- register struct ipv6 *header;
-
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- if (!loifp)
- return;
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
-
- /*
- * Copy mbuf chain in m, and send to loopback interface.
- */
-
- copym=m_copym(m,0,M_COPYALL,M_DONTWAIT);
- if (copym != NULL)
- {
- header=mtod(copym,struct ipv6 *);
- /* Jumbogram? */
- header->ipv6_length = htons(header->ipv6_length);
- /*
- * Also, there's an issue about address collision. You may want to
- * check the ipv6 destination (or the dst address) and set the ifp
- * argument to looutput to be the loopback interface itself iff
- * it is to a solicited nodes multicast.
- *
- * Then again, it may be easier for the soliciting code to burn another
- * m_flags bit, and look for it on loopback.
- */
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
- (*loifp->if_output)(ifp, copym, (struct sockaddr *)dst, NULL);
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
-#if __FreeBSD__
- loif->if_output(ifp,copym,(struct sockaddr *)dst,NULL);
-#else /* __FreeBSD__ */
- looutput(ifp,copym,(struct sockaddr *)dst,NULL);
-#endif /* __FreeBSD__ */
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
- }
- else
- DPRINTF(IDL_GROSS_EVENT,("m_copym() failed in ipv6_mloopback.\n"));
-}
-
-/*----------------------------------------------------------------------
- * Fragment IPv6 datagram.
- *
- * Given a large mbuf chain m, with only its ipv6->ipv6_length field in
- * host order, fragment into mtu sized chunks, and return a meta-chain
- * with m->m_nextpkt being the subsequent fragments. If there's a problem,
- * m_freem all fragments, and return NULL. Also, all ipv6->ipv6_length
- * fields are in network order, i.e. ready-to-transmit.
- *
- * Note that there is an unusually large amount of calls to m_pullup,
- * m_copym2etc. here. This will cause performance hits.
- *
- * A redesign of this is in order, but that will have to wait.
- ----------------------------------------------------------------------*/
-
-struct mbuf *
-ipv6_fragment(m,mtu)
- struct mbuf *m;
- int mtu;
-{
- uint8_t nextopt = IPPROTO_FRAGMENT;
- uint divpoint = sizeof(struct ipv6), chunksize, sofar = 0, goal;
- struct mbuf *retval = NULL, *newfrag = NULL;
- struct ipv6_fraghdr *frag = NULL;
-
- outfragid++;
-
- DPRINTF(IDL_FINISHED,\
- ("Entering ipv6_fragment, m_pkthdr.len = %d, mtu = %d\n",\
- m->m_pkthdr.len, mtu));
-
- /*
- * Find the dividing point between pre-fragment and post-fragment options.
- */
- divpoint = ipv6_finddivpoint(m, &nextopt);
-
- /*
- * Options being larger than MTU can happen, especially given large routing
- * headers and large options bags.
- */
- if (divpoint + sizeof(struct ipv6_fraghdr) >= mtu)
- {
- DPRINTF(IDL_ERROR,
- ("ipv6_fragment(): Options are larger than passed-in MTU.\n"));
- m_freem(m);
- return NULL;
- }
-
-#ifdef DIAGNOSTIC
- if (divpoint & 0x7)
- panic("divpoint not a multiple of 8!");
-#endif
-
- /*
- * sofar keeps track of how much I've fragmented, chunksize is how
- * much per fragment, goal is how much data to actually fragment.
- */
- chunksize = mtu - divpoint - sizeof(struct ipv6_fraghdr);
- chunksize &= ~7;
- goal = m->m_pkthdr.len - divpoint;
-
- DPRINTF(IDL_FINISHED, \
- ("Found divpoint (%d), nextopt (%d), chunksize(%d) goal(%d)\n",\
- divpoint, nextopt, chunksize,goal));
-
- while (sofar < goal)
- if ((newfrag = m_copym2(m, 0, divpoint, M_DONTWAIT)) != NULL)
- {
- struct mbuf *fraghdrmbuf = NULL;
- struct ipv6 *ipv6 = NULL;
- uint tocopy = (chunksize <= (goal - sofar))?chunksize:(goal - sofar);
-
- DPRINTF(IDL_FINISHED,("tocopy == %d\n",tocopy));
-
- /*
- * Create a new IPv6 fragment, using the header that was slightly
- * munged by ipv6_finddivpoint().
- *
- * The above m_copym2() creates a copy of the first
- */
-
- newfrag->m_nextpkt = retval;
- retval = newfrag;
-
- /*
- * Append IPv6 fragment header to pre-fragment
- */
- for(fraghdrmbuf = retval;fraghdrmbuf->m_next != NULL;)
- fraghdrmbuf = fraghdrmbuf->m_next;
- MGET(fraghdrmbuf->m_next,M_DONTWAIT,MT_DATA);
- if (fraghdrmbuf->m_next == NULL)
- {
- DPRINTF(IDL_ERROR,("couldn't get new mbuf for frag hdr\n"));
- ipv6stat.ips_odropped++;
- goto bail;
- }
-
- fraghdrmbuf = fraghdrmbuf->m_next;
- fraghdrmbuf->m_len = sizeof(struct ipv6_fraghdr);
- retval->m_pkthdr.len += sizeof(struct ipv6_fraghdr);
- frag = mtod(fraghdrmbuf,struct ipv6_fraghdr *);
- frag->frag_nexthdr = nextopt;
- frag->frag_reserved = 0;
- frag->frag_bitsoffset = htons(sofar | ((sofar + tocopy) < goal));
- frag->frag_id = outfragid;
-
- /*
- * Copy off (rather than just m_split()) the portion of data that
- * goes with this fragment.
- */
- if ((fraghdrmbuf->m_next = m_copym2(m,divpoint + sofar,tocopy,
- M_DONTWAIT)) == NULL)
- {
- DPRINTF(IDL_ERROR,("couldn't copy segment.\n"));
- goto bail;
- }
- retval->m_pkthdr.len += tocopy;
-
- /*
- * Update fragment's IPv6 header appropriately.
- */
- ipv6 = mtod(retval,struct ipv6 *);
- ipv6->ipv6_length = htons(retval->m_pkthdr.len - sizeof(struct ipv6));
- ipv6->ipv6_nexthdr = IPPROTO_FRAGMENT;
- ipv6stat.ips_ofragments++;
- sofar += tocopy;
- }
- else
- {
- /*
- * Creation of new fragment failed.
- */
- DPRINTF(IDL_ERROR,("m_copym2() failed in fragmentation loop.\n"));
- ipv6stat.ips_odropped++;
- bail:
- DPRINTF(IDL_ERROR,("Bailing out of ipv6_fragment()\n"));
- while (retval != NULL)
- {
- newfrag = retval;
- retval = retval->m_nextpkt;
- m_freem(newfrag);
- }
- m_freem(m);
- return NULL;
- }
-
- m_freem(m);
-
- /* Dump mbuf chain list constructed for debugging purposes. */
- DDO(IDL_FINISHED,\
- for (newfrag = retval; newfrag != NULL; newfrag = newfrag->m_nextpkt)\
- dump_mbuf(newfrag) );
-
- return retval;
-}
-
-/*----------------------------------------------------------------------
- * Find the dividing point between pre-fragment and post-fragment options.
- * The argument nexthdr is read/write, on input, it is the next header
- * value that should be written into the previous header's "next hdr" field,
- * and what is written back is what used to be in the previous field's
- * "next hdr" field. For example:
- *
- * IP (next hdr = routing) becomes --> IP (next hdr = routing)
- * Routing (next hdr = TCP) Routing (next hdr = fragment)
- * TCP TCP
- * argument nexthdr = fragment argument nexthdr = TCP
- *
- * This function returns the length of the pre-fragment options, ideal for
- * calls to m_split.
- *
- * As in ipv6_fragment, too many calls to m_pullup/m_pullup2 are performed
- * here. Another redesign is called for, but not now.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_finddivpoint(m, nexthdr)
- struct mbuf *m;
- uint8_t *nexthdr;
-{
- uint8_t iprevopt, *prevopt = &(iprevopt), new = *nexthdr;
- uint8_t *nextopt;
- uint divpoint,maybe = 0;
-
- /*
- * IPv4 authentication code calls this function too. It is likely that
- * v4 will just return almost immedately, after determining options
- * length. (i.e. never go through the while loop.)
- */
- if (mtod(m, struct ip *)->ip_v == 4)
- {
- iprevopt = IPPROTO_IPV4;
- nextopt = &(mtod(m, struct ip *)->ip_p);
- divpoint = sizeof(struct ip);
- }
- else
- {
- iprevopt = IPPROTO_IPV6;
- nextopt = &(mtod(m, struct ipv6 *)->ipv6_nexthdr);
- divpoint = sizeof(struct ipv6);
- }
-
- /*
- * Scan through options finding dividing point. Dividing point
- * for authentication and fragmentation is the same place.
- *
- * Some weirdness here is that there MIGHT be a "Destination options bag"
- * which is actually a "per source-route-hop" bag. There is a strong
- * argument for giving this particular options bag a separate type, but
- * for now, kludge around it.
- *
- * The "maybe" variable takes into account the length of this options bag.
- */
- while (IS_PREFRAG(*nextopt) && *prevopt != IPPROTO_ROUTING)
- {
- struct ipv6_srcroute0 *i6sr;
- struct ipv6_opthdr *oh;
-
- /*
- * ASSUMES: both nextopt and length will be in the first
- * 8 bytes of ANY pre-fragment header.
- */
-
- if ((divpoint + maybe + 8) > MHLEN)
- {
- /*
- * This becomes complicated. Try and collect invariant part into
- * first (now cluster) mbuf on chain. m_pullup() doesn't work with
- * clusters, so either write m_pullup2() or inline it here.
- *
- * m_pullup2(), unlike m_pullup() will only collect exactly
- * how many bytes the user requested. This is to avoid problems
- * with m_copym() and altering data that is merely referenced
- * multiple times, rather than actually copied. (We may eliminate
- * the Net/2 hack of adding m_copym2().)
- */
- if ((m = m_pullup2(m,divpoint + maybe + 8)) == NULL)
- {
- DPRINTF(IDL_ERROR,\
- ("m_pullup2(%d) failed in ipv6_fragment().\n",\
- divpoint + maybe + 8));
- return 0;
- }
- }
- else
- {
- if ((m = m_pullup(m,divpoint + maybe + 8)) == NULL)
- {
- DPRINTF(IDL_ERROR,\
- ("m_pullup() failed in ipv6_fragment().\n"));
- return 0;
- }
- }
-
- /*
- * Find nextopt, and advance accordingly.
- */
- switch (*nextopt)
- {
- case IPPROTO_HOPOPTS:
- /*
- * Hop-by-hops should be right after IPv6 hdr. If extra is nonzero,
- * then there was a destination options bag. If divpoint is not
- * only the size of the IPv6 header, then something came before
- * hop-by-hop options. This is not good.
- */
- if (maybe || divpoint != sizeof(struct ipv6))
- {
- DPRINTF(IDL_ERROR,
- ("ipv6_input(): Weird ordering in headers.\n"));
- m_freem(m);
- return 0;
- }
- oh = (struct ipv6_opthdr *)(m->m_data + divpoint);
- prevopt = nextopt;
- nextopt = &(oh->oh_nexthdr);
- divpoint += 8 + (oh->oh_extlen << 3);
- if (oh->oh_extlen) {
- if (divpoint > MHLEN)
- {
- if ((m = m_pullup2(m,divpoint)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup2(%d) failed in IPPROTO_HOPOPTS nextopt.\n",\
- divpoint));
- return 0;
- }
- }
- else
- {
- if ((m = m_pullup(m,divpoint)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup() failed in IPPROTO_HOPOPTS nextopt.\n"));
- return 0;
- }
- }
- }
- break;
- case IPPROTO_DSTOPTS:
- oh = (struct ipv6_opthdr *)(m->m_data + divpoint);
- prevopt = nextopt;
- nextopt = &(oh->oh_nexthdr);
- maybe = 8 + (oh->oh_extlen << 3);
- if (oh->oh_extlen) {
- if ( divpoint + maybe > MHLEN)
- {
- if ((m = m_pullup2(m,divpoint + maybe)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup2(%d) failed in IPPROTO_DSTOPTS nextopt.\n",\
- divpoint+maybe));
- return 0;
- }
- }
- else
- {
- if ((m = m_pullup(m,divpoint + maybe)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup() failed in IPPROTO_DSTOPTS nextopt.\n"));
- return 0;
- }
- }
- }
- break;
- case IPPROTO_ROUTING:
- if (maybe) /* i.e. if we had a destination options bag before
- this routing header, we should advance the dividing
- point. */
- divpoint += maybe;
- maybe = 0;
- i6sr = (struct ipv6_srcroute0 *)(m->m_data + divpoint);
- prevopt = nextopt;
- nextopt = &(i6sr->i6sr_nexthdr);
- switch (i6sr->i6sr_type)
- {
- case 0:
- divpoint += 8 + (i6sr->i6sr_len * 8);
- break;
- default:
- DPRINTF(IDL_ERROR,
- ("ipv6_input(): Unknown outbound routing header.\n"));
- break;
- }
- if (divpoint > MHLEN)
- {
- if ((m = m_pullup2(m,divpoint)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup2(%d) failed in IPPROTO_ROUTING nextopt.\n",\
- divpoint));
- return 0;
- }
- }
- else
- {
- if ((m = m_pullup(m,divpoint)) == NULL)
- {
- DPRINTF(IDL_EVENT,\
- ("m_pullup() failed in IPPROTO_ROUTING nextopt.\n"));
- return 0;
- }
- }
- break;
- } /* End of switch statement. */
- }; /* End of while loop. */
- *nexthdr = *nextopt;
- *nextopt = new;
- return divpoint;
-}
-
-int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *payload)
-{
- struct cmsghdr *cmsghdr;
- int error = EINVAL;
- struct mbuf *srcrtm = NULL;
-
- DPRINTF(IDL_EVENT, ("ipv6_controltoheader(m=%08lx, control=%08lx, forceif=%08lx, payload=%08lx)\n", (unsigned long)m, (unsigned long)control, (unsigned long)forceifp, (unsigned long)payload));
- DDO(IDL_EVENT, dump_mchain(control));
-
- while((control = m_pullup2(control, sizeof(struct cmsghdr))) &&
- (cmsghdr = mtod(control, struct cmsghdr *)) &&
- (control = m_pullup2(control, cmsghdr->cmsg_len))) {
- cmsghdr = mtod(control, struct cmsghdr *);
- switch(cmsghdr->cmsg_level) {
- case IPPROTO_IPV6:
- switch(cmsghdr->cmsg_type) {
- case IPV6_PKTINFO:
- {
- struct in6_pktinfo in6_pktinfo;
- struct in6_ifnet *i6ifp;
- struct ifaddr *ifa;
-
- if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo))
- goto ret;
-
- bcopy((caddr_t)cmsghdr + sizeof(struct cmsghdr), &in6_pktinfo, sizeof(struct in6_pktinfo));
-
- if (!in6_pktinfo.ipi6_ifindex) {
- if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) {
- DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in degenerate IPV6_PKTINFO case\n"));
- break;
- } else {
- struct in6_ifaddr *i6a;
- DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in index = unspec, addr = spec case\n"));
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &in6_pktinfo.ipi6_addr))
- goto l2;
- goto ret;
- };
- };
-
- DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec case\n"));
-
- for (i6ifp = in6_ifnet; i6ifp && (i6ifp->i6ifp_ifp->if_index != in6_pktinfo.ipi6_ifindex); i6ifp = i6ifp->i6ifp_next);
-
- if (!i6ifp)
- goto ret;
-
- if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) {
- DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = unspec case\n"));
- goto l1;
- };
-
- DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = spec case\n"));
-
-#ifdef __FreeBSD__
- for (ifa = i6ifp->i6ifp_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) {
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = i6ifp->i6ifp_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) {
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = i6ifp->i6ifp_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if ((ifa->ifa_addr->sa_family == AF_INET6) && !bcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, &in6_pktinfo.ipi6_addr, sizeof(struct in6_addr)))
- goto l1;
- };
- goto ret;
-
-l1: *forceifp = i6ifp->i6ifp_ifp;
-l2: bcopy(&in6_pktinfo.ipi6_addr, &(mtod(*m, struct ipv6 *)->ipv6_src), sizeof(struct in6_addr));
- };
- break;
- case IPV6_HOPLIMIT:
- {
- int i;
-
- if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(int))
- goto ret;
-
- i = *((int *)((caddr_t)cmsghdr + sizeof(struct cmsghdr)));
-
- if (i == -1) {
- if (IN6_IS_ADDR_MULTICAST(&mtod(*m, struct ipv6 *)->ipv6_dst))
- i = IPV6_DEFAULT_MCAST_HOPS;
- else
- i = ipv6_defhoplmt;
- }
-
- if ((i < 0) || (i > 255))
- goto ret;
-
- mtod(*m, struct ipv6 *)->ipv6_hoplimit = i;
- };
- break;
- default:
- goto ret;
- };
- break;
- case IPPROTO_ROUTING:
- MGET(srcrtm, M_DONTWAIT, MT_DATA);
- if (!srcrtm) {
- error = ENOBUFS;
- goto ret;
- };
-
- srcrtm->m_len = cmsghdr->cmsg_len - sizeof(struct cmsghdr) + 3;
- if (srcrtm->m_len > MLEN) {
- DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route that we can't fit in an mbuf (length %d)\n", srcrtm->m_len));
- goto ret;
- };
-
- if (srcrtm->m_len & 7) {
- DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route has an invalid length; %d needs to be a multiple of eight bytes\n", srcrtm->m_len));
- goto ret;
- };
-
- *(mtod(srcrtm, uint8_t *) + 1) = (srcrtm->m_len >> 3) - 1;
- *(mtod(srcrtm, uint8_t *) + 2) = cmsghdr->cmsg_type;
- bcopy((caddr_t *)cmsghdr + sizeof(struct cmsghdr), mtod(srcrtm, uint8_t *) + 3, cmsghdr->cmsg_len - sizeof(struct cmsghdr));
- break;
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- /* XXX */
- goto ret;
- default:
- goto ret;
- };
- m_adj(control, cmsghdr->cmsg_len);
- if (!control->m_len)
- goto finish;
- };
-
- DPRINTF(IDL_ERROR, ("ipv6_controltoheader: pullups failed\n"));
- goto ret;
-
-finish:
- if (srcrtm) {
- struct mbuf *m2;
- DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in srcrtm case\n"));
- if (!(m2 = m_split(*m, sizeof(struct ipv6), M_DONTWAIT)))
- goto ret;
- (*m)->m_next = srcrtm;
- srcrtm->m_next = m2;
- *mtod(srcrtm, uint8_t *) = mtod(*m, struct ipv6 *)->ipv6_nexthdr;
- mtod(*m, struct ipv6 *)->ipv6_nexthdr = IPPROTO_ROUTING;
-
- *payload += srcrtm->m_len;
- (*m)->m_pkthdr.len += srcrtm->m_len;
- };
- m_freem(control);
- DPRINTF(IDL_FINISHED, ("ipv6_controltoheader: returning\n"));
- return 0;
-
-ret:
- DPRINTF(IDL_ERROR, ("ipv6_controltoheader: returning error %d\n", error));
- if (srcrtm)
- m_free(srcrtm);
- if (control)
- m_freem(control);
- return error;
-};
diff --git a/sys/netinet6/ipv6_rtrequest.c b/sys/netinet6/ipv6_rtrequest.c
deleted file mode 100644
index 08c1744df98..00000000000
--- a/sys/netinet6/ipv6_rtrequest.c
+++ /dev/null
@@ -1,1017 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/malloc.h>
-#include <sys/mbuf.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/time.h>
-#include <sys/kernel.h>
-#include <sys/errno.h>
-/*#include <sys/ioctl.h>*/
-#include <sys/syslog.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_systm.h>
-#include <netinet/ip.h>
-
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/ipv6_icmp.h>
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * Globals (and forward function declarations).
- */
-
-/*
- * External globals.
- */
-extern struct sockaddr_in6 in6_allones;
-extern struct discq dqhead;
-extern struct v6router defrtr;
-extern struct v6router nondefrtr;
-
-extern struct in6_ifaddr *in6_ifaddr;
-
-extern int ipv6forwarding;
-
-/*
- * External function prototypes.
- */
-
-/*
- * General notes:
- *
- * These functions are mainly called from ipv6_discovery.c but are
- * fairly generic and generally useful and so live in their own file.
- * danmcd rja
- */
-void tunnel_parent __P((struct rtentry *));
-struct v6router *ipv6_add_defrouter_rtrequest(struct rtentry *);
-void tunnel_child __P((struct rtentry *));
-void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
-void tunnel_parent_clean __P((struct rtentry *));
-int ipv6_delete_defrouter __P((struct v6router *));
-void tunnel_child_clean __P((struct rtentry *));
-
-static int add_defchild __P((struct rtentry *));
-static void add_netchild __P((struct rtentry *));
-void ipv6_setrtifa __P((struct rtentry *));
-static void add_non_default __P((struct rtentry *));
-
-/*
- * Functions and macros.
- */
-
-
-/*----------------------------------------------------------------------
- * add_defchild():
- * Find the best default router out of our list and use it
- * for this destination route.
- ----------------------------------------------------------------------*/
-static int
-add_defchild(rt)
- struct rtentry *rt;
-{
- struct v6router *v6r = defrtr.v6r_next;
- struct sockaddr_in6 *dst;
-
- /*
- * What if this child turns out to be a tunneling route? OUCH!
- *
- * One other thing: If rt_key(rt) is link-local, but it hit from
- * the default router, then perhaps it should
- * go with the ipv6_onlink_query() route.
- */
-
- dst = (struct sockaddr_in6 *)rt_key(rt);
-
- if (!IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) {
- /*
- * Since there are no priorities, just pick one. For now, I guess I'll
- * Just pick v6r_next.
- */
- if (v6r != &defrtr)
- {
- struct v6child *v6c;
- int rc = 0;
-
- DPRINTF(IDL_EVENT,("About to do rt_setgate. rt before is:\n"));
- DDO(IDL_EVENT,dump_rtentry(rt));
- DPRINTF(IDL_EVENT,("Args are (dst, parent->rt_gateway, v6r_rt):\n"));
- DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)dst);\
- dump_smart_sockaddr(rt->rt_parent->rt_gateway);\
- dump_rtentry(v6r->v6r_rt));
-
- if (rt_key(v6r->v6r_rt)->sa_family != AF_INET6)
- {
- /*
- * Hmmm, we must be a tunneling default route. Current
- * conventional wisdom is that we can only see one in our
- * "default router list," and that it was added manually. Using
- * that assumption, get things right, including chasing the
- * parent and using its rt_gateway sockaddr for rt_setgate.
- */
- rt_setgate(rt,(struct sockaddr *)dst,rt->rt_parent->rt_gateway);
- rc = 1;
- }
- else
- {
- rt_setgate(rt,(struct sockaddr *)dst,rt_key(v6r->v6r_rt));
- rt->rt_flags &= ~RTF_TUNNEL; /* In case cloned off an initially
- tunnelling default route. */
- }
-
- DDO(IDL_GROSS_EVENT,\
- printf("After rt_setgate, rt is:\n");dump_rtentry(rt));
-
- rt->rt_llinfo = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
- if (rt->rt_llinfo == NULL)
- {
- DPRINTF(IDL_ERROR, ("add_defchild(): malloc failed.\n"));
- /* Perhaps route should be freed here */
- }
- else
- {
- v6c = (struct v6child *)rt->rt_llinfo;
- insque(v6c,&v6r->v6r_children);
- v6c->v6c_route = rt;
- }
- return rc;
- }
- else
- {
- /*
- * Default route hit, but no default router children.
- *
- * Perhaps delete it?
- */
- DPRINTF(IDL_ERROR,\
- ("Default route hit, but no default routers in list.\n"));
- }
- }
-
- /* Either on-link or in trouble. Needs to be coded. */
- DPRINTF(IDL_ERROR, ("On-link or in trouble in ipv6_rtrequest\n"));
-
- /*
- * Convert off-link entry to indeterminate on-link entry.
- * Subsequent code after a call to this function will do the right thing.
- * (ipv6_rtrequest code to handle new on-link neighbors follows this.)
- */
- rt->rt_flags = RTF_UP|RTF_LLINFO|RTF_HOST;
- rt->rt_llinfo = NULL;
- rt->rt_ifa->ifa_refcnt--;
- rt->rt_ifa = NULL;
- rt->rt_ifp = NULL;
- bzero(rt->rt_gateway,rt->rt_gateway->sa_len);
- rt->rt_gateway->sa_family = AF_LINK;
- rt->rt_gateway->sa_len = sizeof(struct sockaddr_dl);
- rt->rt_rmx.rmx_mtu = 0;
-
- /*
- * QUESTION: If I do this, should I call ipv6_nsolicit() to complete
- * this conversion from default route child to non-deterministic
- * on-link neighbor?
- *
- * ANSWER: Yes, but not here. Just return 1, and let later code take
- * care of the nsolicit.
- */
- return 1;
-}
-
-/*----------------------------------------------------------------------
- * add_netchild():
- * Given a network route child (i.e. non-default),
- * put this child in the parent's descendant list.
- ----------------------------------------------------------------------*/
-
-static void
-add_netchild(rt)
- struct rtentry *rt;
-{
- struct v6router *v6r;
- struct v6child *v6c;
-
- if (rt->rt_parent == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("add_netchild: No parent route. (Must be manually added.)\n"));
- return;
- }
- v6r = (struct v6router *)rt->rt_parent->rt_llinfo;
-
- v6c = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
- if (v6c == NULL)
- {
- DPRINTF(IDL_ERROR, ("add_netchild(): malloc failed.\n"));
- /* Perhaps should free route here */
- return;
- }
- insque(v6c,v6r->v6r_children.v6c_next);
- v6c->v6c_route = rt;
- v6c->v6c_parent = v6r;
- rt->rt_llinfo = (caddr_t)v6c;
-}
-
-/*----------------------------------------------------------------------
- * add_non_default():
- * add a non-default routing entry.
- *
- ----------------------------------------------------------------------*/
-static void
-add_non_default(rt)
- struct rtentry *rt;
-{
- struct v6router *newbie;
-
- if (rt_key(rt)->sa_family != AF_INET6)
- {
- DPRINTF(IDL_ERROR,
- ("IPv6 off-net non-tunnel route w/o IPv6 gwaddr.\n"));
- return;
- }
-
- newbie = malloc(sizeof(*newbie),M_DISCQ,M_NOWAIT);
- if (newbie == NULL)
- {
- DPRINTF(IDL_ERROR, ("add_non_default(): malloc failed.\n"));
- /* Should probably free route */
- return;
- }
- bzero(newbie,sizeof(*newbie));
- newbie->v6r_children.v6c_next = newbie->v6r_children.v6c_prev =
- &newbie->v6r_children;
- insque(newbie,&nondefrtr);
-
- /*
- * On creation of rt, rt_setgate() was called, therefore we take on blind
- * faith that an appropriate neighbor cache entry was created. If not,
- * we're in deep trouble.
- */
- newbie->v6r_rt = rt->rt_gwroute;
- rt->rt_llinfo = (caddr_t)newbie;
-}
-
-
-/*----------------------------------------------------------------------
- * ipv6_setrtifa():
- * Set route's interface address. Source address selection for
- * a route.
- *
- ----------------------------------------------------------------------*/
-void
-ipv6_setrtifa(rt)
- struct rtentry *rt;
-{
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
- struct sockaddr_in6 *src = (struct sockaddr_in6 *)rt->rt_ifa->ifa_addr;
- struct in6_ifaddr *src_in6ifa = (struct in6_ifaddr *)rt->rt_ifa;
- struct sockaddr_in6 *mask = (struct sockaddr_in6 *)rt_mask(rt);
- struct ifaddr *ifa = NULL;
-
- DPRINTF(IDL_EVENT,("Entering ipv6_setrtifa.\n"));
-
- /*
- * If I can't find a "better" source address, stick with the one I got.
- *
- * ASSUMES: rt_ifp is set PROPERLY. This NEEDS to be true.
- */
-
- /*
- * If link-local, use link-local source.
- */
-
- if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
- {
- if (IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
- return;
-#if __FreeBSD__
- for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- {
- struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
-
- if ((current->sin6_family != AF_INET6) ||
- IS_DEPRECATED((struct in6_ifaddr *)ifa))
- continue; /* For loop. */
- if (IN6_IS_ADDR_LINKLOCAL(&current->sin6_addr))
- break; /* For loop. */
- }
- if (ifa == NULL)
- return; /* We're in real trouble. */
- }
-
- /*
- * If v4-compatible, use v4-compatible source address.
- */
- if (ifa == NULL && (sin6->sin6_addr.s6_addr32[0] == 0 &&
- sin6->sin6_addr.s6_addr32[1] == 0 &&
- sin6->sin6_addr.s6_addr32[2] == 0 &&
- sin6->sin6_addr.s6_addr32[3] != htonl(1) &&
- (mask == NULL || mask->sin6_len >=
- sizeof(*mask) - sizeof(struct in_addr))))
- {
- if (IN6_IS_ADDR_V4COMPAT(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
- return;
-#if __FreeBSD__
- for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- {
- struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
-
- if ((current->sin6_family != AF_INET6)
- || IS_DEPRECATED((struct in6_ifaddr *)ifa))
- continue; /* For loop. */
- if (IN6_IS_ADDR_V4COMPAT(&current->sin6_addr))
- break; /* For loop. */
- }
- /*if (ifa == NULL)
- return;*/ /* if ifa == NULL here, pretend it's global, because
- it is global! */
- }
-
- /*
- * If site-local, use a site-local source address.
- */
-
- if (ifa == NULL && IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
- {
- if (IN6_IS_ADDR_SITELOCAL(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
- return;
-#ifdef __FreeBSD__
- for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- {
- struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
-
- if ((current->sin6_family != AF_INET6)
- || IS_DEPRECATED((struct in6_ifaddr *)ifa))
- continue; /* For loop. */
- if (IN6_IS_ADDR_SITELOCAL(&current->sin6_addr))
- break; /* For loop. */
- }
- if (ifa == NULL)
- return; /* We don't want to potentially pollute the global
- internet with site-local traffic. If you feel
- differently, comment out this ifa == NULL check
- and fallthrough. */
- }
-
- if (ifa == NULL)
- {
- /*
- * At this point, the address _could_ be anything, but is most likely
- * a globally routable address that didn't fit into the previous
- * categories.
- *
- * Do gyrations iff rt->rt_ifa's address is link-local and the dest
- * isn't or the src address previously picked is deprecated.
- */
- DPRINTF(IDL_GROSS_EVENT,("In default case of ipv6_setrtifa().\n"));
- if ((!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
- IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) || IS_DEPRECATED(src_in6ifa))
- {
- struct ifaddr *ifa_compat = 0, *ifa_site = 0;
-
- /*
- * For now, pick a non-link-local address using the following
- * order of preference: global, compatible, site-local.
- *
- */
-#ifdef __FreeBSD__
- for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
-#else /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
- for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
-#else /* __NetBSD__ || __OpenBSD__ */
- for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
-#endif /* __NetBSD__ || __OpenBSD__ */
-#endif /* __FreeBSD__ */
- if (ifa->ifa_addr->sa_family == AF_INET6 &&
- !IS_DEPRECATED((struct in6_ifaddr *)ifa) &&
- !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) {
- if (IN6_IS_ADDR_V4COMPAT(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
- ifa_compat = ifa;
- else if (IN6_IS_ADDR_SITELOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
- ifa_site = ifa;
- else /* globally routable address */
- break;
- }
- if (!ifa)
- ifa = ifa_compat ? ifa_compat : ifa_site;
- }
- }
-
- if (ifa != NULL)
- {
- /*
- * Q: Do I call ipv6_rtrequest (through ifa_rtrequest) if I
- * change ifa's on a route?
- */
- /*if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
- rt->rt_ifa->ifa_rtrequest(RTM_DELETE,rt,rt->rt_gateway);*/
- IFAFREE(rt->rt_ifa);
- ifa->ifa_refcnt++;
- rt->rt_ifa = ifa;
- rt->rt_ifp = ifa->ifa_ifp; /* Is this desirable? */
- /*if (ifa->ifa_rtrequest)
- ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_gateway);*/
- }
-}
-
-
-/*----------------------------------------------------------------------
- * ipv6_rtrequest():
- * IPv6-specific route ADD, RESOLVE, and DELETE function.
- * Typically called from route.c's rtrequest() function to handle
- * the IPv6 unique things.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_rtrequest(req, rt, sa)
- int req;
- register struct rtentry *rt;
- struct sockaddr *sa; /* Ignored parameter, I believe. */
-{
- static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}, *sdlp;
- int spray = 0;
-
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0000-Starting.\n"));
- DPRINTF(IDL_EVENT,("req = %d, rt = 0x%lx, sa = 0x%lx\n",req,\
- (unsigned long)rt, (unsigned long)sa));
- DDO(IDL_EVENT,dump_rtentry(rt));
-
- switch (req)
- {
- case RTM_ADD:
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0100-case RTM_ADD:.\n"));
-
- /*
- * Set route Path MTU if not already set on brand new route.
- */
- if (rt->rt_rmx.rmx_mtu == 0)
- rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
-
- /*
- * An explicitly added "all-ones" mask or no mask at all should be
- * a host route.
- */
-
- if (rt_mask(rt) == NULL ||
- (rt_mask(rt)->sa_len == sizeof(struct sockaddr_in6) &&
- IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr,
- &in6_allones.sin6_addr))
- )
- rt->rt_flags |= RTF_HOST;
-
- /*
- * Explicitly add the cloning bit to the route entry.
- * (Do we still want to do this?)
- *
- * One fallout from this is that if someone wants to disable cloning,
- * the disabling will have to be explicitly done after adding the route.
- */
- if ((rt->rt_flags & RTF_HOST) == 0)
- {
- /*
- * All non-host routes that aren't m-cast are cloning!
- */
- if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr))
- rt->rt_flags |= RTF_CLONING;
-
- if (rt->rt_flags & RTF_TUNNEL)
- {
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0150-Calling tunnel_parent().\n"));
- tunnel_parent(rt);
- }
-
- if (rt->rt_flags & RTF_GATEWAY) {
- if (rt_mask(rt)->sa_len == 0)
- {
- if (rt->rt_flags & RTF_TUNNEL)
- {
- /*
- * Add tunnel to default router list.
- */
- ipv6_add_defrouter_rtrequest(rt);
- }
- else
- {
- struct v6router *v6r;
- /*
- * The "default" route has just been added.
- * Do default router stuff here.
- *
- * Search router list to see if I'm already there.
- * this avoids double-adding.
- */
-
- for (v6r = defrtr.v6r_next; v6r != &defrtr;
- v6r = v6r->v6r_next)
- if (v6r->v6r_rt == rt->rt_gwroute)
- break;
-
- if (v6r == &defrtr)
- {
- DPRINTF(IDL_ERROR,
- ("Calling ipv6_add_defrouter from RTM_ADD.\n"));
- ipv6_add_defrouter_rtrequest(rt);
- }
-
- rt->rt_gwroute->rt_flags |= RTF_ISAROUTER;
- }
- rt->rt_flags |= RTF_DEFAULT;
- }
- else
- if (rt->rt_flags & RTF_TUNNEL)
- {
- /*
- * Perhaps if the tunnel bit is already set here, don't
- * do anything.
- */
- }
- else
- {
- /*
- * A non-default network route has just been added.
- * Do non-default router stuff here.
- */
- add_non_default(rt);
- }
- } else if (!(rt->rt_flags & RTF_TUNNEL))
- {
- /*
- * Interface route (i.e. on-link non-host prefix).
- */
- DPRINTF(IDL_EVENT,("Setting up i/f route.\n"));
- rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl);
- sdlp = (struct sockaddr_dl *)rt->rt_gateway;
- sdlp->sdl_type = rt->rt_ifp->if_type;
- sdlp->sdl_index = rt->rt_ifp->if_index;
- }
- }
-
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0199-Falling out of RTM_ADD:.\n"));
- /* FALLTHROUGH... */
- case RTM_RESOLVE:
- {
-#ifdef __FreeBSD__
- extern struct ifnet loif[];
- struct ifnet *loifp = loif;
-#else /* __FreeBSD__ */
-#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
- extern struct ifnet loif;
-#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
- extern struct ifnet *loifp;
-#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
-#endif /* __FreeBSD__ */
-
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0200-Now in case RTM_RESOLVE:.\n"));
-
- /*
- * First, check if ifa addr is same as route addr,
- * If yes, then wire the loopback interface as the destination.
- */
- if ((rt->rt_flags & RTF_HOST) && rt->rt_ifa &&
- IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
- &I6A_SIN(rt->rt_ifa)->sin6_addr)
-#if (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802))
- && loifp
-#endif /* (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)) */
- )
- {
- /* Change ifp to loopback and gateway to itself. */
-#ifdef __FreeBSD__
- rt->rt_ifp = loifp;
- rt->rt_gateway = rt_key(rt);
- rt->rt_rmx.rmx_mtu = loifp->if_mtu;
-#else /* __FreeBSD__ */
-#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
- rt->rt_ifp = &loif;
- rt->rt_gateway = rt_key(rt);
- rt->rt_rmx.rmx_mtu = loif.if_mtu;
-#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
- rt->rt_ifp = loifp;
- rt->rt_gateway = rt_key(rt);
- rt->rt_rmx.rmx_mtu = loifp->if_mtu;
-#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
-#endif /* __FreeBSD__ */
-
- if (rt->rt_parent)
- rt->rt_flags &= ~(RTF_STATIC | RTF_TUNNEL);
-
- break;
- };
- }
-
- /*
- * Second, check the accuracy of the route's interface address (ifa).
- * If possible, update the route's ifa such that it is the "best"
- * source address for packets bound for this destination.
- *
- * This is done to any routes and is subtle enough
- * to merit its own separate function:
- */
-
- ipv6_setrtifa(rt);
-
- if (!(rt->rt_flags & RTF_HOST))
- {
- /*
- * This should be the "none of the above" case. For now,
- * do nothing and just return.
- */
- DPRINTF(IDL_GROSS_EVENT,("In RESOLVE/lateADD, but not RTF_HOST.\n"));
- break;
- }
-
- /*
- * At this point, I know (1) I'm a host route which has either been
- * added manually or (2) I have cloned off to become either:
- *
- * a tunnelling route child,
- * RTF_TUNNEL is set on this entry.
- * I should be okay, although tunnel state may become necessary.
- * I'm a next-hop cache entry.
- *
- * a default router child,
- * a non-default network route child,
- * RTF_GATEWAY is set on this entry.
- * I have to insert this child into the router's children list.
- * My child entry hangs off rt_llinfo. I'm a next-hop cache
- * entry.
- *
- * an interface (i.e. neighbor) route child,
- * a link-local (i.e. off link-local mask) route child
- * rt->rt_gateway is AF_LINK
- * I should set RTF_LLINFO, and set up a discq entry because I'm
- * a neighbor cache entry. (Neighbor caches double as next-hop
- * cache entries.)
- */
-
- if (rt->rt_gateway == NULL)
- panic("ipv6_rtrequest(): No rt_gateway at the RTM_RESOLVE code.");
-
- /*
- * Order of bit checking is very important. I check TUNNEL first,
- * because it's REALLY special. Then I check GATEWAY or not.
- */
-
- if (rt->rt_parent != NULL && rt_mask(rt->rt_parent)->sa_len == 0)
- {
- DPRINTF(IDL_EVENT,("Cloning off default route!\n"));
- /*
- * Find a default router out of the list, and assign it to this
- * child. Clear or add tunnel bits if necessary.
- */
-
- rt->rt_flags &= ~RTF_STATIC;
- /*
- * If add_defchild returns 0, then either it's a tunneling
- * default route, or a link-local converted to a neighbor entry.
- *
- * Set 'spray' to the result of add_defchild, because if it is
- * a neighbor entry (see AF_LINK case below), then spray will
- * indicate the need to do some pre-emptive neighbor adverts.
- */
- if (!(spray = add_defchild(rt))) /* If an actual off-net default
- route... */
- break;
-DPRINTF(IDL_ERROR,("add_defchild returned 1, either on-link or tunnel.\n"));
- /* Otherwise, either tunnel or converted into LINK address. */
- }
- else rt->rt_llinfo = NULL; /* Is this already done? */
-
- if (rt->rt_flags & RTF_TUNNEL)
- {
- DPRINTF(IDL_EVENT,("ipv6_rtrequest():0250-Calling tunnel_child().\n"));
- tunnel_child(rt); /* Be careful if manually added RTF_TUNNEL. */
- break; /* We're done with this Tunnel host route. */
- }
-
- if (rt->rt_flags & RTF_GATEWAY)
- {
- add_netchild(rt);
- if (req == RTM_RESOLVE)
- rt->rt_flags &= ~RTF_STATIC;
- break;
- }
-
- if (rt->rt_gateway->sa_family == AF_LINK)
- {
- /*
- * I may enter here even after RTF_GATEWAY check because
- * get_defrouter() may convert the route entry into an on-link one!
- */
- struct discq *dq;
-
- rt->rt_llinfo = malloc(sizeof(*dq),M_DISCQ,M_NOWAIT);
- dq = (struct discq *)rt->rt_llinfo;
- if (dq == NULL)
- {
- DPRINTF(IDL_ERROR,
- ("ipv6_rtrequest(): malloc for rt_llinfo failed.\n"));
- /* Probably should free route */
- break;
- }
- bzero(dq,sizeof(*dq));
- dq->dq_rt = rt;
- dq->dq_unanswered = -1;
- rt->rt_flags |= RTF_LLINFO;
- insque(dq,&dqhead);
- /*
- * State is already INCOMPLETE, because of link stuff.
- *
- * If this neigbor entry is caused by add_defchild (i.e. link-local)
- * spray nsolicit out all interfaces.
- */
-
- if (spray)
- ipv6_nsolicit(NULL,NULL,rt);
-
- break;
- }
- else
-#if 0
- /*
- * support for PPP goes here.
- * Frame Relay, and ATM will probably probably be handled by the
- * AF_LINK case. Other AF_* will be covered later.
- */
- panic("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor.");
-#else /* 0 */
- {
- DPRINTF(IDL_ERROR, ("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor. rt is:\n"));
- DDO(IDL_ERROR, dump_rtentry(rt));
- }
-#endif /* 0 */
-
- break;
- case RTM_DELETE:
- DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():0300-Now in case RTM_DELETE.\n"));
- /*
- * The FLUSH call ('route flush...') checks the ifp, perhaps I should
- * fill that just in case.
- */
-
- if (rt->rt_ifp == NULL)
- rt->rt_ifp = rt->rt_ifa->ifa_ifp;
-
- if ((rt->rt_flags & RTF_HOST) == 0)
- {
- /*
- * Clean up after network routes.
- */
- if (rt->rt_flags & RTF_TUNNEL)
- {
- DPRINTF(IDL_EVENT,("Cleaning up tunnel route.\n"));
- tunnel_parent_clean(rt);
- if (rt_mask(rt)->sa_len != 0)
- return; /* If default route is RTF_TUNNEL, then continue. */
- }
- if (rt->rt_flags & RTF_GATEWAY) {
- if (rt_mask(rt)->sa_len == 0)
- {
- struct v6router *v6r;
-
- DPRINTF(IDL_GROSS_EVENT,\
- ("Cleaning up THE default route.\n"));
- /*
- * Find manually added default route thing, and clean up that
- * entry.
- *
- * If user deletes default route added by receiving router
- * adverts, then the router is still on the default router
- * list, and needs to be added back.
- */
- for (v6r = defrtr.v6r_next; v6r != &defrtr;
- v6r = v6r->v6r_next)
- {
- /*
- * PROBLEM: rt->rt_gwroute, which is what I REALLY
- * want to check against, is gone at this
- * point.
- *
- * POSSIBLE SOLUTION: Do a check with rt_key(v6r->v6r_rt)
- * and rt->rt_gateway, but there
- * is the question of tunneling
- * default routes, etc., which may
- * mean doing a masked match.
- */
-#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
- /*
- * PRESENTED SOLUTION: Try checking the expiration
- * (easy giveaway for manually
- * added default routes), followed
- * by blatant sockaddr compares
- * (will nail all other normal
- * cases), followed by sa_family
- * check (may be unneccesary).
- */
- if (v6r->v6r_expire == 0 ||
- equal(rt_key(v6r->v6r_rt),rt->rt_gateway) ||
- (rt->rt_gateway->sa_family != AF_INET6 &&
- rt->rt_gateway->sa_family ==
- rt_key(v6r->v6r_rt)->sa_family))
- break;
- }
- if (v6r != &defrtr)
- (void)ipv6_delete_defrouter(v6r);
- if (defrtr.v6r_next != &defrtr)
- {
- /*
- * Somehow re-add the default route.
- *
- * The default route has been deleted from the radix
- * tree already, so re-adding should be relatively
- * straightforward.
- */
- DPRINTF(IDL_ERROR,
- ("Auto-added default routers still there!\n"));
- }
- }
- else
- {
- struct v6router *v6r = (struct v6router *)rt->rt_llinfo;
- struct v6child *v6c;
-
- /* Non-default router. */
- DPRINTF(IDL_GROSS_EVENT,\
- ("Cleaning up a non-{default,host} route.\n"));
-
- if (v6r == NULL)
- panic("Non-default w/o v6router entry.");
-
- v6c = v6r->v6r_children.v6c_next;
- while (v6c != v6c->v6c_next)
- {
- DPRINTF(IDL_ERROR,("Calling RTM_DELETE of child.\n"));
- /* rtrequest() should remove child from the linked list */
- rtrequest(RTM_DELETE,rt_key(v6c->v6c_route),NULL,NULL,0,
- NULL);
- v6c = v6r->v6r_children.v6c_next;
- }
- remque(v6r);
- free(v6r,M_DISCQ);
- }
- }
- /* Anything else that isn't a HOST needs no work, so return. */
- return;
- }
-
- DPRINTF(IDL_EVENT,("v6_discov_rtrequest() deleting a host\n"));
- DPRINTF(IDL_EVENT,("I'm at a host-route point.\n"));
-
- if (rt->rt_flags & RTF_TUNNEL)
- {
- DPRINTF(IDL_EVENT,("Tunneling child.\n"));
- /*
- * PROBLEM: Following check will die if parent went bye-bye.
- * See if you can fix in another way.
- */
- /*if (rt_mask(rt->rt_parent)->sa_len == 0)*/
- if (rt->rt_flags & RTF_DEFAULT)
- {
- /*
- * Tunneling default route child. Clean off meta-state.
- */
- struct v6child *v6c = (struct v6child *)rt->rt_llinfo;
-
- DPRINTF(IDL_ERROR,("Cleaning tunnel-default child.\n"));
- remque(v6c);
- rt->rt_llinfo = NULL;
- free(v6c,M_DISCQ);
- }
- tunnel_child_clean(rt);
- }
- else if (rt->rt_flags & RTF_GATEWAY)
- {
- struct v6child *v6c = (struct v6child *) rt->rt_llinfo;
-
- if (v6c == NULL)
- {
- if (rt->rt_flags & RTF_HOST) {
- DPRINTF(IDL_ERROR,
- ("no v6c in RTM_DELETE of RTF_GATEWAY.\n"));
- }
- }
- else
- {
- remque(v6c);
- rt->rt_llinfo = NULL;
- free(v6c,M_DISCQ);
- }
- }
- else if (rt->rt_flags & RTF_LLINFO)
- {
- struct discq *dq;
-
- /* Neighbor cache entry. */
- if (rt->rt_flags & RTF_ISAROUTER)
- {
- struct v6router *v6r,*head;
-
- /* Clean up all children of this router. */
-
- DPRINTF(IDL_GROSS_EVENT,("Cleaning up router neighbor.\n"));
- if (rt->rt_flags & RTF_DEFAULT)
- head = &defrtr;
- else
- {
- head = &nondefrtr;
- /* Q: Do I want to delete the actual network route too? */
- /* Q2: Do I want to delete the children? */
- }
-
- for (v6r = head->v6r_next; v6r != head; v6r = v6r->v6r_next)
- if (v6r->v6r_rt == rt)
- break;
- if (v6r == head)
- {
- /*
- * At many addresses per interface, this isn't a huge
- * problem, because a router's on-link address might not
- * be tied to a router entry.
- */
- DPRINTF(IDL_EVENT,
- ("Router neighbor inconsistency.\n"));
- }
- else
- if (head == &defrtr)
- (void) ipv6_delete_defrouter(v6r); /* This gets rid of
- children, too! */
- }
-
- dq = (struct discq *)rt->rt_llinfo;
- rt->rt_flags &= ~RTF_LLINFO;
- if (dq == NULL)
- panic("No discq or other rt_llinfo in RTM_DELETE");
- remque(dq);
- rt->rt_llinfo = NULL;
- if (dq->dq_queue)
- {
- /* Send ICMP unreachable error. */
- ipv6_icmp_error(dq->dq_queue, ICMPV6_UNREACH,
- ICMPV6_UNREACH_ADDRESS, 0);
- /* m_freem(dq->dq_queue);*/
- }
- free(dq,M_DISCQ);
- }
- else
- {
- DPRINTF(IDL_GROSS_EVENT,\
- ("Freeing self-wired address. Doing nothing.\n"));
- }
- break;
- }
- DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():1000-Finished.\n"));
-}
-
-/* EOF */
diff --git a/sys/netinet6/ipv6_trans.c b/sys/netinet6/ipv6_trans.c
deleted file mode 100644
index 6493cc696b1..00000000000
--- a/sys/netinet6/ipv6_trans.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/socket.h>
-#include <sys/protosw.h>
-#include <sys/domain.h>
-#include <sys/mbuf.h>
-#include <sys/socket.h>
-#include <sys/socketvar.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/ip_icmp.h>
-
-#include <netinet6/in6.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-
-#if __OpenBSD__
-#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * External globals.
- */
-
-extern struct in6_ifaddr *in6_ifaddr;
-extern int ipv6_defhoplmt;
-
-int ipv6_trans_mtu __P((struct mbuf **, int, int));
-int ipv6_trans_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
-int ipv6_encaps_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
-int ipv6_tunnel_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
-int ipv4_trans_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *));
-int ipv4_encaps_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
-int ipv4_tunnel_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
-
-/*----------------------------------------------------------------------
- * Called from ip_icmp.c, this function will reduce the tunnel path MTU
- * precisely. I know I have enough to reconstruct the IPv6 header, which
- * is all I care about for this case. Return 1 if m0 is intact, and 0 if
- * m0 is corrupted somehow. Don't forget to update m0.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_trans_mtu(m0, newmtu, len)
- struct mbuf **m0;
- int newmtu,len;
-{
- struct ip *ip,*iip;
- struct ipv6 *ipv6;
- struct icmp *icp;
- struct rtentry *rt;
- struct sockaddr_in6 sin6;
- struct in6_ifaddr *i6a;
-
- /*
- * Make packet contiguous into one block of memory. If the IPv6 header is
- * beyond MCLBYTES into the packet, then I'm in big trouble.
- */
- *m0 = m_pullup2(*m0,min(len,MCLBYTES));
- if (*m0 == NULL)
- return 0;
-
- ip = mtod(*m0,struct ip *);
- icp = (struct icmp *) ((caddr_t)ip + (ip->ip_hl << 2));
- iip = &icp->icmp_ip;
- ipv6 = (struct ipv6 *) ((caddr_t)iip + (iip->ip_hl << 2));
-
- /*
- * Verify source is one of mine?
- */
- for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ipv6->ipv6_src))
- break;
- if (i6a == NULL)
- {
- /* Packet didn't originate with me. Drop it. */
- return 1;
- }
-
- /*
- * Find route for this destination and update it.
- */
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
- sin6.sin6_addr = ipv6->ipv6_dst;
-
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)&sin6, 0);
-#endif /* __FreeBSD__ */
-
- if (rt == NULL)
- return 1;
- rt->rt_refcnt--;
- /*
- * Update path MTU.
- */
- if (!(rt->rt_flags & RTF_HOST))
- return 1; /* Can't update path MTU on non-host-route. */
- if (rt->rt_rmx.rmx_mtu < newmtu - sizeof(struct ip))
- panic("MTU WEIRDNESS !!!");
- rt->rt_rmx.rmx_mtu = newmtu - sizeof(struct ip);
- return 1;
-}
-
-/*----------------------------------------------------------------------
- * Handle ICMP errors for IPv6-in-IPv4 tunnels.
- *
- * Security processing should be put in here, as it was with the other
- * ctlinput() functions, but with current ICMP implementations returning
- * only sizeof(struct ip) + 64 bits of offending packet.
- ----------------------------------------------------------------------*/
-#if __OpenBSD__
-#ifdef NRL_IPSEC
-void *ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, void *vp, struct mbuf *incoming)
-#else /* NRL_IPSEC */
-void *ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, void *vp)
-#endif /* NRL_IPSEC */
-#else /* __OpenBSD__ */
-#ifdef IPSEC
-void ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, register struct ip *ip, struct mbuf *incoming)
-#else /* IPSEC */
-void ipv6_trans_ctlinput(int cmd, struct sockaddr *sa, register struct ip *ip)
-#endif /* IPSEC */
-#endif /* __OpenBSD__ */
-{
- struct sockaddr_in *sin = (struct sockaddr_in *)sa;
- struct sockaddr_in6 sin6;
- struct ipv6 *ipv6;
- struct rtentry *rt;
- struct in6_ifaddr *i6a;
-#if __OpenBSD__
- struct ip *ip = (struct ip *)vp;
-#endif /* __OpenBSD__ */
-
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(sin6);
- sin6.sin6_port = 0;
- sin6.sin6_flowinfo = 0;
- DPRINTF(IDL_EVENT,("Entered ipv6_trans_ctlinput().\n"));
-
- /*
- * Do standard checks to see that all parameters are here.
- */
- if ((unsigned)cmd > PRC_NCMDS || sa->sa_family != AF_INET ||
- sin->sin_addr.s_addr == INADDR_ANY || ip == NULL)
- {
- DPRINTF(IDL_EVENT,("Failed one of the four checks. Returning.\n"));
-#ifdef __OpenBSD__
- return NULL;
-#else /* __OpenBSD__ */
- return;
-#endif /* __OpenBSD__ */
- }
-
- /*
- * Okay, at this point I have a contiguous IPv6 in IPv4 datagram.
- * I achieved this effect by convincing ip_icmp.[ch] to pull up
- * more than the first 64 bits.
- */
-
- ipv6 = (struct ipv6 *) ((caddr_t)ip + (ip->ip_hl << 2));
- /*
- * Verify source address is one of mine.
- */
- for (i6a = in6_ifaddr; i6a != NULL; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &ipv6->ipv6_src))
- break;
- if (i6a == NULL)
- {
- /*
- * Packet didn't originate with me. Drop it.
- */
-#ifdef __OpenBSD__
- return NULL;
-#else /* __OpenBSD__ */
- return;
-#endif /* __OpenBSD__ */
- }
-
- sin6.sin6_addr = ipv6->ipv6_dst;
-#ifdef __FreeBSD__
- rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
-#else /* __FreeBSD__ */
- rt = rtalloc1((struct sockaddr *)&sin6, 0);
-#endif /* __FreeBSD__ */
- if (rt == NULL)
-#ifdef __OpenBSD__
- return NULL;
-#else /* __OpenBSD__ */
- return;
-#endif /* __OpenBSD__ */
- rt->rt_refcnt--;
-
- switch (cmd)
- {
- case PRC_MSGSIZE:
- /*
- * This function was called because the actual MTU wasn't grokked
- * from the ICMP packet.
- *
- * If I get this, drop to IPV6_MINMTU. If the actual MTU was in the
- * ICMP packet and was read correctly, it went up a different codepath.
- *
- * RFC 1191 talks about a plateau table. Here's the place to do it,
- * either that, or on increase.
- */
- if (rt->rt_flags & RTF_HOST)
- {
- /*
- * Only attempt path MTU update if I'm a host.
- */
- if (rt->rt_rmx.rmx_mtu == IPV6_MINMTU)
- panic("Too big on v6 MTU of 576!!!");
- rt->rt_rmx.rmx_mtu = IPV6_MINMTU;
- }
- break;
- case PRC_UNREACH_NET:
- rt->rt_flags &= ~RTF_HOST; /* Is this wise? I'm doing this to return
- the right error on future requests. */
- /* FALLTHROUGH */
- case PRC_UNREACH_HOST:
- case PRC_UNREACH_PROTOCOL:
- /*
- * Other end isn't a v6/v4 node.
- */
- rt->rt_flags |= RTF_REJECT; /* Don't want to send any packets. */
- break;
- default:
- break;
- }
-#ifdef __OpenBSD__
- return NULL;
-#endif /* __OpenBSD__ */
-}
-
-/*----------------------------------------------------------------------
- *"IPv6 in IPv4 tunnelling."
- *
- * Output routine for IPv6 in IPv4. Uses M_PREPEND to prepend an IPv4
- * header, and call ip_output().
- *
- * Called in: (only) ipv6_tunnel_output() from this file.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_trans_output(outgoing, v4dst, v6rt)
- struct mbuf *outgoing;
- struct sockaddr_in *v4dst;
- struct rtentry *v6rt;
-{
- struct route v4route;
- struct ip *ip;
- struct ipv6 *ipv6 = mtod(outgoing,struct ipv6 *);
- int rc;
-#ifdef IPSEC
- struct socket *socket;
-#endif /* IPSEC */
-
- /*
- * Like all below-IP(v6) output routines, check RTF_REJECT flag.
- */
- if (v6rt->rt_flags & RTF_REJECT)
- {
- m_freem(outgoing);
- return (v6rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
- }
-
- if (v6rt->rt_gwroute)
- v6rt->rt_gwroute->rt_refcnt++;
- v4route.ro_rt = v6rt->rt_gwroute;
- bcopy(v4dst,&v4route.ro_dst,v4dst->sin_len);
-
- /*
- * Prepend IPv4 header.
- */
- M_PREPEND(outgoing,sizeof(struct ip), M_DONTWAIT);
- if (outgoing == NULL)
- return ENOBUFS;
-
- ip = mtod(outgoing,struct ip *);
- bzero(ip,sizeof(*ip));
-
- /*
- * Following four lines are done here rather than ip_output() because of
- * the *&*&%^^& don't fragment bit.
- */
- ip->ip_v = IPVERSION;
-#if __OpenBSD__
- ip->ip_id = ip_randomid();
-#else /* __OpenBSD__ */
- ip->ip_id = htons(ip_id++);
-#endif /* __OpenBSD__ */
- ip->ip_hl = sizeof(*ip)>>2;
- if (v6rt->rt_rmx.rmx_mtu > IPV6_MINMTU)
- ip->ip_off |= IP_DF;
- ipstat.ips_localout++;
-
- if (v6rt->rt_flags & (RTF_HOST|RTF_GATEWAY))
- ip->ip_dst = v4dst->sin_addr;
- else
- {
- /*
- * If I'm in here, this means I'm not a host route, but when I was
- * supposed to clone, I was supposed to change the v4dst addr.
- *
- * This will only happen if I'm a v6-in-v4 router-to-host route,
- * in which case I have to do the translation on the fly, based on
- * the data in the IPv6 header.
- */
- if (!IN6_IS_ADDR_V4COMPAT(&ipv6->ipv6_dst))
- {
- printf("Oooh boy, v6-in-v4 tunnel ( trans_output() ) trouble!!!\n");
- }
- ip->ip_dst.s_addr = ipv6->ipv6_dst.s6_addr32[3];
- }
-
- ip->ip_src.s_addr = INADDR_ANY;
- ip->ip_p = IPPROTO_IPV6;
- ip->ip_ttl = ip_defttl;
- ip->ip_len = outgoing->m_pkthdr.len;
-
-#ifdef IPSEC
- if (v6rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
- /*
- * A secure route has hanging off its rt_netproc field something which
- * can be tagged onto an outgoing mbuf such that ipv4_output can
- * secure the IPv6-in-IPv4 packet.
- */
- DPRINTF(IDL_EVENT,("Secure route, sending cheesy socket.\n"));
- socket = v6rt->rt_netproc;
- } else
- socket = NULL;
-
- rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL, socket);
-#else /* IPSEC */
-#if __bsdi__
- rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL, NULL);
-#else /* __bsdi__ */
- rc = ip_output(outgoing, NULL, &v4route, IP_RAWOUTPUT, NULL);
-#endif /* __bsdi__ */
-#endif /* IPSEC */
-
- if (rc == EMSGSIZE)
- {
- DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
- }
- if (v4route.ro_rt != NULL)
- RTFREE(v4route.ro_rt);
- return rc;
-}
-
-/*----------------------------------------------------------------------
- * "IPv6 in IPv6 tunnelling."
- *
- * Encapsulate IPv6 packet in another IPv6 packet. This, in combination
- * with passing on a fake socket with a security request, can enable a
- * configured secure tunnel.
- *
- * Called in: (only) ipv6_tunnel_output() from this file.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_encaps_output(outgoing, tundst, tunrt)
- struct mbuf *outgoing;
- struct sockaddr_in6 *tundst;
- struct rtentry *tunrt;
-{
- struct route6 actroute;
- struct ipv6 *ipv6;
- int rc;
-#ifdef IPSEC
- struct socket *socket;
-#endif /* IPSEC */
-
-DPRINTF(IDL_GROSS_EVENT,("\n\nipv6_encaps_output():0000-Hey! I'm in IPV6_in_IPV6 tunnelling code!!\n"));
- if (tunrt->rt_flags & RTF_REJECT)
- {
- m_freem(outgoing);
- return (tunrt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
- }
-
- if (tunrt->rt_gwroute)
- tunrt->rt_gwroute->rt_refcnt++;
- actroute.ro_rt = tunrt->rt_gwroute;
- bcopy(tundst,&actroute.ro_dst,tundst->sin6_len);
-
- M_PREPEND(outgoing,sizeof(struct ipv6), M_DONTWAIT);
- if (outgoing == NULL)
- return ENOBUFS;
-
- ipv6 = mtod(outgoing,struct ipv6 *);
- bzero(ipv6,sizeof(*ipv6));
-
- ipv6->ipv6_versfl = htonl(0x60000000);
- ipv6->ipv6_length = outgoing->m_pkthdr.len - sizeof(struct ipv6);
- ipv6->ipv6_nexthdr = IPPROTO_IPV6;
- ipv6->ipv6_hoplimit = ipv6_defhoplmt;
- ipv6->ipv6_dst = tundst->sin6_addr;
-
-#ifdef IPSEC
- if (tunrt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
- /*
- * A secure route has hanging off its rt_netproc field something which
- * can be tagged onto an outgoing mbuf such that ipv6_output can
- * secure the IPv6-in-IPv6 packet.
- */
- DPRINTF(IDL_EVENT,("ipv6_encaps_output():0500-Secure route, sending cheesy socket.\n"));
- socket = tunrt->rt_netproc;
- } else
- socket = NULL;
-
- rc = ipv6_output(outgoing, &actroute, IPV6_RAWOUTPUT, NULL, NULL, socket);
-#else /* IPSEC */
- rc = ipv6_output(outgoing, &actroute, IPV6_RAWOUTPUT, NULL, NULL, NULL);
-#endif /* IPSEC */
-
- if (rc == EMSGSIZE)
- {
- DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
- }
- if (actroute.ro_rt != NULL)
- RTFREE(actroute.ro_rt);
- return rc;
-
-}
-/*----------------------------------------------------------------------
- * "IPv4 in IPv4 tunnelling"
- *
- * Output routine for IPv4 in IPv4. Uses M_PREPEND to prepend an IPv4
- * header (i.e. the tunnel header), and then call ip_output() to
- * send the tunnel packet. The v4 in v4 tunnel seems redundant, but is
- * useful for setting up secure tunnels.
- *
- * Called in: (only) ipv4_tunnel_output() from this file.
- ----------------------------------------------------------------------*/
-
-int
-ipv4_encaps_output(outgoing, v4tundst, v4rt)
- struct mbuf *outgoing;
- struct sockaddr_in *v4tundst; /* Goto to destination tunnel endpoint. */
- struct rtentry *v4rt; /* The encapsulated (i.e. passenger) IPv4
- * packet's route. */
-{
- struct route v4tunroute; /* The tunnel- (i.e. actual) route to the tunnel's
- * destination endpoint (i.e. to the other
- * side of the tunnel). */
- struct ip *ip; /* For setting up the IPv4 data needed for the
- * tunnel IPv4 packet. */
- int rc; /* Return Code */
-#ifdef IPSEC
- struct socket *socket;
-#endif /* IPSEC */
-
- /*
- * Like all below-IP(v4) output routines, check RTF_REJECT flag.
- * Why? We need to make sure the logical (i.e. encapsulated packet's)
- * route to the final destination is reachable.
- */
- if (v4rt->rt_flags & RTF_REJECT)
- {
- m_freem(outgoing);
- return (v4rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
- }
-
- if (v4rt->rt_gwroute)
- v4rt->rt_gwroute->rt_refcnt++;
- v4tunroute.ro_rt = v4rt->rt_gwroute;
- bcopy(v4tundst,&v4tunroute.ro_dst,v4tundst->sin_len);
-
- /*
- * Prepend IPv4 header.
- */
- M_PREPEND(outgoing,sizeof(struct ip), M_DONTWAIT);
- if (outgoing == NULL)
- return ENOBUFS;
-
- ip = mtod(outgoing,struct ip *);
- bzero(ip,sizeof(*ip));
-
- /*
- * Initialization of IP header: We'll let ip_output() fill in
- * IP's version, ip, offset, and header length as well as update
- * ipstat.ips_localout.
- *
- * Of course, we need to do the rest ...
- */
-
- ip->ip_src.s_addr = INADDR_ANY;
- ip->ip_p = IPPROTO_IPV4;
- ip->ip_ttl = ip_defttl;
- ip->ip_len = outgoing->m_pkthdr.len;
-
- ip->ip_dst = v4tundst->sin_addr;
-
-#ifdef IPSEC
- if (v4rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
- /*
- * A secure route has hanging off its rt_netproc field something which
- * can be tagged onto an outgoing mbuf such that ipv4_output can
- * secure the IPv4-in-IPv4 packet.
- */
- DPRINTF(IDL_EVENT,("ipv4_encaps_output():0500-Secure route, sending cheesy socket.\n"));
- socket = v4rt->rt_netproc;
- } else
- socket = NULL;
-
- rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL, socket);
-#else /* IPSEC */
-#if __bsdi__
- rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL, NULL);
-#else /* __bsdi__ */
- rc = ip_output(outgoing, NULL, &v4tunroute, 0, NULL);
-#endif /* __bsdi__ */
-#endif /* IPSEC */
-
- if (v4tunroute.ro_rt != NULL)
- RTFREE(v4tunroute.ro_rt);
- return rc;
-}
-
-/*----------------------------------------------------------------------
- *"IPv4 in IPv6"
- *
- * Encapsulate IPv4 packet in a IPv6 packet.
- *
- * Called in: (only) ipv4_tunnel_output() from this file.
- *---------------------------------------------------------------------*/
-
-int
-ipv4_trans_output(outgoing, tunv6dst, v4rt)
- struct mbuf *outgoing;
- struct sockaddr_in6 *tunv6dst;
- struct rtentry *v4rt;
-{
- struct route6 tunv6route;
- struct ipv6 *ipv6;
- int rc;
-#ifdef IPSEC
- struct socket *socket;
-#endif /* IPSEC */
-
- if (v4rt->rt_flags & RTF_REJECT)
- {
- m_freem(outgoing);
- return (v4rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH;
- }
-
- if (v4rt->rt_gwroute)
- v4rt->rt_gwroute->rt_refcnt++;
- tunv6route.ro_rt = v4rt->rt_gwroute;
- bcopy(tunv6dst,&tunv6route.ro_dst,tunv6dst->sin6_len);
-
- M_PREPEND(outgoing,sizeof(struct ipv6), M_DONTWAIT);
- if (outgoing == NULL)
- return ENOBUFS;
-
- ipv6 = mtod(outgoing,struct ipv6 *);
- bzero(ipv6,sizeof(*ipv6));
-
- ipv6->ipv6_versfl = htonl(0x60000000);
- ipv6->ipv6_length = outgoing->m_pkthdr.len - sizeof(struct ipv6);
- ipv6->ipv6_nexthdr = IPPROTO_IPV4;
- ipv6->ipv6_hoplimit = ipv6_defhoplmt;
- ipv6->ipv6_dst = tunv6dst->sin6_addr;
-
-#ifdef IPSEC
- if (v4rt->rt_flags & (RTF_CRYPT|RTF_AUTH)) {
- /*
- * A secure route has hanging off its rt_netproc field something which
- * can be tagged onto an outgoing mbuf such that ipv6_output can
- * secure the IPv4-in-IPv6 packet.
- */
- DPRINTF(IDL_EVENT,("ipv4_trans_output():0500-Secure route, sending cheesy socket.\n"));
- socket = v4rt->rt_netproc;
- } else
- socket = NULL;
-
- rc = ipv6_output(outgoing, &tunv6route, IPV6_RAWOUTPUT, NULL, NULL, socket);
-#else /* IPSEC */
- rc = ipv6_output(outgoing, &tunv6route, IPV6_RAWOUTPUT, NULL, NULL, NULL);
-#endif /* IPSEC */
-
- if (rc == EMSGSIZE)
- {
- DPRINTF(IDL_ERROR,("Path MTU adjustment needed in trans_output().\n"));
- }
- if (tunv6route.ro_rt != NULL)
- RTFREE(tunv6route.ro_rt);
- return rc;
-}
-
-
-/*----------------------------------------------------------------------
- * Called by ipv6_output if the RTF_TUNNEL bit is set on a route,
- * this function examines the route, and sees what sort of encapsulation is
- * needed. Often, the rt->rt_gateway sockaddr is used to figure this out.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_tunnel_output(outgoing, dst, rt)
- struct mbuf *outgoing;
- struct sockaddr_in6 *dst;
- struct rtentry *rt;
-{
- DPRINTF(IDL_EVENT,("\n\nipv6_tunnel_output():0000-Just entered.\n"));
-
- /*
- * Determine what type of tunnel it is with rt. Perform correct kind
- * of encapsulation (in IPv4, ESP, etc.) and call output routine of
- * what you want encapsulated.
- */
-
- /* IPv6 in IPv4. */
- if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET)
- return ipv6_trans_output(outgoing,(struct sockaddr_in *)rt->rt_gateway,rt);
-
- /* IPv6 in IPv6. */
- if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET6)
- return ipv6_encaps_output(outgoing,(struct sockaddr_in6 *)rt->rt_gateway,rt);
-
- m_freem(outgoing);
- return EHOSTUNREACH;
-}
-
-
-/*----------------------------------------------------------------------
- * Called by ip_output if the RTF_TUNNEL bit is set on a route,
- * this function examines the route, and sees what sort of encapsulation is
- * needed. Often, the rt->rt_gateway sockaddr is used to figure this out.
- ----------------------------------------------------------------------*/
-
-int
-ipv4_tunnel_output(outgoing, dst, rt)
- struct mbuf *outgoing;
- struct sockaddr_in *dst;
- struct rtentry *rt;
-{
- DPRINTF(IDL_EVENT,("\n\nipv4_tunnel_output():0000-Just entered.\n"));
-
- /*
- * Determine what type of tunnel it is with rt. Perform correct kind
- * of encapsulation (in IPv4, ESP, etc.) and call output routine of
- * what you want encapsulated.
- */
-
- /* IPv4 in IPv6. */
- if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET6)
- return ipv4_trans_output(outgoing,(struct sockaddr_in6 *)rt->rt_gateway,rt);
-
- /* IPv4 in IPv4. */
- if (rt->rt_gateway != NULL && rt->rt_gateway->sa_family == AF_INET)
- return ipv4_encaps_output(outgoing,(struct sockaddr_in *)rt->rt_gateway,rt);
-
- m_freem(outgoing);
- return EHOSTUNREACH;
-}
diff --git a/sys/netinet6/ipv6_trans.h b/sys/netinet6/ipv6_trans.h
deleted file mode 100644
index 5ceb2456e3a..00000000000
--- a/sys/netinet6/ipv6_trans.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-#ifndef _NETINET6_IPV6_TRANS_H
-#define _NETINET6_IPV6_TRANS_H 1
-
-/* I don't include any #includes, as I'm using this for our (NRL)
- * modified netinet/ip_output() function; thus, this #include should be
- * used/stuck-in after all the other necessary includes.
- *
- * And yes, I only put one declaration here. There's no real need
- * to stick the other prototypes in here and have ip_output() fluffed
- * during preprocessing time.
- */
-
-int ipv4_tunnel_output __P((struct mbuf *, struct sockaddr_in *, struct rtentry *));
-
-#endif /* _NETINET6_IPV6_TRANS_H */
diff --git a/sys/netinet6/ipv6_var.h b/sys/netinet6/ipv6_var.h
deleted file mode 100644
index 0ccce023012..00000000000
--- a/sys/netinet6/ipv6_var.h
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-%%% copyright-nrl-95
-This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
-Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
-rights under this copyright have been assigned to the US Naval Research
-Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
-1.1 (January 17, 1995) applies to this software.
-You should have received a copy of the license with this software. If you
-didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
-
-*/
-#ifndef _NETINET6_IPV6_VAR_H
-#define _NETINET6_IPV6_VAR_H 1
-
-#include <netinet6/in6.h>
-
-/*
- * IPv6 multicast "options". Session state for multicast, including
- * weird per-session multicast things.
- */
-
-struct ipv6_moptions
-{
- struct ifnet *i6mo_multicast_ifp; /* ifp for outgoing multicasts */
- u_char i6mo_multicast_ttl; /* TTL for outgoing multicasts.
- Does this matter in IPv6? */
- u_char i6mo_multicast_loop; /* 1 => hear sends if a member */
- u_short i6mo_num_memberships; /* no. memberships this socket */
- struct in6_multi *i6mo_membership[IN6_MAX_MEMBERSHIPS];
-};
-
-/*
- * IPv6 stats.
- */
-
-#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802
-#define _IPV6STAT_TYPE u_quad_t
-#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
-#define _IPV6STAT_TYPE u_long
-#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */
-
-struct ipv6stat {
- _IPV6STAT_TYPE ips_total; /* total packets received */
- _IPV6STAT_TYPE ips_tooshort; /* packet too short */
- _IPV6STAT_TYPE ips_toosmall; /* not enough data */
- _IPV6STAT_TYPE ips_fragments; /* fragments received */
- _IPV6STAT_TYPE ips_fragdropped; /* frags dropped (dups, out of space) */
- _IPV6STAT_TYPE ips_fragtimeout; /* fragments timed out */
- _IPV6STAT_TYPE ips_forward; /* packets forwarded */
- _IPV6STAT_TYPE ips_cantforward; /* packets rcvd for unreachable dest */
- _IPV6STAT_TYPE ips_redirectsent; /* packets forwarded on same net */
- _IPV6STAT_TYPE ips_noproto; /* unknown or unsupported protocol */
- _IPV6STAT_TYPE ips_delivered; /* datagrams delivered to upper level*/
- _IPV6STAT_TYPE ips_localout; /* total ip packets generated here */
- _IPV6STAT_TYPE ips_odropped; /* lost packets due to nobufs, etc. */
- _IPV6STAT_TYPE ips_reassembled; /* total packets reassembled ok */
- _IPV6STAT_TYPE ips_fragmented; /* datagrams sucessfully fragmented */
- _IPV6STAT_TYPE ips_ofragments; /* output fragments created */
- _IPV6STAT_TYPE ips_cantfrag; /* don't fragment flag was set, etc. */
- _IPV6STAT_TYPE ips_badoptions; /* error in option processing */
- _IPV6STAT_TYPE ips_noroute; /* packets discarded due to no route */
- _IPV6STAT_TYPE ips_badvers; /* IPv6 version != 6 */
- _IPV6STAT_TYPE ips_rawout; /* total raw ip packets generated */
-};
-
-#if defined(_KERNEL) || defined(KERNEL)
-/*
- * The IPv6 fragment queue entry structure.
- * Notes:
- * Nodes are stored in ttl order.
- * prefix comes from whichever packet gets here first.
- * data contains a chain of chains of mbufs (m_next down a chain, m_nextpkt
- * chaining chains together) where the chains are ordered by assembly
- * position. When two chains are contiguous for reassembly, they are
- * combined and the frag header disappears.
- * The structure is deliberately sized so MALLOC will round up on the order
- * of much less than the total size instead of doubling the size.
- */
-
-struct ipv6_fragment
-{
- struct ipv6_fragment *next; /* Next fragment chain */
- struct mbuf *prefix; /* Headers before frag header(s) */
- struct mbuf *data; /* Frag headers + whatever data */
- u_char ttl; /* Fragment chain TTL. */
- u_char flags; /* Bit 0 indicates got end of chain */
-};
-
-/*
- * Structures and definitions for discovery mechanisms in IPv6.
- */
-
-/*
- * Neighbor cache:
- *
- * Number of unanswered probes is in discq.
- * "Time of next event" will be in rt->rt_rmx.rmx_expire
- * (rmx_expire will actually be quite overloaded, actually.)
- * Status REACHABLE will be dq_unanswered < 0
- * Status PROBE will be dq_unanswered >= 0
- * Status INCOMPLETE will be link addr length of 0 if held,
- * or deleted if not held.
- *
- * If held, but INCOMPLETE fails set RTF_REJECT and make sure
- * IPv6 and HLP's know how to deal with RTF_REJECT being set.
- */
-
-struct discq /* Similar to v4's llinfo_arp, discovery's "neighbor entry". */
-{
- struct discq *dq_next,*dq_prev; /* For {ins,rem}que(). */
- struct rtentry *dq_rt; /* Back pointer to routing entry for
- an address that may be dead. */
- struct mbuf *dq_queue; /* Queue of outgoing messages. */
- int dq_unanswered; /* Number of unanswered probes. */
-};
-
-#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)
-/* Routing flag redefinitions */
-#define RTF_ISAROUTER RTF_PROTO2 /* Neighbor is a router. */
-#define RTF_DEFAULT RTF_PROTO1 /* Default route. */
-#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */
-
-/*
- * These should be configurable parameters, see ipv6_discovery.c.
- * All units are in comments besides constants.
- */
-
-#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16 /* seconds */
-#define MAX_INITIAL_RTR_ADVERTISEMENTS 3 /* transmissions */
-#define MAX_RTR_RESPONSE_DELAY 2 /* seconds */
-
-#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
-#define RTR_SOLICITATION_INTERVAL 3 /* seconds */
-#define MAX_RTR_SOLICITATIONS 3 /* transmissions */
-
-#define MAX_MULTICAST_SOLICIT 3 /* transmissions */
-#define MAX_UNICAST_SOLICIT 3 /* transmissions */
-#define MAX_ANYCAST_DELAY_TIME 1 /* seconds */
-#define MAX_NEIGHBOR_ADVERTISEMENTS 3 /* transmissions */
-#define MIN_NEIGHBOR_ADVERT_INTERVAL 16 /* seconds */
-#define REACHABLE_TIME 30 /* seconds */
-#define RETRANS_TIMER 3 /* seconds */
-#define DELAY_FIRST_PROBE_TIME 3 /* seconds */
-/* Need to somehow define random factors. */
-
-#define NEXTHOP_CLEAN_INTERVAL 600 /* seconds */
-#define REJECT_TIMER 20 /* seconds */
-
-/*
- * Child of a router or tunnel. Is a "meta-entry" for garbage collection.
- */
-
-struct v6child
-{
- struct v6child *v6c_next,*v6c_prev; /* For {ins,rem}que() */
- struct v6router *v6c_parent; /* Parent router. I'm null if
- I'm the router, or a tunnel
- child. */
- struct rtentry *v6c_route; /* Next-hop cache entry. I won't
- be holding it, but I'm attached
- to it, like discq is to neighbor
- cache entries. */
-};
-
-/*
- * Default router list entry. Should be inserted
- * in priority order. Will also have entries for non-
- * default routers, because I may be a router myself.
- */
-
-struct v6router
-{
- struct v6router *v6r_next,*v6r_prev; /* For {ins,rem}que() */
- struct rtentry *v6r_rt; /* Route for this. Could be neighbor,
- could be tunnel. */
- struct v6child v6r_children; /* Children of this router. */
-
- /* Metric information? */
- uint32_t v6r_expire; /* Expiration time. */
-};
-#define V6R_SIN6(v6r) ((struct sockaddr_in6 *)rt_key((v6r)->v6r_rt))
-
-/*
- * Flags for "flags" argument in ipv6_output().
- */
-
-#define IPV6_FORWARDING 0x1 /* Most of IPv6 header exists? */
-#define IPV6_RAWOUTPUT 0x2 /* Raw IPv6 packet! */
-#define IPV6_ROUTETOIF SO_DONTROUTE /* Include sys/socket.h... */
-
-void ipv6_init __P((void));
-void ipv6_drain __P((void));
-void ipv6_slowtimo __P((void));
-int ipv6_sysctl __P((int *, uint, void *, size_t *, void *, size_t));
-struct route6;
-
-#if __FreeBSD__
-int ipv6_ctloutput __P((struct socket *, struct sockopt *));
-int ripv6_ctloutput __P((struct socket *, struct sockopt *));
-#else /* __FreeBSD__ */
-int ipv6_ctloutput __P((int, struct socket *,int,int, struct mbuf **));
-int ripv6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
-#endif /* __FreeBSD__ */
-void ripv6_init __P((void));
-#if __OpenBSD__
-void ripv6_input __P((struct mbuf *, ...));
-int ripv6_output __P((struct mbuf *, ...));
-#else /* __OpenBSD__ */
-void ripv6_input __P((struct mbuf *, int));
-int ripv6_output __P((struct mbuf *, struct socket *, struct in6_addr *, struct mbuf *));
-#endif /* __OpenBSD__ */
-
-#if __NetBSD__ || __FreeBSD__
-int ripv6_usrreq_send(struct socket *, int, struct mbuf *, struct sockaddr *,
- struct mbuf *, struct proc *);
-#else /* __NetBSD__ || __FreeBSD__ */
-int ripv6_usrreq_send(struct socket *, int, struct mbuf *, struct sockaddr *,
- struct mbuf *);
-#endif /* __NetBSD__ || __FreeBSD__ */
-
-#if __FreeBSD__
-int ripv6_usrreq_abort(struct socket *);
-int ripv6_usrreq_attach(struct socket *, int , struct proc *);
-int ripv6_usrreq_bind(struct socket *, struct sockaddr *, struct proc *);
-int ripv6_usrreq_connect(struct socket *, struct sockaddr *, struct proc *);
-int ripv6_usrreq_control(struct socket *, u_long, caddr_t, struct ifnet *,
- struct proc *);
-int ripv6_usrreq_detach(struct socket *);
-int ripv6_usrreq_peeraddr(struct socket *, struct sockaddr **);
-int ripv6_usrreq_sense(struct socket *, struct stat *);
-int ripv6_usrreq_shutdown(struct socket *);
-int ripv6_usrreq_sockaddr(struct socket *, struct sockaddr **);
-#else /* __FreeBSD__ */
-#if __NetBSD__
-int ripv6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
- struct mbuf *, struct proc *));
-#else /* __NetBSD__ */
-int ripv6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
- struct mbuf *));
-#endif /* __NetBSD__ */
-#endif /* __FreeBSD__ */
-
-#if __OpenBSD__
-void ipv6_input __P((struct mbuf *, ...));
-int ipv6_output __P((struct mbuf *, ...));
-#else /* __OpenBSD__ */
-void ipv6_input __P((struct mbuf *, int));
-int ipv6_output __P((struct mbuf *, struct route6 *, int, struct ipv6_moptions *, struct ifnet *, struct socket *));
-#endif /* __OpenBSD__ */
-void ipv6_reasm __P((struct mbuf *, int));
-void ipv6_hop __P((struct mbuf *, int));
-
-#if __FreeBSD__
-int in6_control __P((struct socket *,int, caddr_t, struct ifnet *,int, struct proc *));
-#else /* __FreeBSD__ */
-#if __NetBSD__
-int in6_control __P((struct socket *,u_long, caddr_t, struct ifnet *,int, struct proc *));
-#else /* __NetBSD__ */
-int in6_control __P((struct socket *,int, caddr_t, struct ifnet *,int));
-#endif /* __NetBSD__ */
-#endif /* __FreeBSD__ */
-void ipv6_stripoptions __P((struct mbuf *, int));
-struct in6_multi *in6_addmulti __P((struct in6_addr *,struct ifnet *));
-void in6_delmulti __P((struct in6_multi *));
-
-#if __FreeBSD__
-/* ripv6_usrreq and ipv6_icmp_usrreq functions */
-extern struct pr_usrreqs ripv6_usrreqs;
-extern struct pr_usrreqs ipv6_icmp_usrreqs;
-
-extern int ripv6_usr_attach(struct socket *, int , struct proc *);
-extern int ripv6_usr_disconnect(struct socket *);
-extern int ripv6_usr_abort(struct socket *);
-extern int ripv6_usr_detach(struct socket *);
-extern int ripv6_usr_bind(struct socket *, struct sockaddr *, struct proc *);
-extern int ripv6_usr_connect(struct socket *, struct sockaddr *, struct proc *);
-extern int ripv6_usr_shutdown(struct socket *);
-extern int ripv6_usr_send(struct socket *, int, struct mbuf *,
- struct sockaddr *, struct mbuf *, struct proc *);
-extern int ripv6_usr_control(struct socket *, int, caddr_t,
- struct ifnet *, struct proc *);
-extern int ripv6_usr_sense(struct socket *, struct stat *);
-extern int ripv6_usr_sockaddr(struct socket *, struct sockaddr **);
-extern int ripv6_usr_peeraddr(struct socket *, struct sockaddr **);
-#endif /* __FreeBSD__ */
-
-extern int ipv6_icmp_send(struct socket *, int, struct mbuf *,
- struct sockaddr *, struct mbuf *, struct proc *);
-
-#if __OpenBSD__
-#ifdef NRL_IPSEC
-void *ipv6_trans_ctlinput __P((int, struct sockaddr *, void *, struct mbuf *));
-#else /* NRL_IPSEC */
-void *ipv6_trans_ctlinput __P((int, struct sockaddr *, void *));
-#endif /* NRL_IPSEC */
-#else /* __OpenBSD__ */
-struct ip;
-#ifdef NRL_IPSEC
-void ipv6_trans_ctlinput __P((int, struct sockaddr *, struct ip *, struct mbuf *));
-#else /* NRL_IPSEC */
-void ipv6_trans_ctlinput __P((int, struct sockaddr *, struct ip *));
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-/* These might belong in in_pcb.h */
-struct inpcb;
-#if __FreeBSD__
-/*
- * FreeBSD, having done away with the *_usrreq() functions no longer needs to
- * pass mbufs to these functions. Thus they pass in sockaddrs instead.
- */
-int in6_pcbbind(struct inpcb *, struct sockaddr *);
-int in6_pcbconnect(struct inpcb *, struct sockaddr *);
-int in6_setsockaddr(struct inpcb *, struct sockaddr **);
-int in6_setpeeraddr(struct inpcb *, struct sockaddr **);
-#else /* __FreeBSD__ */
-int in6_pcbbind(struct inpcb *, struct mbuf *);
-int in6_pcbconnect(struct inpcb *, struct mbuf *);
-int in6_setsockaddr(struct inpcb *, struct mbuf *);
-int in6_setpeeraddr(struct inpcb *, struct mbuf *);
-#endif /* __FreeBSD__ */
-void ipv6_onlink_query(struct sockaddr_in6 *);
-int ipv6_verify_onlink(struct sockaddr_in6 *);
-
-#if __FreeBSD__
-struct inpcbhead; /* XXX? Forward declaration needed. */
-#define __IN6_PCBNOTIFY_FIRSTARG struct inpcbhead *
-#endif /* __FreeBSD__ */
-#if __NetBSD__ || __OpenBSD__
-struct inpcbtable;
-#define __IN6_PCBNOTIFY_FIRSTARG struct inpcbtable *
-#endif /* __NetBSD__ || __OpenBSD__ */
-#if __bsdi__
-struct inpcb;
-#define __IN6_PCBNOTIFY_FIRSTARG struct inpcb *
-#endif /* __bsdi__ */
-
-#if (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC))
-int in6_pcbnotify __P((__IN6_PCBNOTIFY_FIRSTARG, struct sockaddr *, uint,
- struct in6_addr *, uint, int, void (*)(struct inpcb *,
- int), struct mbuf *, int));
-#else /* (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC)) */
-int in6_pcbnotify __P((__IN6_PCBNOTIFY_FIRSTARG, struct sockaddr *, uint,
- struct in6_addr *, uint, int, void (*)(struct inpcb *,
- int)));
-#endif /* (!__OpenBSD__ && defined(IPSEC)) || (__OpenBSD__ && defined(NRL_IPSEC)) */
-
-#undef __IN6_PCBNOTIFY_FIRSTARG
-
-void ipv6_freemoptions __P((struct ipv6_moptions *));
-
-int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *);
-struct mbuf *ipv6_headertocontrol(struct mbuf *m, int extra, int inp_flags);
-#endif /* defined(_KERNEL) || defined(KERNEL) */
-
-#endif /* _NETINET6_IPV6_VAR_H */
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
new file mode 100644
index 00000000000..3fd26c77f33
--- /dev/null
+++ b/sys/netinet6/mld6.c
@@ -0,0 +1,486 @@
+/* $OpenBSD: mld6.c,v 1.1 1999/12/08 06:50:23 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
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#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 <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/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));
+
+ ip6_opts.ip6po_hbh = hbh;
+ /* We will specify the hoplimit by a multicast option. */
+ ip6_opts.ip6po_hlim = -1;
+}
+
+void
+mld6_start_listening(in6m)
+ struct in6_multi *in6m;
+{
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+
+ /*
+ * (draft-ietf-ipngwg-mld, 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, MLD6_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, MLD6_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 mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off);
+ struct ifnet *ifp = m->m_pkthdr.rcvif;
+ struct in6_multi *in6m;
+ struct in6_ifaddr *ia;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct ifmultiaddr *ifma;
+#endif
+ int timer; /* timer value in the MLD query header */
+
+ /* source address validation */
+ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
+ log(LOG_ERR,
+ "mld6_input: src %s is not link-local\n",
+ ip6_sprintf(&ip6->ip6_src));
+ /*
+ * spec(draft-ietf-ipngwg-mld) does not explicitly
+ * specify to discard the packet from a non link-local
+ * source address. But we believe it's expected to do so.
+ */
+ 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->mld6_type) {
+ case MLD6_LISTENER_QUERY:
+ if (ifp->if_flags & IFF_LOOPBACK)
+ break;
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) &&
+ !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr))
+ break; /* print error or log stat? */
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] =
+ 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->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE;
+ if (timer == 0 && mldh->mld6_maxdelay)
+ timer = 1;
+ mld6_all_nodes_linklocal.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+#else
+ for (in6m = ia->ia6_multiaddrs.lh_first;
+ in6m;
+ in6m = in6m->in6m_entry.le_next)
+#endif
+ {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ 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;
+#else
+ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
+ &mld6_all_nodes_linklocal) ||
+ IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
+ IPV6_ADDR_SCOPE_LINKLOCAL)
+ continue;
+#endif
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&mldh->mld6_addr,
+ &in6m->in6m_addr))
+ {
+ if (timer == 0) {
+ /* send a report immediately */
+ mld6_sendpkt(in6m, MLD6_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->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ case MLD6_LISTENER_REPORT:
+ /*
+ * For fast leave to work, we have to know that we are the
+ * last person to send a report for this group. Reports
+ * can potentially get looped back if we are a multicast
+ * router, so discard reports sourced by me.
+ * Note that it is impossible to check IFF_LOOPBACK flag of
+ * ifp for this purpose, since ip6_mloopback pass the physical
+ * interface to looutput.
+ */
+ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
+ break;
+
+ if (!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr))
+ break;
+
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] =
+ htons(ifp->if_index); /* XXX */
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN6_LOOKUP_MULTI(mldh->mld6_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->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ break;
+ default: /* this is impossible */
+ log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type);
+ break;
+ }
+}
+
+void
+mld6_fasttimeo()
+{
+ register 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;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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, MLD6_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 mld6_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)) == 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;
+
+#ifdef IPSEC
+#ifndef __OpenBSD__ /*KAME IPSEC*/
+ mh->m_pkthdr.rcvif = NULL;
+#endif
+#endif
+ mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr);
+ mh->m_len = sizeof(struct ip6_hdr);
+ MH_ALIGN(mh, sizeof(struct ip6_hdr));
+
+ /* fill in the ip6 header */
+ ip6 = mtod(mh, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ 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 mld6_hdr);
+ mldh = mtod(md, struct mld6_hdr *);
+ mldh->mld6_type = type;
+ mldh->mld6_code = 0;
+ mldh->mld6_cksum = 0;
+ /* XXX: we assume the function will not be called for query messages */
+ mldh->mld6_maxdelay = 0;
+ mldh->mld6_reserved = 0;
+ mldh->mld6_addr = in6m->in6m_addr;
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
+ mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
+ mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
+ sizeof(struct mld6_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);
+ if (outif) {
+ icmp6_ifstat_inc(outif, ifs6_out_msg);
+ switch(type) {
+ case MLD6_LISTENER_QUERY:
+ icmp6_ifstat_inc(outif, ifs6_out_mldquery);
+ break;
+ case MLD6_LISTENER_REPORT:
+ icmp6_ifstat_inc(outif, ifs6_out_mldreport);
+ break;
+ case MLD6_LISTENER_DONE:
+ icmp6_ifstat_inc(outif, ifs6_out_mlddone);
+ break;
+ }
+ }
+}
diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h
new file mode 100644
index 00000000000..a43cd5d1e45
--- /dev/null
+++ b/sys/netinet6/mld6_var.h
@@ -0,0 +1,52 @@
+/* $OpenBSD: mld6_var.h,v 1.1 1999/12/08 06:50:23 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.
+ */
+
+#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 00000000000..1a257abe451
--- /dev/null
+++ b/sys/netinet6/nd6.c
@@ -0,0 +1,1801 @@
+/* $OpenBSD: nd6.c,v 1.1 1999/12/08 06:50:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 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 <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>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#include <sys/syslog.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#if !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <net/if_atm.h>
+#endif
+#include <net/route.h>
+
+#include <netinet/in.h>
+#ifndef __NetBSD__
+#include <netinet/if_ether.h>
+#ifdef __FreeBSD__
+#include <netinet/if_fddi.h>
+#endif
+#ifdef __bsdi__
+#include <net/if_fddi.h>
+#endif
+#else /* __NetBSD__ */
+#include <net/if_ether.h>
+#include <netinet/if_inarp.h>
+#include <net/if_fddi.h>
+#endif /* __NetBSD__ */
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/in6_prefix.h>
+#include <netinet6/icmp6.h>
+
+#ifndef __bsdi__
+#include "loop.h"
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+extern struct ifnet loif[NLOOP];
+#endif
+
+#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_proxyall = 0; /* enable Proxy Neighbor Advertisement */
+
+/* preventing too many loops in ND option parsing */
+int nd6_maxndopt = 10; /* max # of ND options allowed */
+
+/* for debugging? */
+static int nd6_inuse, nd6_allocated;
+
+struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6};
+struct nd_ifinfo *nd_ifinfo = NULL;
+struct nd_drhead nd_defrouter = { 0 };
+struct nd_prhead nd_prefix = { 0 };
+
+int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
+#if 0
+extern int ip6_forwarding;
+#endif
+static struct sockaddr_in6 all1_sa;
+
+static void nd6_slowtimo __P((void *));
+
+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;
+
+ nd6_init_done = 1;
+
+ /* start timer */
+ timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
+}
+
+void
+nd6_ifattach(ifp)
+ struct ifnet *ifp;
+{
+ static size_t if_indexlim = 8;
+
+ /*
+ * We have some arrays that should be indexed by if_index.
+ * since if_index will grow dynamically, they should grow too.
+ */
+ if (nd_ifinfo == NULL || if_index >= if_indexlim) {
+ size_t n;
+ caddr_t q;
+
+ while (if_index >= if_indexlim)
+ if_indexlim <<= 1;
+
+ /* grow nd_ifinfo */
+ n = if_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]
+ ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu;
+ ND.chlim = IPV6_DEFHLIM;
+ ND.basereachable = REACHABLE_TIME;
+ ND.reachable = ND_COMPUTE_RTIME(ND.basereachable);
+ ND.retrans = RETRANS_TIMER;
+ ND.receivedra = 0;
+ 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;
+{
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+ 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;
+#if defined(__FreeBSD__) || defined(__bsdi__)
+ case IFT_FDDI:
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+ ndi->maxmtu = MIN(FDDIMTU, ifp->if_mtu);
+#else
+ ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu);
+#endif
+ break;
+#endif
+#if !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
+ case IFT_ATM:
+ ndi->maxmtu = MIN(ATMMTU, 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\n");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_option\n");
+ if (!ndopts->nd_opts_search)
+ return NULL;
+ if (ndopts->nd_opts_done)
+ return NULL;
+
+ nd_opt = ndopts->nd_opts_search;
+
+ 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)) {
+ 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\n");
+ if (!ndopts->nd_opts_last)
+ panic("uninitialized ndopts in nd6_options\n");
+ 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.
+ */
+ 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]) {
+ printf("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.
+ */
+ log(LOG_INFO,
+ "nd6_options: unsupported option %d - "
+ "option ignored\n", nd_opt->nd_opt_type);
+ }
+
+skip1:
+ i++;
+ if (i > nd6_maxndopt) {
+ icmp6stat.icp6s_nd_toomanyopt++;
+ printf("too many loop in nd opt\n");
+ 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;
+ register struct llinfo_nd6 *ln;
+ register struct nd_defrouter *dr;
+ register struct nd_prefix *pr;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ timeout(nd6_timer, (caddr_t)0, nd6_prune * hz);
+
+ ln = llinfo_nd6.ln_next;
+ /* XXX BSD/OS separates this code -- itojun */
+ while (ln && ln != &llinfo_nd6) {
+ struct rtentry *rt;
+ struct ifnet *ifp;
+ struct sockaddr_in6 *dst;
+ struct llinfo_nd6 *next = ln->ln_next;
+
+ if ((rt = ln->ln_rt) == NULL) {
+ ln = next;
+ continue;
+ }
+ if ((ifp = rt->rt_ifp) == NULL) {
+ ln = next;
+ continue;
+ }
+ 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)\n", ln);
+ if (!dst)
+ panic("dst=0 in nd6_timer(ln=%p)\n", 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;
+ }
+ nd6_free(rt);
+ }
+ break;
+ case ND6_LLINFO_REACHABLE:
+ if (ln->ln_expire) {
+ ln->ln_state = ND6_LLINFO_STALE;
+ }
+ break;
+ /*
+ * ND6_LLINFO_STALE state requires nothing for timer
+ * routine.
+ */
+ case ND6_LLINFO_DELAY:
+ ln->ln_asked = 1;
+ ln->ln_state = ND6_LLINFO_PROBE;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr,
+ ln, 0);
+ 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 {
+ nd6_free(rt);
+ }
+ break;
+ case ND6_LLINFO_WAITDELETE:
+ nd6_free(rt);
+ break;
+ }
+ ln = next;
+ }
+
+ /* expire */
+ dr = nd_defrouter.lh_first;
+ while (dr) {
+ if (dr->expire && dr->expire < time_second) {
+ struct nd_defrouter *t;
+ t = dr->dr_next;
+ defrtrlist_del(dr);
+ dr = t;
+ } else
+ dr = dr->dr_next;
+ }
+ pr = nd_prefix.lh_first;
+ while (pr) {
+ struct in6_ifaddr *ia6;
+ struct in6_addrlifetime *lt6;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+
+ if (ia6) {
+ /* check address lifetime */
+ lt6 = &ia6->ia6_lifetime;
+ if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second)
+ ia6->ia6_flags |= IN6_IFF_DEPRECATED;
+ if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ /* xxx ND_OPT_PI_FLAG_ONLINK processing */
+ }
+ }
+
+ /*
+ * check prefix lifetime.
+ * since pltime is just for autoconf, pltime processing for
+ * prefix is not necessary.
+ *
+ * we offset expire time by NDPR_KEEP_EXPIRE, so that we
+ * can use the old prefix information to validate the
+ * next prefix information to come. See prelist_update()
+ * for actual validation.
+ */
+ if (pr->ndpr_expire
+ && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) {
+ struct nd_prefix *t;
+ t = pr->ndpr_next;
+
+ /*
+ * address expiration and prefix expiration are
+ * separate. NEVER perform in6_ifdel here.
+ */
+
+ prelist_remove(pr);
+ pr = t;
+ } else
+ pr = pr->ndpr_next;
+ }
+ splx(s);
+}
+
+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;
+ rt = rtalloc1((struct sockaddr *)&sin6, create
+#ifdef __FreeBSD__
+ , 0UL
+#endif /*__FreeBSD__*/
+ );
+ 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) {
+ /*
+ * 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 rtequest via ifa->ifa_rtrequest.
+ */
+ if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
+ ifa->ifa_addr,
+ (struct sockaddr *)&all1_sa,
+ (ifa->ifa_flags |
+ RTF_HOST | RTF_LLINFO) & ~RTF_CLONING,
+ &rt))
+ log(LOG_ERR,
+ "nd6_lookup: failed to add route for a "
+ "neighbor(%s)\n", ip6_sprintf(addr6));
+ 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.
+ * 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 ||
+ (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(0);
+ }
+ 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 in6_addr *addr;
+ struct ifnet *ifp;
+{
+ register 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. */
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ return(1);
+
+ /*
+ * If the address matches one of our addresses,
+ * it should be a neighbor.
+ */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#else
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+#endif
+ {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ next: continue;
+
+ for (i = 0; i < 4; i++) {
+ if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) &
+ 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, 0, ifp))
+ return(1);
+
+ return(0);
+#undef IFADDR6
+#undef IFMASK6
+}
+
+/*
+ * Free an nd6 llinfo entry.
+ */
+void
+nd6_free(rt)
+ struct rtentry *rt;
+{
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ struct sockaddr_dl *sdl;
+
+ if (ln->ln_router) {
+ /* remove from default router list */
+ struct nd_defrouter *dr;
+ struct in6_addr *in6;
+ int s;
+ in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->
+ sin6_addr,
+ rt->rt_ifp);
+ if (dr)
+ defrtrlist_del(dr);
+ else if (!ip6_forwarding && ip6_accept_rtadv) {
+ /*
+ * rt6_flush must be called in any case.
+ * see the comment in nd6_na_input().
+ */
+ rt6_flush(in6, rt->rt_ifp);
+ }
+ splx(s);
+ }
+
+ if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_alen = 0;
+ ln->ln_state = ND6_LLINFO_WAITDELETE;
+ ln->ln_asked = 0;
+ rt->rt_flags &= ~RTF_REJECT;
+ return;
+ }
+ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
+ 0, (struct rtentry **)0);
+}
+
+/*
+ * Upper-layer reachability hint for Neighbor Unreachability Detection.
+ *
+ * XXX cost-effective metods?
+ */
+void
+nd6_nud_hint(rt, dst6)
+ struct rtentry *rt;
+ struct in6_addr *dst6;
+{
+ struct llinfo_nd6 *ln;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ /*
+ * 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)
+ || (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_INCOMPLETE)
+ return;
+
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ if (ln->ln_expire)
+ ln->ln_expire = time_second +
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+}
+
+#ifdef OLDIP6OUTPUT
+/*
+ * Resolve an IP6 address into an ethernet address. If success,
+ * desten is filled in. If there is no entry in ndptab,
+ * set one up and multicast a solicitation for the IP6 address.
+ * Hold onto this mbuf and resend it once the address
+ * is finally resolved. A return value of 1 indicates
+ * that desten has been filled in and the packet should be sent
+ * normally; a 0 return indicates that the packet has been
+ * taken over here, either now or for later transmission.
+ */
+int
+nd6_resolve(ifp, rt, m, dst, desten)
+ struct ifnet *ifp;
+ struct rtentry *rt;
+ struct mbuf *m;
+ struct sockaddr *dst;
+ u_char *desten;
+{
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
+ struct sockaddr_dl *sdl;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ if (m->m_flags & M_MCAST) {
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
+ desten);
+ return(1);
+ break;
+ case IFT_ARCNET:
+ *desten = 0;
+ return(1);
+ break;
+ default:
+ return(0);
+ }
+ }
+ if (rt && (rt->rt_flags & RTF_LLINFO) != 0)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ else {
+ if ((rt = nd6_lookup(&(SIN6(dst)->sin6_addr), 1, ifp)) != NULL)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ }
+ if (!ln || !rt) {
+ log(LOG_DEBUG, "nd6_resolve: can't allocate llinfo for %s\n",
+ ip6_sprintf(&(SIN6(dst)->sin6_addr)));
+ m_freem(m);
+ return(0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ /*
+ * Ckeck the address family and length is valid, the address
+ * is resolved; otherwise, try to resolve.
+ */
+ if (ln->ln_state >= ND6_LLINFO_REACHABLE
+ && sdl->sdl_family == AF_LINK
+ && sdl->sdl_alen != 0) {
+ bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
+ if (ln->ln_state == ND6_LLINFO_STALE) {
+ ln->ln_asked = 0;
+ ln->ln_state = ND6_LLINFO_DELAY;
+ ln->ln_expire = time_second + nd6_delay;
+ }
+ return(1);
+ }
+ /*
+ * There is an ndp entry, but no ethernet address
+ * response yet. Replace the held mbuf with this
+ * latest one.
+ *
+ * XXX Does the code conform to rate-limiting rule?
+ * (RFC 2461 7.2.2)
+ */
+ if (ln->ln_state == ND6_LLINFO_WAITDELETE ||
+ ln->ln_state == ND6_LLINFO_NOSTATE)
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ ln->ln_hold = m;
+ if (ln->ln_expire) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (ln->ln_asked < nd6_mmaxtries &&
+ ln->ln_expire < time_second) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, NULL, &(SIN6(dst)->sin6_addr),
+ ln, 0);
+ }
+ }
+ return(0);
+}
+#endif /* OLDIP6OUTPUT */
+
+void
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+nd6_rtrequest(req, rt, info)
+ int req;
+ struct rtentry *rt;
+ struct rt_addrinfo *info; /* xxx unused */
+#else
+nd6_rtrequest(req, rt, sa)
+ int req;
+ struct rtentry *rt;
+ struct sockaddr *sa; /* xxx unused */
+#endif
+{
+ 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 !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+
+ switch (req) {
+ case RTM_ADD:
+ /*
+ * There is no backward compatibility :)
+ *
+ * if ((rt->rt_flags & RTF_HOST) == 0 &&
+ * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ * rt->rt_flags |= RTF_CLONING;
+ */
+ if (rt->rt_flags & RTF_CLONING || rt->rt_flags & 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) {
+ /* cludge for desktops */
+#if 0
+ printf("nd6_request: time.tv_sec is zero; "
+ "treat it as 1\n");
+#endif
+ ln->ln_expire = 1;
+ }
+#endif
+ if (rt->rt_flags & RTF_CLONING)
+ break;
+ }
+ /* Announce a new entry if requested. */
+ 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);
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ if (gate->sa_family != AF_LINK ||
+ gate->sa_len < sizeof(null_sdl)) {
+ log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n");
+ break;
+ }
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ if (ln != 0)
+ 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;
+ } 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;
+#if 0
+ insque(ln, &llinfo_nd6);
+#else
+ ln->ln_next = llinfo_nd6.ln_next;
+ llinfo_nd6.ln_next = ln;
+ ln->ln_prev = &llinfo_nd6;
+ ln->ln_next->ln_prev = ln;
+#endif
+
+ /*
+ * 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;
+ if (macp) {
+ Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen);
+ SDL(gate)->sdl_alen = ifp->if_addrlen;
+ }
+ if (nd6_useloopback) {
+#ifdef __bsdi__
+#if _BSDI_VERSION >= 199802
+ extern struct ifnet *loifp;
+ rt->rt_ifp = loifp; /*XXX*/
+#else
+ extern struct ifnet loif;
+ rt->rt_ifp = &loif; /*XXX*/
+#endif
+#else /* non-bsdi */
+ rt->rt_ifp = &loif[0]; /*XXX*/
+#endif
+ /*
+ * 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) {
+ rt->rt_ifa->ifa_refcnt--;
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ }
+ }
+ }
+ break;
+
+ case RTM_DELETE:
+ if (!ln)
+ break;
+ nd6_inuse--;
+#if 0
+ remque(ln);
+#else
+ ln->ln_next->ln_prev = ln->ln_prev;
+ ln->ln_prev->ln_next = ln->ln_next;
+ ln->ln_prev = NULL;
+#endif
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ Free((caddr_t)ln);
+ }
+}
+
+void
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+nd6_p2p_rtrequest(req, rt, info)
+ int req;
+ struct rtentry *rt;
+ struct rt_addrinfo *info; /* xxx unused */
+#else
+nd6_p2p_rtrequest(req, rt, sa)
+ int req;
+ struct rtentry *rt;
+ struct sockaddr *sa; /* xxx unused */
+#endif
+{
+ struct sockaddr *gate = rt->rt_gateway;
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
+ struct ifnet *ifp = rt->rt_ifp;
+ struct ifaddr *ifa;
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+
+ switch (req) {
+ case RTM_ADD:
+ /*
+ * There is no backward compatibility :)
+ *
+ * if ((rt->rt_flags & RTF_HOST) == 0 &&
+ * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ * rt->rt_flags |= RTF_CLONING;
+ */
+ if (rt->rt_flags & RTF_CLONING) {
+ /*
+ * Case 1: This route should come from
+ * a route to interface.
+ */
+ rt_setgate(rt, rt_key(rt),
+ (struct sockaddr *)&null_sdl);
+ gate = rt->rt_gateway;
+ SDL(gate)->sdl_type = ifp->if_type;
+ SDL(gate)->sdl_index = ifp->if_index;
+ break;
+ }
+ /* Announce a new entry if requested. */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ nd6_na_output(ifp,
+ &SIN6(rt_key(rt))->sin6_addr,
+ &SIN6(rt_key(rt))->sin6_addr,
+ ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
+ 1);
+ /* FALLTHROUGH */
+ case RTM_RESOLVE:
+ /*
+ * check if rt_key(rt) is one of my address assigned
+ * to the interface.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp,
+ &SIN6(rt_key(rt))->sin6_addr);
+ if (ifa) {
+ if (nd6_useloopback) {
+#ifdef __bsdi__
+#if _BSDI_VERSION >= 199802
+ extern struct ifnet *loifp;
+ rt->rt_ifp = loifp; /*XXX*/
+#else
+ extern struct ifnet loif;
+ rt->rt_ifp = &loif; /*XXX*/
+#endif
+#else
+ rt->rt_ifp = &loif[0]; /*XXX*/
+#endif /*__bsdi__*/
+ }
+ }
+ break;
+ }
+}
+
+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 nd_defrouter *dr, any;
+ struct nd_prefix *pr;
+ struct rtentry *rt;
+ int i = 0, error = 0;
+ int s;
+
+ switch (cmd) {
+ case SIOCGDRLST_IN6:
+ bzero(drl, sizeof(*drl));
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ dr = nd_defrouter.lh_first;
+ 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 = dr->dr_next;
+ }
+ splx(s);
+ break;
+ case SIOCGPRLST_IN6:
+ bzero(prl, sizeof(*prl));
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ pr = nd_prefix.lh_first;
+ while (pr && i < PRLSTSIZ) {
+ struct nd_pfxrouter *pfr;
+ int j;
+
+ prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
+ 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;
+
+ i++;
+ pr = pr->ndpr_next;
+ }
+ splx(s);
+ {
+ struct rr_prefix *rpp;
+
+ for (rpp = LIST_FIRST(&rr_prefix); rpp;
+ rpp = LIST_NEXT(rpp, rp_entry)) {
+ if (i >= PRLSTSIZ)
+ break;
+ prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr;
+ 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;
+ i++;
+ }
+ }
+
+ break;
+ case SIOCGIFINFO_IN6:
+ ndi->ndi = nd_ifinfo[ifp->if_index];
+ break;
+ case SIOCSNDFLUSH_IN6:
+ /* 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);
+ /* xxx sumikawa: flush prefix list */
+ break;
+ case SIOCSPFXFLUSH_IN6:
+ {
+ /* flush all the prefix advertised by routers */
+ struct nd_prefix *pr, *next;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ for (pr = nd_prefix.lh_first; pr; pr = next) {
+ next = pr->ndpr_next;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ prelist_remove(pr);
+ }
+ splx(s);
+ break;
+ }
+ case SIOCSRTRFLUSH_IN6:
+ {
+ /* flush all the default routers */
+ struct nd_defrouter *dr, *next;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ if ((dr = nd_defrouter.lh_first) != NULL) {
+ /*
+ * The first entry of the list may be stored in
+ * the routing table, so we'll delete it later.
+ */
+ for (dr = dr->dr_next; dr; dr = next) {
+ next = dr->dr_next;
+ defrtrlist_del(dr);
+ }
+ defrtrlist_del(nd_defrouter.lh_first);
+ }
+ 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);
+ }
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
+ error = EINVAL;
+ 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;
+ }
+ }
+ 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 !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ 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
+ is_newentry = 0;
+
+ if (!rt)
+ return NULL;
+ if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
+fail:
+ 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) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (ln->ln_hold) {
+#ifdef OLDIP6OUTPUT
+ (*ifp->if_output)(ifp, ln->ln_hold,
+ rt_key(rt), rt);
+#else
+ nd6_output(ifp, ln->ln_hold,
+ (struct sockaddr_in6 *)rt_key(rt),
+ rt);
+#endif
+ ln->ln_hold = 0;
+ }
+ } 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;
+ }
+
+ return rt;
+}
+
+static void
+nd6_slowtimo(ignored_arg)
+ void *ignored_arg;
+{
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+ register int i;
+ register struct nd_ifinfo *nd6if;
+
+ timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
+ for (i = 1; i < if_index + 1; i++) {
+ 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, m0, dst, rt0)
+ register struct ifnet *ifp;
+ struct mbuf *m0;
+ struct sockaddr_in6 *dst;
+ struct rtentry *rt0;
+{
+ register struct mbuf *m = m0;
+ register struct rtentry *rt = rt0;
+ struct llinfo_nd6 *ln = NULL;
+ int error = 0;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
+ goto sendpkt;
+
+ /*
+ * XXX: we currently do not make neighbor cache on any interface
+ * other than ARCnet, Ethernet and FDDI.
+ */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ break;
+ default:
+ goto sendpkt;
+ }
+
+ /*
+ * next hop determination. This routine is derived from ether_outpout.
+ */
+ if (rt) {
+ if ((rt->rt_flags & RTF_UP) == 0) {
+#ifdef __FreeBSD__
+ if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) !=
+ NULL)
+#else
+ if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1)) !=
+ NULL)
+#endif
+ {
+ rt->rt_refcnt--;
+ if (rt->rt_ifp != ifp)
+ return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */
+ } else
+ senderr(EHOSTUNREACH);
+ }
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_gwroute == 0)
+ goto lookup;
+ if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
+ rtfree(rt); rt = rt0;
+#ifdef __FreeBSD__
+ lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL);
+#else
+ lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
+#endif
+ if ((rt = rt->rt_gwroute) == 0)
+ senderr(EHOSTUNREACH);
+#ifdef __bsdi__
+ /* the "G" test below also prevents rt == rt0 */
+ if ((rt->rt_flags & RTF_GATEWAY) ||
+ (rt->rt_ifp != ifp)) {
+ rt->rt_refcnt--;
+ rt0->rt_gwroute = 0;
+ senderr(EHOSTUNREACH);
+ }
+#endif
+ }
+ }
+ if (rt->rt_flags & RTF_REJECT)
+ senderr(rt == rt0 ? EHOSTDOWN : 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 {
+ if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL)
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ }
+ if (!ln || !rt) {
+ log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s "
+ "(ln=%p, rt=%p)\n",
+ ip6_sprintf(&dst->sin6_addr), ln, rt);
+ senderr(EIO); /* XXX: good error? */
+ }
+
+
+ /*
+ * 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 reloved), 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.
+ *
+ * XXX Does the code conform to rate-limiting rule?
+ * (RFC 2461 7.2.2)
+ */
+ if (ln->ln_state == ND6_LLINFO_WAITDELETE ||
+ ln->ln_state == ND6_LLINFO_NOSTATE)
+ ln->ln_state = ND6_LLINFO_INCOMPLETE;
+ if (ln->ln_hold)
+ m_freem(ln->ln_hold);
+ ln->ln_hold = m;
+ if (ln->ln_expire) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (ln->ln_asked < nd6_mmaxtries &&
+ ln->ln_expire < time_second) {
+ ln->ln_asked++;
+ ln->ln_expire = time_second +
+ nd_ifinfo[ifp->if_index].retrans / 1000;
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
+ }
+ }
+ return(0);
+
+ sendpkt:
+ return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt));
+
+ bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+#undef senderr
+
+int
+nd6_storelladdr(ifp, rt, m, dst, desten)
+ struct ifnet *ifp;
+ struct rtentry *rt;
+ struct mbuf *m;
+ struct sockaddr *dst;
+ u_char *desten;
+{
+ struct sockaddr_dl *sdl;
+
+ if (m->m_flags & M_MCAST) {
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
+ desten);
+ return(1);
+ break;
+ case IFT_ARCNET:
+ *desten = 0;
+ return(1);
+ default:
+ return(0);
+ }
+ }
+
+ if (rt == NULL ||
+ rt->rt_gateway->sa_family != AF_LINK) {
+ printf("nd6_storelladdr: something odd happens\n");
+ return(0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ if (sdl->sdl_alen != 0)
+ bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
+
+ return(1);
+}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
new file mode 100644
index 00000000000..d61d9c8af6b
--- /dev/null
+++ b/sys/netinet6/nd6.h
@@ -0,0 +1,307 @@
+/* $OpenBSD: nd6.h,v 1.1 1999/12/08 06:50:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_ND6_H_
+#define _NETINET6_ND6_H_
+
+#include <sys/queue.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 */
+};
+
+#define ND6_LLINFO_NOSTATE -2
+#define ND6_LLINFO_WAITDELETE -1
+#define ND6_LLINFO_INCOMPLETE 0
+#define ND6_LLINFO_REACHABLE 1
+#define ND6_LLINFO_STALE 2
+#define ND6_LLINFO_DELAY 3
+#define ND6_LLINFO_PROBE 4
+
+struct nd_ifinfo {
+ u_int32_t linkmtu; /* LinkMTU */
+ u_int32_t maxmtu; /* Upper bound of LinkMTU */
+ u_int32_t basereachable; /* BaseReachableTime */
+ u_int32_t reachable; /* Reachable Time */
+ u_int32_t retrans; /* Retrans Timer */
+ int recalctm; /* BaseReacable re-calculation timer */
+ u_int8_t chlim; /* CurHopLimit */
+ u_int8_t receivedra;
+};
+
+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_prlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_long vltime;
+ u_long pltime;
+ u_long expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } prefix[PRLSTSIZ];
+};
+
+struct in6_ndireq {
+ char ifname[IFNAMSIZ];
+ struct nd_ifinfo ndi;
+};
+
+/* 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 ND_COMPUTE_RTIME(x) \
+ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \
+ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000)
+
+struct nd_defrouter {
+ LIST_ENTRY(nd_defrouter) dr_entry;
+#define dr_next dr_entry.le_next
+ struct in6_addr rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ 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;
+ /* list of routers that advertise the prefix: */
+ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs;
+ u_char ndpr_plen;
+ struct ndpr_stateflags {
+ /* if this prefix can be regarded as on-link */
+ u_char onlink : 1;
+ } ndpr_stateflags;
+};
+
+#define ndpr_next ndpr_entry.le_next
+
+#define ndpr_raf ndpr_flags
+#define ndpr_raf_onlink ndpr_flags.onlink
+#define ndpr_raf_auto ndpr_flags.autonomous
+
+#define ndpr_statef_onlink ndpr_stateflags.onlink
+#define ndpr_statef_addmark ndpr_stateflags.addmark
+
+/*
+ * 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 compatability */
+ 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_drhead, nd_defrouter);
+LIST_HEAD(nd_prhead, nd_prefix);
+
+/* nd6.c */
+extern int nd6_prune;
+extern int nd6_delay;
+extern int nd6_umaxtries;
+extern int nd6_mmaxtries;
+extern int nd6_useloopback;
+extern int nd6_proxyall;
+extern struct llinfo_nd6 llinfo_nd6;
+extern struct nd_ifinfo *nd_ifinfo;
+extern struct nd_drhead nd_defrouter;
+extern struct nd_prhead nd_prefix;
+
+union nd_opts {
+ struct nd_opt_hdr *nd_opt_array[9];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ struct nd_opt_hdr *search; /* multiple opts */
+ struct nd_opt_hdr *last; /* multiple opts */
+ int done;
+ struct nd_opt_prefix_info *pi_end;/* multiple opts, end */
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi_beg
+#define nd_opts_pi_end nd_opt_each.pi_end
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_search nd_opt_each.search
+#define nd_opts_last nd_opt_each.last
+#define nd_opts_done nd_opt_each.done
+
+/* XXX: need nd6_var.h?? */
+/* nd6.c */
+void nd6_init __P((void));
+void nd6_ifattach __P((struct ifnet *));
+int nd6_is_addr_neighbor __P((struct in6_addr *, struct ifnet *));
+void nd6_option_init __P((void *, int, union nd_opts *));
+struct nd_opt_hdr *nd6_option __P((union nd_opts *));
+int nd6_options __P((union nd_opts *));
+struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *));
+void nd6_setmtu __P((struct ifnet *));
+void nd6_timer __P((void *));
+void nd6_free __P((struct rtentry *));
+void nd6_nud_hint __P((struct rtentry *, struct in6_addr *));
+int nd6_resolve __P((struct ifnet *, struct rtentry *,
+ struct mbuf *, struct sockaddr *, u_char *));
+#if defined(__bsdi__) && _BSDI_VERSION >= 199802
+void nd6_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *));
+void nd6_p2p_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *));
+#else
+void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+#endif
+int nd6_ioctl __P((u_long, caddr_t, struct ifnet *));
+struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *,
+ char *, int, int, int));
+/* for test */
+int nd6_output __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *,
+ struct rtentry *));
+int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *,
+ struct sockaddr *, u_char *));
+
+/* nd6_nbr.c */
+void nd6_na_input __P((struct mbuf *, int, int));
+void nd6_na_output __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, u_long, int));
+void nd6_ns_input __P((struct mbuf *, int, int));
+void nd6_ns_output __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, struct llinfo_nd6 *, int));
+caddr_t nd6_ifptomac __P((struct ifnet *));
+void nd6_dad_start __P((struct ifaddr *, int *));
+void nd6_dad_duplicated __P((struct ifaddr *));
+
+/* nd6_rtr.c */
+void nd6_rs_input __P((struct mbuf *, int, int));
+void nd6_ra_input __P((struct mbuf *, int, int));
+void prelist_del __P((struct nd_prefix *));
+void defrouter_addreq __P((struct nd_defrouter *));
+void defrouter_delreq __P((struct nd_defrouter *, int));
+void defrtrlist_del __P((struct nd_defrouter *));
+void prelist_remove __P((struct nd_prefix *));
+int prelist_update __P((struct nd_prefix *, struct nd_defrouter *,
+ struct mbuf *));
+struct nd_defrouter *defrouter_lookup __P((struct in6_addr *,
+ struct ifnet *));
+int in6_ifdel __P((struct ifnet *, struct in6_addr *));
+int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr));
+void rt6_flush __P((struct in6_addr *, struct ifnet *));
+
+#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 00000000000..274a7d63c1c
--- /dev/null
+++ b/sys/netinet6/nd6_nbr.c
@@ -0,0 +1,1261 @@
+/* $OpenBSD: nd6_nbr.c,v 1.1 1999/12/08 06:50:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
+#include "opt_inet.h"
+#ifdef __NetBSD__ /*XXX*/
+#include "opt_ipsec.h"
+#endif
+#endif
+
+#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>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#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 <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/icmp6.h>
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+#if 0
+extern struct timeval time;
+#endif
+
+struct dadq;
+static struct dadq *nd6_dad_find __P((struct ifaddr *));
+static void nd6_dad_timer __P((struct ifaddr *));
+static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *));
+static void nd6_dad_ns_input __P((struct ifaddr *));
+static void nd6_dad_na_input __P((struct ifaddr *));
+
+static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/
+static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
+
+/*
+ * Input an Neighbor Solicitation Message.
+ *
+ * Based on RFC 2461
+ * Based on RFC 2462 (duplicated address detection)
+ *
+ * XXX proxy advertisement
+ */
+void
+nd6_ns_input(m, off, icmp6len)
+ 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 nd_neighbor_solicit *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6 = nd_ns->nd_ns_target;
+ struct in6_addr myaddr6;
+ char *lladdr = NULL;
+ struct ifaddr *ifa;
+ int lladdrlen = 0;
+ int anycast = 0, proxy = 0, tentative = 0;
+ int tlladdr;
+ union nd_opts ndopts;
+
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ 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 {
+ log(LOG_INFO, "nd6_ns_input: bad DAD packet "
+ "(wrong ip6 dst)\n");
+ goto bad;
+ }
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ log(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) {
+ log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n");
+ goto bad;
+ }
+
+ 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) {
+ log(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 && nd6_proxyall) {
+ 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
+#ifdef __FreeBSD__
+ , 0
+#endif /* __FreeBSD__ */
+ );
+ if (rt && rt->rt_ifp != ifp) {
+ /*
+ * search link local addr for ifp, and use it for
+ * proxy NA.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa)
+ proxy = 1;
+ }
+ rtfree(rt);
+ }
+ if (!ifa) {
+ /*
+ * We've got a NS packet, and we don't have that adddress
+ * assigned for us. We MUST silently ignore it.
+ * See RFC2461 7.2.3.
+ */
+ return;
+ }
+ myaddr6 = *IFA_IN6(ifa);
+ anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST;
+ tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE;
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED)
+ return;
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_ns_input: lladdrlen mismatch for %s "
+ "(if %d, NS packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
+ log(LOG_INFO,
+ "nd6_ns_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&saddr6));
+ return;
+ }
+
+ /*
+ * 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);
+
+ return;
+ }
+
+ /*
+ * 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);
+ return;
+ }
+
+ 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);
+ return;
+
+ bad:
+ log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6));
+ log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6));
+ log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6));
+ return;
+}
+
+/*
+ * Output an 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;
+ 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;
+ caddr_t mac;
+ struct ifnet *outif = NULL;
+
+ if (IN6_IS_ADDR_MULTICAST(taddr6))
+ return;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+
+ 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;
+ MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enought. but just in case */
+
+ /* fill neighbor solicitation packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ 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); /*XXX*/
+ 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);
+
+#ifdef IPSEC
+#ifndef __OpenBSD__ /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif);
+ 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)
+ */
+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
+ = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);
+#if 0
+ struct in6_addr saddr6 = ip6->ip6_src;
+#endif
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ struct in6_addr taddr6 = nd_na->nd_na_target;
+ int flags = nd_na->nd_na_flags_reserved;
+ int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);
+ int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);
+ int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);
+ 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) {
+ log(LOG_ERR,
+ "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))
+ taddr6.s6_addr16[1] = htons(ifp->if_index);
+
+ if (IN6_IS_ADDR_MULTICAST(&taddr6)) {
+ log(LOG_ERR,
+ "nd6_na_input: invalid target address %s\n",
+ ip6_sprintf(&taddr6));
+ return;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&daddr6))
+ if (is_solicited) {
+ log(LOG_ERR,
+ "nd6_na_input: a solicited adv is multicasted\n");
+ return;
+ }
+
+ icmp6len -= sizeof(*nd_na);
+ nd6_option_init(nd_na + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ 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);
+ return;
+ }
+
+ /* Just for safety, maybe unnecessery. */
+ if (ifa) {
+ log(LOG_ERR,
+ "nd6_na_input: duplicate IP6 address %s\n",
+ ip6_sprintf(&taddr6));
+ return;
+ }
+
+ if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
+ log(LOG_INFO,
+ "nd6_na_input: lladdrlen mismatch for %s "
+ "(if %d, NA packet %d)\n",
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ /*
+ * 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))
+ return;
+
+ 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)
+ return;
+
+ /*
+ * 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;
+ if (ln->ln_expire)
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ ln->ln_expire = time.tv_sec +
+#else
+ ln->ln_expire = time_second +
+#endif
+ nd_ifinfo[rt->rt_ifp->if_index].reachable;
+ } else
+ ln->ln_state = ND6_LLINFO_STALE;
+ ln->ln_router = is_router;
+ } 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;
+ return;
+ } 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;
+ if (ln->ln_expire) {
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ ln->ln_expire = time.tv_sec +
+#else
+ ln->ln_expire = time_second +
+#endif
+ nd_ifinfo[ifp->if_index].reachable;
+ }
+ } else {
+ if (lladdr && llchange)
+ ln->ln_state = ND6_LLINFO_STALE;
+ }
+ }
+
+ 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;
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ 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) {
+#ifdef OLDIP6OUTPUT
+ (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt);
+#else
+ nd6_output(ifp, ln->ln_hold,
+ (struct sockaddr_in6 *)rt_key(rt), rt);
+#endif
+ ln->ln_hold = 0;
+ }
+}
+
+/*
+ * Neighbor advertisement output handling.
+ *
+ * Based on RFC 2461
+ *
+ * XXX NA delay for anycast address is not implemented yet
+ * (RFC 2461 7.2.7)
+ * XXX proxy advertisement?
+ */
+void
+nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr)
+ struct ifnet *ifp;
+ struct in6_addr *daddr6, *taddr6;
+ u_long flags;
+ int tlladdr; /* 1 if include target link-layer address */
+{
+ struct mbuf *m;
+ struct ip6_hdr *ip6;
+ struct nd_neighbor_advert *nd_na;
+ struct in6_ifaddr *ia = NULL;
+ struct ip6_moptions im6o;
+ int icmp6len;
+ caddr_t mac;
+ struct ifnet *outif = NULL;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+
+ 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;
+ MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enough. but just in case */
+
+ /* fill neighbor advertisement packet */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ 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 && (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_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);
+
+#ifdef IPSEC
+#ifndef __OpenBSD__ /*KAME IPSEC*/
+ m->m_pkthdr.rcvif = NULL;
+#endif
+#endif /*IPSEC*/
+ ip6_output(m, NULL, NULL, 0, &im6o, &outif);
+ 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:
+#ifdef __NetBSD__
+ return LLADDR(ifp->if_sadl);
+#else
+ return ((caddr_t)(ifp + 1));
+#endif
+ 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;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ struct callout_handle dad_timer;
+#endif
+};
+
+static struct dadq_head dadq;
+
+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;
+}
+
+/*
+ * 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;
+ static int dad_init = 0;
+
+ 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)) {
+ printf("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) {
+ printf("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));
+ TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
+
+ /* XXXJRT This is probably a purely debugging message. */
+ printf("%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;
+ ifa->ifa_refcnt++; /*just for safety*/
+ dp->dad_count = ip6_dad_count;
+ dp->dad_ns_icount = dp->dad_na_icount = 0;
+ dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
+ if (!tick) {
+ nd6_dad_ns_output(dp, ifa);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ dp->dad_timer =
+#endif
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ 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;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ dp->dad_timer =
+#endif
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ ntick);
+ }
+}
+
+static void
+nd6_dad_timer(ifa)
+ struct ifaddr *ifa;
+{
+ int s;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct dadq *dp;
+
+#ifdef __NetBSD__
+ s = splsoftnet(); /*XXX*/
+#else
+ s = splnet(); /*XXX*/
+#endif
+
+ /* Sanity check */
+ if (ia == NULL) {
+ printf("nd6_dad_timer: called with null parameter\n");
+ goto done;
+ }
+ dp = nd6_dad_find(ifa);
+ if (dp == NULL) {
+ printf("nd6_dad_timer: DAD structure not found\n");
+ goto done;
+ }
+ if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
+ printf("nd6_dad_timer: called with duplicated address "
+ "%s(%s)\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
+ goto done;
+ }
+ if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
+ printf("nd6_dad_timer: called with non-tentative address "
+ "%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) {
+ printf("%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;
+ ifa->ifa_refcnt--;
+ 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);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ dp->dad_timer =
+#endif
+ timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa,
+ 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;
+
+ /* XXXJRT This is probably a purely debugging message */
+ printf("%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;
+ ifa->ifa_refcnt--;
+ }
+ }
+
+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) {
+ printf("nd6_dad_duplicated: DAD structure not found\n");
+ return;
+ }
+
+ log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, "
+ "%d NA\n", if_name(ifa->ifa_ifp),
+ ip6_sprintf(&ia->ia_addr.sin6_addr),
+ dp->dad_ns_icount, dp->dad_na_icount);
+
+ ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ ia->ia6_flags |= IN6_IFF_DUPLICATED;
+
+ /* We are done with DAD, with duplicated address found. (failure) */
+ untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ , dp->dad_timer
+#endif
+ );
+
+ printf("%s: DAD complete for %s - duplicate found\n",
+ if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
+ printf("%s: manual intervention required\n", if_name(ifa->ifa_ifp));
+
+ TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
+ free(dp, M_IP6NDP);
+ dp = NULL;
+ ifa->ifa_refcnt--;
+}
+
+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;
+ 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);
+
+ /*
+ * If it is from myself, ignore this.
+ */
+ if (ifp && (ifp->if_flags & IFF_LOOPBACK))
+ return;
+
+ /* Quickhack - completely ignore DAD NS packets */
+ if (dad_ignore_ns) {
+ log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for "
+ "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 00000000000..b71b16b7eae
--- /dev/null
+++ b/sys/netinet6/nd6_rtr.c
@@ -0,0 +1,1444 @@
+/* $OpenBSD: nd6_rtr.c,v 1.1 1999/12/08 06:50:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#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>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#include <sys/syslog.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/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/icmp6.h>
+
+#include <net/net_osdep.h>
+
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
+static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *));
+static struct nd_prefix *prefix_lookup __P((struct nd_prefix *));
+static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, int));
+static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
+ struct nd_defrouter *));
+static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
+static void pfxrtr_del __P((struct nd_pfxrouter *));
+static void pfxlist_onlink_check __P((void));
+static void nd6_detach_prefix __P((struct nd_prefix *));
+static void nd6_attach_prefix __P((struct nd_prefix *));
+
+static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
+ struct in6_addrlifetime *lt6,
+ int update_vltime));
+
+static int rt6_deleteroute __P((struct radix_node *, void *));
+
+#if 0
+extern struct timeval time;
+#endif
+extern int nd6_recalc_reachtm_interval;
+
+#if 0
+static u_char bmask [] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
+};
+#endif
+
+/*
+ * 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 nd_router_solicit *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+#endif
+ char *lladdr = NULL;
+ int lladdrlen = 0;
+#if 0
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL;
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
+ struct rtentry *rt = NULL;
+ int is_newentry;
+#endif
+ union nd_opts ndopts;
+
+ /* If I'm not a router, ignore it. */
+ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
+ return;
+
+ /* Sanity checks */
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ /*
+ * 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))
+ return;
+
+ icmp6len -= sizeof(*nd_rs);
+ nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ 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) {
+ log(LOG_INFO,
+ "nd6_rs_input: lladdrlen mismatch for %s "
+ "(if %d, RS packet %d)\n",
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
+}
+
+/*
+ * 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 nd_router_advert *)((caddr_t)ip6 + off);
+ struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ int flags = nd_ra->nd_ra_flags_reserved;
+ int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
+ int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
+#endif
+ union nd_opts ndopts;
+ struct nd_defrouter *dr;
+
+ if (ip6_accept_rtadv == 0)
+ return;
+
+ if (ip6->ip6_hlim != 255) {
+ log(LOG_ERR,
+ "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim);
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
+ log(LOG_ERR,
+ "nd6_ra_input: src %s is not link-local\n",
+ ip6_sprintf(&saddr6));
+ return;
+ }
+
+ icmp6len -= sizeof(*nd_ra);
+ nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
+ if (nd6_options(&ndopts) < 0) {
+ log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n");
+ return;
+ }
+
+ {
+ struct nd_defrouter dr0;
+ u_int32_t advreachable = nd_ra->nd_ra_reachable;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ 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;
+ /* unspecified or not? (RFC 2461 6.3.4) */
+ if (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;
+ 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) {
+ log(LOG_INFO, "nd6_ra_input: invalid option "
+ "len %d for prefix information option, "
+ "ignored\n", pi->nd_opt_pi_len);
+ continue;
+ }
+
+ if (128 < pi->nd_opt_pi_prefix_len) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefix "
+ "len %d for prefix information option, "
+ "ignored\n", pi->nd_opt_pi_prefix_len);
+ continue;
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
+ || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefix "
+ "%s, ignored\n",
+ ip6_sprintf(&pi->nd_opt_pi_prefix));
+ continue;
+ }
+
+ /* aggregatable unicast address, rfc2374 */
+ if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20
+ && pi->nd_opt_pi_prefix_len != 64) {
+ log(LOG_INFO, "nd6_ra_input: invalid prefixlen "
+ "%d for rfc2374 prefix %s, ignored\n",
+ pi->nd_opt_pi_prefix_len,
+ ip6_sprintf(&pi->nd_opt_pi_prefix));
+ 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) {
+ log(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 {
+ log(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 {
+ log(LOG_INFO, "nd6_ra_input: mtu option "
+ "mtu=%d sent from %s; maxmtu unknown, "
+ "ignoring\n",
+ mtu, ip6_sprintf(&ip6->ip6_src));
+ }
+ }
+
+ skip:
+
+ /*
+ * Src linkaddress
+ */
+ {
+ 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) {
+ log(LOG_INFO,
+ "nd6_ra_input: lladdrlen mismatch for %s "
+ "(if %d, RA packet %d)\n",
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2);
+ }
+
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
+ }
+}
+
+/*
+ * default router list proccessing sub routines
+ */
+void
+defrouter_addreq(new)
+ struct nd_defrouter *new;
+{
+ struct sockaddr_in6 def, mask, gate;
+ int s;
+#if 0
+ register struct radix_node *rn;
+ register struct radix_node_head *rnh;
+ struct sockaddr *ndst;
+ struct ifnet *ifp = new->ifp;
+ struct ifaddr *ifa;
+ struct rtentry *rt;
+ extern struct pool rtentry_pool;
+#endif
+
+ 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;
+
+#if 1
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ (void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate, (struct sockaddr *)&mask,
+ RTF_GATEWAY, NULL);
+ splx(s);
+ return;
+#else
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (!ifa)
+ return;
+ if ((rnh = rt_tables[AF_INET6]) == 0)
+ return;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+#ifdef __NetBSD__
+ rt = pool_get(&rtentry_pool, PR_NOWAIT);
+#else
+ R_Malloc(rt, struct rtentry *, sizeof(*rt));
+#endif
+ if (!rt)
+ goto bad;
+ Bzero(rt, sizeof(*rt));
+ rt->rt_flags = RTF_UP | RTF_GATEWAY;
+ if (rt_setgate(rt, (struct sockaddr *)&def, (struct sockaddr *)&gate)){
+ Free(rt);
+ goto bad;
+ }
+ ndst = rt_key(rt);
+ Bcopy(&def, ndst, sizeof(def));
+ rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)&mask,
+ rnh, rt->rt_nodes);
+ if (rn == 0) {
+ Free(rt_key(rt));
+ Free(rt);
+ goto bad;
+ }
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ rt->rt_ifp = ifp;
+ rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu;
+ /* xxx
+ * many codes should be stolen from route.c
+ */
+bad:
+ splx(s);
+ return;
+#endif
+}
+
+struct nd_defrouter *
+defrouter_lookup(addr, ifp)
+ struct in6_addr *addr;
+ struct ifnet *ifp;
+{
+ struct nd_defrouter *dr;
+
+ for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next)
+ 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;
+
+ 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, (struct rtentry **)0);
+
+ if (dofree)
+ free(dr, M_IP6NDP);
+
+ if (nd_defrouter.lh_first)
+ defrouter_addreq(nd_defrouter.lh_first);
+
+ /*
+ * xxx update the Destination Cache entries for all
+ * destinations using that neighbor as a router (7.2.5)
+ */
+}
+
+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 == nd_defrouter.lh_first)
+ deldr = dr; /* The router is primary. */
+
+ LIST_REMOVE(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, delete the default route
+ * entry in the routing table.
+ */
+ if (deldr)
+ defrouter_delreq(deldr, 0);
+ free(dr, M_IP6NDP);
+}
+
+static struct nd_defrouter *
+defrtrlist_update(new)
+ struct nd_defrouter *new;
+{
+ struct nd_defrouter *dr, *n;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+
+ 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;
+ if (nd_defrouter.lh_first == NULL) {
+ LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
+ defrouter_addreq(n);
+ } else {
+ LIST_INSERT_AFTER(nd_defrouter.lh_first, n, dr_entry);
+ defrouter_addreq(n);
+ }
+ 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);
+}
+
+static struct nd_prefix *
+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);
+}
+
+static int
+prelist_add(pr, dr)
+ struct nd_prefix *pr;
+ struct nd_defrouter *dr;
+{
+ struct nd_prefix *new;
+ 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;
+
+ /* initilization */
+ new->ndpr_statef_onlink = pr->ndpr_statef_onlink;
+ LIST_INIT(&new->ndpr_advrtrs);
+ in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
+ /* make prefix in the canonical form */
+ for (i = 0; i < 4; i++)
+ new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ new->ndpr_mask.s6_addr32[i];
+
+ /* xxx ND_OPT_PI_FLAG_ONLINK processing */
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ /* link ndpr_entry to nd_prefix list */
+ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
+ splx(s);
+
+ if (dr)
+ pfxrtr_add(new, dr);
+
+ return 0;
+}
+
+void
+prelist_remove(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfr, *next;
+ int s;
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
+ s = splnet();
+#endif
+ /* unlink ndpr_entry from nd_prefix list */
+ LIST_REMOVE(pr, ndpr_entry);
+ splx(s);
+
+ /* free list of routers that adversed the prefix */
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
+ next = pfr->pfr_next;
+
+ free(pfr, M_IP6NDP);
+ }
+ free(pr, M_IP6NDP);
+
+ pfxlist_onlink_check();
+}
+
+/*
+ * NOTE: We set address lifetime to keep
+ * address lifetime <= prefix lifetime
+ * invariant. This is to simplify on-link determination code.
+ * If onlink determination is udated, this routine may have to be updated too.
+ */
+int
+prelist_update(new, dr, m)
+ struct nd_prefix *new;
+ struct nd_defrouter *dr; /* may be NULL */
+ struct mbuf *m;
+{
+ struct in6_ifaddr *ia6 = NULL;
+ struct nd_prefix *pr;
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+ int error = 0;
+ int auth;
+ struct in6_addrlifetime *lt6;
+
+ 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 = prefix_lookup(new)) != NULL) {
+ if (pr->ndpr_ifp != new->ndpr_ifp) {
+ error = EADDRNOTAVAIL;
+ goto end;
+ }
+ /* update prefix information */
+ pr->ndpr_flags = new->ndpr_flags;
+ pr->ndpr_vltime = new->ndpr_vltime;
+ pr->ndpr_pltime = new->ndpr_pltime;
+ pr->ndpr_preferred = new->ndpr_preferred;
+ pr->ndpr_expire = new->ndpr_expire;
+
+ /*
+ * RFC 2462 5.5.3 (d) or (e)
+ * We got a prefix which we have seen in the past.
+ */
+ if (!new->ndpr_raf_auto)
+ goto noautoconf1;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+
+ if (ia6 == NULL) {
+ /*
+ * Special case:
+ * (1) We have seen the prefix advertised before, but
+ * we have never performed autoconfig for this prefix.
+ * This is because Autonomous bit was 0 previously, or
+ * autoconfig failed due to some other reasons.
+ * (2) We have seen the prefix advertised before and
+ * we have performed autoconfig in the past, but
+ * we seem to have no interface address right now.
+ * This is because the interface address have expired.
+ *
+ * This prefix is fresh, with respect to autoconfig
+ * process.
+ *
+ * Add an address based on RFC 2462 5.5.3 (d).
+ */
+ ia6 = in6_ifadd(pr->ndpr_ifp,
+ &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr,
+ new->ndpr_plen);
+ if (!ia6) {
+ error = EADDRNOTAVAIL;
+ log(LOG_ERR, "prelist_update: failed to add a "
+ "new address\n");
+ goto noautoconf1;
+ }
+
+ lt6 = &ia6->ia6_lifetime;
+
+ /* address lifetime <= prefix lifetime */
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ in6_init_address_ltimes(new, lt6, 1);
+ } else {
+#define TWOHOUR (120*60)
+ /*
+ * We have seen the prefix before, and we have added
+ * interface address in the past. We still have
+ * the interface address assigned.
+ *
+ * update address lifetime based on RFC 2462
+ * 5.5.3 (e).
+ */
+ int update = 0;
+
+ lt6 = &ia6->ia6_lifetime;
+
+#if 0 /* RFC 2462 5.5.3 (e) */
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ if (TWOHOUR < new->ndpr_vltime
+ || lt6pr->nd < new->ndpr_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else if (auth
+ && lt6->ia6t_vltime <= TWOHOUR0
+ && new->ndpr_vltime <= lt6->ia6t_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else {
+ lt6->ia6t_vltime = TWOHOUR;
+ update++;
+ }
+
+ /* 2 hour rule is not imposed for pref lifetime */
+ new->ndpr_apltime = new->ndpr_pltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+#else /* update from Jim Bound, (ipng 6712) */
+ if (TWOHOUR < new->ndpr_vltime
+ || lt6->ia6t_vltime < new->ndpr_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else if (auth) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ }
+
+ /* jim bound rule is not imposed for pref lifetime */
+ lt6->ia6t_pltime = new->ndpr_pltime;
+#endif
+ in6_init_address_ltimes(new, lt6, update);
+ }
+
+ noautoconf1:
+
+#if 0
+ /* address lifetime expire processing, RFC 2462 5.5.4. */
+ if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) {
+ struct in6_ifaddr *ia6;
+
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6)
+ ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
+ }
+#endif
+
+ if (dr && pfxrtr_lookup(pr, dr) == NULL)
+ pfxrtr_add(pr, dr);
+ } else {
+ int error_tmp;
+
+ if (new->ndpr_vltime == 0) goto end;
+
+ bzero(&new->ndpr_addr, sizeof(struct in6_addr));
+
+ /*
+ * RFC 2462 5.5.3 (d)
+ * We got a fresh prefix. Perform some sanity checks
+ * and add an interface address by appending interface ID
+ * to the advertised prefix.
+ */
+ if (!new->ndpr_raf_auto)
+ goto noautoconf2;
+
+ ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr,
+ &new->ndpr_addr, new->ndpr_plen);
+ if (!ia6) {
+ error = EADDRNOTAVAIL;
+ log(LOG_ERR, "prelist_update: "
+ "failed to add a new address\n");
+ goto noautoconf2;
+ }
+ /* set onlink bit if an interface route is configured */
+ new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0;
+
+ lt6 = &ia6->ia6_lifetime;
+
+ /* address lifetime <= prefix lifetime */
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ in6_init_address_ltimes(new, lt6, 1);
+
+ noautoconf2:
+ error_tmp = prelist_add(new, dr);
+ error = error_tmp ? error_tmp : error;
+ }
+
+ end:
+ splx(s);
+ return error;
+}
+
+/*
+ * Check if each prefix in the prefix list has at least one available router
+ * that advertised the prefix.
+ * If the check fails, the prefix may be off-link because, for example,
+ * we have moved from the network but the lifetime of the prefix has not
+ * been expired yet. So we should not use the prefix if there is another
+ * prefix that has an available router.
+ * But if there is no prefix that has an availble router, we still regards
+ * all the prefixes as on-link. This is because we can't tell if all the
+ * routers are simply dead or if we really moved from the network and there
+ * is no router around us.
+ */
+static void
+pfxlist_onlink_check()
+{
+ struct nd_prefix *pr;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
+ if (pr->ndpr_advrtrs.lh_first) /* pr has an available router */
+ break;
+
+ if (pr) {
+ /*
+ * There is at least one prefix that has a router. First,
+ * detach prefixes which has no advertising router and then
+ * attach other prefixes. The order is important since an
+ * attached prefix and a detached prefix may have a same
+ * interface route.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (pr->ndpr_advrtrs.lh_first == NULL &&
+ pr->ndpr_statef_onlink) {
+ pr->ndpr_statef_onlink = 0;
+ nd6_detach_prefix(pr);
+ }
+ }
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (pr->ndpr_advrtrs.lh_first &&
+ pr->ndpr_statef_onlink == 0)
+ nd6_attach_prefix(pr);
+ }
+ }
+ else {
+ /* there is no prefix that has a router */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
+ if (pr->ndpr_statef_onlink == 0)
+ nd6_attach_prefix(pr);
+ }
+}
+
+static void
+nd6_detach_prefix(pr)
+ struct nd_prefix *pr;
+{
+ struct in6_ifaddr *ia6;
+ struct sockaddr_in6 sa6, mask6;
+
+ /*
+ * Delete the interface route associated with the prefix.
+ */
+ 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));
+ {
+ int e;
+
+ e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
+ (struct sockaddr *)&mask6, 0, NULL);
+ if (e) {
+ log(LOG_ERR,
+ "nd6_detach_prefix: failed to delete route: "
+ "%s/%d (errno = %d)\n",
+ ip6_sprintf(&sa6.sin6_addr),
+ pr->ndpr_plen,
+ e);
+ }
+ }
+
+ /*
+ * Mark the address derived from the prefix detached so that
+ * it won't be used as a source address for a new connection.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6)
+ ia6->ia6_flags |= IN6_IFF_DETACHED;
+}
+
+static void
+nd6_attach_prefix(pr)
+ struct nd_prefix *pr;
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia6;
+
+ /*
+ * Add the interface route associated with the prefix(if necessary)
+ * Should we consider if the L bit is set in pr->ndpr_flags?
+ */
+ ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix,
+ pr->ndpr_ifp);
+ if (ifa == NULL) {
+ log(LOG_ERR,
+ "nd6_attach_prefix: failed to find any ifaddr"
+ " to add route for a prefix(%s/%d)\n",
+ ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen);
+ }
+ else {
+ int e;
+ struct sockaddr_in6 mask6;
+
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_family = AF_INET6;
+ mask6.sin6_len = sizeof(mask6);
+ mask6.sin6_addr = pr->ndpr_mask;
+ e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
+ ifa->ifa_addr, (struct sockaddr *)&mask6,
+ ifa->ifa_flags, NULL);
+ if (e == 0)
+ pr->ndpr_statef_onlink = 1;
+ else {
+ log(LOG_ERR,
+ "nd6_attach_prefix: failed to add route for"
+ " a prefix(%s/%d), errno = %d\n",
+ ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e);
+ }
+ }
+
+ /*
+ * Now the address derived from the prefix can be used as a source
+ * for a new connection, so clear the detached flag.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
+ ia6 = NULL;
+ else
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6) {
+ ia6->ia6_flags &= ~IN6_IFF_DETACHED;
+ if (pr->ndpr_statef_onlink)
+ ia6->ia_flags |= IFA_ROUTE;
+ }
+}
+
+static struct in6_ifaddr *
+in6_ifadd(ifp, in6, addr, prefixlen)
+ struct ifnet *ifp;
+ struct in6_addr *in6;
+ struct in6_addr *addr;
+ int prefixlen; /* prefix len of the new prefix in "in6" */
+{
+ struct ifaddr *ifa;
+ struct in6_ifaddr *ia, *ib, *oia;
+ int s, error;
+ struct in6_addr mask;
+
+ in6_len2mask(&mask, prefixlen);
+
+ /* find link-local address (will be interface ID) */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ if (ifa)
+ ib = (struct in6_ifaddr *)ifa;
+ else
+ return NULL;
+
+#if 0 /* don't care link local addr state, and always do DAD */
+ /* if link-local address is not eligible, do not autoconfigure. */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) {
+ printf("in6_ifadd: link-local address not ready\n");
+ return NULL;
+ }
+#endif
+
+ /* prefixlen + ifidlen must be equal to 128 */
+ if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) {
+ log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s"
+ "(prefix=%d ifid=%d)\n", if_name(ifp),
+ prefixlen,
+ 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr));
+ return NULL;
+ }
+
+ /* make ifaddr */
+ ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT);
+ if (ia == NULL) {
+ printf("ENOBUFS in in6_ifadd %d\n", __LINE__);
+ return NULL;
+ }
+
+ bzero((caddr_t)ia, sizeof(*ia));
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
+ ia->ia_ifp = ifp;
+
+ /* link to in6_ifaddr */
+ if ((oia = in6_ifaddr) != NULL) {
+ for( ; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
+ } else
+ in6_ifaddr = ia;
+
+ /* link to if_addrlist */
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ if ((ifa = ifp->if_addrlist) != NULL) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ continue;
+ ifa->ifa_next = (struct ifaddr *)ia;
+ }
+#else
+ if (ifp->if_addrlist.tqh_first != NULL) {
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
+ ifa_list);
+ }
+#endif
+#if 0
+ else {
+ /*
+ * this should not be the case because there is at least one
+ * link-local address(see the beginning of the function).
+ */
+ TAILQ_INIT(&ifp->if_addrlist);
+ }
+#endif
+
+ /* new address */
+ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_addr.sin6_family = AF_INET6;
+ /* prefix */
+ bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr));
+ ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
+ ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
+ ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
+ ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
+ /* interface ID */
+ ia->ia_addr.sin6_addr.s6_addr32[0]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
+ ia->ia_addr.sin6_addr.s6_addr32[1]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
+ ia->ia_addr.sin6_addr.s6_addr32[2]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
+ ia->ia_addr.sin6_addr.s6_addr32[3]
+ |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
+
+ /* new prefix */
+ ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ia->ia_prefixmask.sin6_family = AF_INET6;
+ bcopy(&mask, &ia->ia_prefixmask.sin6_addr,
+ sizeof(ia->ia_prefixmask.sin6_addr));
+
+ /* same routine */
+ ia->ia_ifa.ifa_rtrequest =
+ (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ /* add interface route */
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) {
+ log(LOG_NOTICE, "in6_ifadd: failed to add an interface route "
+ "for %s/%d on %s, errno = %d\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen,
+ if_name(ifp), error);
+ }
+ else
+ ia->ia_flags |= IFA_ROUTE;
+
+ *addr = ia->ia_addr.sin6_addr;
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ int error; /* not used */
+ struct in6_addr sol6;
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ /* Restore saved multicast addresses(if any). */
+ in6_restoremkludge(ia, ifp);
+#endif
+
+ /* join solicited node multicast address */
+ bzero(&sol6, sizeof(sol6));
+ sol6.s6_addr16[0] = htons(0xff02);
+ sol6.s6_addr16[1] = htons(ifp->if_index);
+ sol6.s6_addr32[1] = 0;
+ sol6.s6_addr32[2] = htonl(1);
+ sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
+ sol6.s6_addr8[12] = 0xff;
+ (void)in6_addmulti(&sol6, ifp, &error);
+ }
+
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+
+ /*
+ * To make the interface up. Only AF_INET6 in ia is used...
+ */
+ s = splimp();
+ if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) {
+ splx(s);
+ return NULL;
+ }
+ splx(s);
+
+ /* Perform DAD, if needed. */
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+
+ return ia;
+}
+
+int
+in6_ifdel(ifp, in6)
+ struct ifnet *ifp;
+ struct in6_addr *in6;
+{
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ struct ifaddr *ifa;
+#endif
+ struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL;
+ struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL;
+
+ if (!ifp)
+ return -1;
+
+ ia = in6ifa_ifpwithaddr(ifp, in6);
+ if (!ia)
+ return -1;
+
+ if (ifp->if_flags & IFF_MULTICAST) {
+ /*
+ * delete solicited multicast addr for deleting host id
+ */
+ struct in6_multi *in6m;
+ struct in6_addr llsol;
+ bzero(&llsol, sizeof(struct in6_addr));
+ llsol.s6_addr16[0] = htons(0xff02);
+ llsol.s6_addr16[1] = htons(ifp->if_index);
+ llsol.s6_addr32[1] = 0;
+ llsol.s6_addr32[2] = htonl(1);
+ llsol.s6_addr32[3] =
+ ia->ia_addr.sin6_addr.s6_addr32[3];
+ llsol.s6_addr8[12] = 0xff;
+
+ IN6_LOOKUP_MULTI(llsol, ifp, in6m);
+ if (in6m)
+ in6_delmulti(in6m);
+ }
+
+ if (ia->ia_flags & IFA_ROUTE) {
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
+
+#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia) {
+ ifp->if_addrlist = ifa->ifa_next;
+ } else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)ia))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+ else
+ return -1;
+ }
+#else
+ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+#endif
+
+ /* lladdr is never deleted */
+ oia = ia;
+ if (oia == (ia = in6_ifaddr))
+ in6_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ return -1;
+ }
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ in6_savemkludge(oia);
+#endif
+ IFAFREE((&oia->ia_ifa));
+/* xxx
+ rtrequest(RTM_DELETE,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)0
+ (struct sockaddr *)&ia->ia_prefixmask,
+ RTF_UP|RTF_CLONING,
+ (struct rtentry **)0);
+*/
+ return 0;
+}
+
+int
+in6_init_prefix_ltimes(struct nd_prefix *ndpr)
+{
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ /* check if preferred lifetime > valid lifetime */
+ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
+ log(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,
+ int update_vltime)
+{
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+ /* Valid lifetime must not be updated unless explicitly specified. */
+ if (update_vltime) {
+ /* init ia6t_expire */
+ if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_expire = 0;
+ else {
+ lt6->ia6t_expire = time_second;
+ lt6->ia6t_expire += lt6->ia6t_vltime;
+ }
+ /* Ensure addr lifetime <= prefix lifetime. */
+ if (new->ndpr_expire && lt6->ia6t_expire &&
+ new->ndpr_expire < lt6->ia6t_expire)
+ lt6->ia6t_expire = new->ndpr_expire;
+ }
+
+ /* init ia6t_preferred */
+ if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_preferred = 0;
+ else {
+ lt6->ia6t_preferred = time_second;
+ lt6->ia6t_preferred += lt6->ia6t_pltime;
+ }
+ /* Ensure addr lifetime <= prefix lifetime. */
+ if (new->ndpr_preferred && lt6->ia6t_preferred
+ && new->ndpr_preferred < lt6->ia6t_preferred)
+ lt6->ia6t_preferred = new->ndpr_preferred;
+}
+
+/*
+ * 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];
+#ifdef __NetBSD__
+ int s = splsoftnet();
+#else
+ int s = splnet();
+#endif
+
+ /* 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);
+
+ rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway);
+ 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);
+
+ /*
+ * 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
+}
diff --git a/sys/netinet6/pim6.h b/sys/netinet6/pim6.h
new file mode 100644
index 00000000000..dc49e2bbd3c
--- /dev/null
+++ b/sys/netinet6/pim6.h
@@ -0,0 +1,68 @@
+/* $OpenBSD: pim6.h,v 1.1 1999/12/08 06:50:23 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.
+ */
+/*
+ * 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 00000000000..a8208bb926d
--- /dev/null
+++ b/sys/netinet6/pim6_var.h
@@ -0,0 +1,71 @@
+/* $OpenBSD: pim6_var.h,v 1.1 1999/12/08 06:50:23 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.
+ */
+/* KAME Id: pim6_var.h,v 1.2 1999/08/01 15:58:13 itojun Exp */
+
+#ifndef _NETINET6_PIM6_VAR_H_
+#define _NETINET6_PIM6_VAR_H_
+
+/*
+ * 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 PIMCTL_STATS 1 /* statistics (read-only) */
+#define PIMCTL_MAXID 2
+
+#define PIMCTL_NAMES { \
+ { 0, 0 }, \
+ { 0, 0 }, \
+}
+
+#endif /* _NETINET6_PIM6_VAR_H_ */
diff --git a/sys/netinet6/raw_ipv6.c b/sys/netinet6/raw_ipv6.c
index 72f995404a4..a32e501f699 100644
--- a/sys/netinet6/raw_ipv6.c
+++ b/sys/netinet6/raw_ipv6.c
@@ -42,7 +42,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
* SUCH DAMAGE.
*
* @(#)raw_ip.c 8.7 (Berkeley) 5/15/95
- * $Id: raw_ipv6.c,v 1.4 1999/04/28 09:28:16 art Exp $
+ * $Id: raw_ipv6.c,v 1.5 1999/12/08 06:50:23 itojun Exp $
*/
#include <sys/param.h>
@@ -72,11 +72,10 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
-#include <netinet6/in6.h>
#include <netinet6/in6_var.h>
-#include <netinet6/ipv6.h>
-#include <netinet6/ipv6_var.h>
-#include <netinet6/icmpv6.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
#if __OpenBSD__
#undef IPSEC
@@ -110,7 +109,7 @@ struct inpcbtable rawin6pcbtable;
#else /* __NetBSD__ || __OpenBSD__ */
struct inpcb rawin6pcb;
#endif /* __NetBSD__ || __OpenBSD__ */
-struct sockaddr_in6 ripv6src = { sizeof(struct sockaddr_in6), AF_INET6 };
+struct sockaddr_in6 rip6src = { sizeof(struct sockaddr_in6), AF_INET6 };
/*
* Nominal space allocated to a raw ip socket.
@@ -118,8 +117,13 @@ struct sockaddr_in6 ripv6src = { sizeof(struct sockaddr_in6), AF_INET6 };
#define RIPV6SNDQ 8192
#define RIPV6RCVQ 8192
-u_long ripv6_sendspace = RIPV6SNDQ;
-u_long ripv6_recvspace = RIPV6RCVQ;
+#if 0
+u_long rip6_sendspace = RIPV6SNDQ;
+u_long rip6_recvspace = RIPV6RCVQ;
+#else
+extern u_long rip6_sendspace;
+extern u_long rip6_recvspace;
+#endif
/*
* External globals
@@ -129,7 +133,9 @@ static struct inpcbhead ri6pcb;
static struct inpcbinfo ri6pcbinfo;
#endif /* __FreeBSD__ */
-extern struct ipv6stat ipv6stat;
+#if 0
+extern struct ip6_hdrstat ipv6stat;
+#endif
#define RETURN_ERROR(x) { \
DPRINTF(EVENT, ("%s: returning %s\n", DEBUG_STATUS, #x)); \
@@ -145,7 +151,7 @@ extern struct ipv6stat ipv6stat;
----------------------------------------------------------------------*/
void
-ripv6_init()
+rip6_init()
{
#if __FreeBSD__
LIST_INIT(&ri6pcb);
@@ -194,50 +200,50 @@ static int ipv6_findnexthdr(struct mbuf *m, size_t extra)
do {
switch(nexthdr) {
case IPPROTO_IPV6:
- hl = sizeof(struct ipv6);
+ hl = sizeof(struct ip6_hdr);
if ((extra -= hl) < 0)
return -1;
- nexthdr = ((struct ipv6 *)p)->ipv6_nexthdr;
+ nexthdr = ((struct ip6_hdr *)p)->ip6_nxt;
break;
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
- if (extra < sizeof(struct ipv6_opthdr))
+ if (extra < sizeof(struct ip6_ext))
return -1;
- hl = sizeof(struct ipv6_opthdr) +
- (((struct ipv6_opthdr *)p)->oh_extlen << 3);
+ hl = sizeof(struct ip6_ext) +
+ (((struct ip6_ext *)p)->ip6e_len << 3);
if ((extra -= hl) < 0)
return -1;
- nexthdr = ((struct ipv6_opthdr *)p)->oh_nexthdr;
+ nexthdr = ((struct ip6_ext *)p)->ip6e_nxt;
break;
case IPPROTO_ROUTING:
- if (extra < sizeof(struct ipv6_srcroute0))
+ if (extra < sizeof(struct ip6_rthdr0))
return -1;
- hl = sizeof(struct ipv6_srcroute0) +
- (((struct ipv6_srcroute0 *)p)->i6sr_len << 3);
+ hl = sizeof(struct ip6_rthdr0) +
+ (((struct ip6_rthdr0 *)p)->ip6r0_len << 3);
if ((extra -= hl) < 0)
return -1;
- nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr;
+ nexthdr = ((struct ip6_rthdr0 *)p)->ip6r0_nxt;
break;
#ifdef IPSEC
case IPPROTO_AH:
- if (extra < sizeof(struct ipv6_srcroute0))
+ if (extra < sizeof(struct ip6_hdr_srcroute0))
return -1;
- hl = sizeof(struct ipv6_srcroute0) +
- ((struct ipv6_srcroute0 *)p)->i6sr_len << 3;
+ hl = sizeof(struct ip6_hdr_srcroute0) +
+ ((struct ip6_hdr_srcroute0 *)p)->i6sr_len << 3;
if ((extra -= hl) < 0)
return -1;
- nexthdr = ((struct ipv6_srcroute0 *)p)->i6sr_nexthdr;
+ nexthdr = ((struct ip6_hdr_srcroute0 *)p)->i6sr_nexthdr;
break;
#endif /* IPSEC */
default:
@@ -252,52 +258,54 @@ static int ipv6_findnexthdr(struct mbuf *m, size_t extra)
/*----------------------------------------------------------------------
* If no HLP's are found for an IPv6 datagram, this routine is called.
----------------------------------------------------------------------*/
-void
-#if __OpenBSD__
-ripv6_input(struct mbuf *m, ...)
-#else /* __OpenBSD__ */
-ripv6_input(m,extra)
- struct mbuf *m;
- int extra;
-#endif /* __OpenBSD__ */
+int
+rip6_input(mp, offp, proto)
+ struct mbuf **mp;
+ int *offp, proto;
{
- register struct ipv6 *ipv6 = mtod(m, struct ipv6 *); /* Will have been
+ struct mbuf *m = *mp;
+ register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); /* Will have been
pulled up by
ipv6_input(). */
register struct inpcb *inp;
- int nexthdr, icmpv6type;
+ int nexthdr, icmp6type;
int foundone = 0;
struct mbuf *m2 = NULL, *opts = NULL;
struct sockaddr_in6 srcsa;
#ifdef IPSEC
struct sockaddr_in6 dstsa;
#endif /* IPSEC */
-#if __OpenBSD__
- int extra;
- va_list ap;
+ int extra = *offp;
- va_start(ap, m);
- extra = va_arg(ap, int);
- va_end(ap);
-#endif /* __OpenBSD__ */
-
- DPRINTF(FINISHED, ("ripv6_input(m=%08x, extra=%d)\n", OSDEP_PCAST(m), extra));
+ DPRINTF(FINISHED, ("rip6_input(m=%08x, extra=%d)\n", OSDEP_PCAST(m), extra));
DP(FINISHED, m->m_pkthdr.len, d);
- DDO(FINISHED,printf("In ripv6_input(), header is:\n");dump_mchain(m));
- DPRINTF(EVENT, ("In ripv6_input()\n"));
+ DDO(FINISHED,printf("In rip6_input(), header is:\n");dump_mchain(m));
+ DPRINTF(EVENT, ("In rip6_input()\n"));
DPRINTF(EVENT, ("Header is: "));
+#if 0
DDO(GROSSEVENT, dump_ipv6(ipv6));
+#endif
bzero(&srcsa, sizeof(struct sockaddr_in6));
srcsa.sin6_family = AF_INET6;
srcsa.sin6_len = sizeof(struct sockaddr_in6);
- srcsa.sin6_addr = ipv6->ipv6_src;
+ srcsa.sin6_addr = ip6->ip6_src;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&srcsa.sin6_addr))
+ srcsa.sin6_addr.s6_addr16[1] = 0;
+ if (m->m_pkthdr.rcvif) {
+ if (IN6_IS_SCOPE_LINKLOCAL(&srcsa.sin6_addr))
+ srcsa.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
+ else
+ srcsa.sin6_scope_id = 0;
+ } else
+ srcsa.sin6_scope_id = 0;
#if IPSEC
bzero(&dstsa, sizeof(struct sockaddr_in6));
dstsa.sin6_family = AF_INET6;
dstsa.sin6_len = sizeof(struct sockaddr_in6);
- dstsa.sin6_addr = ipv6->ipv6_dst;
+ dstsa.sin6_addr = ip6->ip6_dst;
#endif /* IPSEC */
#if 0
@@ -305,28 +313,28 @@ ripv6_input(m,extra)
if (m->m_len < extra)) {
if (!(m = m_pullup2(m, extra)))
return;
- ipv6 = mtod(m, struct ipv6 *);
+ ip6 = mtod(m, struct ip6_hdr *);
}
#endif /* 0 */
if ((nexthdr = ipv6_findnexthdr(m, extra)) < 0) {
- DPRINTF(ERROR, ("ripv6_input: ipv6_findnexthdr failed\n"));
+ DPRINTF(ERROR, ("rip6_input: ipv6_findnexthdr failed\n"));
goto ret;
}
DP(FINISHED, nexthdr, d);
if (nexthdr == IPPROTO_ICMPV6) {
- if (m->m_len < extra + sizeof(struct icmpv6hdr)) {
- if (!(m = m_pullup2(m, extra + sizeof(struct icmpv6hdr)))) {
- DPRINTF(ERROR, ("ripv6_input: m_pullup2 failed\n"));
+ if (m->m_len < extra + sizeof(struct icmp6_hdr)) {
+ if (!(m = m_pullup2(m, extra + sizeof(struct icmp6_hdr)))) {
+ DPRINTF(ERROR, ("rip6_input: m_pullup2 failed\n"));
goto ret;
}
- ipv6 = mtod(m, struct ipv6 *);
+ ip6 = mtod(m, struct ip6_hdr *);
}
- icmpv6type = ((struct icmpv6hdr *)(mtod(m, caddr_t) + extra))->icmpv6_type;
+ icmp6type = ((struct icmp6_hdr *)(mtod(m, caddr_t) + extra))->icmp6_type;
} else
- icmpv6type = -1;
+ icmp6type = -1;
/*
* Locate raw PCB for incoming datagram.
@@ -337,21 +345,22 @@ ripv6_input(m,extra)
#if __NetBSD__ || __OpenBSD__
for (inp = rawin6pcbtable.inpt_queue.cqh_first;
inp != (struct inpcb *)&rawin6pcbtable.inpt_queue;
- inp = inp->inp_queue.cqe_next) {
+ inp = inp->inp_queue.cqe_next)
#else /* __NetBSD__ || __OpenBSD__ */
- for (inp = rawin6pcb.inp_next; inp != &rawin6pcb; inp = inp->inp_next) {
+ for (inp = rawin6pcb.inp_next; inp != &rawin6pcb; inp = inp->inp_next)
#endif /* __NetBSD__ || __OpenBSD__ */
#endif /* __FreeBSD__ */
- if (inp->inp_ipv6.ipv6_nexthdr && inp->inp_ipv6.ipv6_nexthdr != nexthdr)
+ {
+ if (inp->inp_ipv6.ip6_nxt && inp->inp_ipv6.ip6_nxt != nexthdr)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
- !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &ipv6->ipv6_dst))
+ !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, &ip6->ip6_dst))
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) &&
- !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ipv6->ipv6_src))
+ !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ip6->ip6_src))
continue;
- if ((icmpv6type >= 0) &&
- ICMPV6_FILTER_WILLBLOCK(icmpv6type, &inp->inp_filter))
+ if (inp->inp_icmp6filt &&
+ ICMP6_FILTER_WILLBLOCK(icmp6type, inp->inp_icmp6filt))
continue;
DPRINTF(IDL_EVENT, ("Found a raw pcb (>1)\n"));
@@ -375,8 +384,8 @@ ripv6_input(m,extra)
if ((m2 = m_copym(m, 0, (int)M_COPYALL, M_DONTWAIT))) {
m_adj(m2, extra);
DP(FINISHED, m2->m_pkthdr.len, d);
- if (inp->inp_flags & INP_CONTROLOPTS)
- opts = ipv6_headertocontrol(m, extra, inp->inp_flags);
+ if (inp->inp_flags & IN6P_CONTROLOPTS)
+ ip6_savecontrol(inp, &opts, ip6, m);
else
opts = NULL;
if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&srcsa, m2,
@@ -393,46 +402,54 @@ ripv6_input(m,extra)
* We should send an ICMPv6 protocol unreachable here,
* though original UCB 4.4-lite BSD's IPv4 does not do so.
*/
+#if 0
ipv6stat.ips_noproto++;
ipv6stat.ips_delivered--;
+#endif
}
ret:
if (m)
m_freem(m);
- DPRINTF(FINISHED, ("ripv6_input\n"));
+ DPRINTF(FINISHED, ("rip6_input\n"));
+ return IPPROTO_DONE;
}
/*----------------------------------------------------------------------
- * Output function for raw IPv6. Called from ripv6_usrreq(), and
+ * Output function for raw IPv6. Called from rip6_usrreq(), and
* ipv6_icmp_usrreq().
----------------------------------------------------------------------*/
#if __OpenBSD__
-int ripv6_output(struct mbuf *m, ...)
+int rip6_output(struct mbuf *m, ...)
#else /* __OpenBSD__ */
int
-ripv6_output(m, so, dst, control)
+rip6_output(m, so, dst, control)
struct mbuf *m;
struct socket *so;
struct in6_addr *dst;
struct mbuf *control;
#endif /* __OpenBSD__ */
{
- register struct ipv6 *ipv6;
+ register struct ip6_hdr *ip6;
register struct inpcb *inp;
int flags;
+ int error = 0;
+#if 0
struct ifnet *forceif = NULL;
+#endif
+ struct ip6_pktopts opt, *optp = NULL;
+ struct ifnet *oifp = NULL;
#if __OpenBSD__
va_list ap;
struct socket *so;
- struct in6_addr *dst;
+ struct sockaddr_in6 *dst;
struct mbuf *control;
va_start(ap, m);
so = va_arg(ap, struct socket *);
- dst = va_arg(ap, struct in6_addr *);
+ dst = va_arg(ap, struct sockaddr_in6 *);
control = va_arg(ap, struct mbuf *);
va_end(ap);
#endif /* __OpenBSD__ */
@@ -440,6 +457,15 @@ ripv6_output(m, so, dst, control)
inp = sotoinpcb(so);
flags = (so->so_options & SO_DONTROUTE);
+ if (control) {
+ error = ip6_setpktoptions(control, &opt, so->so_state & SS_PRIV);
+ if (error != 0)
+ goto bad;
+ optp = &opt;
+ } else
+ optp = NULL;
+
+#if 0
if (inp->inp_flags & INP_HDRINCL)
{
flags |= IPV6_RAWOUTPUT;
@@ -448,14 +474,25 @@ ripv6_output(m, so, dst, control)
expects it to be contiguous. */
}
else
+#endif
{
- M_PREPEND(m, sizeof(struct ipv6), M_WAIT);
- ipv6 = mtod(m, struct ipv6 *);
- ipv6->ipv6_nexthdr = inp->inp_ipv6.ipv6_nexthdr;
- ipv6->ipv6_hoplimit = MAXHOPLIMIT;
- ipv6->ipv6_src = inp->inp_laddr6;
- ipv6->ipv6_dst = *dst;
- ipv6->ipv6_versfl = 0; /* Or possibly user flow label, in host order. */
+ struct in6_addr *in6a;
+
+ in6a = in6_selectsrc(dst, optp, inp->inp_moptions6,
+ &inp->inp_route6, &inp->inp_laddr6, &error);
+ if (in6a == NULL) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ goto bad;
+ }
+
+ M_PREPEND(m, sizeof(struct ip6_hdr), M_WAIT);
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0; /* Or possibly user flow label, in host order. */
+ ip6->ip6_vfc = IPV6_VERSION;
+ ip6->ip6_nxt = inp->inp_ipv6.ip6_nxt;
+ bcopy(in6a, &ip6->ip6_src, sizeof(*in6a));
+ ip6->ip6_dst = dst->sin6_addr;
/*
* Question: How do I handle options?
*
@@ -463,22 +500,55 @@ ripv6_output(m, so, dst, control)
*/
}
+ /*
+ * 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_ADDR_LINKLOCAL(&ip6->ip6_dst) ||
+ IN6_IS_ADDR_MC_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 = ifindex2ifnet[pi->ipi6_ifindex];
+ }
+ else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
+ inp->inp_moptions6 &&
+ inp->inp_moptions6->im6o_multicast_ifp) {
+ oifp = inp->inp_moptions6->im6o_multicast_ifp;
+ ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index);
+ } else if (dst->sin6_scope_id) {
+ /* boundary check */
+ if (dst->sin6_scope_id < 0
+ || if_index < dst->sin6_scope_id) {
+ error = ENXIO; /* XXX EINVAL? */
+ goto bad;
+ }
+ ip6->ip6_dst.s6_addr16[1]
+ = htons(dst->sin6_scope_id & 0xffff);/*XXX*/
+ }
+ }
+
+ ip6->ip6_hlim = in6_selecthlim(inp, oifp);
+
{
- int payload = sizeof(struct ipv6);
- int nexthdr = mtod(m, struct ipv6 *)->ipv6_nexthdr;
+ int payload = sizeof(struct ip6_hdr);
+ int nexthdr = mtod(m, struct ip6_hdr *)->ip6_nxt;
+#if 0
int error;
-
- if (control)
- if ((error = ipv6_controltoheader(&m, control, &forceif, &payload))) {
- m_freem(m);
- return error;
- }
+#endif
if (inp->inp_csumoffset >= 0) {
uint16_t *csum;
if (!(m = m_pullup2(m, payload + inp->inp_csumoffset))) {
- DPRINTF(IDL_ERROR, ("ripv6_output: m_pullup2(m, %d) failed\n", payload + inp->inp_csumoffset));
+ DPRINTF(IDL_ERROR, ("rip6_output: m_pullup2(m, %d) failed\n", payload + inp->inp_csumoffset));
m_freem(m);
return ENOBUFS;
};
@@ -486,11 +556,16 @@ ripv6_output(m, so, dst, control)
csum = (uint16_t *)(mtod(m, uint8_t *) + payload + inp->inp_csumoffset);
*csum = 0;
- *csum = in6_cksum(m, nexthdr, m->m_pkthdr.len - payload, payload);
+ *csum = in6_cksum(m, nexthdr, payload, m->m_pkthdr.len - payload);
};
};
- return ipv6_output(m,&inp->inp_route6,flags,inp->inp_moptions6, forceif, so);
+ return ip6_output(m, optp, &inp->inp_route6, flags, inp->inp_moptions6, &oifp);
+
+bad:
+ if (m)
+ m_freem(m);
+ return error;
}
/*----------------------------------------------------------------------
@@ -498,7 +573,7 @@ ripv6_output(m, so, dst, control)
----------------------------------------------------------------------*/
#if __FreeBSD__
-int ripv6_ctloutput(struct socket *so, struct sockopt *sopt)
+int rip6_ctloutput(struct socket *so, struct sockopt *sopt)
{
register struct inpcb *inp = sotoinpcb(so);
int op;
@@ -506,7 +581,7 @@ int ripv6_ctloutput(struct socket *so, struct sockopt *sopt)
int optname;
int optval;
- DPRINTF(FINISHED, ("ripv6_ctloutput(so=%08x, sopt=%08x)\n",
+ DPRINTF(FINISHED, ("rip6_ctloutput(so=%08x, sopt=%08x)\n",
OSDEP_PCAST(so), OSDEP_PCAST(sopt)));
switch(sopt->sopt_dir) {
@@ -524,7 +599,7 @@ int ripv6_ctloutput(struct socket *so, struct sockopt *sopt)
optname = sopt->sopt_name;
#else /* __FreeBSD__ */
int
-ripv6_ctloutput (op, so, level, optname, m)
+rip6_ctloutput (op, so, level, optname, m)
int op;
struct socket *so;
int level, optname;
@@ -532,7 +607,7 @@ ripv6_ctloutput (op, so, level, optname, m)
{
register struct inpcb *inp = sotoinpcb(so);
- DPRINTF(FINISHED, ("ripv6_ctloutput(op=%x,so,level=%x,optname=%x,m)\n", op, level, optname));
+ DPRINTF(FINISHED, ("rip6_ctloutput(op=%x,so,level=%x,optname=%x,m)\n", op, level, optname));
#endif /* __FreeBSD__ */
if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ICMPV6)) {
@@ -572,34 +647,35 @@ ripv6_ctloutput (op, so, level, optname, m)
return 0;
};
break;
- case ICMPV6_FILTER:
+ case ICMP6_FILTER:
if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
#if __FreeBSD__
if (!sopt->sopt_val || (sopt->sopt_valsize !=
- sizeof(struct icmpv6_filter)))
+ sizeof(struct icmp6_filter)))
RETURN_ERROR(EINVAL);
if (op == PRCO_SETOPT) {
- struct icmpv6_filter icmpv6_filter;
- int error = sooptcopyin(sopt, &icmpv6_filter,
- sizeof(struct icmpv6_filter), sizeof(struct icmpv6_filter));
+ struct icmp6_filter icmp6_filter;
+ int error = sooptcopyin(sopt, &icmp6_filter,
+ sizeof(struct icmp6_filter), sizeof(struct icmp6_filter));
if (error)
return error;
- inp->inp_filter = icmpv6_filter;
+ bcopy(&icmp6_filter, inp->inp_icmp6filt, sizeof(icmp6_filter));
return 0;
} else
- return sooptcopyout(sopt, &inp->inp_filter,
- sizeof(struct icmpv6_filter));
+ return sooptcopyout(sopt, inp->inp_icmp6filt,
+ sizeof(struct icmp6_filter));
#else /* __FreeBSD__ */
- if (!m || !*m || (*m)->m_len != sizeof(struct icmpv6_filter))
+ if (!m || !*m || (*m)->m_len != sizeof(struct icmp6_filter))
RETURN_ERROR(EINVAL);
if (op == PRCO_SETOPT) {
- inp->inp_filter = *mtod(*m, struct icmpv6_filter *);
+ bcopy(mtod(*m, struct icmp6_filter *), inp->inp_icmp6filt,
+ sizeof(struct icmp6_filter));
m_freem(*m);
} else {
- (*m)->m_len = sizeof(struct icmpv6_filter);
- *mtod(*m, struct icmpv6_filter *) = inp->inp_filter;
+ (*m)->m_len = sizeof(struct icmp6_filter);
+ *mtod(*m, struct icmp6_filter *) = *inp->inp_icmp6filt;
};
return 0;
#endif /* __FreeBSD__ */
@@ -683,9 +759,9 @@ ripv6_ctloutput (op, so, level, optname, m)
};
}
#if __FreeBSD__
- return ipv6_ctloutput(so, sopt);
+ return ip6_ctloutput(so, sopt);
#else /* __FreeBSD__ */
- return ipv6_ctloutput(op, so, level, optname, m);
+ return ip6_ctloutput(op, so, level, optname, m);
#endif /* __FreeBSD__ */
}
@@ -698,17 +774,17 @@ ripv6_ctloutput (op, so, level, optname, m)
#endif /* __GNUC__ && __GNUC__ >= 2 && __OPTIMIZE__ && !__FreeBSD__ */
#if __NetBSD__ || __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_attach(struct socket *so, int proto,
struct proc *p)
#else /* __NetBSD__ || __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto)
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_attach(struct socket *so, int proto)
#endif /* __NetBSD__ || __FreeBSD__ */
{
register struct inpcb *inp = sotoinpcb(so);
register int error = 0;
if (inp)
- panic("ripv6_attach - Already got PCB");
+ panic("rip6_attach - Already got PCB");
#if __NetBSD__ || __FreeBSD__
if (p == 0 || (error = suser(p->p_ucred, &p->p_acflag)))
@@ -719,7 +795,7 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto)
error = EACCES;
return error;
}
- if ((error = soreserve(so, ripv6_sendspace, ripv6_recvspace)) ||
+ if ((error = soreserve(so, rip6_sendspace, rip6_recvspace)) ||
#if __FreeBSD__
(error = in_pcballoc(so, &ri6pcbinfo, p)))
@@ -735,48 +811,55 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_attach(struct socket *so, int proto)
inp = sotoinpcb(so);
#ifdef __alpha__
- inp->inp_ipv6.ipv6_nexthdr = (u_long)proto; /*nam; Nam contains protocol
+ inp->inp_ipv6.ip6_nxt = (u_long)proto; /*nam; Nam contains protocol
type, apparently. */
#else
- inp->inp_ipv6.ipv6_nexthdr = (int)proto; /*nam; Nam contains protocol
+ inp->inp_ipv6.ip6_nxt = (int)proto; /*nam; Nam contains protocol
type, apparently. */
#endif
- if (inp->inp_ipv6.ipv6_nexthdr == IPPROTO_ICMPV6)
+ if (inp->inp_ipv6.ip6_nxt == IPPROTO_ICMPV6)
inp->inp_csumoffset = 2;
+ inp->inp_icmp6filt = (struct icmp6_filter *)
+ malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
+ ICMP6_FILTER_SETPASSALL(inp->inp_icmp6filt);
return error;
}
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_detach(struct socket *so)
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_detach(struct socket *so)
{
register struct inpcb *inp = sotoinpcb(so);
if (inp == 0)
- panic("ripv6_detach");
+ panic("rip6_detach");
#ifdef MROUTING
/* More MROUTING stuff. */
#endif
+ if (inp->inp_icmp6filt) {
+ free(inp->inp_icmp6filt, M_PCB);
+ inp->inp_icmp6filt = NULL;
+ }
in_pcbdetach(inp);
return 0;
}
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_abort(struct socket *so)
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_abort(struct socket *so)
{
soisdisconnected(so);
- return ripv6_usrreq_detach(so);
+ return rip6_usrreq_detach(so);
}
-static MAYBEINLINE int ripv6_usrreq_disconnect(struct socket *so)
+static MAYBEINLINE int rip6_usrreq_disconnect(struct socket *so)
{
if ((so->so_state & SS_ISCONNECTED) == 0)
return ENOTCONN;
- return ripv6_usrreq_abort(so);
+ return rip6_usrreq_abort(so);
}
#if __NetBSD__ || __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_bind(struct socket *so,
struct sockaddr *nam, struct proc *p)
#else /* __NetBSD__ || __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_bind(struct socket *so,
struct sockaddr *nam)
#endif /* __NetBSD__ || __FreeBSD__ */
{
@@ -800,27 +883,30 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_bind(struct socket *so,
}
#if __NetBSD__ || __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_connect(struct socket *so,
struct sockaddr *nam, struct proc *p)
#else /* __NetBSD__ || __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_connect(struct socket *so,
struct sockaddr *nam)
#endif /* __NetBSD__ || __FreeBSD__ */
{
register struct inpcb *inp = sotoinpcb(so);
register struct sockaddr_in6 *addr = (struct sockaddr_in6 *) nam;
+ int error;
+ struct in6_addr *in6a;
if (addr->sin6_family != AF_INET6)
return EAFNOSUPPORT;
-#if __NetBSD__ || __FreeBSD__ || __OpenBSD__
- if (ifnet.tqh_first == 0) {
-#else /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
- if (ifnet == 0) {
-#endif /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
- return EADDRNOTAVAIL; /* This is a weird way to say there
- are no interfaces, no? */
- }
+ in6a = in6_selectsrc(addr, inp->inp_outputopts6,
+ inp->inp_moptions6, &inp->inp_route6, &inp->inp_laddr6,
+ &error);
+ if (in6a == NULL) {
+ if (error == 0)
+ error = EADDRNOTAVAIL;
+ return error;
+ }
+ inp->inp_laddr6 = *in6a;
inp->inp_faddr6 = addr->sin6_addr; /* Will structure assignment
work with this compiler? */
@@ -828,28 +914,31 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_connect(struct socket *so,
return 0;
}
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_shutdown(struct socket *so)
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_shutdown(struct socket *so)
{
socantsendmore(so);
return 0;
}
+static int rip6_usrreq_send __P((struct socket *so, int flags, struct mbuf *m,
+ struct sockaddr *addr, struct mbuf *control));
+
#if __NetBSD__ || __FreeBSD__
/*
* Note that flags and p are not used, but required by protosw in
* FreeBSD.
*/
-int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
+static int rip6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *addr, struct mbuf *control,
struct proc *p)
#else /* __NetBSD__ || __FreeBSD__ */
-int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
+static int rip6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *addr, struct mbuf *control)
#endif /* __NetBSD__ || __FreeBSD__ */
{
register struct inpcb *inp = sotoinpcb(so);
register int error = 0;
- struct in6_addr *dst;
+ struct sockaddr_in6 *dst, tmp;
if (inp == 0) {
m_freem(m);
@@ -865,26 +954,30 @@ int ripv6_usrreq_send(struct socket *so, int flags, struct mbuf *m,
if (addr)
return EISCONN;
- dst = &(inp->inp_faddr6);
+ bzero(&tmp, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_len = sizeof(tmp);
+ tmp.sin6_addr = inp->inp_faddr6;
+ dst = &tmp;
}
else
{
if (addr == NULL)
return ENOTCONN;
- dst = &((struct sockaddr_in6 *)addr)->sin6_addr;
+ dst = (struct sockaddr_in6 *)addr;
}
- error = ripv6_output(m,so,dst,control);
+ error = rip6_output(m,so,dst,control);
/* m = NULL; */
return error;
}
#if __NetBSD__ || __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, u_long cmd,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_control(struct socket *so, u_long cmd,
caddr_t data, struct ifnet *ifp, struct proc *p)
#else /* __NetBSD__ || __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, int cmd,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_control(struct socket *so, int cmd,
caddr_t data, struct ifnet *ifp)
#endif /* __NetBSD__ || __FreeBSD__ */
{
@@ -897,7 +990,7 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_control(struct socket *so, int cmd,
#endif /* __NetBSD__ || __FreeBSD__ */
}
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sense(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_sense(struct socket *so,
struct stat *sb)
{
/* services stat(2) call. */
@@ -905,10 +998,10 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sense(struct socket *so,
}
#if __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_sockaddr(struct socket *so,
struct sockaddr **nam)
#else /* __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_sockaddr(struct socket *so,
struct mbuf *nam)
#endif /* __FreeBSD__ */
{
@@ -917,10 +1010,10 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_sockaddr(struct socket *so,
}
#if __FreeBSD__
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_peeraddr(struct socket *so,
struct sockaddr **nam)
#else /* __FreeBSD__ */
-MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so,
+MAYBESTATIC MAYBEINLINE int rip6_usrreq_peeraddr(struct socket *so,
struct mbuf *nam)
#endif /* __FreeBSD__ */
{
@@ -929,13 +1022,13 @@ MAYBESTATIC MAYBEINLINE int ripv6_usrreq_peeraddr(struct socket *so,
}
#if __FreeBSD__
-struct pr_usrreqs ripv6_usrreqs = {
- ripv6_usrreq_abort, pru_accept_notsupp, ripv6_usrreq_attach,
- ripv6_usrreq_bind, ripv6_usrreq_connect, pru_connect2_notsupp,
- ripv6_usrreq_control, ripv6_usrreq_detach, ripv6_usrreq_detach,
- pru_listen_notsupp, ripv6_usrreq_peeraddr, pru_rcvd_notsupp,
- pru_rcvoob_notsupp, ripv6_usrreq_send, ripv6_usrreq_sense,
- ripv6_usrreq_shutdown, ripv6_usrreq_sockaddr, sosend, soreceive, sopoll
+struct pr_usrreqs rip6_usrreqs = {
+ rip6_usrreq_abort, pru_accept_notsupp, rip6_usrreq_attach,
+ rip6_usrreq_bind, rip6_usrreq_connect, pru_connect2_notsupp,
+ rip6_usrreq_control, rip6_usrreq_detach, rip6_usrreq_detach,
+ pru_listen_notsupp, rip6_usrreq_peeraddr, pru_rcvd_notsupp,
+ pru_rcvoob_notsupp, rip6_usrreq_send, rip6_usrreq_sense,
+ rip6_usrreq_shutdown, rip6_usrreq_sockaddr, sosend, soreceive, sopoll
};
#endif /* __FreeBSD__ */
@@ -944,21 +1037,15 @@ struct pr_usrreqs ripv6_usrreqs = {
----------------------------------------------------------------------*/
#if !__FreeBSD__
int
-#if __NetBSD__
-ripv6_usrreq(so, req, m, nam, control, p)
-#else /* __NetBSD__ */
-ripv6_usrreq(so, req, m, nam, control)
-#endif /* __NetBSD__ */
+rip6_usrreq(so, req, m, nam, control, p)
struct socket *so;
int req;
struct mbuf *m, *nam, *control;
-#if __NetBSD__
struct proc *p;
-#endif /* __NetBSD__ */
{
register int error = 0;
- DPRINTF(IDL_EVENT, ("ripv6_usrreq(so, req, m, nam, control)\n"));
+ DPRINTF(IDL_EVENT, ("rip6_usrreq(so, req, m, nam, control)\n"));
#ifdef MROUTING
/*
@@ -974,23 +1061,23 @@ ripv6_usrreq(so, req, m, nam, control)
{
case PRU_ATTACH:
#if __NetBSD__
- error = ripv6_usrreq_attach(so, 0, p); /* XXX */
+ error = rip6_usrreq_attach(so, (long)nam, p);
#else /* __NetBSD__ */
- error = ripv6_usrreq_attach(so, 0); /* XXX */
+ error = rip6_usrreq_attach(so, (long)nam);
#endif /* __NetBSD__ */
break;
case PRU_DISCONNECT:
- error = ripv6_usrreq_disconnect(so);
+ error = rip6_usrreq_disconnect(so);
break;
/* NOT */
/* FALLTHROUGH */
case PRU_ABORT:
- error = ripv6_usrreq_abort(so);
+ error = rip6_usrreq_abort(so);
break;
/* NOT */
/* FALLTHROUGH */
case PRU_DETACH:
- error = ripv6_usrreq_detach(so);
+ error = rip6_usrreq_detach(so);
break;
case PRU_BIND:
if (nam->m_len != sizeof(struct sockaddr_in6))
@@ -999,9 +1086,9 @@ ripv6_usrreq(so, req, m, nam, control)
* Be strict regarding sockaddr_in6 fields.
*/
#if __NetBSD__
- error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *), p);
+ error = rip6_usrreq_bind(so, mtod(nam, struct sockaddr *), p);
#else /* __NetBSD__ */
- error = ripv6_usrreq_bind(so, mtod(nam, struct sockaddr *));
+ error = rip6_usrreq_bind(so, mtod(nam, struct sockaddr *));
#endif /* __NetBSD__ */
break;
case PRU_CONNECT:
@@ -1011,13 +1098,13 @@ ripv6_usrreq(so, req, m, nam, control)
if (nam->m_len != sizeof(struct sockaddr_in6))
return EINVAL;
#if __NetBSD__
- error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *), p);
+ error = rip6_usrreq_connect(so, mtod(nam, struct sockaddr *), p);
#else /* __NetBSD__ */
- error = ripv6_usrreq_connect(so, mtod(nam, struct sockaddr *));
+ error = rip6_usrreq_connect(so, mtod(nam, struct sockaddr *));
#endif /* __NetBSD__ */
break;
case PRU_SHUTDOWN:
- error = ripv6_usrreq_shutdown(so);
+ error = rip6_usrreq_shutdown(so);
break;
case PRU_SEND:
/*
@@ -1026,22 +1113,22 @@ ripv6_usrreq(so, req, m, nam, control)
if (nam->m_len != sizeof(struct sockaddr_in6))
return EINVAL;
#if __NetBSD__
- error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control, p);
+ error = rip6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control, p);
#else /* __NetBSD__ */
- error = ripv6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control);
+ error = rip6_usrreq_send(so, 0, m, mtod(nam, struct sockaddr *), control);
#endif /* __NetBSD__ */
m = NULL;
break;
case PRU_CONTROL:
#if __NetBSD__
- return ripv6_usrreq_control(so, (u_long)m, (caddr_t) nam,
+ return rip6_usrreq_control(so, (u_long)m, (caddr_t) nam,
(struct ifnet *) control, p);
#else /* __NetBSD__ */
- return ripv6_usrreq_control(so, (int)m, (caddr_t) nam,
+ return rip6_usrreq_control(so, (int)m, (caddr_t) nam,
(struct ifnet *) control);
#endif /* __NetBSD__ */
case PRU_SENSE:
- return ripv6_usrreq_sense(so, NULL); /* XXX */
+ return rip6_usrreq_sense(so, NULL); /* XXX */
case PRU_CONNECT2:
case PRU_RCVOOB:
case PRU_LISTEN:
@@ -1051,13 +1138,13 @@ ripv6_usrreq(so, req, m, nam, control)
error = EOPNOTSUPP;
break;
case PRU_SOCKADDR:
- error = ripv6_usrreq_sockaddr(so, nam);
+ error = rip6_usrreq_sockaddr(so, nam);
break;
case PRU_PEERADDR:
- error = ripv6_usrreq_peeraddr(so, nam);
+ error = rip6_usrreq_peeraddr(so, nam);
break;
default:
- panic ("ripv6_usrreq - unknown req\n");
+ panic ("rip6_usrreq - unknown req\n");
}
if (m != NULL)
m_freem(m);
diff --git a/sys/netinet6/route6.c b/sys/netinet6/route6.c
new file mode 100644
index 00000000000..7fda631cec5
--- /dev/null
+++ b/sys/netinet6/route6.c
@@ -0,0 +1,172 @@
+/* $OpenBSD: route6.c,v 1.1 1999/12/08 06:50:24 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/mbuf.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/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 */
+{
+ register struct ip6_hdr *ip6;
+ register struct mbuf *m = *mp;
+ register struct ip6_rthdr *rh;
+ int off = *offp, rhlen;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE);
+ ip6 = mtod(m, struct ip6_hdr *);
+ rh = (struct ip6_rthdr *)((caddr_t)ip6 + off);
+#else
+ ip6 = mtod(m, struct ip6_hdr *);
+ IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh));
+ if (rh == NULL) {
+ ip6stat.ip6s_tooshort++;
+ return IPPROTO_DONE;
+ }
+#endif
+
+ switch(rh->ip6r_type) {
+ case IPV6_RTHDR_TYPE_0:
+ rhlen = (rh->ip6r_len + 1) << 3;
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE);
+#else
+ 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
+ */
+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 stict/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--;
+ nextaddr = rh0->ip6r0_addr + index;
+
+ if (IN6_IS_ADDR_MULTICAST(nextaddr) ||
+ IN6_IS_ADDR_MULTICAST(&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/tcpipv6.h b/sys/netinet6/tcpipv6.h
index 5363797baa9..27aec0a1864 100644
--- a/sys/netinet6/tcpipv6.h
+++ b/sys/netinet6/tcpipv6.h
@@ -12,11 +12,11 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#ifndef _NETINET6_TCPIPV6_H
#define _NETINET6_TCPIPV6_H 1
-#include <netinet6/ipv6.h>
+#include <netinet6/ip6.h>
#include <netinet/tcp.h>
struct tcpipv6hdr {
- struct ipv6 ti6_i;
+ struct ip6_hdr ti6_i;
struct tcphdr ti6_t;
};
diff --git a/sys/netiso/if_eon.c b/sys/netiso/if_eon.c
index 5499d1ce1db..62093ae1a35 100644
--- a/sys/netiso/if_eon.c
+++ b/sys/netiso/if_eon.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_eon.c,v 1.6 1999/04/22 20:02:44 art Exp $ */
+/* $OpenBSD: if_eon.c,v 1.7 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: if_eon.c,v 1.15 1996/05/09 22:29:37 scottr Exp $ */
/*-
@@ -78,6 +78,7 @@ SOFTWARE.
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
+#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
@@ -108,8 +109,11 @@ SOFTWARE.
#include <machine/stdarg.h>
+#include "loop.h"
+
+extern struct ifnet loif[NLOOP];
+
extern struct timeval time;
-extern struct ifnet loif;
#define EOK 0
@@ -294,7 +298,7 @@ eonrtrequest(cmd, rt, gate)
case RTM_ADD:
case RTM_RESOLVE:
- rt->rt_rmx.rmx_mtu = loif.if_mtu; /* unless better below */
+ rt->rt_rmx.rmx_mtu = loif[0].if_mtu; /* unless better below */
R_Malloc(el, struct eon_llinfo *, sizeof(*el));
rt->rt_llinfo = (caddr_t) el;
if (el == 0)
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index 3d405cc1f8f..26922235c3e 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: malloc.h,v 1.26 1999/11/20 11:11:27 matthieu Exp $ */
+/* $OpenBSD: malloc.h,v 1.27 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: malloc.h,v 1.39 1998/07/12 19:52:01 augustss Exp $ */
/*
@@ -146,7 +146,9 @@
#define M_DISCQ 93 /* IPv6 discq */
#define M_FRAGQ 94 /* IPv6 fragq */
#define M_SECA 95 /* Sec Assoc */
+#if 0 /* NRL IPv6 */
#define M_I6IFP 96 /* IPv6 if info */
+#endif
#define M_RAIDFRAME 97 /* Raidframe data */
@@ -157,6 +159,13 @@
#define M_USB 101 /* USB general */
#define M_USBDEV 102 /* USB device driver */
#define M_USBHC 103 /* USB host controller */
+
+
+/* KAME IPv6 */
+#define M_IP6OPT 123 /* IPv6 options */
+#define M_IP6NDP 124 /* IPv6 Neighbour Discovery */
+#define M_IP6RR 125 /* IPv6 Router Renumbering Prefix */
+#define M_RR_ADDR 126 /* IPv6 Router Renumbering Ifid */
#define M_PIPE 104 /* Pipe structures */
@@ -277,7 +286,11 @@
NULL, NULL, NULL, NULL, NULL, \
NULL, NULL, NULL, NULL, NULL, \
NULL, NULL, NULL, NULL, NULL, \
- NULL, NULL, NULL, NULL, NULL, \
+ NULL, \
+ "ip6_options", /* 123 M_IP6OPT */ \
+ "NDP", /* 124 M_IP6NDP */ \
+ "ip6rr", /* 125 M_IP6RR */ \
+ "rp_addr", /* 126 M_RR_ADDR */ \
"temp", /* 127 M_TEMP */ \
}
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index b4da33a59dd..b2af0b40f22 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: mbuf.h,v 1.13 1999/12/05 07:30:31 angelos Exp $ */
+/* $OpenBSD: mbuf.h,v 1.14 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */
/*
@@ -51,7 +51,7 @@
#define MLEN (MSIZE - sizeof(struct m_hdr)) /* normal data len */
#define MHLEN (MLEN - sizeof(struct pkthdr)) /* data len w/pkthdr */
-#define MINCLSIZE (MHLEN+MLEN+1) /* smallest amount to put in cluster */
+#define MINCLSIZE (MHLEN + 1) /* smallest amount to put in cluster */
#define M_MAXCOMPRESS (MHLEN / 2) /* max amount to copy for compression */
/*
@@ -130,13 +130,25 @@ struct mbuf {
#define M_MCAST 0x0200 /* send/received as link-level multicast */
#define M_CONF 0x0400 /* packet was encrypted (ESP-transport) */
#define M_AUTH 0x0800 /* packet was authenticated (AH) */
+#if 0 /* NRL IPv6 */
#define M_TUNNEL 0x1000 /* packet was tunneled */
-
#define M_DAD 0x2000 /* Used on outbound packets to indicate that
* this is for duplicate address detection */
+#endif
+
+/* KAME IPv6 */
+#define M_ANYCAST6 0x4000 /* received as IPv6 anycast */
+#if 0 /*KAME IPSEC*/
+#define M_AUTHIPHDR 0x0010 /* data origin authentication for IP header */
+#define M_DECRYPTED 0x0020 /* confidentiality */
+#endif
+#define M_LOOP 0x0040 /* for Mbuf statistics */
+#if 0 /*KAME IPSEC*/
+#define M_AUTHIPDGM 0x0080 /* data origin authentication */
+#endif
/* flags copied when copying m_pkthdr */
-#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_BCAST|M_MCAST|M_CONF|M_AUTH|M_TUNNEL|M_DAD)
+#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_BCAST|M_MCAST|M_CONF|M_AUTH|M_ANYCAST6|M_LOOP)
/* mbuf types */
#define MT_FREE 0 /* should be on free list */
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index 8c71d6fb068..eb1e34226b6 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: socket.h,v 1.29 1999/06/06 23:19:08 deraadt Exp $ */
+/* $OpenBSD: socket.h,v 1.30 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: socket.h,v 1.14 1996/02/09 18:25:36 christos Exp $ */
/*
@@ -144,6 +144,14 @@ struct sockaddr {
/*
* Sockaddr type which can hold any sockaddr type available
* in the system.
+ *
+ * Note: __ss_{len,family} is defined in RFC2553. During RFC2553 discussion
+ * the field name went back and forth between ss_len and __ss_len,
+ * and RFC2553 specifies it to be __ss_len. openbsd picked ss_len.
+ * For maximum portability, userland programmer would need to
+ * (1) make the code never touch ss_len portion (cast it into sockaddr and
+ * touch sa_len), or (2) add "-Dss_len=__ss_len" into CFLAGS to unify all
+ * occurences (including header file) to __ss_len.
*/
struct sockaddr_storage {
u_int8_t ss_len; /* total length */
@@ -169,6 +177,7 @@ struct sockproto {
#define PF_LOCAL AF_LOCAL
#define PF_UNIX PF_LOCAL /* backward compatibility */
#define PF_INET AF_INET
+#define PF_INET6 AF_INET6
#define PF_IMPLINK AF_IMPLINK
#define PF_PUP AF_PUP
#define PF_CHAOS AF_CHAOS
@@ -340,7 +349,7 @@ struct cmsghdr {
(((caddr_t)(cmsg) + (cmsg)->cmsg_len + sizeof(struct cmsghdr) > \
(mhdr)->msg_control + (mhdr)->msg_controllen) ? \
(struct cmsghdr *)NULL : \
- (struct cmsghdr *)((caddr_t)(cmsg) + ALIGN((cmsg)->cmsg_len)))
+ (struct cmsghdr *)((caddr_t)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len)))
#define CMSG_FIRSTHDR(mhdr) ((struct cmsghdr *)(mhdr)->msg_control)
@@ -409,6 +418,8 @@ __END_DECLS
# define COMPAT_OLDSOCK
# define MSG_COMPAT 0x8000
# endif
+
+void pfctlinput __P((int, struct sockaddr *));
#endif /* !_KERNEL */
#endif /* !_SYS_SOCKET_H_ */
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index 421014f4b9d..20601ce8e98 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: socketvar.h,v 1.16 1999/02/19 15:06:52 millert Exp $ */
+/* $OpenBSD: socketvar.h,v 1.17 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $ */
/*-
@@ -234,6 +234,8 @@ int sbappendcontrol __P((struct sockbuf *sb, struct mbuf *m0,
void sbappendrecord __P((struct sockbuf *sb, struct mbuf *m0));
void sbcheck __P((struct sockbuf *sb));
void sbcompress __P((struct sockbuf *sb, struct mbuf *m, struct mbuf *n));
+struct mbuf *
+ sbcreatecontrol __P((caddr_t p, int size, int type, int level));
void sbdrop __P((struct sockbuf *sb, int len));
void sbdroprecord __P((struct sockbuf *sb));
void sbflush __P((struct sockbuf *sb));
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
index 28211d2613f..a037802bad9 100644
--- a/sys/sys/sockio.h
+++ b/sys/sys/sockio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sockio.h,v 1.9 1999/03/19 02:46:55 jason Exp $ */
+/* $OpenBSD: sockio.h,v 1.10 1999/12/08 06:50:24 itojun Exp $ */
/* $NetBSD: sockio.h,v 1.5 1995/08/23 00:40:47 thorpej Exp $ */
/*-
@@ -75,6 +75,12 @@
#define SIOCAIFADDR _IOW('i', 26, struct ifaliasreq)/* add/chg IF alias */
#define SIOCGIFDATA _IOWR('i', 27, struct ifreq) /* get if_data */
+/* KAME IPv6 */
+/* SIOCAIFALIAS? */
+#define SIOCALIFADDR _IOW('i', 28, struct if_laddrreq) /* add IF addr */
+#define SIOCGLIFADDR _IOWR('i', 29, struct if_laddrreq) /* get IF addr */
+#define SIOCDLIFADDR _IOW('i', 30, struct if_laddrreq) /* delete IF addr */
+
#define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* add m'cast addr */
#define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* del m'cast addr */
#define SIOCGETVIFCNT _IOWR('u', 51, struct sioc_vif_req)/* vif pkt cnt */
@@ -83,6 +89,10 @@
#define SIOCSIFMEDIA _IOWR('i', 53, struct ifreq) /* set net media */
#define SIOCGIFMEDIA _IOWR('i', 54, struct ifmediareq) /* get net media */
+#define SIOCSIFPHYADDR _IOW('i', 70, struct ifaliasreq) /* set gif addres */
+#define SIOCGIFPSRCADDR _IOWR('i', 71, struct ifreq) /* get gif psrc addr */
+#define SIOCGIFPDSTADDR _IOWR('i', 72, struct ifreq) /* get gif pdst addr */
+
#define SIOCBRDGADD _IOWR('i', 60, struct ifbreq) /* add bridge ifs */
#define SIOCBRDGDEL _IOWR('i', 61, struct ifbreq) /* del bridge ifs */
#define SIOCBRDGGIFFLGS _IOWR('i', 62, struct ifbreq) /* get brdg if flags */