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.
This commit is contained in:
Ondrej Zajicek (work) 2021-01-12 15:37:01 +01:00
parent 910adaa08b
commit d774f6d721
3 changed files with 77 additions and 19 deletions

View 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 static inline u32
get_af3(byte *buf) get_af3(byte *buf)
{ {

View file

@ -559,6 +559,7 @@ static inline void
bgp_unset_attr(ea_list **to, struct linpool *pool, uint code) 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; } { 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); 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); ea_list * bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len);

View file

@ -417,6 +417,50 @@ mrt_rib_table_header(struct mrt_table_dump_state *s, net_addr *n)
mrt_put_u16(b, 0); 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 static void
mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r) 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); mrt_put_u16(b, 0);
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
if (r->attrs->eattrs) mrt_rib_table_entry_bgp_attrs(s, r);
{
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;
}
#endif #endif
s->entry_count++; s->entry_count++;