[PATCH 1/4] Core changes to support SADR

Dean Luga dluga93 at gmail.com
Sun May 21 23:01:34 CEST 2017


From: dean <dluga93 at gmail.com>

This patch adds a new network of type NET_SADR_IP6, including new
structures, constants, and switch cases for the new network type.
Some existing functions are duplicates to handle the new network
type, and netlink can now handle SADR routes.

The net_route_sadr_ip6 function is a bit of a hack.
Routing in SADR is supposed to match the destination address first,
then out of the entries with that dst, it should choose the most
specific matching src address.
For the destination-only part, I use a net_addr_ip6 as the
destination prefix. Its type is changed to NET_SADR_IP6 to satisfy
the assertion in fib_find. The fib_find function checks the length
of the prefix to distinguish between destination-only or full SADR
searches.
---
 lib/net.c              | 34 ++++++++++++++++++++++++++
 lib/net.h              | 66 +++++++++++++++++++++++++++++++++++++++++++++++---
 nest/rt-fib.c          | 16 ++++++++++++
 nest/rt-table.c        | 35 +++++++++++++++++++++++++-
 sysdep/linux/netlink.c | 26 ++++++++++++++++++--
 sysdep/unix/krt.c      |  4 ++-
 6 files changed, 174 insertions(+), 7 deletions(-)

diff --git a/lib/net.c b/lib/net.c
index a00ff27..41084fe 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -15,6 +15,7 @@ const char * const net_label[] = {
   [NET_FLOW4] 	= "flow4",
   [NET_FLOW6] 	= "flow6",
   [NET_MPLS]	= "mpls",
+  [NET_SADR_IP6] = "sadr6",
 };
 
 const u16 net_addr_length[] = {
@@ -27,6 +28,7 @@ const u16 net_addr_length[] = {
   [NET_FLOW4] 	= 0,
   [NET_FLOW6] 	= 0,
   [NET_MPLS]	= sizeof(net_addr_mpls),
+  [NET_SADR_IP6]= sizeof(net_addr_sadr_ip6),
 };
 
 const u8 net_max_prefix_length[] = {
@@ -39,6 +41,7 @@ const u8 net_max_prefix_length[] = {
   [NET_FLOW4] 	= IP4_MAX_PREFIX_LENGTH,
   [NET_FLOW6] 	= IP6_MAX_PREFIX_LENGTH,
   [NET_MPLS]	= 0,
+  [NET_SADR_IP6]= IP6_MAX_PREFIX_LENGTH,
 };
 
 const u16 net_max_text_length[] = {
@@ -51,6 +54,7 @@ const u16 net_max_text_length[] = {
   [NET_FLOW4] 	= 0,	/* "flow4 { ... }" */
   [NET_FLOW6] 	= 0,	/* "flow6 { ... }" */
   [NET_MPLS]	= 7,	/* "1048575" */
+  [NET_SADR_IP6]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
 };
 
 
@@ -104,6 +108,8 @@ net_format(const net_addr *N, char *buf, int buflen)
     return flow6_net_format(buf, buflen, &n->flow6);
   case NET_MPLS:
     return bsnprintf(buf, buflen, "%u", n->mpls.label);
+  case NET_SADR_IP6:
+    return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->sadr_ip6.dst_prefix, n->sadr_ip6.dst_pxlen, n->sadr_ip6.src_prefix, n->sadr_ip6.src_pxlen);
   }
 
   bug("unknown network type");
@@ -126,6 +132,9 @@ net_pxmask(const net_addr *a)
   case NET_FLOW6:
     return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
 
+  case NET_SADR_IP6:
+    return ipa_from_ip6(ip6_mkmask(net6_sadr_dst_pxlen(a)));
+
   case NET_MPLS:
   default:
     return IPA_NONE;
@@ -133,6 +142,16 @@ net_pxmask(const net_addr *a)
 }
 
 int
+net_compare_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b)
+{
+  int dst_cmp = ip6_compare(a->dst_prefix, b->dst_prefix) ?: uint_cmp(a->dst_pxlen, b->dst_pxlen);
+  if (dst_cmp)
+    return dst_cmp;
+  else
+    return ip6_compare(a->src_prefix, b->src_prefix) ?: uint_cmp(a->src_pxlen, b->src_pxlen);
+}
+
+int
 net_compare(const net_addr *a, const net_addr *b)
 {
   if (a->type != b->type)
@@ -158,6 +177,8 @@ net_compare(const net_addr *a, const net_addr *b)
     return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b);
   case NET_MPLS:
     return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
+  case NET_SADR_IP6:
+    return net_compare_sadr_ip6((const net_addr_sadr_ip6 *) a, (const net_addr_sadr_ip6 *) b);
   }
   return 0;
 }
@@ -178,6 +199,7 @@ net_hash(const net_addr *n)
   case NET_FLOW4: return NET_HASH(n, flow4);
   case NET_FLOW6: return NET_HASH(n, flow6);
   case NET_MPLS: return NET_HASH(n, mpls);
+  case NET_SADR_IP6: return NET_HASH(n, ip6);
   default: bug("invalid type");
   }
 }
@@ -199,6 +221,7 @@ net_validate(const net_addr *n)
   case NET_FLOW4: return NET_VALIDATE(n, flow4);
   case NET_FLOW6: return NET_VALIDATE(n, flow6);
   case NET_MPLS: return NET_VALIDATE(n, mpls);
+  case NET_SADR_IP6: return NET_VALIDATE(n, sadr_ip6);
   default: return 0;
   }
 }
@@ -224,6 +247,9 @@ net_normalize(net_addr *N)
 
   case NET_MPLS:
     return;
+
+  case NET_SADR_IP6:
+    return net_normalize_sadr_ip6(&n->sadr_ip6);
   }
 }
 
@@ -248,6 +274,9 @@ net_classify(const net_addr *N)
 
   case NET_MPLS:
     return IADDR_HOST | SCOPE_UNIVERSE;
+
+  case NET_SADR_IP6:
+    return ip6_zero(n->sadr_ip6.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->sadr_ip6.dst_prefix);
   }
 
   return IADDR_INVALID;
@@ -274,6 +303,11 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
     return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
 			    ip6_mkmask(net6_pxlen(n))));
 
+  case NET_SADR_IP6:
+    if (ipa_is_ip4(a)) return 0;
+    return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_sadr_dst_prefix(n)),
+          ip6_mkmask(net6_sadr_dst_pxlen(n))));
+
   case NET_MPLS:
   default:
     return 0;
diff --git a/lib/net.h b/lib/net.h
index 332f4c9..22eee60 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -12,7 +12,6 @@
 
 #include "lib/ip.h"
 
-
 #define NET_IP4		1
 #define NET_IP6		2
 #define NET_VPN4	3
@@ -22,7 +21,8 @@
 #define NET_FLOW4	7
 #define NET_FLOW6	8
 #define NET_MPLS	9
-#define NET_MAX		10
+#define NET_SADR_IP6	10
+#define NET_MAX		11
 
 #define NB_IP4		(1 << NET_IP4)
 #define NB_IP6		(1 << NET_IP6)
@@ -33,8 +33,9 @@
 #define NB_FLOW4	(1 << NET_FLOW4)
 #define NB_FLOW6	(1 << NET_FLOW6)
 #define NB_MPLS		(1 << NET_MPLS)
+#define NB_SADR		(1 << NET_SADR_IP6)
 
-#define NB_IP		(NB_IP4 | NB_IP6)
+#define NB_IP		(NB_IP4 | NB_IP6 | NB_SADR)
 #define NB_VPN		(NB_VPN4 | NB_VPN6)
 #define NB_FLOW		(NB_FLOW4 | NB_FLOW6)
 #define NB_DEST		(NB_IP | NB_VPN | NB_MPLS)
@@ -120,6 +121,15 @@ typedef struct net_addr_mpls {
   u32 label;
 } net_addr_mpls;
 
