mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Nest: Improve keeping track of IPv6 link-local addresses
Most protocols in IPv6 mode use link-local source addresses and expect that there is one on each active interface. The old code depended on assumption that if there is some IPv6 address on iface, there is also an IPv6 link-local address on that iface (added by kernel when the iface went up). Unfortunately, that is not generally true, as a configured global address sometimes ceases to be tentative (finishes DOD) before a link-local address on the same iface. In such case a protocol iface (namely RAdv and Babel) is activated, but fails to found link-local address and stays in failed state. The patch fixes that by tracking 'primary' IPv6 link-local address, sending iface restart notifications when it changes and making protocols ignore iface-up notifications when no such address is selected for an iface.
This commit is contained in:
parent
716b904f4e
commit
81489b79e0
20
nest/iface.c
20
nest/iface.c
@ -470,10 +470,24 @@ struct ifa *kif_choose_primary(struct iface *i);
|
||||
static int
|
||||
ifa_recalc_primary(struct iface *i)
|
||||
{
|
||||
struct ifa *a = kif_choose_primary(i);
|
||||
struct ifa *a;
|
||||
int c = 0;
|
||||
|
||||
#ifdef IPV6
|
||||
struct ifa *ll = NULL;
|
||||
|
||||
WALK_LIST(a, i->addrs)
|
||||
if (ipa_is_link_local(a->ip) && (!ll || (a == i->llv6)))
|
||||
ll = a;
|
||||
|
||||
c = (ll != i->llv6);
|
||||
i->llv6 = ll;
|
||||
#endif
|
||||
|
||||
a = kif_choose_primary(i);
|
||||
|
||||
if (a == i->addr)
|
||||
return 0;
|
||||
return c;
|
||||
|
||||
if (i->addr)
|
||||
i->addr->flags &= ~IA_PRIMARY;
|
||||
@ -577,7 +591,7 @@ ifa_delete(struct ifa *a)
|
||||
b->flags &= ~IF_UP;
|
||||
ifa_notify_change(IF_CHANGE_DOWN, b);
|
||||
}
|
||||
if (b->flags & IA_PRIMARY)
|
||||
if ((b->flags & IA_PRIMARY) || (b == ifa_llv6(i)))
|
||||
{
|
||||
if_change_flags(i, i->flags | IF_TMP_DOWN);
|
||||
ifa_recalc_primary(i);
|
||||
|
13
nest/iface.h
13
nest/iface.h
@ -37,6 +37,9 @@ struct iface {
|
||||
unsigned master_index; /* Interface index of master iface */
|
||||
list addrs; /* Addresses assigned to this interface */
|
||||
struct ifa *addr; /* Primary address */
|
||||
#ifdef IPV6
|
||||
struct ifa *llv6; /* Selected IPv6 link-local address */
|
||||
#endif
|
||||
struct iface *master; /* Master iface (e.g. for VRF) */
|
||||
list neighbors; /* All neighbors on this interface */
|
||||
};
|
||||
@ -103,6 +106,16 @@ struct iface *if_find_by_name(char *);
|
||||
struct iface *if_get_by_name(char *);
|
||||
void ifa_recalc_all_primary_addresses(void);
|
||||
|
||||
static inline struct ifa *
|
||||
ifa_llv6(struct iface *i UNUSED4)
|
||||
{
|
||||
#ifdef IPV6
|
||||
return i->llv6;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* The Neighbor Cache */
|
||||
|
||||
|
@ -1488,17 +1488,10 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con
|
||||
ifa->cf = ic;
|
||||
ifa->pool = pool;
|
||||
ifa->ifname = new->name;
|
||||
ifa->addr = new->llv6->ip;
|
||||
|
||||
add_tail(&p->interfaces, NODE ifa);
|
||||
|
||||
struct ifa *addr;
|
||||
WALK_LIST(addr, new->addrs)
|
||||
if (ipa_is_link_local(addr->ip))
|
||||
ifa->addr = addr->ip;
|
||||
|
||||
if (ipa_zero(ifa->addr))
|
||||
log(L_WARN "%s: Cannot find link-local addr on %s", p->p.name, new->name);
|
||||
|
||||
init_list(&ifa->neigh_list);
|
||||
ifa->hello_seqno = 1;
|
||||
|
||||
@ -1551,6 +1544,10 @@ babel_if_notify(struct proto *P, unsigned flags, struct iface *iface)
|
||||
if (!(iface->flags & IF_MULTICAST))
|
||||
return;
|
||||
|
||||
/* Ignore ifaces without link-local address */
|
||||
if (!iface->llv6)
|
||||
return;
|
||||
|
||||
if (ic)
|
||||
babel_add_iface(p, iface, ic);
|
||||
|
||||
|
@ -1087,6 +1087,7 @@ babel_open_socket(struct babel_iface *ifa)
|
||||
sk->sport = ifa->cf->port;
|
||||
sk->dport = ifa->cf->port;
|
||||
sk->iface = ifa->iface;
|
||||
sk->saddr = ifa->addr;
|
||||
sk->vrf = p->p.vrf;
|
||||
|
||||
sk->rx_hook = babel_rx_hook;
|
||||
|
@ -281,17 +281,6 @@ radv_iface_add(struct object_lock *lock)
|
||||
radv_iface_notify(ifa, RA_EV_INIT);
|
||||
}
|
||||
|
||||
static inline struct ifa *
|
||||
find_lladdr(struct iface *iface)
|
||||
{
|
||||
struct ifa *a;
|
||||
WALK_LIST(a, iface->addrs)
|
||||
if (a->scope == SCOPE_LINK)
|
||||
return a;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf)
|
||||
{
|
||||
@ -305,18 +294,12 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
|
||||
ifa->ra = p;
|
||||
ifa->cf = cf;
|
||||
ifa->iface = iface;
|
||||
ifa->addr = iface->llv6;
|
||||
init_list(&ifa->prefixes);
|
||||
ifa->prune_time = TIME_INFINITY;
|
||||
|
||||
add_tail(&p->iface_list, NODE ifa);
|
||||
|
||||
ifa->addr = find_lladdr(iface);
|
||||
if (!ifa->addr)
|
||||
{
|
||||
log(L_ERR "%s: Missing link-local address on interface %s", p->p.name, iface->name);
|
||||
return;
|
||||
}
|
||||
|
||||
timer *tm = tm_new(pool);
|
||||
tm->hook = radv_timer;
|
||||
tm->data = ifa;
|
||||
@ -360,6 +343,10 @@ radv_if_notify(struct proto *P, unsigned flags, struct iface *iface)
|
||||
struct radv_iface_config *ic = (struct radv_iface_config *)
|
||||
iface_patt_find(&cf->patt_list, iface, NULL);
|
||||
|
||||
/* Ignore ifaces without link-local address */
|
||||
if (!iface->llv6)
|
||||
return;
|
||||
|
||||
if (ic)
|
||||
radv_iface_new(p, iface, ic);
|
||||
|
||||
|
@ -739,16 +739,9 @@ rip_open_socket(struct rip_iface *ifa)
|
||||
sk->sport = ifa->cf->port;
|
||||
sk->dport = ifa->cf->port;
|
||||
sk->iface = ifa->iface;
|
||||
sk->saddr = rip_is_v2(p) ? ifa->iface->addr->ip : ifa_llv6(ifa->iface)->ip;
|
||||
sk->vrf = p->p.vrf;
|
||||
|
||||
/*
|
||||
* For RIPv2, we explicitly choose a primary address, mainly to ensure that
|
||||
* RIP and BFD uses the same one. For RIPng, we left it to kernel, which
|
||||
* should choose some link-local address based on the same scope rule.
|
||||
*/
|
||||
if (rip_is_v2(p))
|
||||
sk->saddr = ifa->iface->addr->ip;
|
||||
|
||||
sk->rx_hook = rip_rx_hook;
|
||||
sk->tx_hook = rip_tx_hook;
|
||||
sk->err_hook = rip_err_hook;
|
||||
|
@ -764,6 +764,10 @@ rip_if_notify(struct proto *P, unsigned flags, struct iface *iface)
|
||||
{
|
||||
struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
|
||||
|
||||
/* For RIPng, ignore ifaces without link-local address */
|
||||
if (rip_is_ng(p) && !ifa_llv6(iface))
|
||||
return;
|
||||
|
||||
if (ic)
|
||||
rip_add_iface(p, iface, ic);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user