diff options
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 */ |