Nest: Neighbor cache cleanups
Simplify neighbor cache code, fix several minor bugs, and improve handling of ONLINK flag.
This commit is contained in:
parent
45f28d8581
commit
586c1800c4
13 changed files with 267 additions and 272 deletions
|
@ -974,7 +974,7 @@ interpret(struct f_inst *what)
|
||||||
case SA_GW:
|
case SA_GW:
|
||||||
{
|
{
|
||||||
ip_addr ip = v1.val.ip;
|
ip_addr ip = v1.val.ip;
|
||||||
neighbor *n = neigh_find(rta->src->proto, &ip, 0);
|
neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0);
|
||||||
if (!n || (n->scope == SCOPE_HOST))
|
if (!n || (n->scope == SCOPE_HOST))
|
||||||
runtime( "Invalid gw address" );
|
runtime( "Invalid gw address" );
|
||||||
|
|
||||||
|
|
|
@ -230,4 +230,11 @@ mem_hash(void *p, uint s)
|
||||||
return mem_hash_value(&h);
|
return mem_hash_value(&h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint
|
||||||
|
ptr_hash(void *ptr)
|
||||||
|
{
|
||||||
|
uintptr_t p = (uintptr_t) ptr;
|
||||||
|
return p ^ (p << 8) ^ (p >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
20
nest/iface.h
20
nest/iface.h
|
@ -124,29 +124,21 @@ typedef struct neighbor {
|
||||||
ip_addr addr; /* Address of the neighbor */
|
ip_addr addr; /* Address of the neighbor */
|
||||||
struct ifa *ifa; /* Ifa on related iface */
|
struct ifa *ifa; /* Ifa on related iface */
|
||||||
struct iface *iface; /* Interface it's connected to */
|
struct iface *iface; /* Interface it's connected to */
|
||||||
|
struct iface *ifreq; /* Requested iface, NULL for any */
|
||||||
struct proto *proto; /* Protocol this belongs to */
|
struct proto *proto; /* Protocol this belongs to */
|
||||||
void *data; /* Protocol-specific data */
|
void *data; /* Protocol-specific data */
|
||||||
unsigned aux; /* Protocol-specific data */
|
uint aux; /* Protocol-specific data */
|
||||||
unsigned flags;
|
u16 flags; /* NEF_* flags */
|
||||||
int scope; /* Address scope, -1 for unreachable sticky neighbors,
|
s16 scope; /* Address scope, -1 for unreachable neighbors,
|
||||||
SCOPE_HOST when it's our own address */
|
SCOPE_HOST when it's our own address */
|
||||||
} neighbor;
|
} neighbor;
|
||||||
|
|
||||||
#define NEF_STICKY 1
|
#define NEF_STICKY 1
|
||||||
#define NEF_ONLINK 2
|
#define NEF_ONLINK 2
|
||||||
#define NEF_BIND 4 /* Used internally for neighbors bound to an iface */
|
#define NEF_IFACE 4 /* Entry for whole iface */
|
||||||
#define NEF_IFACE 8 /* Neighbors bound to iface */
|
|
||||||
|
|
||||||
|
|
||||||
neighbor *neigh_find(struct proto *, ip_addr *, unsigned flags);
|
neighbor *neigh_find(struct proto *p, ip_addr a, struct iface *ifa, uint flags);
|
||||||
neighbor *neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags);
|
|
||||||
neighbor *neigh_find_iface(struct proto *p, struct iface *ifa);
|
|
||||||
|
|
||||||
static inline int neigh_connected_to(struct proto *p, ip_addr *a, struct iface *i)
|
|
||||||
{
|
|
||||||
neighbor *n = neigh_find(p, a, 0);
|
|
||||||
return n && n->iface == i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void neigh_dump(neighbor *);
|
void neigh_dump(neighbor *);
|
||||||
void neigh_dump_all(void);
|
void neigh_dump_all(void);
|
||||||
|
|
431
nest/neighbor.c
431
nest/neighbor.c
|
@ -2,6 +2,8 @@
|
||||||
* BIRD -- Neighbor Cache
|
* BIRD -- Neighbor Cache
|
||||||
*
|
*
|
||||||
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
||||||
|
* (c) 2008--2018 Ondrej Zajicek <santiago@crfreenet.org>
|
||||||
|
* (c) 2008--2018 CZ.NIC z.s.p.o.
|
||||||
*
|
*
|
||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
@ -10,32 +12,38 @@
|
||||||
* DOC: Neighbor cache
|
* DOC: Neighbor cache
|
||||||
*
|
*
|
||||||
* Most routing protocols need to associate their internal state data with
|
* Most routing protocols need to associate their internal state data with
|
||||||
* neighboring routers, check whether an address given as the next hop
|
* neighboring routers, check whether an address given as the next hop attribute
|
||||||
* attribute of a route is really an address of a directly connected host
|
* of a route is really an address of a directly connected host and which
|
||||||
* and which interface is it connected through. Also, they often need to
|
* interface is it connected through. Also, they often need to be notified when
|
||||||
* be notified when a neighbor ceases to exist or when their long awaited
|
* a neighbor ceases to exist or when their long awaited neighbor becomes
|
||||||
* neighbor becomes connected. The neighbor cache is there to solve all
|
* connected. The neighbor cache is there to solve all these problems.
|
||||||
* these problems.
|
|
||||||
*
|
*
|
||||||
* The neighbor cache maintains a collection of neighbor entries. Each
|
* The neighbor cache maintains a collection of neighbor entries. Each entry
|
||||||
* entry represents one IP address corresponding to either our directly
|
* represents one IP address corresponding to either our directly connected
|
||||||
* connected neighbor or our own end of the link (when the scope of the
|
* neighbor or our own end of the link (when the scope of the address is set to
|
||||||
* address is set to %SCOPE_HOST) together with per-neighbor data belonging to a
|
* %SCOPE_HOST) together with per-neighbor data belonging to a single protocol.
|
||||||
* single protocol.
|
* A neighbor entry may be bound to a specific interface, which is required for
|
||||||
|
* link-local IP addresses and optional for global IP addresses.
|
||||||
*
|
*
|
||||||
* Active entries represent known neighbors and are stored in a hash
|
* Neighbor cache entries are stored in a hash table, which is indexed by triple
|
||||||
* table (to allow fast retrieval based on the IP address of the node) and
|
* (protocol, IP, requested-iface), so if both regular and iface-bound neighbors
|
||||||
* two linked lists: one global and one per-interface (allowing quick
|
* are requested, they are represented by two neighbor cache entries. Active
|
||||||
* processing of interface change events). Inactive entries exist only
|
* entries are also linked in per-interface list (allowing quick processing of
|
||||||
* when the protocol has explicitly requested it via the %NEF_STICKY
|
* interface change events). Inactive entries exist only when the protocol has
|
||||||
* flag because it wishes to be notified when the node will again become
|
* explicitly requested it via the %NEF_STICKY flag because it wishes to be
|
||||||
* a neighbor. Such entries are enqueued in a special list which is walked
|
* notified when the node will again become a neighbor. Such entries are instead
|
||||||
* whenever an interface changes its state to up. Neighbor entry VRF
|
* linked in a special list, which is walked whenever an interface changes its
|
||||||
* association is implied by respective protocol.
|
* state to up. Neighbor entry VRF association is implied by respective
|
||||||
|
* protocol.
|
||||||
|
*
|
||||||
|
* Besides the already mentioned %NEF_STICKY flag, there is also %NEF_ONLINK,
|
||||||
|
* which specifies that neighbor should be considered reachable on given iface
|
||||||
|
* regardless of associated address ranges, and %NEF_IFACE, which represents
|
||||||
|
* pseudo-neighbor entry for whole interface (and uses %IPA_NONE IP address).
|
||||||
*
|
*
|
||||||
* When a neighbor event occurs (a neighbor gets disconnected or a sticky
|
* When a neighbor event occurs (a neighbor gets disconnected or a sticky
|
||||||
* inactive neighbor becomes connected), the protocol hook neigh_notify()
|
* inactive neighbor becomes connected), the protocol hook neigh_notify() is
|
||||||
* is called to advertise the change.
|
* called to advertise the change.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#undef LOCAL_DEBUG
|
#undef LOCAL_DEBUG
|
||||||
|
@ -43,126 +51,151 @@
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
#include "nest/iface.h"
|
#include "nest/iface.h"
|
||||||
#include "nest/protocol.h"
|
#include "nest/protocol.h"
|
||||||
|
#include "lib/hash.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
|
|
||||||
#define NEIGH_HASH_SIZE 256
|
#define NEIGH_HASH_SIZE 256
|
||||||
#define NEIGH_HASH_OFFSET 24
|
#define NEIGH_HASH_OFFSET 24
|
||||||
|
|
||||||
static slab *neigh_slab;
|
static slab *neigh_slab;
|
||||||
static list sticky_neigh_list, iface_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE];
|
static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list;
|
||||||
|
|
||||||
static inline uint
|
static inline uint
|
||||||
neigh_hash(struct proto *p, ip_addr *a)
|
neigh_hash(struct proto *p, ip_addr a, struct iface *i)
|
||||||
{
|
{
|
||||||
return (p->hash_key ^ ipa_hash(*a)) >> NEIGH_HASH_OFFSET;
|
return (p->hash_key ^ ipa_hash(a) ^ ptr_hash(i)) >> NEIGH_HASH_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
if_connected(ip_addr *a, struct iface *i, struct ifa **ap)
|
if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
|
||||||
{
|
{
|
||||||
struct ifa *b;
|
struct ifa *b;
|
||||||
|
|
||||||
if (!(i->flags & IF_UP))
|
/* Handle iface pseudo-neighbors */
|
||||||
{
|
if (flags & NEF_IFACE)
|
||||||
*ap = NULL;
|
return *ap = NULL, (i->flags & IF_UP) ? SCOPE_HOST : -1;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Host addresses match even if iface is down */
|
||||||
|
WALK_LIST(b, i->addrs)
|
||||||
|
if (ipa_equal(a, b->ip))
|
||||||
|
return *ap = b, SCOPE_HOST;
|
||||||
|
|
||||||
|
/* Rest do not match if iface is down */
|
||||||
|
if (!(i->flags & IF_UP))
|
||||||
|
return *ap = NULL, -1;
|
||||||
|
|
||||||
|
/* Regular neighbors */
|
||||||
WALK_LIST(b, i->addrs)
|
WALK_LIST(b, i->addrs)
|
||||||
{
|
{
|
||||||
*ap = b;
|
|
||||||
|
|
||||||
if (ipa_equal(*a, b->ip))
|
|
||||||
return SCOPE_HOST;
|
|
||||||
if (b->flags & IA_PEER)
|
if (b->flags & IA_PEER)
|
||||||
{
|
{
|
||||||
if (ipa_equal(*a, b->opposite))
|
if (ipa_equal(a, b->opposite))
|
||||||
return b->scope;
|
return *ap = b, b->scope;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ipa_in_netX(*a, &b->prefix))
|
if (ipa_in_netX(a, &b->prefix))
|
||||||
{
|
{
|
||||||
/* Do not allow IPv4 network and broadcast addresses */
|
/* Do not allow IPv4 network and broadcast addresses */
|
||||||
if (ipa_is_ip4(*a) &&
|
if (ipa_is_ip4(a) &&
|
||||||
(net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) &&
|
(net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) &&
|
||||||
(ipa_equal(*a, net_prefix(&b->prefix)) || /* Network address */
|
(ipa_equal(a, net_prefix(&b->prefix)) || /* Network address */
|
||||||
ipa_equal(*a, b->brd))) /* Broadcast */
|
ipa_equal(a, b->brd))) /* Broadcast */
|
||||||
|
return *ap = NULL, -1;
|
||||||
|
|
||||||
|
return *ap = b, b->scope;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle ONLINK flag */
|
||||||
|
if (flags & NEF_ONLINK)
|
||||||
|
return *ap = NULL, ipa_classify(a) & IADDR_SCOPE_MASK;
|
||||||
|
|
||||||
|
return *ap = NULL, -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa **addr, uint flags)
|
||||||
{
|
{
|
||||||
*ap = NULL;
|
struct iface *i;
|
||||||
return -1;
|
struct ifa *b;
|
||||||
|
int s, scope = -1;
|
||||||
|
|
||||||
|
*iface = NULL;
|
||||||
|
*addr = NULL;
|
||||||
|
|
||||||
|
/* Get first match, but prefer SCOPE_HOST to other matches */
|
||||||
|
WALK_LIST(i, iface_list)
|
||||||
|
if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
|
||||||
|
if ((scope < 0) || ((scope > SCOPE_HOST) && (s == SCOPE_HOST)))
|
||||||
|
{
|
||||||
|
*iface = i;
|
||||||
|
*addr = b;
|
||||||
|
scope = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return b->scope;
|
return scope;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ap = NULL;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* neigh_find - find or create a neighbor entry.
|
* neigh_find - find or create a neighbor entry
|
||||||
* @p: protocol which asks for the entry.
|
* @p: protocol which asks for the entry
|
||||||
* @a: pointer to IP address of the node to be searched for.
|
* @a: IP address of the node to be searched for
|
||||||
* @flags: 0 or %NEF_STICKY if you want to create a sticky entry.
|
* @iface: optionally bound neighbor to this iface (may be NULL)
|
||||||
|
* @flags: %NEF_STICKY for sticky entry, %NEF_ONLINK for onlink entry
|
||||||
*
|
*
|
||||||
* Search the neighbor cache for a node with given IP address. If
|
* Search the neighbor cache for a node with given IP address. Iface can be
|
||||||
* it's found, a pointer to the neighbor entry is returned. If no
|
* specified for link-local addresses or for cases, where neighbor is expected
|
||||||
* such entry exists and the node is directly connected on
|
* on given interface. If it is found, a pointer to the neighbor entry is
|
||||||
* one of our active interfaces, a new entry is created and returned
|
* returned. If no such entry exists and the node is directly connected on one
|
||||||
* to the caller with protocol-dependent fields initialized to zero.
|
* of our active interfaces, a new entry is created and returned to the caller
|
||||||
* If the node is not connected directly or *@a is not a valid unicast
|
* with protocol-dependent fields initialized to zero. If the node is not
|
||||||
* IP address, neigh_find() returns %NULL.
|
* connected directly or *@a is not a valid unicast IP address, neigh_find()
|
||||||
|
* returns %NULL.
|
||||||
*/
|
*/
|
||||||
neighbor *
|
neighbor *
|
||||||
neigh_find(struct proto *p, ip_addr *a, unsigned flags)
|
neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
|
||||||
{
|
|
||||||
return neigh_find2(p, a, NULL, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
neighbor *
|
|
||||||
neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
|
|
||||||
{
|
{
|
||||||
neighbor *n;
|
neighbor *n;
|
||||||
int class, scope = -1;
|
int class, scope = -1;
|
||||||
uint h = neigh_hash(p, a);
|
uint h = neigh_hash(p, a, iface);
|
||||||
struct iface *i;
|
struct iface *ifreq = iface;
|
||||||
struct ifa *addr;
|
struct ifa *addr = NULL;
|
||||||
|
|
||||||
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
|
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
|
||||||
if (n->proto == p && ipa_equal(*a, n->addr) && (!ifa || (ifa == n->iface)))
|
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
class = ipa_classify(*a);
|
if (flags & NEF_IFACE)
|
||||||
|
{
|
||||||
|
if (ipa_nonzero(a) || !iface)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
class = ipa_classify(a);
|
||||||
if (class < 0) /* Invalid address */
|
if (class < 0) /* Invalid address */
|
||||||
return NULL;
|
return NULL;
|
||||||
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
|
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
|
||||||
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && (ifa == NULL)) ||
|
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && !iface) ||
|
||||||
!(class & IADDR_HOST))
|
!(class & IADDR_HOST))
|
||||||
return NULL; /* Bad scope or a somecast */
|
return NULL; /* Bad scope or a somecast */
|
||||||
|
}
|
||||||
|
|
||||||
if (ifa)
|
if ((flags & NEF_ONLINK) && !iface)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (iface)
|
||||||
{
|
{
|
||||||
scope = if_connected(a, ifa, &addr);
|
scope = if_connected(a, iface, &addr, flags);
|
||||||
flags |= NEF_BIND;
|
iface = (scope < 0) ? NULL : iface;
|
||||||
|
|
||||||
if ((scope < 0) && (flags & NEF_ONLINK))
|
|
||||||
scope = class & IADDR_SCOPE_MASK;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
WALK_LIST(i, iface_list)
|
scope = if_connected_any(a, p->vrf, &iface, &addr, flags);
|
||||||
if ((!p->vrf || p->vrf == i->master) &&
|
|
||||||
((scope = if_connected(a, i, &addr)) >= 0))
|
|
||||||
{
|
|
||||||
ifa = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* scope < 0 means i don't know neighbor */
|
/* scope < 0 means i don't know neighbor */
|
||||||
/* scope >= 0 implies ifa != NULL */
|
/* scope >= 0 <=> iface != NULL */
|
||||||
|
|
||||||
if ((scope < 0) && !(flags & NEF_STICKY))
|
if ((scope < 0) && !(flags & NEF_STICKY))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -170,52 +203,15 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
|
||||||
n = sl_alloc(neigh_slab);
|
n = sl_alloc(neigh_slab);
|
||||||
memset(n, 0, sizeof(neighbor));
|
memset(n, 0, sizeof(neighbor));
|
||||||
|
|
||||||
n->addr = *a;
|
|
||||||
if (scope >= 0)
|
|
||||||
{
|
|
||||||
add_tail(&neigh_hash_table[h], &n->n);
|
add_tail(&neigh_hash_table[h], &n->n);
|
||||||
add_tail(&ifa->neighbors, &n->if_n);
|
add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
|
||||||
}
|
n->addr = a;
|
||||||
else
|
|
||||||
{
|
|
||||||
add_tail(&sticky_neigh_list, &n->n);
|
|
||||||
scope = -1;
|
|
||||||
}
|
|
||||||
n->iface = ifa;
|
|
||||||
n->ifa = addr;
|
n->ifa = addr;
|
||||||
|
n->iface = iface;
|
||||||
|
n->ifreq = ifreq;
|
||||||
n->proto = p;
|
n->proto = p;
|
||||||
n->data = NULL;
|
|
||||||
n->aux = 0;
|
|
||||||
n->flags = flags;
|
n->flags = flags;
|
||||||
n->scope = scope;
|
n->scope = scope;
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
neighbor *
|
|
||||||
neigh_find_iface(struct proto *p, struct iface *ifa)
|
|
||||||
{
|
|
||||||
neighbor *n;
|
|
||||||
node *nn;
|
|
||||||
|
|
||||||
/* We keep neighbors with NEF_IFACE foremost in ifa->neighbors list */
|
|
||||||
WALK_LIST2(n, nn, ifa->neighbors, if_n)
|
|
||||||
{
|
|
||||||
if (! (n->flags & NEF_IFACE))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (n->proto == p)
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = sl_alloc(neigh_slab);
|
|
||||||
memset(n, 0, sizeof(neighbor));
|
|
||||||
|
|
||||||
add_tail(&iface_neigh_list, &n->n);
|
|
||||||
add_head(&ifa->neighbors, &n->if_n);
|
|
||||||
n->iface = ifa;
|
|
||||||
n->proto = p;
|
|
||||||
n->flags = NEF_IFACE;
|
|
||||||
n->scope = (ifa->flags & IF_UP) ? SCOPE_HOST : -1;
|
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -224,30 +220,26 @@ neigh_find_iface(struct proto *p, struct iface *ifa)
|
||||||
* neigh_dump - dump specified neighbor entry.
|
* neigh_dump - dump specified neighbor entry.
|
||||||
* @n: the entry to dump
|
* @n: the entry to dump
|
||||||
*
|
*
|
||||||
* This functions dumps the contents of a given neighbor entry
|
* This functions dumps the contents of a given neighbor entry to debug output.
|
||||||
* to debug output.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
neigh_dump(neighbor *n)
|
neigh_dump(neighbor *n)
|
||||||
{
|
{
|
||||||
debug("%p %I ", n, n->addr);
|
debug("%p %I %s %s ", n, n->addr,
|
||||||
if (n->iface)
|
n->iface ? n->iface->name : "[]",
|
||||||
debug("%s ", n->iface->name);
|
n->ifreq ? n->ifreq->name : "[]");
|
||||||
else
|
|
||||||
debug("[] ");
|
|
||||||
debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
|
debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
|
||||||
if (n->flags & NEF_STICKY)
|
if (n->flags & NEF_STICKY)
|
||||||
debug(" STICKY");
|
debug(" STICKY");
|
||||||
if (n->flags & NEF_IFACE)
|
if (n->flags & NEF_ONLINK)
|
||||||
debug(" IFACE");
|
debug(" ONLINK");
|
||||||
debug("\n");
|
debug("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* neigh_dump_all - dump all neighbor entries.
|
* neigh_dump_all - dump all neighbor entries.
|
||||||
*
|
*
|
||||||
* This function dumps the contents of the neighbor cache to
|
* This function dumps the contents of the neighbor cache to debug output.
|
||||||
* debug output.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
neigh_dump_all(void)
|
neigh_dump_all(void)
|
||||||
|
@ -256,73 +248,109 @@ neigh_dump_all(void)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
debug("Known neighbors:\n");
|
debug("Known neighbors:\n");
|
||||||
WALK_LIST(n, sticky_neigh_list)
|
|
||||||
neigh_dump(n);
|
|
||||||
WALK_LIST(n, iface_neigh_list)
|
|
||||||
neigh_dump(n);
|
|
||||||
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
||||||
WALK_LIST(n, neigh_hash_table[i])
|
WALK_LIST(n, neigh_hash_table[i])
|
||||||
neigh_dump(n);
|
neigh_dump(n);
|
||||||
debug("\n");
|
debug("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
neigh_notify(neighbor *n)
|
||||||
|
{
|
||||||
|
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
||||||
|
n->proto->neigh_notify(n);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a)
|
neigh_up(neighbor *n, struct iface *i, struct ifa *a, int scope)
|
||||||
{
|
{
|
||||||
DBG("Waking up sticky neighbor %I\n", n->addr);
|
DBG("Waking up sticky neighbor %I\n", n->addr);
|
||||||
n->iface = i;
|
n->iface = i;
|
||||||
n->ifa = a;
|
n->ifa = a;
|
||||||
n->scope = scope;
|
n->scope = scope;
|
||||||
|
|
||||||
if (! (n->flags & NEF_IFACE))
|
rem_node(&n->if_n);
|
||||||
{
|
|
||||||
add_tail(&i->neighbors, &n->if_n);
|
add_tail(&i->neighbors, &n->if_n);
|
||||||
rem_node(&n->n);
|
|
||||||
add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
neigh_notify(n);
|
||||||
n->proto->neigh_notify(n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
neigh_down(neighbor *n)
|
neigh_down(neighbor *n)
|
||||||
{
|
{
|
||||||
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
|
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
|
||||||
if (! (n->flags & (NEF_BIND | NEF_IFACE)))
|
|
||||||
n->iface = NULL;
|
n->iface = NULL;
|
||||||
n->ifa = NULL;
|
n->ifa = NULL;
|
||||||
n->scope = -1;
|
n->scope = -1;
|
||||||
|
|
||||||
if (! (n->flags & NEF_IFACE))
|
|
||||||
{
|
|
||||||
rem_node(&n->if_n);
|
rem_node(&n->if_n);
|
||||||
rem_node(&n->n);
|
add_tail(&sticky_neigh_list, &n->if_n);
|
||||||
|
|
||||||
|
neigh_notify(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
static inline void
|
||||||
n->proto->neigh_notify(n);
|
neigh_free(neighbor *n)
|
||||||
|
|
||||||
if (n->flags & NEF_STICKY)
|
|
||||||
{
|
{
|
||||||
add_tail(&sticky_neigh_list, &n->n);
|
rem_node(&n->n);
|
||||||
|
rem_node(&n->if_n);
|
||||||
|
sl_free(neigh_slab, n);
|
||||||
|
}
|
||||||
|
|
||||||
/* Respawn neighbor if there is another matching prefix */
|
/**
|
||||||
struct iface *i;
|
* neigh_update: update neighbor entry w.r.t. change on specific iface
|
||||||
struct ifa *a;
|
* @n: neighbor to update
|
||||||
int scope;
|
* @iface: changed iface
|
||||||
|
*
|
||||||
if (!n->iface)
|
* The function recalculates state of the neighbor entry @n assuming that only
|
||||||
WALK_LIST(i, iface_list)
|
* the interface @iface may changed its state or addresses. Then, appropriate
|
||||||
if ((scope = if_connected(&n->addr, i, &a)) >= 0)
|
* actions are executed (the neighbor goes up, down, up-down, or just notified).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
neigh_update(neighbor *n, struct iface *iface)
|
||||||
{
|
{
|
||||||
neigh_up(n, i, scope, a);
|
struct ifa *ifa = NULL;
|
||||||
|
int scope = -1;
|
||||||
|
|
||||||
|
/* Iface-bound neighbors ignore other ifaces */
|
||||||
|
if (n->ifreq && (n->ifreq != iface))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* VRF-bound neighbors ignore changes in other VRFs */
|
||||||
|
if (n->proto->vrf && (n->proto->vrf != iface->master))
|
||||||
|
return;
|
||||||
|
|
||||||
|
scope = if_connected(n->addr, iface, &ifa, n->flags);
|
||||||
|
|
||||||
|
/* When neighbor is going down, try to respawn it on other ifaces */
|
||||||
|
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
|
||||||
|
scope = if_connected_any(n->addr, n->proto->vrf, &iface, &ifa, n->flags);
|
||||||
|
|
||||||
|
/* No change or minor change - ignore or notify */
|
||||||
|
if ((scope == n->scope) && (iface == n->iface))
|
||||||
|
{
|
||||||
|
if (ifa != n->ifa)
|
||||||
|
{
|
||||||
|
n->ifa = ifa;
|
||||||
|
neigh_notify(n);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Major change - going down and/or going up */
|
||||||
|
|
||||||
|
if (n->scope >= 0)
|
||||||
|
neigh_down(n);
|
||||||
|
|
||||||
|
if ((n->scope < 0) && !(n->flags & NEF_STICKY))
|
||||||
|
{
|
||||||
|
neigh_free(n);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (n->flags & (NEF_STICKY | NEF_IFACE)))
|
if (scope >= 0)
|
||||||
sl_free(neigh_slab, n);
|
neigh_up(n, iface, ifa, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,21 +366,11 @@ neigh_down(neighbor *n)
|
||||||
void
|
void
|
||||||
neigh_if_up(struct iface *i)
|
neigh_if_up(struct iface *i)
|
||||||
{
|
{
|
||||||
struct ifa *a;
|
|
||||||
neighbor *n;
|
neighbor *n;
|
||||||
node *x, *y;
|
node *x, *y;
|
||||||
int scope;
|
|
||||||
|
|
||||||
/* Wake up all iface neighbors */
|
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
neigh_update(n, i);
|
||||||
if ((n->scope < 0) && (n->flags & NEF_IFACE))
|
|
||||||
neigh_up(n, i, SCOPE_HOST, NULL);
|
|
||||||
|
|
||||||
/* Wake up appropriate sticky neighbors */
|
|
||||||
WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
|
|
||||||
if ((!n->iface || n->iface == i) &&
|
|
||||||
((scope = if_connected(&n->addr, i, &a)) >= 0))
|
|
||||||
neigh_up(n, i, scope, a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,8 +379,7 @@ neigh_if_up(struct iface *i)
|
||||||
*
|
*
|
||||||
* Notify the neighbor cache that an interface has ceased to exist.
|
* Notify the neighbor cache that an interface has ceased to exist.
|
||||||
*
|
*
|
||||||
* It causes all entries belonging to neighbors connected to this interface
|
* It causes all neighbors connected to this interface to be updated or removed.
|
||||||
* to be flushed.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
neigh_if_down(struct iface *i)
|
neigh_if_down(struct iface *i)
|
||||||
|
@ -371,16 +388,15 @@ neigh_if_down(struct iface *i)
|
||||||
node *x, *y;
|
node *x, *y;
|
||||||
|
|
||||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||||
neigh_down(n);
|
neigh_update(n, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* neigh_if_link - notify neighbor cache about interface link change
|
* neigh_if_link - notify neighbor cache about interface link change
|
||||||
* @i: the interface in question
|
* @i: the interface in question
|
||||||
*
|
*
|
||||||
* Notify the neighbor cache that an interface changed link state.
|
* Notify the neighbor cache that an interface changed link state. All owners of
|
||||||
* All owners of neighbor entries connected to this interface are
|
* neighbor entries connected to this interface are notified.
|
||||||
* notified.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
neigh_if_link(struct iface *i)
|
neigh_if_link(struct iface *i)
|
||||||
|
@ -389,8 +405,7 @@ neigh_if_link(struct iface *i)
|
||||||
node *x, *y;
|
node *x, *y;
|
||||||
|
|
||||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||||
if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
|
neigh_notify(n);
|
||||||
n->proto->neigh_notify(n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -407,21 +422,16 @@ void
|
||||||
neigh_ifa_update(struct ifa *a)
|
neigh_ifa_update(struct ifa *a)
|
||||||
{
|
{
|
||||||
struct iface *i = a->iface;
|
struct iface *i = a->iface;
|
||||||
struct ifa *aa;
|
|
||||||
node *x, *y;
|
|
||||||
neighbor *n;
|
neighbor *n;
|
||||||
int scope;
|
node *x, *y;
|
||||||
|
|
||||||
/* Remove all neighbors whose scope has changed */
|
/* Update all neighbors whose scope has changed */
|
||||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||||
if (n->ifa && (if_connected(&n->addr, i, &aa) != n->scope))
|
neigh_update(n, i);
|
||||||
neigh_down(n);
|
|
||||||
|
|
||||||
/* Wake up all sticky neighbors that are reachable now */
|
/* Wake up all sticky neighbors that are reachable now */
|
||||||
WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
|
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||||
if ((!n->iface || n->iface == i) &&
|
neigh_update(n, i);
|
||||||
((scope = if_connected(&n->addr, i, &aa)) >= 0))
|
|
||||||
neigh_up(n, i, scope, aa);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -429,10 +439,8 @@ neigh_prune_one(neighbor *n)
|
||||||
{
|
{
|
||||||
if (n->proto->proto_state != PS_DOWN)
|
if (n->proto->proto_state != PS_DOWN)
|
||||||
return;
|
return;
|
||||||
rem_node(&n->n);
|
|
||||||
if (n->if_n.next)
|
neigh_free(n);
|
||||||
rem_node(&n->if_n);
|
|
||||||
sl_free(neigh_slab, n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -453,10 +461,6 @@ neigh_prune(void)
|
||||||
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
for(i=0; i<NEIGH_HASH_SIZE; i++)
|
||||||
WALK_LIST_DELSAFE(n, m, neigh_hash_table[i])
|
WALK_LIST_DELSAFE(n, m, neigh_hash_table[i])
|
||||||
neigh_prune_one(n);
|
neigh_prune_one(n);
|
||||||
WALK_LIST_DELSAFE(n, m, sticky_neigh_list)
|
|
||||||
neigh_prune_one(n);
|
|
||||||
WALK_LIST_DELSAFE(n, m, iface_neigh_list)
|
|
||||||
neigh_prune_one(n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -471,9 +475,8 @@ neigh_init(pool *if_pool)
|
||||||
{
|
{
|
||||||
neigh_slab = sl_new(if_pool, sizeof(neighbor));
|
neigh_slab = sl_new(if_pool, sizeof(neighbor));
|
||||||
|
|
||||||
init_list(&sticky_neigh_list);
|
|
||||||
init_list(&iface_neigh_list);
|
|
||||||
|
|
||||||
for(int i = 0; i < NEIGH_HASH_SIZE; i++)
|
for(int i = 0; i < NEIGH_HASH_SIZE; i++)
|
||||||
init_list(&neigh_hash_table[i]);
|
init_list(&neigh_hash_table[i]);
|
||||||
|
|
||||||
|
init_list(&sticky_neigh_list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
#include "conf/conf.h"
|
#include "conf/conf.h"
|
||||||
#include "filter/filter.h"
|
#include "filter/filter.h"
|
||||||
|
#include "lib/hash.h"
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
#include "lib/alloca.h"
|
#include "lib/alloca.h"
|
||||||
|
|
||||||
|
@ -2220,13 +2221,6 @@ rt_feed_channel_abort(struct channel *c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned
|
|
||||||
ptr_hash(void *ptr)
|
|
||||||
{
|
|
||||||
uintptr_t p = (uintptr_t) ptr;
|
|
||||||
return p ^ (p << 8) ^ (p >> 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32
|
static inline u32
|
||||||
hc_hash(ip_addr a, rtable *dep)
|
hc_hash(ip_addr a, rtable *dep)
|
||||||
{
|
{
|
||||||
|
|
|
@ -775,7 +775,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
|
struct neighbor *nb = neigh_find(&p->p, n->addr, n->iface, NEF_STICKY);
|
||||||
if (!nb)
|
if (!nb)
|
||||||
{
|
{
|
||||||
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
|
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
|
||||||
|
|
|
@ -1277,7 +1277,7 @@ bgp_start_locked(struct object_lock *lock)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
neighbor *n = neigh_find2(&p->p, &cf->remote_ip, cf->iface, NEF_STICKY);
|
neighbor *n = neigh_find(&p->p, cf->remote_ip, cf->iface, NEF_STICKY);
|
||||||
if (!n)
|
if (!n)
|
||||||
{
|
{
|
||||||
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
|
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
|
||||||
|
@ -1521,7 +1521,7 @@ bgp_channel_start(struct channel *C)
|
||||||
if (ipa_zero(c->next_hop_addr))
|
if (ipa_zero(c->next_hop_addr))
|
||||||
{
|
{
|
||||||
/* We know the iface for single-hop, we make lookup for multihop */
|
/* We know the iface for single-hop, we make lookup for multihop */
|
||||||
struct neighbor *nbr = p->neigh ?: neigh_find2(&p->p, &src, NULL, 0);
|
struct neighbor *nbr = p->neigh ?: neigh_find(&p->p, src, NULL, 0);
|
||||||
struct iface *iface = nbr ? nbr->iface : NULL;
|
struct iface *iface = nbr ? nbr->iface : NULL;
|
||||||
|
|
||||||
if (bgp_channel_is_ipv4(c) && iface && iface->addr4)
|
if (bgp_channel_is_ipv4(c) && iface && iface->addr4)
|
||||||
|
|
|
@ -744,9 +744,9 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
|
||||||
|
|
||||||
/* GW_DIRECT -> single_hop -> p->neigh != NULL */
|
/* GW_DIRECT -> single_hop -> p->neigh != NULL */
|
||||||
if (ipa_nonzero(gw))
|
if (ipa_nonzero(gw))
|
||||||
nbr = neigh_find2(&p->p, &gw, NULL, 0);
|
nbr = neigh_find(&p->p, gw, NULL, 0);
|
||||||
else if (ipa_nonzero(ll))
|
else if (ipa_nonzero(ll))
|
||||||
nbr = neigh_find2(&p->p, &ll, p->neigh->iface, 0);
|
nbr = neigh_find(&p->p, ll, p->neigh->iface, 0);
|
||||||
|
|
||||||
if (!nbr || (nbr->scope == SCOPE_HOST))
|
if (!nbr || (nbr->scope == SCOPE_HOST))
|
||||||
WITHDRAW(BAD_NEXT_HOP);
|
WITHDRAW(BAD_NEXT_HOP);
|
||||||
|
|
|
@ -1987,7 +1987,7 @@ again1:
|
||||||
for (nh = nf->n.nhs; nh; nh = nh->next)
|
for (nh = nf->n.nhs; nh; nh = nh->next)
|
||||||
if (ipa_nonzero(nh->gw))
|
if (ipa_nonzero(nh->gw))
|
||||||
{
|
{
|
||||||
neighbor *ng = neigh_find2(&p->p, &nh->gw, nh->iface, 0);
|
neighbor *ng = neigh_find(&p->p, nh->gw, nh->iface, 0);
|
||||||
if (!ng || (ng->scope == SCOPE_HOST))
|
if (!ng || (ng->scope == SCOPE_HOST))
|
||||||
{ reset_ri(nf); break; }
|
{ reset_ri(nf); break; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -627,7 +627,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack
|
||||||
|
|
||||||
if (ipa_nonzero(rte.next_hop))
|
if (ipa_nonzero(rte.next_hop))
|
||||||
{
|
{
|
||||||
neighbor *nbr = neigh_find2(&p->p, &rte.next_hop, ifa->iface, 0);
|
neighbor *nbr = neigh_find(&p->p, rte.next_hop, ifa->iface, 0);
|
||||||
if (!nbr || (nbr->scope <= 0))
|
if (!nbr || (nbr->scope <= 0))
|
||||||
rte.next_hop = IPA_NONE;
|
rte.next_hop = IPA_NONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,7 +377,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
|
||||||
struct rip_neighbor *
|
struct rip_neighbor *
|
||||||
rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
|
rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
|
||||||
{
|
{
|
||||||
neighbor *nbr = neigh_find2(&p->p, a, ifa->iface, 0);
|
neighbor *nbr = neigh_find(&p->p, *a, ifa->iface, 0);
|
||||||
|
|
||||||
if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa))
|
if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -205,10 +205,9 @@ static_add_rte(struct static_proto *p, struct static_route *r)
|
||||||
|
|
||||||
for (r2 = r; r2; r2 = r2->mp_next)
|
for (r2 = r; r2; r2 = r2->mp_next)
|
||||||
{
|
{
|
||||||
n = ipa_nonzero(r2->via) ?
|
n = neigh_find(&p->p, r2->via, r2->iface, NEF_STICKY |
|
||||||
neigh_find2(&p->p, &r2->via, r2->iface,
|
(r2->onlink ? NEF_ONLINK : 0) |
|
||||||
NEF_STICKY | (r2->onlink ? NEF_ONLINK : 0)) :
|
(ipa_zero(r2->via) ? NEF_IFACE : 0));
|
||||||
neigh_find_iface(&p->p, r2->iface);
|
|
||||||
|
|
||||||
if (!n)
|
if (!n)
|
||||||
{
|
{
|
||||||
|
|
|
@ -696,7 +696,7 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, struct rtattr
|
||||||
rv->flags |= RNF_ONLINK;
|
rv->flags |= RNF_ONLINK;
|
||||||
|
|
||||||
neighbor *nbr;
|
neighbor *nbr;
|
||||||
nbr = neigh_find2(&p->p, &rv->gw, rv->iface,
|
nbr = neigh_find(&p->p, rv->gw, rv->iface,
|
||||||
(rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
(rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
||||||
if (!nbr || (nbr->scope == SCOPE_HOST))
|
if (!nbr || (nbr->scope == SCOPE_HOST))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1636,7 +1636,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
||||||
ra->nh.flags |= RNF_ONLINK;
|
ra->nh.flags |= RNF_ONLINK;
|
||||||
|
|
||||||
neighbor *nbr;
|
neighbor *nbr;
|
||||||
nbr = neigh_find2(&p->p, &(ra->nh.gw), ra->nh.iface,
|
nbr = neigh_find(&p->p, ra->nh.gw, ra->nh.iface,
|
||||||
(ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
(ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
||||||
if (!nbr || (nbr->scope == SCOPE_HOST))
|
if (!nbr || (nbr->scope == SCOPE_HOST))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue