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

BGP: AIGP metric support (RFC 7311)

parent ca2dacfc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -652,6 +652,7 @@ void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *);

u32 rt_get_igp_metric(rte *rt);
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);

+13 −1
Original line number Diff line number Diff line
@@ -44,6 +44,10 @@
#include "lib/string.h"
#include "lib/alloca.h"

#ifdef CONFIG_BGP
#include "proto/bgp/bgp.h"
#endif

pool *rt_table_pool;

static slab *rte_slab;
@@ -2934,7 +2938,7 @@ if_local_addr(ip_addr a, struct iface *i)
  return 0;
}

static u32
u32
rt_get_igp_metric(rte *rt)
{
  eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
@@ -2956,6 +2960,14 @@ rt_get_igp_metric(rte *rt)
    return rt->u.rip.metric;
#endif

#ifdef CONFIG_BGP
  if (a->source == RTS_BGP)
  {
    u64 metric = bgp_total_aigp_metric(rt);
    return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
  }
#endif

  if (a->source == RTS_DEVICE)
    return 0;

+252 −7
Original line number Diff line number Diff line
@@ -199,6 +199,179 @@ bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
}


/*
 *	AIGP handling
 */

static int
bgp_aigp_valid(byte *data, uint len, char *err, uint elen)
{
  byte *pos = data;
  char *err_dsc = NULL;
  uint err_val = 0;

#define BAD(DSC,VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
  while (len)
  {
    if (len < 3)
      BAD("TLV framing error", len);

    /* Process one TLV */
    uint ptype = pos[0];
    uint plen = get_u16(pos + 1);

    if (len < plen)
      BAD("TLV framing error", plen);

    if (plen < 3)
      BAD("Bad TLV length", plen);

    if ((ptype == BGP_AIGP_METRIC) && (plen != 11))
      BAD("Bad AIGP TLV length", plen);

    ADVANCE(pos, len, plen);
  }
#undef BAD

  return 1;

bad:
  if (err)
    if (bsnprintf(err, elen, "%s (%u) at %d", err_dsc, err_val, (int) (pos - data)) < 0)
      err[0] = 0;

  return 0;
}

static const byte *
bgp_aigp_get_tlv(const struct adata *ad, uint type)
{
  if (!ad)
    return NULL;

  uint len = ad->length;
  const byte *pos = ad->data;

  while (len)
  {
    uint ptype = pos[0];
    uint plen = get_u16(pos + 1);

    if (ptype == type)
      return pos;

    ADVANCE(pos, len, plen);
  }

  return NULL;
}

static const struct adata *
bgp_aigp_set_tlv(struct linpool *pool, const struct adata *ad, uint type, byte *data, uint dlen)
{
  uint len = ad ? ad->length : 0;
  const byte *pos = ad ? ad->data : NULL;
  struct adata *res = lp_alloc_adata(pool, len + 3 + dlen);
  byte *dst = res->data;
  byte *tlv = NULL;
  int del = 0;

  while (len)
  {
    uint ptype = pos[0];
    uint plen = get_u16(pos + 1);

    /* Find position for new TLV */
    if ((ptype >= type) && !tlv)
    {
      tlv = dst;
      dst += 3 + dlen;
    }

    /* Skip first matching TLV, copy others */
    if ((ptype == type) && !del)
      del = 1;
    else
    {
      memcpy(dst, pos, plen);
      dst += plen;
    }

    ADVANCE(pos, len, plen);
  }

  if (!tlv)
  {
    tlv = dst;
    dst += 3 + dlen;
  }

  /* Store the TLD */
  put_u8(tlv + 0, type);
  put_u16(tlv + 1, 3 + dlen);
  memcpy(tlv + 3, data, dlen);

  /* Update length */
  res->length = dst - res->data;

  return res;
}

static u64 UNUSED
bgp_aigp_get_metric(const struct adata *ad, u64 def)
{
  const byte *b = bgp_aigp_get_tlv(ad, BGP_AIGP_METRIC);
  return b ? get_u64(b + 3) : def;
}

static const struct adata *
bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
{
  byte data[8];
  put_u64(data, metric);
  return bgp_aigp_set_tlv(pool, ad, BGP_AIGP_METRIC, data, 8);
}

