]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Maintain separate IPv4, IPv6 and LLv6 preferred addresses
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 7 Dec 2017 12:06:01 +0000 (13:06 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 7 Dec 2017 12:06:01 +0000 (13:06 +0100)
Also redesign preferred address selection and update protocols to use
appropriate preferred address.

Based on a previous work by Jan Maria Matejka.

17 files changed:
nest/iface.c
nest/iface.h
nest/rt-dev.c
proto/babel/babel.c
proto/babel/packets.c
proto/bgp/bgp.c
proto/radv/packets.c
proto/radv/radv.c
proto/rip/packets.c
proto/rip/rip.c
sysdep/bsd/krt-sock.c
sysdep/bsd/sysio.h
sysdep/linux/netlink.c
sysdep/unix/krt.Y
sysdep/unix/krt.c
sysdep/unix/krt.h
sysdep/unix/unix.h

index f14e70a0bf7ec40e6f71c99e61d2840c99257017..01b1aa48114b017e4ffcedd89daede63f3340db2 100644 (file)
 #include "lib/resource.h"
 #include "lib/string.h"
 #include "conf/conf.h"
+#include "sysdep/unix/krt.h"
 
 static pool *if_pool;
 
 list iface_list;
 
+static void if_recalc_preferred(struct iface *i);
+
 /**
  * ifa_dump - dump interface address
  * @a: interface address descriptor
@@ -183,6 +186,7 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i)
            (c & IF_CHANGE_DOWN) ? "goes down" :
            (c & IF_CHANGE_MTU) ? "changes MTU" :
            (c & IF_CHANGE_LINK) ? "changes link" :
+           (c & IF_CHANGE_PREFERRED) ? "changes preferred address" :
            (c & IF_CHANGE_CREATE) ? "created" :
            "sends unknown event");
       p->if_notify(p, c, i);
@@ -211,20 +215,14 @@ if_notify_change(unsigned c, struct iface *i)
 
   if (c & IF_CHANGE_DOWN)
     WALK_LIST(a, i->addrs)
-      {
-       a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
-       ifa_notify_change_(IF_CHANGE_DOWN, a);
-      }
+      ifa_notify_change_(IF_CHANGE_DOWN, a);
 
   WALK_LIST(p, proto_list)
     if_send_notify(p, c, i);
 
   if (c & IF_CHANGE_UP)
     WALK_LIST(a, i->addrs)
-      {
-       a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
-       ifa_notify_change_(IF_CHANGE_UP, a);
-      }
+      ifa_notify_change_(IF_CHANGE_UP, a);
 
   if (c & IF_CHANGE_UP)
     neigh_if_up(i);
@@ -233,24 +231,23 @@ if_notify_change(unsigned c, struct iface *i)
     neigh_if_link(i);
 }
 
-static unsigned
-if_recalc_flags(struct iface *i, unsigned flags)
+static uint
+if_recalc_flags(struct iface *i UNUSED, uint flags)
 {
-  if ((flags & (IF_SHUTDOWN | IF_TMP_DOWN)) ||
-      !(flags & IF_ADMIN_UP) ||
-      !i->addr)
-    flags &= ~IF_UP;
-  else
+  if ((flags & IF_ADMIN_UP) && !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)))
     flags |= IF_UP;
+  else
+    flags &= ~IF_UP;
+
   return flags;
 }
 
 static void
-if_change_flags(struct iface *i, unsigned flags)
+if_change_flags(struct iface *i, uint flags)
 {
-  unsigned of = i->flags;
-
+  uint of = i->flags;
   i->flags = if_recalc_flags(i, flags);
+
   if ((i->flags ^ of) & IF_UP)
     if_notify_change((i->flags & IF_UP) ? IF_CHANGE_UP : IF_CHANGE_DOWN, i);
 }
@@ -298,7 +295,6 @@ if_update(struct iface *new)
   WALK_LIST(i, iface_list)
     if (!strcmp(new->name, i->name))
       {
-       new->addr = i->addr;
        new->flags = if_recalc_flags(new, new->flags);
        c = if_what_changed(i, new);
        if (c & IF_CHANGE_TOO_MUCH)     /* Changed a lot, convert it to down/up */
@@ -306,10 +302,13 @@ if_update(struct iface *new)
            DBG("Interface %s changed too much -- forcing down/up transition\n", i->name);
            if_change_flags(i, i->flags | IF_TMP_DOWN);
            rem_node(&i->n);
-           new->addr = i->addr;
+           new->addr4 = i->addr4;
+           new->addr6 = i->addr6;
+           new->llv6 = i->llv6;
+           new->sysdep = i->sysdep;
            memcpy(&new->addrs, &i->addrs, sizeof(i->addrs));
            memcpy(i, new, sizeof(*i));
-           i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
+           i->flags &= ~IF_UP;         /* IF_TMP_DOWN will be added later */
            goto newif;
          }
 
@@ -340,13 +339,16 @@ if_start_update(void)
     {
       i->flags &= ~IF_UPDATED;
       WALK_LIST(a, i->addrs)
-       a->flags &= ~IF_UPDATED;
+       a->flags &= ~IA_UPDATED;
     }
 }
 
 void
 if_end_partial_update(struct iface *i)
 {
+  if (i->flags & IF_NEEDS_RECALC)
+    if_recalc_preferred(i);
+
   if (i->flags & IF_TMP_DOWN)
     if_change_flags(i, i->flags & ~IF_TMP_DOWN);
 }
@@ -364,7 +366,7 @@ if_end_update(void)
       else
        {
          WALK_LIST_DELSAFE(a, b, i->addrs)
-           if (!(a->flags & IF_UPDATED))
+           if (!(a->flags & IA_UPDATED))
              ifa_delete(a);
          if_end_partial_update(i);
        }
@@ -461,40 +463,99 @@ if_get_by_name(char *name)
   return i;
 }
 
-struct ifa *kif_choose_primary(struct iface *i);
+static inline void
+if_set_preferred(struct ifa **pos, struct ifa *new)
+{
+  if (*pos)
+    (*pos)->flags &= ~IA_PRIMARY;
+  if (new)
+    new->flags |= IA_PRIMARY;
 
-static int
-ifa_recalc_primary(struct iface *i)
+  *pos = new;
+}
+
+static void
+if_recalc_preferred(struct iface *i)
 {
-  struct ifa *a = kif_choose_primary(i);
+  /*
+   * Preferred address selection priority:
+   * 1) Address configured in Device protocol
+   * 2) Sysdep IPv4 address (BSD)
+   * 3) Old preferred address
+   * 4) First address in list
+   */
 
-  if (a == i->addr)
-    return 0;
+  struct kif_iface_config *ic = kif_get_iface_config(i);
+  struct ifa *a4 = i->addr4, *a6 = i->addr6, *ll = i->llv6;
+  ip_addr pref_v4 = ic->pref_v4;
+  uint change = 0;
+
+  if (kif_update_sysdep_addr(i))
+    change |= IF_CHANGE_SYSDEP;
 
-  if (i->addr)
-    i->addr->flags &= ~IA_PRIMARY;
+  /* BSD sysdep address */
+  if (ipa_zero(pref_v4) && ip4_nonzero(i->sysdep))
+    pref_v4 = ipa_from_ip4(i->sysdep);
 
-  if (a)
+  struct ifa *a;
+  WALK_LIST(a, i->addrs)
     {
-      a->flags |= IA_PRIMARY;
-      rem_node(&a->n);
-      add_head(&i->addrs, &a->n);
+      /* Secondary address is never selected */
+      if (a->flags & IA_SECONDARY)
+       continue;
+
+      if (ipa_is_ip4(a->ip)) {
+       if (!a4 || ipa_equal(a->ip, pref_v4))
+         a4 = a;
+      } else if (!ipa_is_link_local(a->ip)) {
+       if (!a6 || ipa_equal(a->ip, ic->pref_v6))
+         a6 = a;
+      } else {
+       if (!ll || ipa_equal(a->ip, ic->pref_ll))
+         ll = a;
+      }
     }
 
-  i->addr = a;
-  return 1;
+  if (a4 != i->addr4)
+  {
+    if_set_preferred(&i->addr4, a4);
+    change |= IF_CHANGE_ADDR4;
+  }
+
+  if (a6 != i->addr6)
+  {
+    if_set_preferred(&i->addr6, a6);
+    change |= IF_CHANGE_ADDR6;
+  }
+
+  if (ll != i->llv6)
+  {
+    if_set_preferred(&i->llv6, ll);
+    change |= IF_CHANGE_LLV6;
+  }
+
+  i->flags &= ~IF_NEEDS_RECALC;
+
+  /*
+   * FIXME: There should be proper notification instead of iface restart:
+   * if_notify_change(change, i)
+   */
+  if (change)
+    if_change_flags(i, i->flags | IF_TMP_DOWN);
 }
 
 void
-ifa_recalc_all_primary_addresses(void)
+if_recalc_all_preferred_addresses(void)
 {
   struct iface *i;
 
   WALK_LIST(i, iface_list)
-    {
-      if (ifa_recalc_primary(i))
-       if_change_flags(i, i->flags | IF_TMP_DOWN);
-    }
+  {
+    if_recalc_preferred(i);
+
+    if (i->flags & IF_TMP_DOWN)
+      if_change_flags(i, i->flags & ~IF_TMP_DOWN);
+  }
 }
 
 static inline int
@@ -526,7 +587,7 @@ ifa_update(struct ifa *a)
            b->scope == a->scope &&
            !((b->flags ^ a->flags) & IA_PEER))
          {
-           b->flags |= IF_UPDATED;
+           b->flags |= IA_UPDATED;
            return b;
          }
        ifa_delete(b);
@@ -534,15 +595,15 @@ ifa_update(struct ifa *a)
       }
 
   if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd))
-    log(L_ERR "Missing broadcast address for interface %s", i->name);
+    log(L_WARN "Missing broadcast address for interface %s", i->name);
 
   b = mb_alloc(if_pool, sizeof(struct ifa));
   memcpy(b, a, sizeof(struct ifa));
   add_tail(&i->addrs, &b->n);
-  b->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
-  if (ifa_recalc_primary(i))
-    if_change_flags(i, i->flags | IF_TMP_DOWN);
-  if (b->flags & IF_UP)
+  b->flags |= IA_UPDATED;
+
+  i->flags |= IF_NEEDS_RECALC;
+  if (i->flags & IF_UP)
     ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b);
   return b;
 }
@@ -565,16 +626,24 @@ ifa_delete(struct ifa *a)
     if (ifa_same(b, a))
       {
        rem_node(&b->n);
-       if (b->flags & IF_UP)
-         {
-           b->flags &= ~IF_UP;
-           ifa_notify_change(IF_CHANGE_DOWN, b);
-         }
+
        if (b->flags & IA_PRIMARY)
          {
-           if_change_flags(i, i->flags | IF_TMP_DOWN);
-           ifa_recalc_primary(i);
+           /*
+            * We unlink deleted preferred address and mark for recalculation.
+            * FIXME: This could break if we make iface scan non-atomic, as
+            * protocols still could use the freed address until they get
+            * if_notify from preferred route recalculation.
+            */
+           if (b == i->addr4) i->addr4 = NULL;
+           if (b == i->addr6) i->addr6 = NULL;
+           if (b == i->llv6) i->llv6 = NULL;
+           i->flags |= IF_NEEDS_RECALC;
          }
+
+       if (i->flags & IF_UP)
+         ifa_notify_change(IF_CHANGE_DOWN, b);
+
        mb_free(b);
        return;
       }
@@ -741,16 +810,17 @@ iface_patts_equal(list *a, list *b, int (*comp)(struct iface_patt *, struct ifac
 static void
 if_show_addr(struct ifa *a)
 {
-  byte opp[IPA_MAX_TEXT_LENGTH + 16];
+  byte *flg, opp[IPA_MAX_TEXT_LENGTH + 16];
+
+  flg = (a->flags & IA_PRIMARY) ? "Preferred, " : (a->flags & IA_SECONDARY) ? "Secondary, " : "";
 
   if (ipa_nonzero(a->opposite))
-    bsprintf(opp, ", opposite %I", a->opposite);
+    bsprintf(opp, "opposite %I, ", a->opposite);
   else
     opp[0] = 0;
-  cli_msg(-1003, "\t%I/%d (%s%s, scope %s)",
-         a->ip, a->prefix.pxlen,
-         (a->flags & IA_PRIMARY) ? "Primary" : (a->flags & IA_SECONDARY) ? "Secondary" : "Unselected",
-         opp, ip_scope_text(a->scope));
+
+  cli_msg(-1003, "\t%I/%d (%s%sscope %s)",
+         a->ip, a->prefix.pxlen, flg, opp, ip_scope_text(a->scope));
 }
 
 void
@@ -765,7 +835,7 @@ if_show(void)
       if (i->flags & IF_SHUTDOWN)
        continue;
 
-      cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index);
+      cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "Up" : "Down", i->index);
       if (!(i->flags & IF_MULTIACCESS))
        type = "PtP";
       else
@@ -779,10 +849,13 @@ if_show(void)
              (i->flags & IF_LOOPBACK) ? " Loopback" : "",
              (i->flags & IF_IGNORE) ? " Ignored" : "",
              i->mtu);
-      if (i->addr)
-       if_show_addr(i->addr);
+
+      WALK_LIST(a, i->addrs)
+       if (a->prefix.type == NET_IP4)
+         if_show_addr(a);
+
       WALK_LIST(a, i->addrs)
-       if (a != i->addr)
+       if (a->prefix.type == NET_IP6)
          if_show_addr(a);
     }
   cli_msg(0, "");
@@ -792,16 +865,25 @@ void
 if_show_summary(void)
 {
   struct iface *i;
-  byte addr[IPA_MAX_TEXT_LENGTH + 16];
 
-  cli_msg(-2005, "interface state address");
+  cli_msg(-2005,  "%-10s %-6s %-18s %s", "Interface", "State", "IPv4 address", "IPv6 address");
   WALK_LIST(i, iface_list)
     {
-      if (i->addr)
-       bsprintf(addr, "%I/%d", i->addr->ip, i->addr->prefix.pxlen);
+      byte a4[IPA_MAX_TEXT_LENGTH + 17];
+      byte a6[IPA_MAX_TEXT_LENGTH + 17];
+
+      if (i->addr4)
+       bsprintf(a4, "%I/%d", i->addr4->ip, i->addr4->prefix.pxlen);
       else
-       addr[0] = 0;
-      cli_msg(-1005, "%-9s %-5s %s", i->name, (i->flags & IF_UP) ? "up" : "DOWN", addr);
+       a4[0] = 0;
+
+      if (i->addr6)
+       bsprintf(a6, "%I/%d", i->addr6->ip, i->addr6->prefix.pxlen);
+      else
+       a6[0] = 0;
+
+      cli_msg(-1005, "%-10s %-6s %-18s %s",
+             i->name, (i->flags & IF_UP) ? "Up" : "Down", a4, a6);
     }
   cli_msg(0, "");
 }
index de5070d672fffe5a115c1cbf92a322271589ea08..59b1253c9777a2191a3d50f28b024f7a319a9f52 100644 (file)
@@ -35,11 +35,14 @@ struct iface {
   unsigned mtu;
   unsigned index;                      /* OS-dependent interface index */
   list addrs;                          /* Addresses assigned to this interface */
-  struct ifa *addr;                    /* Primary address */
+  struct ifa *addr4;                   /* Primary address for IPv4 */
+  struct ifa *addr6;                   /* Primary address for IPv6 */
+  struct ifa *llv6;                    /* Primary link-local address for IPv6 */
+  ip4_addr sysdep;                     /* Arbitrary IPv4 address for internal sysdep use */
   list neighbors;                      /* All neighbors on this interface */
 };
 
-#define IF_UP 1                                /* IF_ADMIN_UP and IP address known */
+#define IF_UP 1                                /* Currently just IF_ADMIN_UP */
 #define IF_MULTIACCESS 2
 #define IF_BROADCAST 4
 #define IF_MULTICAST 8
@@ -70,7 +73,10 @@ struct iface {
 
 #define IF_JUST_CREATED 0x10000000     /* Send creation event as soon as possible */
 #define IF_TMP_DOWN 0x20000000         /* Temporary shutdown due to interface reconfiguration */
-#define IF_UPDATED 0x40000000          /* Touched in last scan */
+#define IF_UPDATED 0x40000000          /* Iface touched in last scan */
+#define IF_NEEDS_RECALC        0x80000000      /* Preferred address recalculation is needed */
+
+#define IA_UPDATED IF_UPDATED          /* Address touched in last scan */
 
 /* Interface change events */
 
@@ -79,8 +85,14 @@ struct iface {
 #define IF_CHANGE_MTU 4
 #define IF_CHANGE_CREATE 8             /* Seen this interface for the first time */
 #define IF_CHANGE_LINK 0x10
+#define IF_CHANGE_ADDR4        0x100           /* Change of iface->addr4 */
+#define IF_CHANGE_ADDR6        0x200           /* ... */
+#define IF_CHANGE_LLV6 0x400
+#define IF_CHANGE_SYSDEP 0x800
 #define IF_CHANGE_TOO_MUCH 0x40000000  /* Used internally */
 
+#define IF_CHANGE_PREFERRED (IF_CHANGE_ADDR4 | IF_CHANGE_ADDR6 | IF_CHANGE_LLV6)
+
 void if_init(void);
 void if_dump(struct iface *);
 void if_dump_all(void);
@@ -99,7 +111,7 @@ void if_feed_baby(struct proto *);
 struct iface *if_find_by_index(unsigned);
 struct iface *if_find_by_name(char *);
 struct iface *if_get_by_name(char *);
-void ifa_recalc_all_primary_addresses(void);
+void if_recalc_all_preferred_addresses(void);
 
 
 /* The Neighbor Cache */
index 9993da24d3afa5c22e7f94621a9d3943b530a5da..b3d5bf9776f073688b21937b0626e14fe428bde2 100644 (file)
@@ -33,7 +33,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
   struct channel *c;
 
   if (!EMPTY_LIST(cf->iface_list) &&
-      !iface_patt_find(&cf->iface_list, ad->iface, ad->iface->addr))
+      !iface_patt_find(&cf->iface_list, ad->iface, ad))
     /* Empty list is automatically treated as "*" */
     return;
 
index a0ec3629d3dedac17b546f000e2f2fb646ecd36f..237461556256b8928531d3b38060505d812df991 100644 (file)
@@ -1501,26 +1501,14 @@ 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);
 
-  ip_addr addr4 = IPA_NONE;
-  struct ifa *addr;
-  WALK_LIST(addr, new->addrs)
-  {
-    if (ipa_is_link_local(addr->ip))
-      ifa->addr = addr->ip;
-
-    if (ipa_zero(addr4) && ipa_is_ip4(addr->ip))
-      addr4 = addr->ip;
-  }
-
+  ip_addr addr4 = new->addr4 ? new->addr4->ip : IPA_NONE;
   ifa->next_hop_ip4 = ipa_nonzero(ic->next_hop_ip4) ? ic->next_hop_ip4 : addr4;
   ifa->next_hop_ip6 = ipa_nonzero(ic->next_hop_ip6) ? ic->next_hop_ip6 : ifa->addr;
 
-  if (ipa_zero(ifa->addr))
-    log(L_WARN "%s: Cannot find link-local addr on %s", p->p.name, new->name);
-
   if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
     log(L_WARN "%s: Cannot find IPv4 next hop addr on %s", p->p.name, new->name);
 
@@ -1576,6 +1564,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);
 
@@ -1615,21 +1607,8 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b
 
   ifa->cf = new;
 
-  if (ipa_nonzero(new->next_hop_ip4))
-    ifa->next_hop_ip4 = new->next_hop_ip4;
-  else
-  {
-    ifa->next_hop_ip4 = IPA_NONE;
-
-    struct ifa *addr;
-    WALK_LIST(addr, ifa->iface->addrs)
-      if (ipa_is_ip4(addr->ip))
-      {
-       ifa->next_hop_ip4 = addr->ip;
-       break;
-      }
-  }
-
+  ip_addr addr4 = ifa->iface->addr4 ? ifa->iface->addr4->ip : IPA_NONE;
+  ifa->next_hop_ip4 = ipa_nonzero(new->next_hop_ip4) ? new->next_hop_ip4 : addr4;
   ifa->next_hop_ip6 = ipa_nonzero(new->next_hop_ip6) ? new->next_hop_ip6 : ifa->addr;
 
   if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
@@ -1660,7 +1639,15 @@ babel_reconfigure_ifaces(struct babel_proto *p, struct babel_config *cf)
 
   WALK_LIST(iface, iface_list)
   {
-    if (! (iface->flags & IF_UP))
+    if (!(iface->flags & IF_UP))
+      continue;
+
+    /* Ignore non-multicast ifaces */
+    if (!(iface->flags & IF_MULTICAST))
+      continue;
+
+    /* Ignore ifaces without link-local address */
+    if (!iface->llv6)
       continue;
 
     struct babel_iface *ifa = babel_find_iface(p, iface);
index 78c133e0f97dc822698e6ba91127a1e0661c4bd9..d136efe808a36947aa0f36967608bb7f461ce0b1 100644 (file)
@@ -1294,7 +1294,7 @@ babel_rx_hook(sock *sk, uint len)
       sk->iface->name, sk->faddr, sk->laddr);
 
   /* Silently ignore my own packets */
-  if (ipa_equal(ifa->iface->addr->ip, sk->faddr))
+  if (ipa_equal(sk->faddr, sk->saddr))
     return 1;
 
   if (!ipa_is_link_local(sk->faddr))
@@ -1329,6 +1329,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->rx_hook = babel_rx_hook;
   sk->tx_hook = babel_tx_hook;
index cccced57e349c0da0a1febc71a089c0aba08b3b2..a8d5cf9d9433ecf6de112ae17d68b7cd6075678c 100644 (file)
@@ -1094,19 +1094,8 @@ bgp_start_neighbor(struct bgp_proto *p)
 
   if (ipa_is_link_local(p->source_addr))
     p->link_addr = p->source_addr;
-  else
-  {
-    /* Find some link-local address for given iface */
-    struct ifa *a;
-    WALK_LIST(a, p->neigh->iface->addrs)
-      if (a->scope == SCOPE_LINK)
-      {
-       p->link_addr = a->ip;
-       break;
-      }
-
-    DBG("%s: Selected link-local address %I\n", p->p.name, p->link_addr);
-  }
+  else if (p->neigh->iface->llv6)
+    p->link_addr = p->neigh->iface->llv6->ip;
 
   bgp_initiate(p);
 }
index 15ca57385d590bb456e80860d9cfe84d47163c6c..9ea8feee2623e8be774b6a811e54948e3aa5a0b9 100644 (file)
@@ -358,7 +358,7 @@ radv_rx_hook(sock *sk, uint size)
   if (sk->lifindex != sk->iface->index)
     return 1;
 
-  if (ipa_equal(sk->faddr, ifa->addr->ip))
+  if (ipa_equal(sk->faddr, sk->saddr))
     return 1;
 
   if (size < 8)
index b4b1bc584f9091abbdc75e57a6751fdbf6142655..2c0a23ad6a48345a545ed012d9aa9638a341aca4 100644 (file)
@@ -138,17 +138,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->prefix.type == NET_IP6) && (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)
 {
@@ -161,16 +150,10 @@ 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;
 
   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;
@@ -216,8 +199,15 @@ radv_if_notify(struct proto *P, unsigned flags, struct iface *iface)
 
   if (flags & IF_CHANGE_UP)
   {
-    struct radv_iface_config *ic = (struct radv_iface_config *)
-      iface_patt_find(&cf->patt_list, iface, NULL);
+    struct radv_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
+
+    /* Ignore non-multicast ifaces */
+    if (!(iface->flags & IF_MULTICAST))
+      return;
+
+    /* Ignore ifaces without link-local address */
+    if (!iface->llv6)
+      return;
 
     if (ic)
       radv_iface_new(p, iface, ic);
@@ -395,6 +385,17 @@ radv_reconfigure(struct proto *P, struct proto_config *CF)
   struct iface *iface;
   WALK_LIST(iface, iface_list)
   {
+    if (!(iface->flags & IF_UP))
+      continue;
+
+    /* Ignore non-multicast ifaces */
+    if (!(iface->flags & IF_MULTICAST))
+      continue;
+
+    /* Ignore ifaces without link-local address */
+    if (!iface->llv6)
+      continue;
+
     struct radv_iface *ifa = radv_iface_find(p, iface);
     struct radv_iface_config *ic = (struct radv_iface_config *)
       iface_patt_find(&new->patt_list, iface, NULL);
index e97809c83dd4ff131f422e6e91ace5854a0b6b62..de48fd0ad7ef78291914b706502a30eaa5a7fba8 100644 (file)
@@ -669,8 +669,7 @@ rip_rx_hook(sock *sk, uint len)
       sk->iface->name, sk->faddr, sk->laddr);
 
   /* Silently ignore my own packets */
-  /* FIXME: Better local address check */
-  if (ipa_equal(ifa->iface->addr->ip, sk->faddr))
+  if (ipa_equal(sk->faddr, sk->saddr))
     return 1;
 
   if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr))
@@ -742,14 +741,7 @@ rip_open_socket(struct rip_iface *ifa)
   sk->sport = ifa->cf->port;
   sk->dport = ifa->cf->port;
   sk->iface = ifa->iface;
-
-  /*
-   * 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->saddr = rip_is_v2(p) ? ifa->iface->addr4->ip : ifa->iface->llv6->ip;
 
   sk->rx_hook = rip_rx_hook;
   sk->tx_hook = rip_tx_hook;
index 55fb47c569a941bb025a512066bc468051d7f26f..820c5117e62b5c030094dab939b1da9227c8b04f 100644 (file)
@@ -630,7 +630,13 @@ rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config
   else if (ic->mode == RIP_IM_MULTICAST)
     ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS;
   else /* Broadcast */
-    ifa->addr = iface->addr->brd;
+    ifa->addr = iface->addr4->brd;
+  /*
+   * The above is just a workaround for BSD as it can't send broadcasts
+   * to 255.255.255.255. BSD systems need the network broadcast address instead.
+   *
+   * TODO: move this to sysdep code
+   */
 
   init_list(&ifa->neigh_list);
 
@@ -706,7 +712,11 @@ rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf)
 
   WALK_LIST(iface, iface_list)
   {
-    if (! (iface->flags & IF_UP))
+    if (!(iface->flags & IF_UP))
+      continue;
+
+    /* Ignore ifaces without appropriate address */
+    if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
       continue;
 
     struct rip_iface *ifa = rip_find_iface(p, iface);
@@ -744,6 +754,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);
 
+    /* Ignore ifaces without appropriate address */
+    if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
+      return;
+
     if (ic)
       rip_add_iface(p, iface, ic);
 
index df639816cd7046b4469a2d9c5742bb4b9fb7cb75..be8b50d63cff7bc41c5722e163916b1d4e578c95 100644 (file)
@@ -287,18 +287,21 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 #endif
   {
     /* Fallback for all other valid cases */
-    if (!i->addr)
-    {
-      log(L_ERR "KRT: interface %s has no IP addess", i->name);
-      return -1;
-    }
 
 #ifdef RTF_CLONING
     if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS)       /* PTP */
       msg.rtm.rtm_flags |= RTF_CLONING;
 #endif
 
-    sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
+    struct ifa *addr = (net->n.addr->type == NET_IP4) ? i->addr4 : (i->addr6 ?: i->llv6);
+
+    if (!addr)
+    {
+      log(L_ERR "KRT: interface %s has no IP addess", i->name);
+      return -1;
+    }
+
+    sockaddr_fill(&gate, af, addr->ip, i, 0);
     msg.rtm.rtm_addrs |= RTA_GATEWAY;
     break;
   }
@@ -1124,13 +1127,11 @@ kif_sys_shutdown(struct kif_proto *p)
   krt_buffer_release(&p->p);
 }
 
-
-struct ifa *
-kif_get_primary_ip(struct iface *i UNUSED)
+int
+kif_update_sysdep_addr(struct iface *i)
 {
-#if 0
   static int fd = -1;
-  
+
   if (fd < 0)
     fd = socket(AF_INET, SOCK_DGRAM, 0);
 
@@ -1140,20 +1141,10 @@ kif_get_primary_ip(struct iface *i UNUSED)
 
   int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr);
   if (rv < 0)
-    return NULL;
-
-  ip_addr addr;
-  struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
-  memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr));
-  ipa_ntoh(addr);
+    return 0;
 
-  struct ifa *a;
-  WALK_LIST(a, i->addrs)
-  {
-    if (ipa_equal(a->ip, addr))
-      return a;
-  }
-#endif
+  ip4_addr old = i->sysdep;
+  i->sysdep = ip4_from_ipa(ipa_from_sa4(&ifr.ifr_addr);
 
-  return NULL;
+  return !ip4_equal(i->sysdep, addr);
 }
index 0e895e20078f8d732703e92a3ab813d0b407ed91..68296e653f02e19ce2548c3cc710ebd45da8ca49 100644 (file)
  */
 
 #define INIT_MREQ4(maddr,ifa) \
-  { .imr_multiaddr = ipa_to_in4(maddr), .imr_interface = ipa_to_in4(ifa->addr->ip) }
+  { .imr_multiaddr = ipa_to_in4(maddr), .imr_interface = ip4_to_in4(ifa->sysdep) }
 
 static inline int
 sk_setup_multicast4(sock *s)
 {
-  struct in_addr ifa = ipa_to_in4(s->iface->addr->ip);
+  struct in_addr ifa = ip4_to_in4(s->iface->sysdep);
   u8 ttl = s->ttl;
   u8 n = 0;
 
index 2b7b13fb9aa6d7827f36cf2d6167118380315e34..c9d5cdec5314e4e2155ede631afcd68d3b886750 100644 (file)
@@ -2030,3 +2030,9 @@ void
 kif_sys_shutdown(struct kif_proto *p UNUSED)
 {
 }
+
+int
+kif_update_sysdep_addr(struct iface *i UNUSED)
+{
+  return 0;
+}
index b261c91e135661cefa72a1e1245b33a2f8bdc57d..3bf7da652186ad65b1b0b521b45e012914f12368 100644 (file)
@@ -14,6 +14,7 @@ CF_DEFINES
 
 #define THIS_KRT ((struct krt_config *) this_proto)
 #define THIS_KIF ((struct kif_config *) this_proto)
+#define KIF_IFACE ((struct kif_iface_config *) this_ipatt)
 
 static void
 krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit)
@@ -25,6 +26,17 @@ krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit)
   cc->merge_limit = limit;
 }
 
+static void
+kif_set_preferred(ip_addr ip)
+{
+  if (ipa_is_ip4(ip))
+    KIF_IFACE->pref_v4 = ip;
+  else if (!ipa_is_link_local(ip))
+    KIF_IFACE->pref_v6 = ip;
+  else
+    KIF_IFACE->pref_ll = ip;
+}
+
 CF_DECLS
 
 CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS)
@@ -88,18 +100,38 @@ CF_ADDTO(kif_proto, kif_proto kif_item ';')
 
 kif_item:
    proto_item
+ | INTERFACE kif_iface
  | SCAN TIME expr {
       /* Scan time of 0 means scan on startup only */
       THIS_KIF->scan_time = $3;
    }
- | PRIMARY opttext net_or_ipa {
-     struct kif_primary_item *kpi = cfg_alloc(sizeof (struct kif_primary_item));
-     kpi->pattern = $2;
-     kpi->addr = $3;
-     add_tail(&THIS_KIF->primary, &kpi->n);
-   }
  ;
 
+kif_iface_start:
+{
+  this_ipatt = cfg_allocz(sizeof(struct kif_iface_config));
+  add_tail(&THIS_KIF->iface_list, NODE this_ipatt);
+  init_list(&this_ipatt->ipn_list);
+}
+
+kif_iface_item:
+   PREFERRED ipa { kif_set_preferred($2); }
+ ;
+
+kif_iface_opts:
+   /* empty */
+ | kif_iface_opts kif_iface_item ';'
+ ;
+
+kif_iface_opt_list:
+   /* empty */
+ | '{' kif_iface_opts '}'
+ ;
+
+kif_iface:
+  kif_iface_start iface_patt_list_nopx kif_iface_opt_list;
+
+
 CF_ADDTO(dynamic_attr, KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_SOURCE); })
 CF_ADDTO(dynamic_attr, KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_METRIC); })
 
index f0241777a7ce5184a4a7d837f5c3970badce5bf7..0349a09ff0cdab7726f5fbc3fe3624028f8ed647 100644 (file)
@@ -89,6 +89,16 @@ static struct kif_config *kif_cf;
 static timer *kif_scan_timer;
 static bird_clock_t kif_last_shot;
 
+static struct kif_iface_config kif_default_iface = {};
+
+struct kif_iface_config *
+kif_get_iface_config(struct iface *iface)
+{
+  struct kif_config *cf = (void *) (kif_proto->p.cf);
+  struct kif_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
+  return ic ?: &kif_default_iface;
+}
+
 static void
 kif_scan(timer *t)
 {
@@ -116,57 +126,6 @@ kif_request_scan(void)
     tm_start(kif_scan_timer, 1);
 }
 
-static inline int
-prefer_addr(struct ifa *a, struct ifa *b)
-{
-  int sa = a->scope > SCOPE_LINK;
-  int sb = b->scope > SCOPE_LINK;
-
-  if (sa < sb)
-    return 0;
-  else if (sa > sb)
-    return 1;
-  else
-    return ipa_compare(a->ip, b->ip) < 0;
-}
-
-static inline struct ifa *
-find_preferred_ifa(struct iface *i, const net_addr *n)
-{
-  struct ifa *a, *b = NULL;
-
-  WALK_LIST(a, i->addrs)
-    {
-      if (!(a->flags & IA_SECONDARY) &&
-         (!n || ipa_in_netX(a->ip, n)) &&
-         (!b || prefer_addr(a, b)))
-       b = a;
-    }
-
-  return b;
-}
-
-struct ifa *
-kif_choose_primary(struct iface *i)
-{
-  struct kif_config *cf = (struct kif_config *) (kif_proto->p.cf);
-  struct kif_primary_item *it;
-  struct ifa *a;
-
-  WALK_LIST(it, cf->primary)
-    {
-      if (!it->pattern || patmatch(it->pattern, i->name))
-       if (a = find_preferred_ifa(i, &it->addr))
-         return a;
-    }
-
-  if (a = kif_get_primary_ip(i))
-    return a;
-
-  return find_preferred_ifa(i, NULL);
-}
-
-
 static struct proto *
 kif_init(struct proto_config *c)
 {
@@ -224,15 +183,15 @@ kif_reconfigure(struct proto *p, struct proto_config *new)
       tm_start(kif_scan_timer, n->scan_time);
     }
 
-  if (!EMPTY_LIST(o->primary) || !EMPTY_LIST(n->primary))
+  if (!EMPTY_LIST(o->iface_list) || !EMPTY_LIST(n->iface_list))
     {
       /* This is hack, we have to update a configuration
        * to the new value just now, because it is used
-       * for recalculation of primary addresses.
+       * for recalculation of preferred addresses.
        */
       p->cf = new;
 
-      ifa_recalc_all_primary_addresses();
+      if_recalc_all_preferred_addresses();
     }
 
   return 1;
@@ -254,7 +213,7 @@ kif_init_config(int class)
 
   kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
   kif_cf->scan_time = 60;
-  init_list(&kif_cf->primary);
+  init_list(&kif_cf->iface_list);
 
   kif_sys_init_config(kif_cf);
   return (struct proto_config *) kif_cf;
@@ -266,14 +225,13 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
   struct kif_config *d = (struct kif_config *) dest;
   struct kif_config *s = (struct kif_config *) src;
 
-  /* Copy primary addr list */
-  cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
+  /* Copy interface config list */
+  cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct kif_iface_config));
 
   /* Fix sysdep parts */
   kif_sys_copy_config(d, s);
 }
 
-
 struct protocol proto_unix_iface = {
   .name =              "Device",
   .template =          "device%d",
index cb404de370ce09f664d0b83c024fbd8c140331b4..089c97ad537e4b371b5b80c5f7432baf437556bc 100644 (file)
@@ -94,17 +94,20 @@ void krt_got_route_async(struct krt_proto *p, struct rte *e, int new);
 
 extern struct protocol proto_unix_iface;
 
-struct kif_primary_item {
-  node n;
-  byte *pattern;
-  net_addr addr;
-};
-
 struct kif_config {
   struct proto_config c;
   struct kif_params sys;       /* Sysdep params */
+
+  list iface_list;             /* List of iface configs (struct kif_iface_config) */
   int scan_time;               /* How often we re-scan interfaces */
-  list primary;                        /* Preferences for primary addresses (struct kif_primary_item) */
+};
+
+struct kif_iface_config {
+  struct iface_patt i;
+
+  ip_addr pref_v4;
+  ip_addr pref_v6;
+  ip_addr pref_ll;
 };
 
 struct kif_proto {
@@ -116,6 +119,7 @@ extern struct kif_proto *kif_proto;
 
 #define KIF_CF ((struct kif_config *)p->p.cf)
 
+struct kif_iface_config * kif_get_iface_config(struct iface *iface);
 struct proto_config * krt_init_config(int class);
 
 
@@ -150,6 +154,6 @@ void kif_sys_copy_config(struct kif_config *, struct kif_config *);
 
 void kif_do_scan(struct kif_proto *);
 
-struct ifa *kif_get_primary_ip(struct iface *i);
+int kif_update_sysdep_addr(struct iface *i);
 
 #endif
index dcaab72973612e3bbcceb16f2f687ce41338990c..4b0fb0050ea9fade9e1ab9a87e0e54399726f330 100644 (file)
@@ -80,6 +80,9 @@ static inline ip_addr ipa_from_sa(sockaddr *sa)
 static inline struct in_addr ipa_to_in4(ip_addr a)
 { return (struct in_addr) { htonl(ipa_to_u32(a)) }; }
 
+static inline struct in_addr ip4_to_in4(ip4_addr a)
+{ return (struct in_addr) { htonl(ip4_to_u32(a)) }; }
+
 static inline struct in6_addr ipa_to_in6(ip_addr a)
 { return (struct in6_addr) { .s6_addr32 = { htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a)) } }; }