📄 nd6_rtr.c
字号:
//==========================================================================
//
// src/sys/netinet6/nd6_rtr.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_rtr.c,v 1.187 2001/12/18 02:01:25 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/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/queue.h>
#ifdef __OpenBSD__
#include <dev/rndvar.h>
#endif
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <net/radix.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet6/in6_ifattach.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
#include <netinet6/scope6_var.h>
#ifdef MIP6
#include <netinet6/mip6.h>
#endif /* MIP6 */
#define SDL(s) ((struct sockaddr_dl *)s)
static int rtpref __P((struct nd_defrouter *));
static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
static struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *));
static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
struct nd_defrouter *));
static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
static void pfxrtr_del __P((struct nd_pfxrouter *));
static struct nd_pfxrouter *find_pfxlist_reachable_router
__P((struct nd_prefix *));
static void defrouter_delreq __P((struct nd_defrouter *));
static void defrouter_addifreq __P((struct ifnet *));
static void defrouter_delifreq __P((void));
static void nd6_rtmsg __P((int, struct rtentry *));
static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
struct in6_addrlifetime *lt6));
static int rt6_deleteroute __P((struct radix_node *, void *));
extern int nd6_recalc_reachtm_interval;
static struct ifnet *nd6_defifp;
int nd6_defifindex;
static struct ifaddr *nd6_defif_installed = NULL;
int ip6_use_tempaddr = 0;
int ip6_desync_factor;
u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME;
u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME;
int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE;
/* RTPREF_MEDIUM has to be 0! */
#define RTPREF_HIGH 1
#define RTPREF_MEDIUM 0
#define RTPREF_LOW (-1)
#define RTPREF_INVALID (-2)
/*
* Receive Router Solicitation Message - just for routers.
* Router solicitation/advertisement is mostly managed by userland program
* (rtadvd) so here we have no function like nd6_ra_output().
*
* Based on RFC 2461
*/
void
nd6_rs_input(m, off, icmp6len)
struct mbuf *m;
int off, icmp6len;
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_router_solicit *nd_rs;
struct in6_addr saddr6 = ip6->ip6_src;
#if 0
struct in6_addr daddr6 = ip6->ip6_dst;
#endif
char *lladdr = NULL;
int lladdrlen = 0;
#if 0
struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL;
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
struct rtentry *rt = NULL;
int is_newentry;
#endif
union nd_opts ndopts;
/* If I'm not a router, ignore it. */
if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
goto freeit;
/* Sanity checks */
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n",
ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
goto bad;
}
/*
* Don't update the neighbor cache, if src = ::.
* This indicates that the src has no IP address assigned yet.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
goto freeit;
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
if (nd_rs == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
icmp6len -= sizeof(*nd_rs);
nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO,
"nd6_rs_input: invalid ND option, ignored\n"));
/* nd6_options have incremented stats */
goto freeit;
}
if (ndopts.nd_opts_src_lladdr) {
lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
nd6log((LOG_INFO,
"nd6_rs_input: lladdrlen mismatch for %s "
"(if %d, RS packet %d)\n",
ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
freeit:
m_freem(m);
return;
bad:
icmp6stat.icp6s_badrs++;
m_freem(m);
}
/*
* Receive Router Advertisement Message.
*
* Based on RFC 2461
* TODO: on-link bit on prefix information
* TODO: ND_RA_FLAG_{OTHER,MANAGED} processing
*/
void
nd6_ra_input(m, off, icmp6len)
struct mbuf *m;
int off, icmp6len;
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
struct nd_router_advert *nd_ra;
struct in6_addr saddr6 = ip6->ip6_src;
#if 0
struct in6_addr daddr6 = ip6->ip6_dst;
int flags; /* = nd_ra->nd_ra_flags_reserved; */
int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
#endif
union nd_opts ndopts;
struct nd_defrouter *dr;
/*
* We only accept RAs only when
* the system-wide variable allows the acceptance, and
* per-interface variable allows RAs on the receiving interface.
*/
if (ip6_accept_rtadv == 0)
goto freeit;
if (!(nd_ifinfo[ifp->if_index].flags & ND6_IFF_ACCEPT_RTADV))
goto freeit;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
"nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n",
ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));
goto bad;
}
if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
nd6log((LOG_ERR,
"nd6_ra_input: src %s is not link-local\n",
ip6_sprintf(&saddr6)));
goto bad;
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, icmp6len,);
nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
#else
IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
if (nd_ra == NULL) {
icmp6stat.icp6s_tooshort++;
return;
}
#endif
icmp6len -= sizeof(*nd_ra);
nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
nd6log((LOG_INFO,
"nd6_ra_input: invalid ND option, ignored\n"));
/* nd6_options have incremented stats */
goto freeit;
}
{
struct nd_defrouter dr0;
u_int32_t advreachable = nd_ra->nd_ra_reachable;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
Bzero(&dr0, sizeof(dr0));
dr0.rtaddr = saddr6;
dr0.flags = nd_ra->nd_ra_flags_reserved;
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
dr0.expire = time_second + dr0.rtlifetime;
dr0.ifp = ifp;
dr0.advint = 0; /* Mobile IPv6 */
dr0.advint_expire = 0; /* Mobile IPv6 */
dr0.advints_lost = 0; /* Mobile IPv6 */
/* unspecified or not? (RFC 2461 6.3.4) */
if (advreachable) {
NTOHL(advreachable);
if (advreachable <= MAX_REACHABLE_TIME &&
ndi->basereachable != advreachable) {
ndi->basereachable = advreachable;
ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable);
ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */
}
}
if (nd_ra->nd_ra_retransmit)
ndi->retrans = ntohl(nd_ra->nd_ra_retransmit);
if (nd_ra->nd_ra_curhoplimit)
ndi->chlim = nd_ra->nd_ra_curhoplimit;
dr = defrtrlist_update(&dr0);
}
/*
* prefix
*/
if (ndopts.nd_opts_pi) {
struct nd_opt_hdr *pt;
struct nd_opt_prefix_info *pi = NULL;
struct nd_prefix pr;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
pt = (struct nd_opt_hdr *)((caddr_t)pt +
(pt->nd_opt_len << 3))) {
if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION)
continue;
pi = (struct nd_opt_prefix_info *)pt;
if (pi->nd_opt_pi_len != 4) {
nd6log((LOG_INFO,
"nd6_ra_input: invalid option "
"len %d for prefix information option, "
"ignored\n", pi->nd_opt_pi_len));
continue;
}
if (128 < pi->nd_opt_pi_prefix_len) {
nd6log((LOG_INFO,
"nd6_ra_input: invalid prefix "
"len %d for prefix information option, "
"ignored\n", pi->nd_opt_pi_prefix_len));
continue;
}
if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix)
|| IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
nd6log((LOG_INFO,
"nd6_ra_input: invalid prefix "
"%s, ignored\n",
ip6_sprintf(&pi->nd_opt_pi_prefix)));
continue;
}
/* aggregatable unicast address, rfc2374 */
if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20
&& pi->nd_opt_pi_prefix_len != 64) {
nd6log((LOG_INFO,
"nd6_ra_input: invalid prefixlen "
"%d for rfc2374 prefix %s, ignored\n",
pi->nd_opt_pi_prefix_len,
ip6_sprintf(&pi->nd_opt_pi_prefix)));
continue;
}
bzero(&pr, sizeof(pr));
pr.ndpr_prefix.sin6_family = AF_INET6;
pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix);
pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix;
pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
#ifdef MIP6
pr.ndpr_raf_router = (pi->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_ROUTER) ? 1 : 0;
#endif /* MIP6 */
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);
pr.ndpr_lastupdate = time_second;
if (in6_init_prefix_ltimes(&pr))
continue; /* prefix lifetime init failed */
#ifdef MIP6
if (MIP6_IS_MN) {
if (mip6_prefix_list_update(&saddr6, &pr, dr, m)) {
mip6log((LOG_ERR,
"%s:%d: "
"prefix info processing "
"failed\n",
__FILE__, __LINE__));
goto freeit;
}
}
#endif /* MIP6 */
(void)prelist_update(&pr, dr, m);
}
}
#ifdef MIP6
if (MIP6_IS_MN) {
/* check reachability of all routers. */
mip6_probe_routers();
}
/* update home agent list. */
if ((MIP6_IS_MN || MIP6_IS_HA)
&& dr
&& (dr->flags & ND_RA_FLAG_HOME_AGENT)) {
if (mip6_ha_list_update_hainfo(&mip6_ha_list,
dr, ndopts.nd_opts_hai)) {
mip6log((LOG_ERR,
"%s:%d: global HA list update failed\n",
__FILE__, __LINE__));
}
}
if (dr == NULL) {
struct mip6_ha *mha;
/* the home agent is shutting down. */
mha = mip6_ha_list_find_withaddr(&mip6_ha_list, &saddr6);
if (mha) {
if (mip6_ha_list_remove(&mip6_ha_list, mha)) {
mip6log((LOG_ERR,
"%s:%d: HA entry remove failed.\n",
__FILE__, __LINE__));
}
}
}
#endif /* MIP6 */
/*
* MTU
*/
if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
/* lower bound */
if (mtu < IPV6_MMTU) {
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option "
"mtu=%d sent from %s, ignoring\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -