summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2018-11-08 20:43:04 +0100
committerOndrej Zajicek (work) <santiago@crfreenet.org>2018-11-08 20:43:04 +0100
commit81489b79e07d448f30434a0c1d22f8f744f0fda8 (patch)
tree0680afdea35c0c4ce19ab458e6e279f621ab18b0 /nest
parent716b904f4eb14349cdf66656eea0d90b040d51e5 (diff)
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.
Diffstat (limited to 'nest')
-rw-r--r--nest/iface.c20
-rw-r--r--nest/iface.h13
2 files changed, 30 insertions, 3 deletions
diff --git a/nest/iface.c b/nest/iface.c
index 56de1f5c..7acadc7d 100644
--- a/nest/iface.c
+++ b/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);
diff --git a/nest/iface.h b/nest/iface.h
index b8e69838..cf81660b 100644
--- a/nest/iface.h
+++ b/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 */