Commit d774f6d7 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

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.
parent 910adaa0
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -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)
{
+1 −0
Original line number Diff line number Diff line
@@ -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);
+45 −19
Original line number Diff line number Diff line
@@ -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++;