From d774f6d721b0e52ed800c4b9a3a482c8ce9dd074 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 12 Jan 2021 15:37:01 +0100 Subject: [PATCH] MRT: Fix IPv6 table dumps 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 | 31 +++++++++++++++++++++++ proto/bgp/bgp.h | 1 + proto/mrt/mrt.c | 64 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index b2c37301..6752cb7f 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -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) { diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 5cabd327..dd7dc28f 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -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); diff --git a/proto/mrt/mrt.c b/proto/mrt/mrt.c index 7a396a84..76d6c48f 100644 --- a/proto/mrt/mrt.c +++ b/proto/mrt/mrt.c @@ -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++;