]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Improve keeping track of IPv6 link-local addresses
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 8 Nov 2018 19:43:04 +0000 (20:43 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 8 Nov 2018 19:43:04 +0000 (20:43 +0100)
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.

nest/iface.c
nest/iface.h
proto/babel/babel.c
proto/babel/packets.c
proto/radv/radv.c
proto/rip/packets.c
proto/rip/rip.c

index 56de1f5cf48f480d68a77784e0c2188bf8619c8b..7acadc7d8100c4af4ba7c1cf5048adfdfe0c11c4 100644 (file)
@@ -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);
index b8e6983846b2516373ecde70d3936e26be27b2a3..cf81660bf697e255f838425cc945b3fd77e198b6 100644 (file)
@@ -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 */
 
index 38be69090f5c52de9d4efb79da769b6430b794bb..0cfc2cacde44b83a0ce729a16b4fbdee9fd7a48c 100644 (file)
@@ -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);
 
index 768858d03fa9c4db864b43ea1bca2b65c036e88c..4b40ff97a2cc77e9fa810801fbcd793b7ed0ab62 100644 (file)
@@ -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;
index 7e8950c5dc256b852a04f98ecd60169cfad62357..22e4b2f4eae13a189be62b8bcb6a23d539eeea31 100644 (file)
@@ -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);
 
index 722a9012d95da1dc2e327f22a826d6f5476f4985..d2f968d375f8bf58cd27d2a8d5f3b607d4fbcdcf 100644 (file)
@@ -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;
index 7b3800975d0d682bea771ff7b70be18024d4f5e0..88d7b7c8b4b732aa92b6cec32340ce82a4c845af 100644 (file)
@@ -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);