int
bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
{
  eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
  if (!a)
    return 0;

  const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
  if (!b)
    return 0;

  u64 aigp = get_u64(b + 3);
  u64 step = e->attrs->igp_metric;

  if (!rte_resolvable(e) || (step >= IGP_METRIC_UNKNOWN))
    step = BGP_AIGP_MAX;

  if (!step)
    step = 1;

  *ad = a->u.ptr;
  *metric = aigp + step;
  if (*metric < aigp)
    *metric = BGP_AIGP_MAX;

  return 1;
}

static inline int
bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
{
  if (e->attrs->source == RTS_BGP)
    return 0;

  *metric = rt_get_igp_metric(e);
  *ad = NULL;
  return *metric < IGP_METRIC_UNKNOWN;
}


/*
 *	Attribute hooks
 */
@@ -604,6 +777,41 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt
  bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
}


static void
bgp_export_aigp(struct bgp_export_state *s, eattr *a)
{
  if (!s->channel->cf->aigp)
    UNSET(a);
}

static void
bgp_decode_aigp(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  char err[128];

  /* FIXME handle flags */

  /* Acceptability test postponed to bgp_finish_attrs() */

  if (!bgp_aigp_valid(data, len, err, sizeof(err)))
    DISCARD("Malformed AIGP attribute - %s", err);

  bgp_set_attr_data(to, s->pool, BA_AIGP, flags, data, len);
}

static void
bgp_format_aigp(eattr *a, byte *buf, uint size UNUSED)
{
  const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);

  if (!b)
    bsprintf(buf, "?");
  else
    bsprintf(buf, "%lu", get_u64(b + 3));
}


static void
bgp_export_large_community(struct bgp_export_state *s, eattr *a)
{
@@ -820,6 +1028,15 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
    .decode = bgp_decode_as4_aggregator,
    .format = bgp_format_aggregator,
  },
  [BA_AIGP] = {
    .name = "aigp",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_OPTIONAL,
    .export = bgp_export_aigp,
    .encode = bgp_encode_raw,
    .decode = bgp_decode_aigp,
    .format = bgp_format_aigp,
  },
  [BA_LARGE_COMMUNITY] = {
    .name = "large_community",
    .type = EAF_TYPE_LC_SET,
@@ -1150,6 +1367,17 @@ withdraw:
  return NULL;
}

void
bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
{
  /* AIGP test here instead of in bgp_decode_aigp() - we need to know channel */
  if (BIT32_TEST(s->attrs_seen, BA_AIGP) && !s->channel->cf->aigp)
  {
    REPORT("Discarding AIGP attribute received on non-AIGP session");
    bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
  }
}


/*
 *	Route bucket hash table
@@ -1481,6 +1709,16 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
  if (p->is_interior && ! bgp_find_attr(attrs0, BA_LOCAL_PREF))
    bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);

  /* AIGP attribute - accumulate it when local next hop is used */
  u64 metric;
  if (s.local_next_hop &&
      (bgp_total_aigp_metric_(e, &metric, &ad) ||
       (c->cf->aigp_originate && bgp_init_aigp_metric(e, &metric, &ad))))
  {
    ad = bgp_aigp_set_metric(pool, ad, metric);
    bgp_set_attr_ptr(&attrs, pool, BA_AIGP, 0, ad);
  }

  /* IBGP route reflection, RFC 4456 */
  if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
  {
@@ -1578,12 +1816,6 @@ bgp_get_neighbor(rte *r)
  return p->cf->confederation ?: p->local_as;
}

static inline int
rte_resolvable(rte *rt)
{
  return rt->attrs->dest == RTD_UNICAST;
}

static inline int
rte_stale(rte *r)
{
@@ -1639,6 +1871,14 @@ bgp_rte_better(rte *new, rte *old)
  if (n < o)
    return 0;

  /* RFC 7311 4.1 - Apply AIGP metric */
  u64 n2 = bgp_total_aigp_metric(new);
  u64 o2 = bgp_total_aigp_metric(old);
  if (n2 < o2)
    return 1;
  if (n2 > o2)
    return 0;

  /* RFC 4271 9.1.2.2. a)  Use AS path lengths */
  if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
  {
@@ -2062,7 +2302,12 @@ bgp_get_route_info(rte *e, byte *buf)
  if (rte_stale(e))
    buf += bsprintf(buf, "s");

  if (e->attrs->hostentry)
  u64 metric = bgp_total_aigp_metric(e);
  if (metric < BGP_AIGP_MAX)
  {
    buf += bsprintf(buf, "/%lu", metric);
  }
  else if (e->attrs->igp_metric)
  {
    if (!rte_resolvable(e))
      buf += bsprintf(buf, "/-");
+13 −2
Original line number Diff line number Diff line
@@ -1979,6 +1979,14 @@ bgp_postconfig(struct proto_config *CF)
    if (cc->llgr_time == ~0U)
      cc->llgr_time = cf->llgr_time;

    /* AIGP enabled by default on interior sessions */
    if (cc->aigp == 0xff)
      cc->aigp = interior;

    /* Default cost for AIGP sessions */
    if (cc->aigp && !cc->cost)
      cc->cost = BGP_DEFAULT_COST;

    /* Default values of IGP tables */
    if ((cc->gw_mode == GW_RECURSIVE) && !cc->desc->no_igp)
    {
@@ -2087,13 +2095,16 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
  if (new->mandatory && !old->mandatory && (C->channel_state != CS_UP))
    return 0;

  if (new->gw_mode != old->gw_mode)
  if ((new->gw_mode != old->gw_mode) ||
      (new->aigp != old->aigp) ||
      (new->cost != old->cost))
    *import_changed = 1;

  if (!ipa_equal(new->next_hop_addr, old->next_hop_addr) ||
      (new->next_hop_self != old->next_hop_self) ||
      (new->next_hop_keep != old->next_hop_keep) ||
      (new->missing_lladdr != old->missing_lladdr))
      (new->missing_lladdr != old->missing_lladdr) ||
      (new->aigp != old->aigp))
    *export_changed = 1;

  c->cf = new;
+27 −0
Original line number Diff line number Diff line
@@ -149,6 +149,9 @@ struct bgp_channel_config {
  uint llgr_time;			/* Long-lived graceful restart stale time */
  u8 ext_next_hop;			/* Allow both IPv4 and IPv6 next hops */
  u8 add_path;				/* Use ADD-PATH extension [RFC 7911] */
  u8 aigp;				/* AIGP is allowed on this session */
  u8 aigp_originate;			/* AIGP is originated automatically */
  u32 cost;				/* IGP cost for direct next hops */
  u8 import_table;			/* Use c.in_table as Adj-RIB-In */
  u8 export_table;			/* Use c.out_table as Adj-RIB-Out */

@@ -195,6 +198,8 @@ struct bgp_channel_config {

#define BGP_BFD_GRACEFUL	2	/* BFD down triggers graceful restart */

#define BGP_DEFAULT_COST	10	/* Default cost for direct BGP */


struct bgp_af_caps {
  u32 afi;
@@ -379,6 +384,7 @@ struct bgp_export_state {

  u32 attrs_seen[1];
  uint err_withdraw;
  uint local_next_hop;
};

struct bgp_write_state {
@@ -493,6 +499,11 @@ void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len);
struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);

static inline int
rte_resolvable(rte *rt)
{
  return rt->attrs->dest == RTD_UNICAST;
}


#ifdef LOCAL_DEBUG
@@ -541,6 +552,7 @@ bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)

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);
void bgp_finish_attrs(struct bgp_parse_state *s, rta *a);

void bgp_init_bucket_table(struct bgp_channel *c);
void bgp_free_bucket_table(struct bgp_channel *c);
@@ -560,6 +572,20 @@ void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *ol
int bgp_preexport(struct proto *, struct rte **, struct linpool *);
int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *buf);
int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);

#define BGP_AIGP_METRIC		1
#define BGP_AIGP_MAX		U64(0xffffffffffffffff)

static inline u64
bgp_total_aigp_metric(rte *r)
{
  u64 metric = BGP_AIGP_MAX;
  const struct adata *ad;

  bgp_total_aigp_metric_(r, &metric, &ad);
  return metric;
}


/* packets.c */
@@ -610,6 +636,7 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
#define BA_EXT_COMMUNITY	0x10	/* RFC 4360 */
#define BA_AS4_PATH             0x11	/* RFC 6793 */
#define BA_AS4_AGGREGATOR       0x12	/* RFC 6793 */
#define BA_AIGP			0x1a	/* RFC 7311 */
#define BA_LARGE_COMMUNITY	0x20	/* RFC 8092 */

/* Bird's private internal BGP attributes */
Loading