Commit 81489b79 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

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.
parent 716b904f
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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 */

+5 −8
Original line number Diff line number Diff line
@@ -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);

+1 −0
Original line number Diff line number Diff line
@@ -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;
+5 −18
Original line number Diff line number Diff line
@@ -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);

Loading