]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
MRT: Fix IPv6 table dumps
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 12 Jan 2021 14:37:01 +0000 (15:37 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 12 Jan 2021 14:37:01 +0000 (15:37 +0100)
Add fake MP_REACH_NLRI attribute with BGP next hop when encoding MRT
table dumps for IPv6 routes. That is necessary to encode next hop as
NEXT_HOP attribute is not used for MP-BGP.

Thanks to Santiago Aggio for the bugreport.

proto/bgp/attrs.c
proto/bgp/bgp.h
proto/mrt/mrt.c

index b2c373010d18fc9f543e4f38cd979ca3893f1a63..6752cb7f0a5ccd8d0bc4f7b42e0c18ff21c2cc61 100644 (file)
@@ -683,6 +683,37 @@ bgp_format_cluster_list(const eattr *a, byte *buf, uint size)
 }
 
 
+int
+bgp_encode_mp_reach_mrt(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
+{
+  /*
+   *   Limited version of MP_REACH_NLRI used for MRT table dumps (IPv6 only):
+   *
+   *   3 B     MP_REACH_NLRI header
+   *   1 B     MP_REACH_NLRI data - Length of Next Hop Network Address
+   *   var     MP_REACH_NLRI data - Network Address of Next Hop
+   */
+
+  ip_addr *nh = (void *) a->u.ptr->data;
+  uint len = a->u.ptr->length;
+
+  ASSERT((len == 16) || (len == 32));
+
+  if (size < (3+1+len))
+    return -1;
+
+  bgp_put_attr_hdr3(buf, BA_MP_REACH_NLRI, BAF_OPTIONAL, 1+len);
+  buf[3] = len;
+  buf += 4;
+
+  put_ip6(buf, ipa_to_ip6(nh[0]));
+
+  if (len == 32)
+    put_ip6(buf+16, ipa_to_ip6(nh[1]));
+
+  return 3+1+len;
+}
+
 static inline u32
 get_af3(byte *buf)
 {
index 5cabd327c1f66ed365dfc9924a925794175a286c..dd7dc28fd03d125ead9965f7939c62aa5644fd43 100644 (file)
@@ -559,6 +559,7 @@ static inline void
 bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
 { eattr *e = bgp_set_attr(to, pool, code, 0, 0); e->type = EAF_TYPE_UNDEF; }
 
+int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
 
 int bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end);
 ea_list * bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len);
index 7a396a842035ccd8dcc476a8c8d0e5d4f458ab54..76d6c48f9bb50dcc9ad41dd3c6c094e67393cfa3 100644 (file)
@@ -417,6 +417,50 @@ mrt_rib_table_header(struct mrt_table_dump_state *s, net_addr *n)
   mrt_put_u16(b, 0);
 }
 
+#ifdef CONFIG_BGP
+static void
+mrt_rib_table_entry_bgp_attrs(struct mrt_table_dump_state *s, rte *r)
+{
+  struct ea_list *eattrs = r->attrs->eattrs;
+  buffer *b = &s->buf;
+
+  if (!eattrs)
+    return;
+
+  /* Attribute list must be normalized for bgp_encode_attrs() */
+  if (!rta_is_cached(r->attrs))
+    ea_normalize(eattrs);
+
+  mrt_buffer_need(b, MRT_ATTR_BUFFER_SIZE);
+  byte *pos = b->pos;
+
+  s->bws->mp_next_hop = NULL;
+
+  /* Encode BGP attributes */
+  int len = bgp_encode_attrs(s->bws, eattrs, pos, b->end);
+  if (len < 0)
+    goto fail;
+  pos += len;
+
+  /* Encode IPv6 next hop separately as fake MP_REACH_NLRI attribute */
+  if (s->bws->mp_next_hop)
+  {
+    len = bgp_encode_mp_reach_mrt(s->bws, s->bws->mp_next_hop, pos, b->end - pos);
+    if (len < 0)
+      goto fail;
+    pos += len;
+  }
+
+  /* Update attribute length and advance buffer pos */
+  put_u16(b->pos - 2, pos - b->pos);
+  b->pos = pos;
+  return;
+
+fail:
+  mrt_log(s, "Attribute list too long for %N", r->net->n.addr);
+}
+#endif
+
 static void
 mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
 {
@@ -447,25 +491,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
   mrt_put_u16(b, 0);
 
 #ifdef CONFIG_BGP
-  if (r->attrs->eattrs)
-  {
-    struct ea_list *eattrs = r->attrs->eattrs;
-
-    if (!rta_is_cached(r->attrs))
-      ea_normalize(eattrs);
-
-    mrt_buffer_need(b, MRT_ATTR_BUFFER_SIZE);
-    int alen = bgp_encode_attrs(s->bws, eattrs, b->pos, b->end);
-
-    if (alen < 0)
-    {
-      mrt_log(s, "Attribute list too long for %N", r->net->n.addr);
-      alen = 0;
-    }
-
-    put_u16(b->pos - 2, alen);
-    b->pos += alen;
-  }
+  mrt_rib_table_entry_bgp_attrs(s, r);
 #endif
 
   s->entry_count++;