+typedef struct net_addr_sadr_ip6 {
+  u8 type;
+  u8 dst_pxlen;
+  u16 length;
+  ip6_addr dst_prefix;
+  u32 src_pxlen; // if u8, NET_ADDR_SADR_IP6 would leave non-zero padding
+  ip6_addr src_prefix;
+} net_addr_sadr_ip6;
+
 typedef union net_addr_union {
   net_addr n;
   net_addr_ip4 ip4;
@@ -131,6 +141,7 @@ typedef union net_addr_union {
   net_addr_flow4 flow4;
   net_addr_flow6 flow6;
   net_addr_mpls mpls;
+  net_addr_sadr_ip6 sadr_ip6;
 } net_addr_union;
 
 
@@ -169,6 +180,9 @@ extern const u16 net_max_text_length[];
 #define NET_ADDR_MPLS(label) \
   ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
 
+#define NET_ADDR_SADR_IP6(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
+  ((net_addr_sadr_ip6) { NET_SADR_IP6, dst_pxlen, sizeof(net_addr_sadr_ip6), dst_prefix, src_pxlen, src_prefix })
+
 
 static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
 { *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
@@ -191,6 +205,9 @@ static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint
 static inline void net_fill_mpls(net_addr *a, u32 label)
 { *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
 
+static inline void net_fill_sadr_ip6(net_addr *a, ip6_addr dst_prefix, uint dst_pxlen, ip6_addr src_prefix, uint src_pxlen)
+{ *(net_addr_sadr_ip6 *)a = NET_ADDR_SADR_IP6(dst_prefix, dst_pxlen, src_prefix, src_pxlen); }
+
 static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
 {
   if (ipa_is_ip4(prefix))
@@ -236,6 +253,9 @@ static inline int net_is_roa(const net_addr *a)
 static inline int net_is_vpn(const net_addr *a)
 { return (a->type == NET_VPN4) || (a->type == NET_VPN6); }
 
+static inline int net_is_sadr(const net_addr *a)
+{ return (a->type == NET_SADR_IP6); }
+
 
 static inline ip4_addr net4_prefix(const net_addr *a)
 { return ((net_addr_ip4 *) a)->prefix; }
@@ -243,6 +263,12 @@ static inline ip4_addr net4_prefix(const net_addr *a)
 static inline ip6_addr net6_prefix(const net_addr *a)
 { return ((net_addr_ip6 *) a)->prefix; }
 
+static inline ip6_addr net6_sadr_dst_prefix(const net_addr *a)
+{ return ((net_addr_sadr_ip6 *) a)->dst_prefix; }
+
+static inline ip6_addr net6_sadr_src_prefix(const net_addr *a)
+{ return ((net_addr_sadr_ip6 *) a)->src_prefix; }
+
 static inline ip_addr net_prefix(const net_addr *a)
 {
   switch (a->type)
@@ -259,6 +285,9 @@ static inline ip_addr net_prefix(const net_addr *a)
   case NET_FLOW6:
     return ipa_from_ip6(net6_prefix(a));
 
+  case NET_SADR_IP6:
+    return ipa_from_ip6(net6_sadr_dst_prefix(a));
+
   case NET_MPLS:
   default:
     return IPA_NONE;
@@ -279,6 +308,12 @@ static inline uint net4_pxlen(const net_addr *a)
 static inline uint net6_pxlen(const net_addr *a)
 { return a->pxlen; }
 
+static inline uint net6_sadr_dst_pxlen(const net_addr *a)
+{ return ((net_addr_sadr_ip6 *) a)->dst_pxlen; }
+
+static inline uint net6_sadr_src_pxlen(const net_addr *a)
+{ return ((net_addr_sadr_ip6 *) a)->src_pxlen; }
+
 static inline uint net_pxlen(const net_addr *a)
 { return a->pxlen; }
 
@@ -327,6 +362,13 @@ static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6
 static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
 { return !memcmp(a, b, sizeof(net_addr_mpls)); }
 
+typedef net_addr_ip6 net_addr_sadr_dst;
+static inline int net_equal_sadr_dst(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{ return net_equal_ip6(a, b); }
+
+static inline int net_equal_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b)
+{ return !memcmp(a, b, sizeof(net_addr_sadr_ip6)); }
+
 
 static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 { return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
@@ -390,6 +432,8 @@ static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow
 static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
 { return uint_cmp(a->label, b->label); }
 
+int net_compare_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b);
+
 int net_compare(const net_addr *a, const net_addr *b);
 
 
@@ -423,6 +467,9 @@ static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src
 static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
 { memcpy(dst, src, sizeof(net_addr_mpls)); }
 
+static inline void net_copy_sadr_ip6(net_addr_sadr_ip6 *dst, const net_addr_sadr_ip6 *src)
+{ memcpy(dst, src, sizeof(net_addr_sadr_ip6)); }
+
 
 /* XXXX */
 static inline u32 u64_hash(u64 a)
@@ -455,6 +502,12 @@ static inline u32 net_hash_flow6(const net_addr_flow6 *n)
 static inline u32 net_hash_mpls(const net_addr_mpls *n)
 { return n->label; }
 
+static inline u32 net_hash_sadr_dst(const net_addr_sadr_dst* n)
+{ return net_hash_ip6(n); }
+
+static inline u32 net_hash_sadr_ip6(const net_addr_sadr_ip6 *n)
+{ return ip6_hash(n->dst_prefix) ^ ((u32) n->dst_pxlen << 26); }
+
 u32 net_hash(const net_addr *a);
 
 
@@ -504,6 +557,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n)
 static inline int net_validate_mpls(const net_addr_mpls *n)
 { return n->label < (1 << 20); }
 
+static inline int net_validate_sadr_ip6(const net_addr_sadr_ip6 *n)
+{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
+
 int net_validate(const net_addr *N);
 
 
@@ -519,6 +575,10 @@ static inline void net_normalize_vpn4(net_addr_vpn4 *n)
 static inline void net_normalize_vpn6(net_addr_vpn6 *n)
 { net_normalize_ip6((net_addr_ip6 *) n); }
 
+static inline void net_normalize_sadr_ip6(net_addr_sadr_ip6 *n)
+{ n->dst_prefix = ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen));
+  n->src_prefix = ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen)); }
+
 void net_normalize(net_addr *N);
 
 
diff --git a/nest/rt-fib.c b/nest/rt-fib.c
index 11c31d0..5831b33 100644
--- a/nest/rt-fib.c
+++ b/nest/rt-fib.c
@@ -195,6 +195,7 @@ fib_hash(struct fib *f, const net_addr *a)
   case NET_ROA6: return FIB_HASH(f, a, roa6);
   case NET_FLOW4: return FIB_HASH(f, a, flow4);
   case NET_FLOW6: return FIB_HASH(f, a, flow6);
+  case NET_SADR_IP6: return FIB_HASH(f, a, sadr_ip6);
   default: bug("invalid type");
   }
 }
@@ -231,6 +232,20 @@ fib_find(struct fib *f, const net_addr *a)
   case NET_ROA6: return FIB_FIND(f, a, roa6);
   case NET_FLOW4: return FIB_FIND(f, a, flow4);
   case NET_FLOW6: return FIB_FIND(f, a, flow6);
+  case NET_SADR_IP6:
+    {
+      if (a->length != sizeof(net_addr_sadr_ip6))
+      {
+        // dst only search
+        net_addr_ip6 a0;
+        net_copy((net_addr *)&a0, a);
+        a0.length = sizeof(net_addr_sadr_ip6);
+
+        return FIB_FIND(f, &a0, sadr_dst);
+      }
+      else
+        return FIB_FIND(f, a, sadr_ip6);
+    }
   default: bug("invalid type");
   }
 }
@@ -250,6 +265,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
   case NET_ROA6: FIB_INSERT(f, a, e, roa6); return;
   case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return;
   case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
+  case NET_SADR_IP6: FIB_INSERT(f, a, e, sadr_ip6); return;
   default: bug("invalid type");
   }
 }
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 67ccc88..49347f8 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -95,6 +95,35 @@ net_route_ip6(rtable *t, net_addr_ip6 *n)
   return r;
 }
 
+static inline void *
+net_route_sadr_ip6(rtable *t, net_addr_sadr_ip6 *n)
+{
+  net *r;
+
+  net_addr_ip6 dst = NET_ADDR_IP6(n->dst_prefix, n->dst_pxlen);
+  dst.type = NET_SADR_IP6;
+
+  // search for matching dst
+  while (r = net_route_ip6(t, &dst))
+  {
+    // found a matching dst, start search for entries that match both prefixes
+    net_addr_sadr_ip6 full_addr = 
+      NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, n->src_prefix, n->src_pxlen);
+
+    while (r = net_find_valid(t, (net_addr *) &full_addr), (!r) && (full_addr.src_pxlen > 0))
+    {
+      full_addr.src_pxlen--;
+      ip6_clrbit(&full_addr.src_prefix, full_addr.src_pxlen);
+    }
+
+    if (r)
+      return r;
+    dst.pxlen--;
+  }
+
+  return NULL;
+}
+
 void *
 net_route(rtable *tab, const net_addr *n)
 {
@@ -115,6 +144,9 @@ net_route(rtable *tab, const net_addr *n)
   case NET_ROA6:
     return net_route_ip6(tab, (net_addr_ip6 *) n0);
 
+  case NET_SADR_IP6:
+    return net_route_sadr_ip6(tab, (net_addr_sadr_ip6 *) n0);
+
   default:
     return NULL;
   }
@@ -1747,6 +1779,7 @@ rt_preconfig(struct config *c)
 
   rt_new_table(cf_get_symbol("master4"), NET_IP4);
   rt_new_table(cf_get_symbol("master6"), NET_IP6);
+  rt_new_table(cf_get_symbol("master_sadr6"), NET_SADR_IP6);
 }
 
 
@@ -1980,7 +2013,7 @@ rt_new_table(struct symbol *s, uint addr_type)
   /* Hack that allows to 'redefine' the master table */
   if ((s->class == SYM_TABLE) &&
       (s->def == new_config->def_tables[addr_type]) &&
-      ((addr_type == NET_IP4) || (addr_type == NET_IP6)))
+      ((addr_type == NET_IP4) || (addr_type == NET_IP6) || (addr_type == NET_SADR_IP6)))
     return s->def;
 
   struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 083a8d9..d89ae10 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -369,6 +369,7 @@ static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
 
 static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
   [RTA_DST]	  = { 1, 1, sizeof(ip6_addr) },
+  [RTA_SRC]   = { 1, 1, sizeof(ip6_addr) },
   [RTA_IIF]	  = { 1, 1, sizeof(u32) },
   [RTA_OIF]	  = { 1, 1, sizeof(u32) },
   [RTA_GATEWAY]	  = { 1, 1, sizeof(ip6_addr) },
@@ -1161,8 +1162,17 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
     nl_add_attr_mpls(&r->h, rsize, RTA_DST, 1, &label);
   }
   else
+  {
     nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
 
+    // add source address for SADR routes
+    if (net->n.addr->type == NET_SADR_IP6)
+    {
+      r->r.rtm_src_len = net6_sadr_src_pxlen(net->n.addr);
+      nl_add_attr_ipa(&r->h, rsize, RTA_SRC, net6_sadr_src_prefix(net->n.addr));
+    }
+  }
+
   /*
    * Strange behavior for RTM_DELROUTE:
    * 1) rtm_family is ignored in IPv6, works for IPv4
@@ -1394,7 +1404,8 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
   struct rtattr *a[BIRD_RTA_MAX];
   int new = h->nlmsg_type == RTM_NEWROUTE;
 
-  net_addr dst;
+  net_addr dst, sadr_src;
+  net_fill_ip6(&sadr_src, IP6_NONE, 0);
   u32 oif = ~0;
   u32 table_id;
   u32 priority = 0;
@@ -1420,6 +1431,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
       if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a)))
 	return;
 
+      if (a[RTA_SRC])
+	net_fill_ip6(&sadr_src, rta_get_ip6(a[RTA_SRC]), i->rtm_src_len);
+
       if (a[RTA_DST])
 	net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len);
       else
@@ -1496,7 +1510,15 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
       src = KRT_SRC_ALIEN;
     }
 
-  net *net = net_get(p->p.main_channel->table, &dst);
+  net *net;
+  if (p->p.main_channel->table->addr_type == NET_SADR_IP6)
+  {
+    net_addr_sadr_ip6 sadr_addr =
+      NET_ADDR_SADR_IP6(net_prefix(&dst), net_pxlen(&dst), net_prefix(&sadr_src), net_pxlen(&sadr_src));
+    net = net_get(p->p.main_channel->table, (net_addr *)&sadr_addr);
+  }
+  else
+    net = net_get(p->p.main_channel->table, &dst);
 
   if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
     nl_announce_route(s);
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index c6ff627..a9ce96b 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -1141,7 +1141,9 @@ krt_start(struct proto *P)
   switch (p->p.net_type)
   {
   case NET_IP4:	p->af = AF_INET; break;
-  case NET_IP6:	p->af = AF_INET6; break;
+  case NET_IP6:
+  case NET_SADR_IP6:
+    p->af = AF_INET6; break;
   case NET_MPLS: p->af = AF_MPLS; break;
   default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
   }
-- 
2.7.4



More information about the Bird-users mailing list