[PATCH v3] babel: Add source-specific routing support

Toke Høiland-Jørgensen toke at toke.dk
Tue Feb 6 20:46:22 CET 2018


This adds support for source-specific routing to the babel protocol. It
changes the protocol to support both NET_IP6 and NET_IP6_SADR channels
for IPv6 addresses, preferring the NET_IP6_SADR channel. If only a
NET_IP6 channel is configured, source-specific updates are ignored.
Otherwise, non-source-specific routes are simply treated as
source-specific routes with sadr prefix 0.

Signed-off-by: Toke Høiland-Jørgensen <toke at toke.dk>
---
This version also works with plain ipv6 channels.

 proto/babel/babel.c   |  34 +++++++---
 proto/babel/babel.h   |  20 ++++--
 proto/babel/packets.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 206 insertions(+), 21 deletions(-)

diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index aa7e8b68..59e350e3 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -1950,13 +1950,13 @@ babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable)
       srcs++;
 
     if (e->valid)
-      cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u",
+      cli_msg(-1025, "%-50N %-23lR %6u %5u %7u %7u",
 	      e->n.addr, e->router_id, e->metric, e->seqno, rts, srcs);
     else if (r = e->selected)
-      cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u",
+      cli_msg(-1025, "%-50N %-23lR %6u %5u %7u %7u",
 	      e->n.addr, r->router_id, r->metric, r->seqno, rts, srcs);
     else
-      cli_msg(-1025, "%-24N %-23s %6s %5s %7u %7u",
+      cli_msg(-1025, "%-50N %-23s %6s %5s %7u %7u",
 	      e->n.addr, "<none>", "-", "-", rts, srcs);
   }
   FIB_WALK_END;
@@ -1975,7 +1975,7 @@ babel_show_entries(struct proto *P)
   }
 
   cli_msg(-1025, "%s:", p->p.name);
-  cli_msg(-1025, "%-24s %-23s %6s %5s %7s %7s",
+  cli_msg(-1025, "%-50s %-23s %6s %5s %7s %7s",
 	  "Prefix", "Router ID", "Metric", "Seqno", "Routes", "Sources");
 
   babel_show_entries_(p, &p->ip4_rtable);
@@ -1994,7 +1994,7 @@ babel_show_routes_(struct babel_proto *p UNUSED, struct fib *rtable)
     {
       char c = (r == e->selected) ? '*' : (r->feasible ? '+' : ' ');
       btime time = r->expires ? r->expires - current_time() : 0;
-      cli_msg(-1025, "%-24N %-25I %-10s %5u %c %5u %7t",
+      cli_msg(-1025, "%-50N %-25I %-10s %5u %c %5u %7t",
 	      e->n.addr, r->next_hop, r->neigh->ifa->ifname,
 	      r->metric, c, r->seqno, MAX(time, 0));
     }
@@ -2015,7 +2015,7 @@ babel_show_routes(struct proto *P)
   }
 
   cli_msg(-1025, "%s:", p->p.name);
-  cli_msg(-1025, "%-24s %-25s %-9s %6s F %5s %7s",
+  cli_msg(-1025, "%-50s %-25s %-9s %6s F %5s %7s",
 	  "Prefix", "Nexthop", "Interface", "Metric", "Seqno", "Expires");
 
   babel_show_routes_(p, &p->ip4_rtable);
@@ -2187,9 +2187,14 @@ babel_init(struct proto_config *CF)
 {
   struct proto *P = proto_new(CF);
   struct babel_proto *p = (void *) P;
+  struct channel_config *ip6_chan = proto_cf_find_channel(CF, NET_IP6_SADR);
+
+  /* Only want one of IP6 or IP6 SADR */
+  if (!ip6_chan)
+    ip6_chan = proto_cf_find_channel(CF, NET_IP6);
 
   proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
-  proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
+  proto_configure_channel(P, &p->ip6_channel, ip6_chan);
 
   P->if_notify = babel_if_notify;
   P->rt_notify = babel_rt_notify;
@@ -2207,10 +2212,11 @@ babel_start(struct proto *P)
 {
   struct babel_proto *p = (void *) P;
   struct babel_config *cf = (void *) P->cf;
+  u8 ip6_type = p->ip6_channel ? p->ip6_channel->net_type : NET_IP6_SADR;
 
   fib_init(&p->ip4_rtable, P->pool, NET_IP4, sizeof(struct babel_entry),
 	   OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
-  fib_init(&p->ip6_rtable, P->pool, NET_IP6, sizeof(struct babel_entry),
+  fib_init(&p->ip6_rtable, P->pool, ip6_type, sizeof(struct babel_entry),
 	   OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
 
   init_list(&p->interfaces);
@@ -2258,11 +2264,19 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
 {
   struct babel_proto *p = (void *) P;
   struct babel_config *new = (void *) CF;
+  struct channel_config *ip6_chan;
 
   TRACE(D_EVENTS, "Reconfiguring");
 
+  /* Only want one of IP6 or IP6 SADR */
+  if (!(ip6_chan = proto_cf_find_channel(CF, NET_IP6_SADR)))
+    ip6_chan = proto_cf_find_channel(CF, NET_IP6);
+
+  if (ip6_chan && p->ip6_channel && ip6_chan->net_type != p->ip6_channel->net_type)
+    return 0;
+
   if (!proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) ||
-      !proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)))
+      !proto_configure_channel(P, &p->ip6_channel, ip6_chan))
     return 0;
 
   p->p.cf = CF;
@@ -2280,7 +2294,7 @@ struct protocol proto_babel = {
   .template =		"babel%d",
   .attr_class =		EAP_BABEL,
   .preference =		DEF_PREF_BABEL,
-  .channel_mask =	NB_IP,
+  .channel_mask =	(NB_IP | NB_IP6_SADR),
   .proto_size =		sizeof(struct babel_proto),
   .config_size =	sizeof(struct babel_config),
   .init =		babel_init,
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index 1128d261..0902241a 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -85,7 +85,10 @@ enum babel_tlv_type {
 
 enum babel_subtlv_type {
   BABEL_SUBTLV_PAD1		= 0,
-  BABEL_SUBTLV_PADN		= 1
+  BABEL_SUBTLV_PADN		= 1,
+
+  /* mandatory subtlvs */
+  BABEL_SUBTLV_SOURCE_PREFIX    = 128,
 };
 
 enum babel_iface_type {
@@ -303,7 +306,10 @@ struct babel_msg_update {
   u16 seqno;
   u16 metric;
   u64 router_id;
-  net_addr net;
+  union {
+    net_addr net;
+    net_addr_ip6_sadr net_sadr;
+  };
   ip_addr next_hop;
   ip_addr sender;
 };
@@ -311,7 +317,10 @@ struct babel_msg_update {
 struct babel_msg_route_request {
   u8 type;
   u8 full;
-  net_addr net;
+  union {
+    net_addr net;
+    net_addr_ip6_sadr net_sadr;
+  };
 };
 
 struct babel_msg_seqno_request {
@@ -319,7 +328,10 @@ struct babel_msg_seqno_request {
   u8 hop_count;
   u16 seqno;
   u64 router_id;
-  net_addr net;
+  union {
+    net_addr net;
+    net_addr_ip6_sadr net_sadr;
+  };
   ip_addr sender;
 };
 
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index dd86222a..321f7f8c 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -105,6 +105,13 @@ struct babel_tlv_seqno_request {
   u8 addr[0];
 } PACKED;
 
+struct babel_subtlv_source_prefix {
+  u8 type;
+  u8 length;
+  u8 plen;
+  u8 addr[0];
+} PACKED;
+
 
 /* Hello flags */
 #define BABEL_HF_UNICAST	0x8000
@@ -127,6 +134,7 @@ struct babel_parse_state {
   u8 def_ip6_prefix_seen;	/* def_ip6_prefix is valid */
   u8 def_ip4_prefix_seen;	/* def_ip4_prefix is valid */
   u8 current_tlv_endpos;	/* End of self-terminating TLVs (offset from start) */
+  u8 sadr_enabled;
 };
 
 enum parse_result {
@@ -237,6 +245,8 @@ static int babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *msg, stru
 static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
 static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
 static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+
 
 static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
 static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
@@ -244,6 +254,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct
 static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
 static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
 static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
+static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
 
 struct babel_tlv_data {
   u8 min_length;
@@ -639,6 +650,8 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
 
     ip6_addr prefix6 = get_ip6(buf);
     net_fill_ip6(&msg->net, prefix6, tlv->plen);
+    if (state->sadr_enabled)
+      msg->net.type = NET_IP6_SADR;
 
     if (tlv->flags & BABEL_UF_DEF_PREFIX)
     {
@@ -739,11 +752,13 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
   }
   else
   {
+    u8 buf[16], omit;
+    int l;
+
     tlv->ae = BABEL_AE_IP6;
     tlv->plen = net6_pxlen(&msg->net);
 
     /* Address compression - omit initial matching bytes */
-    u8 buf[16], omit;
     put_ip6(buf, net6_prefix(&msg->net));
     omit = bytes_equal(buf, state->def_ip6_prefix,
 		       MIN(tlv->plen, state->def_ip6_pxlen) / 8);
@@ -764,6 +779,12 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
       put_ip6(state->def_ip6_prefix, net6_prefix(&msg->net));
       state->def_ip6_pxlen = tlv->plen;
     }
+
+    l = babel_write_source_prefix(hdr, &msg->net, max_len - len - len0);
+    if (l < 0)
+      return 0;
+
+    len += l;
   }
 
   put_time16(&tlv->interval, msg->interval);
@@ -811,6 +832,9 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip6_px(&msg->net, tlv->addr, tlv->plen);
+    if (state->sadr_enabled)
+      msg->net.type = NET_IP6_SADR;
+
     state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
@@ -851,9 +875,17 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
   }
   else
   {
+    int l;
+
     tlv->ae = BABEL_AE_IP6;
     tlv->plen = net6_pxlen(&msg->net);
     put_ip6_px(tlv->addr, &msg->net);
+
+    l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
+    if (l < 0)
+      return 0;
+
+    len += l;
   }
 
   return len;
@@ -899,6 +931,9 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
       return PARSE_ERROR;
 
     read_ip6_px(&msg->net, tlv->addr, tlv->plen);
+    if (state->sadr_enabled)
+      msg->net.type = NET_IP6_SADR;
+
     state->current_tlv_endpos += BYTES(tlv->plen);
     return PARSE_SUCCESS;
 
@@ -920,6 +955,7 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
   struct babel_msg_seqno_request *msg = &m->seqno_request;
 
   uint len = sizeof(struct babel_tlv_seqno_request) + NET_SIZE(&msg->net);
+  int l;
 
   if (len > max_len)
     return 0;
@@ -943,34 +979,156 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
   tlv->hop_count = msg->hop_count;
   put_u64(&tlv->router_id, msg->router_id);
 
+  l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
+  if (l < 0)
+    return 0;
+
+  return len + l;
+}
+
+/* Add a subtlv to the tlv pointed to by header, with the specified subtlv type
+   and len. The length of the surrounding TLV is expanded.
+
+   Caller must ensure the buffer has enough space. */
+static struct babel_tlv *
+babel_add_subtlv(struct babel_tlv *hdr,
+		 u8 type,
+		 u8 len)
+{
+  struct babel_tlv *subtlv = (struct babel_tlv *) ((byte *) hdr + TLV_LENGTH(hdr));
+  hdr->length += len;
+  TLV_HDR(subtlv, type, len);
+  return subtlv;
+}
+
+static int
+babel_write_source_prefix(struct babel_tlv *hdr,
+			  net_addr *net,
+			  uint max_len)
+{
+  struct babel_subtlv_source_prefix *tlv;
+  struct net_addr_ip6_sadr *adr = (void *) net;
+  uint len;
+
+  if (!net_is_sadr_set(net))
+    return 0;
+
+  len = sizeof(*tlv) + BYTES(adr->src_pxlen);
+
+  if (len > max_len)
+    return -1;
+
+  tlv = (void *) babel_add_subtlv(hdr, BABEL_SUBTLV_SOURCE_PREFIX, len);
+  net_addr_ip6 sadr = NET_ADDR_IP6(adr->src_prefix, adr->src_pxlen);
+  tlv->plen = adr->src_pxlen;
+  put_ip6_px(tlv->addr, (void *) &sadr);
+
   return len;
 }
 
+static int
+babel_read_source_prefix(struct babel_tlv *hdr,
+			 union babel_msg *msg,
+			 struct babel_parse_state *state)
+{
+  struct babel_subtlv_source_prefix *tlv = (void *) hdr;
+  struct net_addr_ip6_sadr *adr;
+  struct net_addr_ip6_sadr sadr;
+
+  /* We are not configured to process source-specific routes */
+  if (!state->sadr_enabled) {
+    DBG("Not configured for SADR, ignoring source-specific route\n");
+    return PARSE_IGNORE;
+  }
+
+  if (tlv->length < BYTES(tlv->plen) + 1 ||
+      tlv->plen > IP6_MAX_PREFIX_LENGTH)
+    return PARSE_ERROR;
+
+  /* plen MUST NOT be 0 */
+  if (tlv->plen == 0)
+    return PARSE_IGNORE;
+
+  switch(msg->type) {
+  case BABEL_TLV_UPDATE:
+    /* wildcard updates with source prefix MUST be silently ignored */
+    if (msg->update.wildcard)
+      return PARSE_IGNORE;
+
+    adr = (void *) &msg->update.net;
+    break;
+
+  case BABEL_TLV_ROUTE_REQUEST:
+    /* wildcard requests with source addresses MUST be silently ignored */
+    if (msg->route_request.full)
+      return PARSE_IGNORE;
+
+    adr = (void *) &msg->route_request.net;
+    break;
+
+  case BABEL_TLV_SEQNO_REQUEST:
+    adr = (void *) &msg->seqno_request.net;
+    break;
+
+  default:
+    return PARSE_ERROR;
+  }
+
+  if (!net_is_sadr((net_addr *) adr))
+    return PARSE_ERROR;
+
+  /* Duplicate source prefix subtlv; SHOULD ignore TLV */
+  if (adr->src_pxlen > 0)
+    return PARSE_IGNORE;
+
+  /* read_ip6_px() reads a prefix into a net_addr_ip6_sadr struct, which in this
+     case is the sadr */
+  read_ip6_px((void *) &sadr, tlv->addr, tlv->plen);
+  adr->src_prefix = sadr.dst_prefix;
+  adr->src_pxlen = sadr.dst_pxlen;
+
+  return PARSE_SUCCESS;
+}
+
 static inline int
 babel_read_subtlvs(struct babel_tlv *hdr,
-		   union babel_msg *msg UNUSED,
+		   union babel_msg *msg,
 		   struct babel_parse_state *state)
 {
   struct babel_tlv *tlv;
+  byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
+  int res;
 
   for (tlv = (void *) hdr + state->current_tlv_endpos;
-       (void *) tlv < (void *) hdr + TLV_LENGTH(hdr);
+       (byte *) tlv < end;
        tlv = NEXT_TLV(tlv))
   {
+    if (tlv->type == BABEL_TLV_PAD1)
+      continue;
+
+    pos = (byte *)tlv + sizeof(struct babel_tlv);
+    if ((pos > end) || (pos + tlv->length > end))
+    {
+      DBG("Babel: subtlv framing error.\n");
+      return PARSE_ERROR;
+    }
+
     /*
      * The subtlv type space is non-contiguous (due to the mandatory bit), so
      * use a switch for dispatch instead of the mapping array we use for TLVs
      */
     switch (tlv->type)
     {
-    case BABEL_SUBTLV_PAD1:
-    case BABEL_SUBTLV_PADN:
-      /* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */
+    case BABEL_SUBTLV_SOURCE_PREFIX:
+      res = babel_read_source_prefix(tlv, msg, state);
+      if (res != PARSE_SUCCESS)
+	return res;
       break;
 
+    case BABEL_SUBTLV_PADN:
     default:
       /* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
-      if (tlv->type > 128)
+      if (tlv->type >= 128)
       {
 	DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type);
 	return PARSE_IGNORE;
@@ -1197,6 +1355,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
     .ifa	  = ifa,
     .saddr	  = saddr,
     .next_hop_ip6 = saddr,
+    .sadr_enabled = (p->ip6_rtable.addr_type == NET_IP6_SADR),
   };
 
   if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
-- 
2.16.1



More information about the Bird-users mailing list