diff options
author | 1999-12-08 06:50:14 +0000 | |
---|---|---|
committer | 1999-12-08 06:50:14 +0000 | |
commit | 287546ea80ee896bda0c88b8a8c85a1dc6ff37f9 (patch) | |
tree | cef428e54b6d2bca56fb9b461aa0667c7fb5f6a2 | |
parent | add GENERIC.v6 (IPv6 test configuration). to be integrated into GENREIC. (diff) | |
download | wireguard-openbsd-287546ea80ee896bda0c88b8a8c85a1dc6ff37f9.tar.xz wireguard-openbsd-287546ea80ee896bda0c88b8a8c85a1dc6ff37f9.zip |
bring in KAME IPv6 code, dated 19991208.
replaces NRL IPv6 layer. reuses NRL pcb layer. no IPsec-on-v6 support.
see sys/netinet6/{TODO,IMPLEMENTATION} for more details.
GENERIC configuration should work fine as before. GENERIC.v6 works fine
as well, but you'll need KAME userland tools to play with IPv6 (will be
bringed into soon).
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 *)⁡ + +#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 *)⁡ + +#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 *)⁡ + +#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(¤t->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(¤t->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(¤t->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 */ |