[PATCH 2/4] Static protocol supports SADR

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


From: dean <dluga93 at gmail.com>

A new channel, sadr_ip6, is used for SADR both in the kernel
and static protocols. In the static protocol, routes can be
inserted with the following syntax:

route <dst_prefix> from <src_prefix> via "interface"
route <dst_prefix> from <src_prefix> recursive <ip>

There is a bug in the Linux kernel that causes undefined behavior
when both SADR and dst-only routes are put in the same table. This
bug is present at least until kernel version 4.10.15. A workaround
would be to use 2000::/3 as a source prefix instead of ::/0, but
it would reduce the range of acceptable source addresses.

This bug also affects the gateway of recursive routes. There
shouldn't be SADR entries with the gateway as destination in the
routing table.
---
 conf/conf.c            |  1 +
 nest/config.Y          |  5 +++--
 nest/route.h           |  9 +++++++++
 nest/rt-table.c        | 39 +++++++++++++++++++++++++++++++++++----
 proto/static/config.Y  | 15 +++++++++++++++
 proto/static/static.c  | 34 ++++++++++++++++++++++++++++++++--
 proto/static/static.h  |  2 ++
 sysdep/linux/netlink.c |  3 ++-
 8 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/conf/conf.c b/conf/conf.c
index a283788..16f81d9 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -129,6 +129,7 @@ config_parse(struct config *c)
   int done = 0;
   DBG("Parsing configuration file `%s'\n", c->file_name);
   new_config = c;
+
   cfg_mem = c->mem;
   if (setjmp(conf_jmpbuf))
     goto cleanup;
diff --git a/nest/config.Y b/nest/config.Y
index b0f9642..407ab8e 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -76,7 +76,7 @@ CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CL
 CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
 
 /* For r_args_channel */
-CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
+CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, SADR_IP6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
 
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
 	RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
@@ -157,9 +157,10 @@ net_type:
  | ROA6 { $$ = NET_ROA6; }
  | FLOW4{ $$ = NET_FLOW4; }
  | FLOW6{ $$ = NET_FLOW6; }
+ | SADR_IP6 { $$ = NET_SADR_IP6; }
  ;
 
-CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6)
+CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR_IP6)
 
 
 /* Creation of routing tables */
diff --git a/nest/route.h b/nest/route.h
index 6c9b00c..b12d208 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -195,6 +195,7 @@ struct hostentry {
   ip_addr addr;				/* IP address of host, part of key */
   ip_addr link;				/* (link-local) IP address of host, used as gw
 					   if host is directly attached */
+  net_addr_ip6 src_addr;		/* Source IP address to be used for SADR recursive routes */
   struct rtable *tab;			/* Dependent table, part of key */
   struct hostentry *next;		/* Next in hash chain */
   unsigned hash_key;			/* Hash key */
@@ -576,6 +577,7 @@ void rta_dump_all(void);
 void rta_show(struct cli *, rta *, ea_list *);
 
 struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
+struct hostentry * rt_get_sadr_hostentry(rtable *tab, net_addr_sadr_ip6* a, ip_addr ll, rtable *dep);
 void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
 
 static inline void
@@ -584,6 +586,13 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr
   rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
 }
 
+static inline void
+rta_set_recursive_sadr_next_hop(rtable *dep, rta *a, rtable *tab, net_addr_sadr_ip6* gw, ip_addr ll, mpls_label_stack *mls)
+{
+  struct hostentry *he = rt_get_sadr_hostentry(tab, gw, ll, dep);
+  rta_apply_hostentry(a, he, mls);
+}
+
 /*
  * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills
  * rta->hostentry field.  New hostentry has zero use count. Cached rta locks its
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 49347f8..7d2d2c7 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -2436,9 +2436,21 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
   he->dest = RTD_UNREACHABLE;
   he->igp_metric = 0;
 
-  net_addr he_addr;
-  net_fill_ip_host(&he_addr, he->addr);
-  net *n = net_route(tab, &he_addr);
+  net *n;
+  net_addr_sadr_ip6 trie_he_addr;
+
+  if (tab->addr_type == NET_SADR_IP6)
+  {
+    trie_he_addr = NET_ADDR_SADR_IP6(he->addr, IP6_MAX_PREFIX_LENGTH,
+      he->src_addr.prefix, he->src_addr.pxlen);
+    n = net_route(tab, (net_addr *)(&trie_he_addr));
+  }
+  else
+  {
+    net_fill_ip_host((net_addr *)&trie_he_addr, he->addr);
+    n = net_route(tab, (net_addr *)&trie_he_addr);
+  }
+
   if (n)
     {
       rte *e = n->routes;
@@ -2479,7 +2491,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
 
 done:
   /* Add a prefix range to the trie */
-  trie_add_prefix(tab->hostcache->trie, &he_addr, pxlen, he_addr.pxlen);
+  trie_add_prefix(tab->hostcache->trie, (net_addr *)&trie_he_addr, pxlen, trie_he_addr.dst_pxlen);
 
   rta_free(old_src);
   return old_src != he->src;
@@ -2531,6 +2543,25 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
   return he;
 }
 
+struct hostentry *
+rt_get_sadr_hostentry(rtable *tab, net_addr_sadr_ip6* a, ip_addr ll, rtable *dep)
+{
+  struct hostentry *he;
+
+  if (!tab->hostcache)
+    rt_init_hostcache(tab);
+
+  u32 k = hc_hash(a->dst_prefix, dep);
+  struct hostcache *hc = tab->hostcache;
+  for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
+    if (ipa_equal(he->addr, a->dst_prefix) && (he->tab == dep))
+      return he;
+
+  he = hc_new_hostentry(hc, a->dst_prefix, ipa_zero(ll) ? a->dst_prefix : ll, dep, k);
+  he->src_addr = NET_ADDR_IP6(a->src_prefix, a->src_pxlen);
+  rt_update_hostentry(tab, he);
+  return he;
+}
 
 /*
  *  CLI commands
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 66ae3c9..3a176d4 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -67,6 +67,8 @@ static_proto:
       STATIC_CFG->igp_table_ip4 = $4;
     else if ($4->addr_type == NET_IP6)
       STATIC_CFG->igp_table_ip6 = $4;
+    else if ($4->addr_type == NET_SADR_IP6)
+      STATIC_CFG->igp_table_sadr_ip6 = $4;
     else
       cf_error("Incompatible IGP table type");
    }
@@ -109,6 +111,19 @@ stat_route0: ROUTE net_any {
      this_srt->mp_next = NULL;
      this_snh = NULL;
   }
+ | ROUTE net_ip6_ FROM net_ip6_ {
+     this_srt = cfg_allocz(sizeof(struct static_route));
+     add_tail(&STATIC_CFG->routes, &this_srt->n);
+
+     net_addr_sadr_ip6 *address = cfg_alloc(sizeof(net_addr_sadr_ip6));
+     net_fill_sadr_ip6((net_addr *)address,
+        net6_prefix(&$2), net6_pxlen(&$2), net6_prefix(&$4), net6_pxlen(&$4));
+     this_srt->net = (net_addr *)address;
+
+     this_srt_last_cmd = &(this_srt->cmds);
+     this_srt->mp_next = NULL;
+     this_snh = NULL;
+  }
  ;
 
 stat_route:
diff --git a/proto/static/static.c b/proto/static/static.c
index f74ecee..aee3e44 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -89,8 +89,25 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
 
   if (r->dest == RTDX_RECURSIVE)
   {
-    rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
-    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
+    rtable *tab;
+    if (ipa_is_ip4(r->via))
+      tab = p->igp_table_ip4;
+    else
+    {
+      if (r->net->type == NET_SADR_IP6)
+        tab = p->igp_table_sadr_ip6;
+      else
+        tab = p->igp_table_ip6;
+    }
+
+    if (net_is_sadr(r->net))
+    {
+      net_addr_sadr_ip6 addr =
+        NET_ADDR_SADR_IP6(r->via, IP6_MAX_PREFIX_LENGTH, net6_sadr_src_prefix(r->net), net6_sadr_src_pxlen(r->net));
+      rta_set_recursive_sadr_next_hop(p->p.main_channel->table, a, tab, &addr, IPA_NONE, r->mls);
+    }
+    else
+      rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
   }
 
   /* Already announced */
@@ -376,6 +393,10 @@ static_postconfig(struct proto_config *CF)
     cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ?
       cc->table : cf->c.global->def_tables[NET_IP6];
 
+  if (!cf->igp_table_sadr_ip6)
+    cf->igp_table_sadr_ip6 = (cc->table->addr_type == NET_SADR_IP6) ?
+      cc->table : cf->c.global->def_tables[NET_SADR_IP6];
+
   WALK_LIST(r, cf->routes)
     if (r->net && (r->net->type != CF->net_type))
       cf_error("Route %N incompatible with channel type", r->net);
@@ -399,6 +420,9 @@ static_init(struct proto_config *CF)
   if (cf->igp_table_ip6)
     p->igp_table_ip6 = cf->igp_table_ip6->table;
 
+  if (cf->igp_table_sadr_ip6)
+    p->igp_table_sadr_ip6 = cf->igp_table_sadr_ip6->table;
+
   return P;
 }
 
@@ -418,6 +442,9 @@ static_start(struct proto *P)
   if (p->igp_table_ip6)
     rt_lock_table(p->igp_table_ip6);
 
+  if (p->igp_table_sadr_ip6)
+    rt_lock_table(p->igp_table_sadr_ip6);
+
   p->event = ev_new(p->p.pool);
   p->event->hook = static_announce_marked;
   p->event->data = p;
@@ -457,6 +484,9 @@ static_cleanup(struct proto *P)
 
   if (p->igp_table_ip6)
     rt_unlock_table(p->igp_table_ip6);
+
+  if (p->igp_table_sadr_ip6)
+    rt_unlock_table(p->igp_table_sadr_ip6);
 }
 
 static void
diff --git a/proto/static/static.h b/proto/static/static.h
index c84dfa9..2d919e2 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -19,6 +19,7 @@ struct static_config {
   int check_link;			/* Whether iface link state is used */
   struct rtable_config *igp_table_ip4;	/* Table for recursive IPv4 next hop lookups */
   struct rtable_config *igp_table_ip6;	/* Table for recursive IPv6 next hop lookups */
+  struct rtable_config *igp_table_sadr_ip6;	/* Table for recursive SADR IPv6 next hop lookups */
 };
 
 struct static_proto {
@@ -28,6 +29,7 @@ struct static_proto {
   BUFFER(struct static_route *) marked;	/* Routes marked for reannouncement */
   rtable *igp_table_ip4;		/* Table for recursive IPv4 next hop lookups */
   rtable *igp_table_ip6;		/* Table for recursive IPv6 next hop lookups */
+  rtable *igp_table_sadr_ip6;		/* Table for recursive SADR IPv6 next hop lookups */
 };
 
 struct static_route {
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index d89ae10..073bf65 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -1937,7 +1937,8 @@ krt_sys_start(struct krt_proto *p)
 {
   struct krt_proto *old = HASH_FIND(nl_table_map, RTH, p->af, krt_table_id(p));
 
-  if (old)
+  // checking net_type as well so that ipv6 and sadr_ip6 are put in the same table
+  if (old && old->p.net_type == p->p.net_type)
     {
       log(L_ERR "%s: Kernel table %u already registered by %s",
 	  p->p.name, krt_table_id(p), old->p.name);
-- 
2.7.4



More information about the Bird-users mailing list