nd6 defrouter: consolidate nd_defrouter manipulations in nd6_rtr.c
Move the nd_defrouter along with the sysctl handler from nd6.c to nd6_rtr.c and make the variable file static. Provide (temporary) new accessor functions for code manipulating nd_defrouter from nd6.c, and stop exporting functions no longer needed outside nd6_rtr.c. This also shuffles a few functions around in nd6_rtr.c without functional changes. Given all nd_defrouter logic is now in one place we can tidy up the code, locking and, and other open items. MFC after: 3 weeks X-MFC: keep exporting the functions Sponsored by: Netflix
This commit is contained in:
parent
228fa8096b
commit
4a111320e1
@ -116,7 +116,6 @@ VNET_DEFINE(int, nd6_debug) = 0;
|
||||
|
||||
static eventhandler_tag lle_event_eh, iflladdr_event_eh, ifnet_link_event_eh;
|
||||
|
||||
VNET_DEFINE(struct nd_drhead, nd_defrouter);
|
||||
VNET_DEFINE(struct nd_prhead, nd_prefix);
|
||||
VNET_DEFINE(struct rwlock, nd6_lock);
|
||||
VNET_DEFINE(uint64_t, nd6_list_genid);
|
||||
@ -218,7 +217,7 @@ nd6_init(void)
|
||||
rw_init(&V_nd6_lock, "nd6 list");
|
||||
|
||||
LIST_INIT(&V_nd_prefix);
|
||||
TAILQ_INIT(&V_nd_defrouter);
|
||||
nd6_defrouter_init();
|
||||
|
||||
/* Start timers. */
|
||||
callout_init(&V_nd6_slowtimo_ch, 0);
|
||||
@ -901,28 +900,16 @@ nd6_timer(void *arg)
|
||||
{
|
||||
CURVNET_SET((struct vnet *) arg);
|
||||
struct epoch_tracker et;
|
||||
struct nd_drhead drq;
|
||||
struct nd_prhead prl;
|
||||
struct nd_defrouter *dr, *ndr;
|
||||
struct nd_prefix *pr, *npr;
|
||||
struct ifnet *ifp;
|
||||
struct in6_ifaddr *ia6, *nia6;
|
||||
uint64_t genid;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
LIST_INIT(&prl);
|
||||
|
||||
ND6_WLOCK();
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr)
|
||||
if (dr->expire && dr->expire < time_uptime)
|
||||
defrouter_unlink(dr, &drq);
|
||||
ND6_WUNLOCK();
|
||||
|
||||
NET_EPOCH_ENTER(et);
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
nd6_defrouter_timer();
|
||||
|
||||
/*
|
||||
* expire interface addresses.
|
||||
@ -1146,34 +1133,15 @@ regen_tmpaddr(struct in6_ifaddr *ia6)
|
||||
void
|
||||
nd6_purge(struct ifnet *ifp)
|
||||
{
|
||||
struct nd_drhead drq;
|
||||
struct nd_prhead prl;
|
||||
struct nd_defrouter *dr, *ndr;
|
||||
struct nd_prefix *pr, *npr;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
LIST_INIT(&prl);
|
||||
|
||||
/*
|
||||
* Nuke default router list entries toward ifp.
|
||||
* We defer removal of default router list entries that is installed
|
||||
* in the routing table, in order to keep additional side effects as
|
||||
* small as possible.
|
||||
*/
|
||||
ND6_WLOCK();
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) {
|
||||
if (dr->installed)
|
||||
continue;
|
||||
if (dr->ifp == ifp)
|
||||
defrouter_unlink(dr, &drq);
|
||||
}
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) {
|
||||
if (!dr->installed)
|
||||
continue;
|
||||
if (dr->ifp == ifp)
|
||||
defrouter_unlink(dr, &drq);
|
||||
}
|
||||
/* Purge default router list entries toward ifp. */
|
||||
nd6_defrouter_purge(ifp);
|
||||
|
||||
ND6_WLOCK();
|
||||
/*
|
||||
* Remove prefixes on ifp. We should have already removed addresses on
|
||||
* this interface, so no addresses should be referencing these prefixes.
|
||||
@ -1184,11 +1152,7 @@ nd6_purge(struct ifnet *ifp)
|
||||
}
|
||||
ND6_WUNLOCK();
|
||||
|
||||
/* Delete the unlinked router and prefix objects. */
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
/* Delete the unlinked prefix objects. */
|
||||
while ((pr = LIST_FIRST(&prl)) != NULL) {
|
||||
LIST_REMOVE(pr, ndpr_entry);
|
||||
nd6_prefix_del(pr);
|
||||
@ -1376,7 +1340,7 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
|
||||
* as on-link, and thus, as a neighbor.
|
||||
*/
|
||||
if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV &&
|
||||
TAILQ_EMPTY(&V_nd_defrouter) &&
|
||||
nd6_defrouter_list_empty() &&
|
||||
V_nd6_defifindex == ifp->if_index) {
|
||||
return (1);
|
||||
}
|
||||
@ -1819,22 +1783,9 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
|
||||
case SIOCSRTRFLUSH_IN6:
|
||||
{
|
||||
/* flush all the default routers */
|
||||
struct nd_drhead drq;
|
||||
struct nd_defrouter *dr;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
|
||||
defrouter_reset();
|
||||
|
||||
ND6_WLOCK();
|
||||
while ((dr = TAILQ_FIRST(&V_nd_defrouter)) != NULL)
|
||||
defrouter_unlink(dr, &drq);
|
||||
ND6_WUNLOCK();
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
|
||||
nd6_defrouter_flush_all();
|
||||
defrouter_select();
|
||||
break;
|
||||
}
|
||||
@ -2626,14 +2577,9 @@ clear_llinfo_pqueue(struct llentry *ln)
|
||||
ln->la_hold = NULL;
|
||||
}
|
||||
|
||||
static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS);
|
||||
static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
SYSCTL_DECL(_net_inet6_icmp6);
|
||||
SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
|
||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter",
|
||||
"NDP default router list");
|
||||
SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist,
|
||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, nd6_sysctl_prlist, "S,in6_prefix",
|
||||
@ -2643,42 +2589,6 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen,
|
||||
SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_gctimer,
|
||||
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_gctimer), (60 * 60 * 24), "");
|
||||
|
||||
static int
|
||||
nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct in6_defrouter d;
|
||||
struct nd_defrouter *dr;
|
||||
int error;
|
||||
|
||||
if (req->newptr != NULL)
|
||||
return (EPERM);
|
||||
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
bzero(&d, sizeof(d));
|
||||
d.rtaddr.sin6_family = AF_INET6;
|
||||
d.rtaddr.sin6_len = sizeof(d.rtaddr);
|
||||
|
||||
ND6_RLOCK();
|
||||
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
|
||||
d.rtaddr.sin6_addr = dr->rtaddr;
|
||||
error = sa6_recoverscope(&d.rtaddr);
|
||||
if (error != 0)
|
||||
break;
|
||||
d.flags = dr->raflags;
|
||||
d.rtlifetime = dr->rtlifetime;
|
||||
d.expire = dr->expire + (time_second - time_uptime);
|
||||
d.if_index = dr->ifp->if_index;
|
||||
error = SYSCTL_OUT(req, &d, sizeof(d));
|
||||
if (error != 0)
|
||||
break;
|
||||
}
|
||||
ND6_RUNLOCK();
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
|
@ -335,7 +335,6 @@ VNET_DECLARE(int, nd6_mmaxtries);
|
||||
VNET_DECLARE(int, nd6_useloopback);
|
||||
VNET_DECLARE(int, nd6_maxnudhint);
|
||||
VNET_DECLARE(int, nd6_gctimer);
|
||||
VNET_DECLARE(struct nd_drhead, nd_defrouter);
|
||||
VNET_DECLARE(struct nd_prhead, nd_prefix);
|
||||
VNET_DECLARE(int, nd6_debug);
|
||||
VNET_DECLARE(int, nd6_onlink_ns_rfc4861);
|
||||
@ -346,7 +345,6 @@ VNET_DECLARE(int, nd6_onlink_ns_rfc4861);
|
||||
#define V_nd6_useloopback VNET(nd6_useloopback)
|
||||
#define V_nd6_maxnudhint VNET(nd6_maxnudhint)
|
||||
#define V_nd6_gctimer VNET(nd6_gctimer)
|
||||
#define V_nd_defrouter VNET(nd_defrouter)
|
||||
#define V_nd_prefix VNET(nd_prefix)
|
||||
#define V_nd6_debug VNET(nd6_debug)
|
||||
#define V_nd6_onlink_ns_rfc4861 VNET(nd6_onlink_ns_rfc4861)
|
||||
@ -477,14 +475,18 @@ void nd6_dad_stop(struct ifaddr *);
|
||||
void nd6_rs_input(struct mbuf *, int, int);
|
||||
void nd6_ra_input(struct mbuf *, int, int);
|
||||
void nd6_ifnet_link_event(void *, struct ifnet *, int);
|
||||
struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *);
|
||||
struct nd_defrouter *defrouter_lookup_locked(struct in6_addr *, struct ifnet *);
|
||||
void defrouter_reset(void);
|
||||
void defrouter_select_fib(int fibnum);
|
||||
void defrouter_select(void);
|
||||
void defrouter_ref(struct nd_defrouter *);
|
||||
void defrouter_rele(struct nd_defrouter *);
|
||||
bool defrouter_remove(struct in6_addr *, struct ifnet *);
|
||||
void defrouter_unlink(struct nd_defrouter *, struct nd_drhead *);
|
||||
void defrouter_del(struct nd_defrouter *);
|
||||
bool nd6_defrouter_list_empty(void);
|
||||
void nd6_defrouter_flush_all(void);
|
||||
void nd6_defrouter_purge(struct ifnet *);
|
||||
void nd6_defrouter_timer(void);
|
||||
void nd6_defrouter_init(void);
|
||||
int nd6_prelist_add(struct nd_prefixctl *, struct nd_defrouter *,
|
||||
struct nd_prefix **);
|
||||
void nd6_prefix_unlink(struct nd_prefix *, struct nd_prhead *);
|
||||
@ -494,8 +496,6 @@ void nd6_prefix_rele(struct nd_prefix *);
|
||||
int nd6_prefix_onlink(struct nd_prefix *);
|
||||
int nd6_prefix_offlink(struct nd_prefix *);
|
||||
void pfxlist_onlink_check(void);
|
||||
struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *);
|
||||
struct nd_defrouter *defrouter_lookup_locked(struct in6_addr *, struct ifnet *);
|
||||
struct nd_prefix *nd6_prefix_lookup(struct nd_prefixctl *);
|
||||
void rt6_flush(struct in6_addr *, struct ifnet *);
|
||||
int nd6_setdefaultiface(int);
|
||||
|
@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/errno.h>
|
||||
#include <sys/rmlock.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
@ -91,6 +92,9 @@ static void in6_init_address_ltimes(struct nd_prefix *,
|
||||
|
||||
static int rt6_deleteroute(const struct rtentry *, void *);
|
||||
|
||||
VNET_DEFINE_STATIC(struct nd_drhead, nd_defrouter);
|
||||
#define V_nd_defrouter VNET(nd_defrouter)
|
||||
|
||||
VNET_DECLARE(int, nd6_recalc_reachtm_interval);
|
||||
#define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval)
|
||||
|
||||
@ -117,6 +121,37 @@ VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1;
|
||||
#define RTPREF_RESERVED (-2)
|
||||
#define RTPREF_INVALID (-3) /* internal */
|
||||
|
||||
static void
|
||||
defrouter_ref(struct nd_defrouter *dr)
|
||||
{
|
||||
|
||||
refcount_acquire(&dr->refcnt);
|
||||
}
|
||||
|
||||
void
|
||||
defrouter_rele(struct nd_defrouter *dr)
|
||||
{
|
||||
|
||||
if (refcount_release(&dr->refcnt))
|
||||
free(dr, M_IP6NDP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a router from the global list and optionally stash it in a
|
||||
* caller-supplied queue.
|
||||
*/
|
||||
static void
|
||||
defrouter_unlink(struct nd_defrouter *dr, struct nd_drhead *drq)
|
||||
{
|
||||
|
||||
ND6_WLOCK_ASSERT();
|
||||
|
||||
TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry);
|
||||
V_nd6_list_genid++;
|
||||
if (drq != NULL)
|
||||
TAILQ_INSERT_TAIL(drq, dr, dr_entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive Router Solicitation Message - just for routers.
|
||||
* Router solicitation/advertisement is mostly managed by userland program
|
||||
@ -670,21 +705,6 @@ defrouter_lookup(struct in6_addr *addr, struct ifnet *ifp)
|
||||
return (dr);
|
||||
}
|
||||
|
||||
void
|
||||
defrouter_ref(struct nd_defrouter *dr)
|
||||
{
|
||||
|
||||
refcount_acquire(&dr->refcnt);
|
||||
}
|
||||
|
||||
void
|
||||
defrouter_rele(struct nd_defrouter *dr)
|
||||
{
|
||||
|
||||
if (refcount_release(&dr->refcnt))
|
||||
free(dr, M_IP6NDP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the default route for a given router.
|
||||
* This is just a subroutine function for defrouter_select_fib(), and
|
||||
@ -759,47 +779,7 @@ defrouter_reset(void)
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a matching default router list entry and remove it. Returns true if a
|
||||
* matching entry was found, false otherwise.
|
||||
*/
|
||||
bool
|
||||
defrouter_remove(struct in6_addr *addr, struct ifnet *ifp)
|
||||
{
|
||||
struct nd_defrouter *dr;
|
||||
|
||||
ND6_WLOCK();
|
||||
dr = defrouter_lookup_locked(addr, ifp);
|
||||
if (dr == NULL) {
|
||||
ND6_WUNLOCK();
|
||||
return (false);
|
||||
}
|
||||
|
||||
defrouter_unlink(dr, NULL);
|
||||
ND6_WUNLOCK();
|
||||
defrouter_del(dr);
|
||||
defrouter_rele(dr);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a router from the global list and optionally stash it in a
|
||||
* caller-supplied queue.
|
||||
*
|
||||
* The ND lock must be held.
|
||||
*/
|
||||
void
|
||||
defrouter_unlink(struct nd_defrouter *dr, struct nd_drhead *drq)
|
||||
{
|
||||
|
||||
ND6_WLOCK_ASSERT();
|
||||
TAILQ_REMOVE(&V_nd_defrouter, dr, dr_entry);
|
||||
V_nd6_list_genid++;
|
||||
if (drq != NULL)
|
||||
TAILQ_INSERT_TAIL(drq, dr, dr_entry);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
defrouter_del(struct nd_defrouter *dr)
|
||||
{
|
||||
struct nd_defrouter *deldr = NULL;
|
||||
@ -850,6 +830,30 @@ defrouter_del(struct nd_defrouter *dr)
|
||||
defrouter_rele(dr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Look up a matching default router list entry and remove it. Returns true if a
|
||||
* matching entry was found, false otherwise.
|
||||
*/
|
||||
bool
|
||||
defrouter_remove(struct in6_addr *addr, struct ifnet *ifp)
|
||||
{
|
||||
struct nd_defrouter *dr;
|
||||
|
||||
ND6_WLOCK();
|
||||
dr = defrouter_lookup_locked(addr, ifp);
|
||||
if (dr == NULL) {
|
||||
ND6_WUNLOCK();
|
||||
return (false);
|
||||
}
|
||||
|
||||
defrouter_unlink(dr, NULL);
|
||||
ND6_WUNLOCK();
|
||||
defrouter_del(dr);
|
||||
defrouter_rele(dr);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Default Router Selection according to Section 6.3.6 of RFC 2461 and
|
||||
* draft-ietf-ipngwg-router-selection:
|
||||
@ -2532,3 +2536,132 @@ nd6_setdefaultiface(int ifindex)
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct in6_defrouter d;
|
||||
struct nd_defrouter *dr;
|
||||
int error;
|
||||
|
||||
if (req->newptr != NULL)
|
||||
return (EPERM);
|
||||
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
bzero(&d, sizeof(d));
|
||||
d.rtaddr.sin6_family = AF_INET6;
|
||||
d.rtaddr.sin6_len = sizeof(d.rtaddr);
|
||||
|
||||
ND6_RLOCK();
|
||||
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
|
||||
d.rtaddr.sin6_addr = dr->rtaddr;
|
||||
error = sa6_recoverscope(&d.rtaddr);
|
||||
if (error != 0)
|
||||
break;
|
||||
d.flags = dr->raflags;
|
||||
d.rtlifetime = dr->rtlifetime;
|
||||
d.expire = dr->expire + (time_second - time_uptime);
|
||||
d.if_index = dr->ifp->if_index;
|
||||
error = SYSCTL_OUT(req, &d, sizeof(d));
|
||||
if (error != 0)
|
||||
break;
|
||||
}
|
||||
ND6_RUNLOCK();
|
||||
return (error);
|
||||
}
|
||||
SYSCTL_DECL(_net_inet6_icmp6);
|
||||
SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
|
||||
CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, nd6_sysctl_drlist, "S,in6_defrouter",
|
||||
"NDP default router list");
|
||||
|
||||
bool
|
||||
nd6_defrouter_list_empty(void)
|
||||
{
|
||||
|
||||
return (TAILQ_EMPTY(&V_nd_defrouter));
|
||||
}
|
||||
|
||||
void
|
||||
nd6_defrouter_timer(void)
|
||||
{
|
||||
struct nd_defrouter *dr, *ndr;
|
||||
struct nd_drhead drq;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
|
||||
ND6_WLOCK();
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr)
|
||||
if (dr->expire && dr->expire < time_uptime)
|
||||
defrouter_unlink(dr, &drq);
|
||||
ND6_WUNLOCK();
|
||||
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Nuke default router list entries toward ifp.
|
||||
* We defer removal of default router list entries that is installed in the
|
||||
* routing table, in order to keep additional side effects as small as possible.
|
||||
*/
|
||||
void
|
||||
nd6_defrouter_purge(struct ifnet *ifp)
|
||||
{
|
||||
struct nd_defrouter *dr, *ndr;
|
||||
struct nd_drhead drq;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
|
||||
ND6_WLOCK();
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) {
|
||||
if (dr->installed)
|
||||
continue;
|
||||
if (dr->ifp == ifp)
|
||||
defrouter_unlink(dr, &drq);
|
||||
}
|
||||
TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) {
|
||||
if (!dr->installed)
|
||||
continue;
|
||||
if (dr->ifp == ifp)
|
||||
defrouter_unlink(dr, &drq);
|
||||
}
|
||||
ND6_WUNLOCK();
|
||||
|
||||
/* Delete the unlinked router objects. */
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nd6_defrouter_flush_all(void)
|
||||
{
|
||||
struct nd_defrouter *dr;
|
||||
struct nd_drhead drq;
|
||||
|
||||
TAILQ_INIT(&drq);
|
||||
|
||||
ND6_WLOCK();
|
||||
while ((dr = TAILQ_FIRST(&V_nd_defrouter)) != NULL)
|
||||
defrouter_unlink(dr, &drq);
|
||||
ND6_WUNLOCK();
|
||||
|
||||
while ((dr = TAILQ_FIRST(&drq)) != NULL) {
|
||||
TAILQ_REMOVE(&drq, dr, dr_entry);
|
||||
defrouter_del(dr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nd6_defrouter_init(void)
|
||||
{
|
||||
|
||||
TAILQ_INIT(&V_nd_defrouter);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user