📄 nd6.c
字号:
//==========================================================================
//
// src/sys/netinet6/nd6.c
//
//==========================================================================
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD,
// FreeBSD or other sources, and are covered by the appropriate
// copyright disclaimers included herein.
//
// Portions created by Red Hat are
// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
/* $KAME: nd6.c,v 1.221 2001/12/18 02:23:45 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/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#ifndef __NetBSD__
#include <netinet/if_ether.h>
#ifdef __bsdi__
#include <net/if_fddi.h>
#endif
#ifdef __OpenBSD__
#include <netinet/ip_ipsp.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 <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
#if defined(__NetBSD__)
extern struct ifnet loif[NLOOP];
#endif
#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
#define SIN6(s) ((struct sockaddr_in6 *)s)
#define SDL(s) ((struct sockaddr_dl *)s)
/* timer values */
int nd6_prune = 1; /* walk list every 1 seconds */
int nd6_delay = 5; /* delay first probe time 5 second */
int nd6_umaxtries = 3; /* maximum unicast query */
int nd6_mmaxtries = 3; /* maximum multicast query */
int nd6_useloopback = 1; /* use loopback interface for local traffic */
int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */
/* preventing too many loops in ND option parsing */
int nd6_maxndopt = 10; /* max # of ND options allowed */
int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */
int nd6_debug = 1;
/* for debugging? */
static int nd6_inuse, nd6_allocated;
struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6};
static size_t nd_ifinfo_indexlim = 8;
struct nd_ifinfo *nd_ifinfo = NULL;
struct nd_drhead nd_defrouter;
struct nd_prhead nd_prefix = { 0 };
int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
static struct sockaddr_in6 all1_sa;
static void nd6_slowtimo __P((void *));
static int regen_tmpaddr __P((struct in6_ifaddr *));
static struct llinfo_nd6 *nd6_free __P((struct rtentry *, int));
#ifdef __NetBSD__
struct callout nd6_slowtimo_ch = CALLOUT_INITIALIZER;
struct callout nd6_timer_ch = CALLOUT_INITIALIZER;
extern struct callout in6_tmpaddrtimer_ch;
#elif (defined(__FreeBSD__) && __FreeBSD__ >= 3)
struct callout nd6_slowtimo_ch;
struct callout nd6_timer_ch;
extern struct callout in6_tmpaddrtimer_ch;
#elif defined(__OpenBSD__)
struct timeout nd6_slowtimo_ch;
struct timeout nd6_timer_ch;
extern struct timeout in6_tmpaddrtimer_ch;
#endif
void
nd6_init()
{
static int nd6_init_done = 0;
int i;
if (nd6_init_done) {
log(LOG_NOTICE, "nd6_init called more than once(ignored)\n");
return;
}
all1_sa.sin6_family = AF_INET6;
all1_sa.sin6_len = sizeof(struct sockaddr_in6);
for (i = 0; i < sizeof(all1_sa.sin6_addr); i++)
all1_sa.sin6_addr.s6_addr[i] = 0xff;
/* initialization of the default router list */
TAILQ_INIT(&nd_defrouter);
nd6_init_done = 1;
/* start timer */
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
nd6_slowtimo, NULL);
#elif defined(__OpenBSD__)
timeout_set(&nd6_slowtimo_ch, nd6_slowtimo, NULL);
timeout_add(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz);
#else
timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz);
#endif
}
void
nd6_ifattach(ifp)
struct ifnet *ifp;
{
/*
* We have some arrays that should be indexed by if_index.
* since if_index will grow dynamically, they should grow too.
*/
if (nd_ifinfo == NULL || if_index >= nd_ifinfo_indexlim) {
size_t n;
caddr_t q;
while (if_index >= nd_ifinfo_indexlim)
nd_ifinfo_indexlim <<= 1;
/* grow nd_ifinfo */
n = nd_ifinfo_indexlim * sizeof(struct nd_ifinfo);
q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK);
bzero(q, n);
if (nd_ifinfo) {
bcopy((caddr_t)nd_ifinfo, q, n/2);
free((caddr_t)nd_ifinfo, M_IP6NDP);
}
nd_ifinfo = (struct nd_ifinfo *)q;
}
#define ND nd_ifinfo[ifp->if_index]
/*
* Don't initialize if called twice.
* XXX: to detect this, we should choose a member that is never set
* before initialization of the ND structure itself. We formaly used
* the linkmtu member, which was not suitable because it could be
* initialized via "ifconfig mtu".
*/
if (ND.basereachable)
return;
#ifdef DIAGNOSTIC
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
if (!ifnet_byindex(ifp->if_index))
panic("nd6_ifattach: ifnet_byindex is NULL");
#else
if (!ifindex2ifnet[ifp->if_index])
panic("nd6_ifattach: ifindex2ifnet is NULL");
#endif
#endif
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
ND.linkmtu = ifnet_byindex(ifp->if_index)->if_mtu;
#else
ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu;
#endif
ND.chlim = IPV6_DEFHLIM;
ND.basereachable = REACHABLE_TIME;
ND.reachable = ND_COMPUTE_RTIME(ND.basereachable);
ND.retrans = RETRANS_TIMER;
ND.receivedra = 0;
/*
* Note that the default value of ip6_accept_rtadv is 0, which means
* we won't accept RAs by default even if we set ND6_IFF_ACCEPT_RTADV
* here.
*/
ND.flags = ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV;
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;
{
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
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 0 // FIXME
#if defined(__bsdi__) && _BSDI_VERSION >= 199802
ndi->maxmtu = MIN(FDDIMTU, ifp->if_mtu);
#else
ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu);
#endif
#endif
break;
#endif
#if !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
case IFT_ATM:
#if 0 // FIXME
ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu);
#endif
break;
#endif
case IFT_IEEE1394: /* XXX should be IEEE1394MTU(1500) */
ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu);
break;
#ifdef IFT_IEEE80211
case IFT_IEEE80211: /* XXX should be IEEE80211MTU(1500) */
ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu);
break;
#endif
default:
ndi->maxmtu = ifp->if_mtu;
break;
}
if (oldmaxmtu != ndi->maxmtu) {
/*
* If the ND level MTU is not set yet, or if the maxmtu
* is reset to a smaller value than the ND level MTU,
* also reset the ND level MTU.
*/
if (ndi->linkmtu == 0 ||
ndi->maxmtu < ndi->linkmtu) {
ndi->linkmtu = ndi->maxmtu;
/* also adjust in6_maxmtu if necessary. */
if (oldlinkmtu == 0) {
/*
* XXX: the case analysis is grotty, but
* it is not efficient to call in6_setmaxmtu()
* here when we are during the initialization
* procedure.
*/
if (in6_maxmtu < ndi->linkmtu)
in6_maxmtu = ndi->linkmtu;
} else
in6_setmaxmtu();
}
}
#undef MIN
}
void
nd6_option_init(opt, icmp6len, ndopts)
void *opt;
int icmp6len;
union nd_opts *ndopts;
{
bzero(ndopts, sizeof(*ndopts));
ndopts->nd_opts_search = (struct nd_opt_hdr *)opt;
ndopts->nd_opts_last
= (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len);
if (icmp6len == 0) {
ndopts->nd_opts_done = 1;
ndopts->nd_opts_search = NULL;
}
}
/*
* Take one ND option.
*/
struct nd_opt_hdr *
nd6_option(ndopts)
union nd_opts *ndopts;
{
struct nd_opt_hdr *nd_opt;
int olen;
if (!ndopts)
panic("ndopts == NULL in nd6_option\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;
/* make sure nd_opt_len is inside the buffer */
if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) {
bzero(ndopts, sizeof(*ndopts));
return NULL;
}
olen = nd_opt->nd_opt_len << 3;
if (olen == 0) {
/*
* Message validation requires that all included
* options have a length that is greater than zero.
*/
bzero(ndopts, sizeof(*ndopts));
return NULL;
}
ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen);
if (ndopts->nd_opts_search > ndopts->nd_opts_last) {
/* option overruns the end of buffer, invalid */
bzero(ndopts, sizeof(*ndopts));
return NULL;
} else if (ndopts->nd_opts_search == ndopts->nd_opts_last) {
/* reached the end of options chain */
ndopts->nd_opts_done = 1;
ndopts->nd_opts_search = NULL;
}
return nd_opt;
}
/*
* Parse multiple ND options.
* This function is much easier to use, for ND routines that do not need
* multiple options of the same type.
*/
int
nd6_options(ndopts)
union nd_opts *ndopts;
{
struct nd_opt_hdr *nd_opt;
int i = 0;
if (!ndopts)
panic("ndopts == NULL in nd6_options\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.
*/
icmp6stat.icp6s_nd_badopt++;
bzero(ndopts, sizeof(*ndopts));
return -1;
}
if (!nd_opt)
goto skip1;
switch (nd_opt->nd_opt_type) {
case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_TARGET_LINKADDR:
case ND_OPT_MTU:
case ND_OPT_REDIRECTED_HEADER:
case ND_OPT_ADVINTERVAL:
case ND_OPT_SOURCE_ADDRLIST:
case ND_OPT_TARGET_ADDRLIST:
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
nd6log((LOG_INFO,
"duplicated ND6 option found (type=%d)\n",
nd_opt->nd_opt_type));
/* XXX bark? */
} else {
ndopts->nd_opt_array[nd_opt->nd_opt_type]
= nd_opt;
}
break;
case ND_OPT_PREFIX_INFORMATION:
if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
ndopts->nd_opt_array[nd_opt->nd_opt_type]
= nd_opt;
}
ndopts->nd_opts_pi_end =
(struct nd_opt_prefix_info *)nd_opt;
break;
case ND_OPT_HOMEAGENT_INFO:
break;
default:
/*
* Unknown options must be silently ignored,
* to accomodate future extension to the protocol.
*/
nd6log((LOG_DEBUG,
"nd6_options: unsupported option %d - "
"option ignored\n", nd_opt->nd_opt_type));
}
skip1:
i++;
if (i > nd6_maxndopt) {
icmp6stat.icp6s_nd_toomanyopt++;
nd6log((LOG_INFO, "too many loop in nd opt\n"));
break;
}
if (ndopts->nd_opts_done)
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -