]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - nest/iface.c
Merge remote-tracking branch 'origin/master' into mq-filter-stack
[thirdparty/bird.git] / nest / iface.c
index 5e88b21bf4187bbc904972d588307cfc5e5702d0..c1966ac65f59ad4f28822c62a437cfd3ac3b54e4 100644 (file)
 #include "lib/resource.h"
 #include "lib/string.h"
 #include "conf/conf.h"
+#include "sysdep/unix/krt.h"
 
 static pool *if_pool;
 
-static void auto_router_id(void);
-
 list iface_list;
 
+static void if_recalc_preferred(struct iface *i);
+
 /**
  * ifa_dump - dump interface address
  * @a: interface address descriptor
@@ -48,10 +49,11 @@ list iface_list;
 void
 ifa_dump(struct ifa *a)
 {
-  debug("\t%I, net %I/%-2d bc %I -> %I%s%s%s\n", a->ip, a->prefix, a->pxlen, a->brd, a->opposite,
-       (a->flags & IF_UP) ? "" : " DOWN",
-       (a->flags & IA_PRIMARY) ? "" : " SEC",
-       (a->flags & IA_UNNUMBERED) ? " UNNUM" : "");
+  debug("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite,
+       (a->flags & IA_PRIMARY) ? " PRIMARY" : "",
+       (a->flags & IA_SECONDARY) ? " SEC" : "",
+       (a->flags & IA_HOST) ? " HOST" : "",
+       (a->flags & IA_PEER) ? " PEER" : "");
 }
 
 /**
@@ -67,13 +69,13 @@ if_dump(struct iface *i)
   struct ifa *a;
 
   debug("IF%d: %s", i->index, i->name);
-  if (i->flags & IF_ADMIN_DOWN)
-    debug(" ADMIN-DOWN");
+  if (i->flags & IF_SHUTDOWN)
+    debug(" SHUTDOWN");
   if (i->flags & IF_UP)
     debug(" UP");
   else
     debug(" DOWN");
-  if (i->flags & IF_LINK_UP)
+  if (i->flags & IF_ADMIN_UP)
     debug(" LINK-UP");
   if (i->flags & IF_MULTIACCESS)
     debug(" MA");
@@ -91,7 +93,8 @@ if_dump(struct iface *i)
   WALK_LIST(a, i->addrs)
     {
       ifa_dump(a);
-      ASSERT((a != i->addr) == !(a->flags & IA_PRIMARY));
+      ASSERT(!!(a->flags & IA_PRIMARY) ==
+            ((a == i->addr4) || (a == i->addr6) || (a == i->llv6)));
     }
 }
 
@@ -117,12 +120,14 @@ if_what_changed(struct iface *i, struct iface *j)
 {
   unsigned c;
 
-  if (((i->flags ^ j->flags) & ~(IF_UP | IF_ADMIN_DOWN | IF_UPDATED | IF_LINK_UP | IF_TMP_DOWN | IF_JUST_CREATED))
-      || i->index != j->index)
+  if (((i->flags ^ j->flags) & ~(IF_UP | IF_SHUTDOWN | IF_UPDATED | IF_ADMIN_UP | IF_LINK_UP | IF_TMP_DOWN | IF_JUST_CREATED))
+      || (i->index != j->index) || (i->master != j->master))
     return IF_CHANGE_TOO_MUCH;
   c = 0;
   if ((i->flags ^ j->flags) & IF_UP)
     c |= (i->flags & IF_UP) ? IF_CHANGE_DOWN : IF_CHANGE_UP;
+  if ((i->flags ^ j->flags) & IF_LINK_UP)
+    c |= IF_CHANGE_LINK;
   if (i->mtu != j->mtu)
     c |= IF_CHANGE_MTU;
   return c;
@@ -133,42 +138,62 @@ if_copy(struct iface *to, struct iface *from)
 {
   to->flags = from->flags | (to->flags & IF_TMP_DOWN);
   to->mtu = from->mtu;
+  to->master_index = from->master_index;
+  to->master = from->master;
 }
 
 static inline void
 ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
 {
-  if (p->ifa_notify)
+  if (p->ifa_notify &&
+      (p->proto_state != PS_DOWN) &&
+      (!p->vrf || p->vrf == a->iface->master))
     {
       if (p->debug & D_IFACES)
-       log(L_TRACE "%s < %s address %I/%d on interface %s %s",
-           p->name, (a->flags & IA_PRIMARY) ? "primary" : "secondary",
-           a->prefix, a->pxlen, a->iface->name,
+       log(L_TRACE "%s < address %N on interface %s %s",
+           p->name, &a->prefix, a->iface->name,
            (c & IF_CHANGE_UP) ? "added" : "removed");
       p->ifa_notify(p, c, a);
     }
 }
 
 static void
-ifa_notify_change(unsigned c, struct ifa *a)
+ifa_notify_change_(unsigned c, struct ifa *a)
 {
   struct proto *p;
 
   DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
-  WALK_LIST(p, active_proto_list)
+
+  WALK_LIST(p, proto_list)
     ifa_send_notify(p, c, a);
 }
 
+static inline void
+ifa_notify_change(unsigned c, struct ifa *a)
+{
+  if (c & IF_CHANGE_DOWN)
+    neigh_ifa_update(a);
+
+  ifa_notify_change_(c, a);
+
+  if (c & IF_CHANGE_UP)
+    neigh_ifa_update(a);
+}
+
 static inline void
 if_send_notify(struct proto *p, unsigned c, struct iface *i)
 {
-  if (p->if_notify)
+  if (p->if_notify &&
+      (p->proto_state != PS_DOWN) &&
+      (!p->vrf || p->vrf == i->master))
     {
       if (p->debug & D_IFACES)
        log(L_TRACE "%s < interface %s %s", p->name, i->name,
            (c & IF_CHANGE_UP) ? "goes up" :
            (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);
@@ -192,50 +217,68 @@ if_notify_change(unsigned c, struct iface *i)
   if_dump(i);
 #endif
 
-  if (c & IF_CHANGE_UP)
-    neigh_if_up(i);
+  if (c & IF_CHANGE_DOWN)
+    neigh_if_down(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, active_proto_list)
+  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);
-      }
-  if (c & IF_CHANGE_DOWN)
-    neigh_if_down(i);
+      ifa_notify_change_(IF_CHANGE_UP, a);
+
+  if (c & IF_CHANGE_UP)
+    neigh_if_up(i);
+
+  if ((c & (IF_CHANGE_UP | IF_CHANGE_DOWN | IF_CHANGE_LINK)) == IF_CHANGE_LINK)
+    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_ADMIN_DOWN | IF_TMP_DOWN)) ||
-      !(flags & IF_LINK_UP) ||
-      !i->addr)
-    flags &= ~IF_UP;
-  else
+  if ((flags & IF_ADMIN_UP) &&
+      !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) &&
+      !(i->master_index && !i->master))
     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);
 }
 
+/**
+ * if_delete - remove interface
+ * @old: interface
+ *
+ * This function is called by the low-level platform dependent code
+ * whenever it notices an interface disappears. It is just a shorthand
+ * for if_update().
+ */
+
+void
+if_delete(struct iface *old)
+{
+  struct iface f = {};
+  strncpy(f.name, old->name, sizeof(f.name)-1);
+  f.flags = IF_SHUTDOWN;
+  if_update(&f);
+}
+
 /**
  * if_update - update interface status
  * @new: new interface status
@@ -261,7 +304,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 */
@@ -269,16 +311,20 @@ 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 */
            goto newif;
          }
-       else if (c)
-         {
-           if_copy(i, new);
-           if_notify_change(c, i);
-         }
+
+       if_copy(i, new);
+       if (c)
+         if_notify_change(c, i);
+
        i->flags |= IF_UPDATED;
        return i;
       }
@@ -302,13 +348,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);
 }
@@ -319,23 +368,29 @@ if_end_update(void)
   struct iface *i;
   struct ifa *a, *b;
 
-  if (!config->router_id)
-    auto_router_id();
-
   WALK_LIST(i, iface_list)
     {
       if (!(i->flags & IF_UPDATED))
-       if_change_flags(i, (i->flags & ~IF_LINK_UP) | IF_ADMIN_DOWN);
+       if_change_flags(i, (i->flags & ~IF_ADMIN_UP) | IF_SHUTDOWN);
       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);
        }
     }
 }
 
+void
+if_flush_ifaces(struct proto *p)
+{
+  if (p->debug & D_EVENTS)
+    log(L_TRACE "%s: Flushing interfaces", p->name);
+  if_start_update();
+  if_end_update();
+}
+
 /**
  * if_feed_baby - advertise interfaces to a new protocol
  * @p: protocol to feed
@@ -375,7 +430,7 @@ if_find_by_index(unsigned idx)
   struct iface *i;
 
   WALK_LIST(i, iface_list)
-    if (i->index == idx)
+    if (i->index == idx && !(i->flags & IF_SHUTDOWN))
       return i;
   return NULL;
 }
@@ -394,45 +449,125 @@ if_find_by_name(char *name)
   struct iface *i;
 
   WALK_LIST(i, iface_list)
-    if (!strcmp(i->name, name))
+    if (!strcmp(i->name, name) && !(i->flags & IF_SHUTDOWN))
       return i;
   return NULL;
 }
 
-struct ifa *kif_choose_primary(struct iface *i);
+struct iface *
+if_get_by_name(char *name)
+{
+  struct iface *i;
 
-static int
-ifa_recalc_primary(struct iface *i)
+  WALK_LIST(i, iface_list)
+    if (!strcmp(i->name, name))
+      return i;
+
+  /* No active iface, create a dummy */
+  i = mb_allocz(if_pool, sizeof(struct iface));
+  strncpy(i->name, name, sizeof(i->name)-1);
+  i->flags = IF_SHUTDOWN;
+  init_list(&i->addrs);
+  init_list(&i->neighbors);
+  add_tail(&iface_list, &i->n);
+  return i;
+}
+
+static inline void
+if_set_preferred(struct ifa **pos, struct ifa *new)
 {
-  struct ifa *a = kif_choose_primary(i);
+  if (*pos)
+    (*pos)->flags &= ~IA_PRIMARY;
+  if (new)
+    new->flags |= IA_PRIMARY;
 
-  if (a == i->addr)
-    return 0;
+  *pos = new;
+}
 
-  if (i->addr)
-    i->addr->flags &= ~IA_PRIMARY;
+static void
+if_recalc_preferred(struct iface *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
+   */
+
+  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;
+
+  /* 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) || (i->flags & IF_LOST_ADDR4))
+  {
+    if_set_preferred(&i->addr4, a4);
+    change |= IF_CHANGE_ADDR4;
+  }
+
+  if ((a6 != i->addr6) || (i->flags & IF_LOST_ADDR6))
+  {
+    if_set_preferred(&i->addr6, a6);
+    change |= IF_CHANGE_ADDR6;
+  }
+
+  if ((ll != i->llv6) || (i->flags & IF_LOST_LLV6))
+  {
+    if_set_preferred(&i->llv6, ll);
+    change |= IF_CHANGE_LLV6;
+  }
+
+  i->flags &= ~(IF_NEEDS_RECALC | IF_LOST_ADDR4 | IF_LOST_ADDR6 | IF_LOST_LLV6);
+
+  if (change)
+    if_notify_change(change, i);
 }
 
 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
+ifa_same(struct ifa *a, struct ifa *b)
+{
+  return ipa_equal(a->ip, b->ip) && net_equal(&a->prefix, &b->prefix);
 }
 
 
@@ -451,36 +586,30 @@ ifa_update(struct ifa *a)
   struct ifa *b;
 
   WALK_LIST(b, i->addrs)
-    if (ipa_equal(b->ip, a->ip))
+    if (ifa_same(b, a))
       {
-       if (ipa_equal(b->prefix, a->prefix) &&
-           b->pxlen == a->pxlen &&
-           ipa_equal(b->brd, a->brd) &&
+       if (ipa_equal(b->brd, a->brd) &&
            ipa_equal(b->opposite, a->opposite) &&
            b->scope == a->scope &&
-           !((b->flags ^ a->flags) & IA_UNNUMBERED))
+           !((b->flags ^ a->flags) & IA_PEER))
          {
-           b->flags |= IF_UPDATED;
+           b->flags |= IA_UPDATED;
            return b;
          }
        ifa_delete(b);
        break;
       }
 
-  if (!(i->flags & IF_MULTIACCESS) && a->pxlen < BITS_PER_IP_ADDRESS - 2)
-    log(L_WARN "Strange prefix length %d for point-to-point interface %s", a->pxlen, i->name);
-#ifndef IPV6
-  if ((i->flags & IF_BROADCAST) && !ipa_nonzero(a->brd))
-    log(L_ERR "Missing broadcast address for interface %s", i->name);
-#endif
+  if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd))
+    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;
 }
@@ -500,43 +629,75 @@ ifa_delete(struct ifa *a)
   struct ifa *b;
 
   WALK_LIST(b, i->addrs)
-    if (ipa_equal(b->ip, a->ip))
+    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. We should fix and
+            * simplify this in the future by having struct ifa refcounted
+            */
+           if (b == i->addr4) { i->addr4 = NULL; i->flags |= IF_LOST_ADDR4; }
+           if (b == i->addr6) { i->addr6 = NULL; i->flags |= IF_LOST_ADDR6; }
+           if (b == i->llv6)  { i->llv6 = NULL;  i->flags |= IF_LOST_LLV6; }
+           i->flags |= IF_NEEDS_RECALC;
          }
+
+       if (i->flags & IF_UP)
+         ifa_notify_change(IF_CHANGE_DOWN, b);
+
        mb_free(b);
        return;
       }
 }
 
-static void
-auto_router_id(void)
+u32
+if_choose_router_id(struct iface_patt *mask, u32 old_id)
 {
-#ifndef IPV6
-  struct iface *i, *j;
+  struct iface *i;
+  struct ifa *a, *b;
 
-  j = NULL;
+  b = NULL;
   WALK_LIST(i, iface_list)
-    if ((i->flags & IF_LINK_UP) &&
-       !(i->flags & (IF_IGNORE | IF_ADMIN_DOWN)) &&
-       i->addr &&
-       !(i->addr->flags & IA_UNNUMBERED) &&
-       (!j || ipa_to_u32(i->addr->ip) < ipa_to_u32(j->addr->ip)))
-      j = i;
-  if (!j)
-    die("Cannot determine router ID (no suitable network interface found), please configure it manually");
-  log(L_INFO "Guessed router ID %I according to interface %s", j->addr->ip, j->name);
-  config->router_id = ipa_to_u32(j->addr->ip);
-#endif
+    {
+      if (!(i->flags & IF_ADMIN_UP) ||
+         (i->flags & IF_SHUTDOWN))
+       continue;
+
+      WALK_LIST(a, i->addrs)
+       {
+         if (a->prefix.type != NET_IP4)
+           continue;
+
+         if (a->flags & IA_SECONDARY)
+           continue;
+
+         if (a->scope <= SCOPE_LINK)
+           continue;
+
+         /* Check pattern if specified */
+         if (mask && !iface_patt_match(mask, i, a))
+           continue;
+
+         /* No pattern or pattern matched */
+         if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip))
+           b = a;
+       }
+    }
+
+  if (!b)
+    return 0;
+
+  u32 id = ipa_to_u32(b->ip);
+  if (id != old_id)
+    log(L_INFO "Chosen router ID %R according to interface %s", id, b->iface->name);
+
+  return id;
 }
 
 /**
@@ -557,8 +718,8 @@ if_init(void)
  *     Interface Pattern Lists
  */
 
-static int
-iface_patt_match(struct iface_patt *ifp, struct iface *i)
+int
+iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a)
 {
   struct iface_patt_node *p;
 
@@ -579,23 +740,32 @@ iface_patt_match(struct iface_patt *ifp, struct iface *i)
            continue;
        }
 
-      if (p->pxlen)
-       if (!i->addr || !ipa_in_net(i->addr->ip, p->prefix, p->pxlen))
-         continue;
+      if (p->prefix.pxlen == 0)
+       return pos;
 
-      return pos;
+      if (!a)
+       continue;
+
+      if (ipa_in_netX(a->ip, &p->prefix))
+       return pos;
+
+      if ((a->flags & IA_PEER) &&
+         ipa_in_netX(a->opposite, &p->prefix))
+       return pos;
+
+      continue;
     }
 
   return 0;
 }
 
 struct iface_patt *
-iface_patt_find(list *l, struct iface *i)
+iface_patt_find(list *l, struct iface *i, struct ifa *a)
 {
   struct iface_patt *p;
 
   WALK_LIST(p, *l)
-    if (iface_patt_match(p, i))
+    if (iface_patt_match(p, i, a))
       return p;
 
   return NULL;
@@ -614,8 +784,7 @@ iface_plists_equal(struct iface_patt *pa, struct iface_patt *pb)
          (!x->pattern && y->pattern) ||        /* This nasty lines where written by me... :-( Feela */
          (!y->pattern && x->pattern) ||
          ((x->pattern != y->pattern) && strcmp(x->pattern, y->pattern)) ||
-         !ipa_equal(x->prefix, y->prefix) ||
-         (x->pxlen != y->pxlen))
+         !net_equal(&x->prefix, &y->prefix))
        return 0;
       x = (void *) x->n.next;
       y = (void *) y->n.next;
@@ -648,23 +817,17 @@ iface_patts_equal(list *a, list *b, int (*comp)(struct iface_patt *, struct ifac
 static void
 if_show_addr(struct ifa *a)
 {
-  byte broad[STD_ADDRESS_P_LENGTH + 16];
-  byte opp[STD_ADDRESS_P_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->brd))
-    bsprintf(broad, ", broadcast %I", a->brd);
-  else
-    broad[0] = 0;
   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%s, scope %s%s)",
-         a->ip, a->pxlen,
-         (a->flags & IA_PRIMARY) ? "Primary" : (a->flags & IA_SECONDARY) ? "Secondary" : "Unselected",
-         broad, opp,
-         ip_scope_text(a->scope),
-         (a->flags & IA_UNNUMBERED) ? ", unnumbered" : "");
+
+  cli_msg(-1003, "\t%I/%d (%s%sscope %s)",
+         a->ip, a->prefix.pxlen, flg, opp, ip_scope_text(a->scope));
 }
 
 void
@@ -676,7 +839,16 @@ if_show(void)
 
   WALK_LIST(i, iface_list)
     {
-      cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index);
+      if (i->flags & IF_SHUTDOWN)
+       continue;
+
+      char mbuf[16 + sizeof(i->name)] = {};
+      if (i->master)
+       bsprintf(mbuf, " master=%s", i->master->name);
+      else if (i->master_index)
+       bsprintf(mbuf, " master=#%u", i->master_index);
+
+      cli_msg(-1001, "%s %s (index=%d%s)", i->name, (i->flags & IF_UP) ? "up" : "down", i->index, mbuf);
       if (!(i->flags & IF_MULTIACCESS))
        type = "PtP";
       else
@@ -685,15 +857,18 @@ if_show(void)
              type,
              (i->flags & IF_BROADCAST) ? " Broadcast" : "",
              (i->flags & IF_MULTICAST) ? " Multicast" : "",
-             (i->flags & IF_ADMIN_DOWN) ? "Down" : "Up",
+             (i->flags & IF_ADMIN_UP) ? "Up" : "Down",
              (i->flags & IF_LINK_UP) ? "Up" : "Down",
              (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 != i->addr)
+       if (a->prefix.type == NET_IP4)
+         if_show_addr(a);
+
+      WALK_LIST(a, i->addrs)
+       if (a->prefix.type == NET_IP6)
          if_show_addr(a);
     }
   cli_msg(0, "");
@@ -703,16 +878,28 @@ void
 if_show_summary(void)
 {
   struct iface *i;
-  byte addr[STD_ADDRESS_P_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->pxlen);
+      byte a4[IPA_MAX_TEXT_LENGTH + 17];
+      byte a6[IPA_MAX_TEXT_LENGTH + 17];
+
+      if (i->flags & IF_SHUTDOWN)
+       continue;
+
+      if (i->addr4)
+       bsprintf(a4, "%I/%d", i->addr4->ip, i->addr4->prefix.pxlen);
+      else
+       a4[0] = 0;
+
+      if (i->addr6)
+       bsprintf(a6, "%I/%d", i->addr6->ip, i->addr6->prefix.pxlen);
       else
-       addr[0] = 0;
-      cli_msg(-1005, "%-9s %-5s %s", i->name, (i->flags & IF_UP) ? "up" : "DOWN", addr);
+       a6[0] = 0;
+
+      cli_msg(-1005, "%-10s %-6s %-18s %s",
+             i->name, (i->flags & IF_UP) ? "up" : "down", a4, a6);
     }
   cli_msg(0, "");
 }