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

BGP: Support for MPLS labels and VPN SAFI

Basic support for SAFI 4 and 128 (MPLS labeled IP and VPN) for IPv4 and
IPv6. Should work for route reflector, but does not properly handle
originating routes with next hop self.

Based on patches from Jan Matejka.
parent ead7b8f4
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@
#define NB_MPLS		(1 << NET_MPLS)

#define NB_IP		(NB_IP4 | NB_IP6)
#define NB_VPN		(NB_VPN4 | NB_VPN6)
#define NB_FLOW		(NB_FLOW4 | NB_FLOW6)
#define NB_ANY		0xffffffff


@@ -481,6 +483,12 @@ static inline void net_normalize_ip4(net_addr_ip4 *n)
static inline void net_normalize_ip6(net_addr_ip6 *n)
{ n->prefix = ip6_and(n->prefix, ip6_mkmask(n->pxlen)); }

static inline void net_normalize_vpn4(net_addr_vpn4 *n)
{ net_normalize_ip4((net_addr_ip4 *) n); }

static inline void net_normalize_vpn6(net_addr_vpn6 *n)
{ net_normalize_ip6((net_addr_ip6 *) n); }

void net_normalize(net_addr *N);


+14 −0
Original line number Diff line number Diff line
@@ -28,6 +28,13 @@ get_u16(const void *p)
  return ntohs(x);
}

static inline u32
get_u24(const void *P)
{
  const byte *p = P;
  return (p[0] << 16) + (p[1] << 8) + p[2];
}

static inline u32
get_u32(const void *p)
{
@@ -52,6 +59,13 @@ put_u16(void *p, u16 x)
  memcpy(p, &x, 2);
}

static inline void
put_u24(void *p, u32 x)
{
  x = htonl(x);
  memcpy(p, ((char *) &x) + 1, 3);
}

static inline void
put_u32(void *p, u32 x)
{
+9 −1
Original line number Diff line number Diff line
@@ -551,7 +551,15 @@ static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta
void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *, ea_list *);
void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls);

struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);

static inline void
rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
{
  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
}

/*
 * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills
+4 −10
Original line number Diff line number Diff line
@@ -1766,7 +1766,7 @@ rta_next_hop_outdated(rta *a)
    (!he->nexthop_linkable) || !nexthop_same(&(a->nh), &(he->src->nh));
}

static inline void
void
rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
{
  a->hostentry = he;
@@ -2475,7 +2475,7 @@ rt_update_hostcache(rtable *tab)
  tab->hcu_scheduled = 0;
}

static struct hostentry *
struct hostentry *
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
{
  struct hostentry *he;
@@ -2489,17 +2489,11 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
    if (ipa_equal(he->addr, a) && (he->tab == dep))
      return he;

  he = hc_new_hostentry(hc, a, ll, dep, k);
  he = hc_new_hostentry(hc, a, ipa_zero(ll) ? a : ll, dep, k);
  rt_update_hostentry(tab, he);
  return he;
}

void
rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
{
  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ipa_zero(ll) ? gw : ll, dep), mls);
}


/*
 *  CLI commands
+80 −4
Original line number Diff line number Diff line
@@ -629,6 +629,75 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
  bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
}

static void
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
{
  net_addr *n = s->route->net->n.addr;
  u32 *labels = (u32 *) a->u.ptr->data;
  uint lnum = a->u.ptr->length / 4;

  /* Perhaps we should just ignore it? */
  if (!s->mpls)
    WITHDRAW("Unexpected MPLS stack");

  /* Empty MPLS stack is not allowed */
  if (!lnum)
    WITHDRAW("Malformed MPLS stack - empty");

  /* This is ugly, but we must ensure that labels fit into NLRI field */
  if ((24*lnum + (net_is_vpn(n) ? 64 : 0) + net_pxlen(n)) > 255)
    WITHDRAW("Malformed MPLS stack - too many labels (%u)", lnum);

  for (uint i = 0; i < lnum; i++)
  {
    if (labels[i] > 0xfffff)
      WITHDRAW("Malformed MPLS stack - invalid label (%u)", labels[i]);

    /* TODO: Check for special-purpose label values? */
  }
}

static int
bgp_encode_mpls_label_stack(struct bgp_write_state *s, eattr *a, byte *buf UNUSED, uint size UNUSED)
{
  /*
   * MPLS labels are encoded as a part of the NLRI in MP_REACH_NLRI attribute,
   * so we store MPLS_LABEL_STACK and encode it later by AFI-specific hooks.
   */

  s->mpls_labels = a->u.ptr;
  return 0;
}

static void
bgp_decode_mpls_label_stack(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data UNUSED, uint len UNUSED, ea_list **to UNUSED)
{
  DISCARD("Discarding received attribute #0");
}

static void
bgp_format_mpls_label_stack(eattr *a, byte *buf, uint size)
{
  u32 *labels = (u32 *) a->u.ptr->data;
  uint lnum = a->u.ptr->length / 4;
  char *pos = buf;

  for (uint i = 0; i < lnum; i++)
  {
    if (size < 20)
    {
      bsprintf(pos, "...");
      return;
    }

    uint l = bsprintf(pos, "%d/", labels[i]);
    ADVANCE(pos, size, l);
  }

  /* Clear last slash or terminate empty string */
  pos[lnum ? -1 : 0] = 0;
}

static inline void
bgp_decode_unknown(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
{
@@ -763,6 +832,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
    .encode = bgp_encode_u32s,
    .decode = bgp_decode_large_community,
  },
  [BA_MPLS_LABEL_STACK] = {
    .name = "mpls_label_stack",
    .type = EAF_TYPE_INT_SET,
    .export = bgp_export_mpls_label_stack,
    .encode = bgp_encode_mpls_label_stack,
    .decode = bgp_decode_mpls_label_stack,
    .format = bgp_format_mpls_label_stack,
  },
};

static inline int
@@ -849,7 +926,6 @@ bgp_export_attrs(struct bgp_export_state *s, ea_list *attrs)
    return NULL;

  return new;

}


@@ -1340,7 +1416,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
{
  struct proto *SRC = e->attrs->src->proto;
  struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
  struct bgp_export_state s = { .proto = p, .channel =c, .pool = pool, .src = src, .route = e };
  struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
  ea_list *attrs = attrs0;
  eattr *a;
  adata *ad;
@@ -1453,13 +1529,13 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old, ea

  if (new)
  {
    attrs = bgp_update_attrs(p, c, new, attrs, bgp_linpool);
    attrs = bgp_update_attrs(p, c, new, attrs, bgp_linpool2);

    /* If attributes are invalid, we fail back to withdraw */
    buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
    path = new->attrs->src->global_id;

    lp_flush(bgp_linpool);
    lp_flush(bgp_linpool2);
  }
  else
  {
Loading