Commit 3c744164 authored by Jan Moskyto Matejka's avatar Jan Moskyto Matejka
Browse files

Nexthop: Fixed recursive route mpls label merging

parent a5d2a344
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ CF_DECLS
  struct proto_spec ps;
  struct channel_limit cl;
  struct timeformat *tf;
  u32 *lbl;
  mpls_label_stack *mls;
}

%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
@@ -85,7 +85,7 @@ CF_DECLS
%type <a> ipa
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_
%type <lbl> label_stack_start label_stack
%type <mls> label_stack_start label_stack

%type <t> text opttext

@@ -288,18 +288,17 @@ net_or_ipa:

label_stack_start: NUM
{
  $$ = cfg_allocz(sizeof(u32) * (MPLS_MAX_LABEL_STACK+1));
  $$[0] = 1;
  $$[1] = $1;
  $$ = cfg_allocz(sizeof(mpls_label_stack));
  $$->len = 1;
  $$->stack[0] = $1;
};

label_stack:
    label_stack_start
  | label_stack '/' NUM {
    if ($1[0] >= MPLS_MAX_LABEL_STACK)
    if ($1->len >= MPLS_MAX_LABEL_STACK)
      cf_error("Too many labels in stack");
    $1[0]++;
    $1[*$1] = $3;
    $1->stack[$1->len++] = $3;
    $$ = $1;
  }
;
+5 −0
Original line number Diff line number Diff line
@@ -326,6 +326,11 @@ static inline ip6_addr ip6_ntoh(ip6_addr a)
{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }

#define MPLS_MAX_LABEL_STACK 8
typedef struct mpls_label_stack {
  uint len;
  u32 stack[MPLS_MAX_LABEL_STACK];
} mpls_label_stack;

static inline int
mpls_get(const char *buf, int buflen, u32 *stack)
{
+1 −1
Original line number Diff line number Diff line
@@ -551,7 +551,7 @@ 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);
void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls);

/*
 * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills
+74 −33
Original line number Diff line number Diff line
@@ -1767,55 +1767,80 @@ rta_next_hop_outdated(rta *a)
}

static inline void
rta_apply_hostentry(rta *a, struct hostentry *he)
rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
{
  a->hostentry = he;
  a->dest = he->dest;
  a->igp_metric = he->igp_metric;

  if ((a->nh.labels_orig == 0) && (!a->nh.next) && he->nexthop_linkable)
  if (a->dest != RTD_UNICAST)
  {
    a->nh = he->src->nh;
    /* No nexthop */
no_nexthop:
    a->nh = (struct nexthop) {};
    if (mls)
    { /* Store the label stack for later changes */
      a->nh.labels_orig = a->nh.labels = mls->len;
      memcpy(a->nh.label, mls->stack, mls->len * sizeof(u32));
    }
    return;
  }

  struct nexthop *nhp = alloca(NEXTHOP_MAX_SIZE);
  if (((!mls) || (!mls->len)) && he->nexthop_linkable)
  { /* Just link the nexthop chain, no label append happens. */
    memcpy(&(a->nh), &(he->src->nh), nexthop_size(&(he->src->nh)));
    return;
  }

  for (struct nexthop *nhe = &(a->nh); nhe; nhe = nhe->next)
  {
    int labels_orig = nhe->labels_orig;	    /* Number of labels (at the bottom of stack) */
    u32 label_stack[MPLS_MAX_LABEL_STACK];
    memcpy(label_stack, nhe->label, labels_orig * sizeof(u32));
  struct nexthop *nhp = NULL, *nhr = NULL;
  int skip_nexthop = 0;
  
  for (struct nexthop *nh = &(he->src->nh); nh; nh = nh->next)
  {
    if (skip_nexthop)
      skip_nexthop--;
    else
    {
      nhr = nhp;
      nhp = (nhp ? (nhp->next = lp_allocz(rte_update_pool, NEXTHOP_MAX_SIZE)) : &(a->nh));
    }

    nhp->iface = nh->iface;
      nhp->weight = nh->weight; /* FIXME: Ignoring the recursive nexthop's weight */
      nhp->labels = nh->labels + labels_orig;
      nhp->labels_orig = labels_orig;
    nhp->weight = nh->weight;
    if (mls)
    {
      nhp->labels = nh->labels + mls->len;
      nhp->labels_orig = mls->len;
      if (nhp->labels <= MPLS_MAX_LABEL_STACK)
      {
	memcpy(nhp->label, nh->label, nh->labels * sizeof(u32)); /* First the hostentry labels */
	memcpy(&(nhp->label[nh->labels]), label_stack, labels_orig * sizeof(u32)); /* Then the bottom labels */
	memcpy(&(nhp->label[nh->labels]), mls->stack, mls->len * sizeof(u32)); /* Then the bottom labels */
      }
      else
      {
	log(L_WARN "Sum of label stack sizes %d + %d = %d exceedes allowed maximum (%d)",
	    nh->labels, labels_orig, nhp->labels, MPLS_MAX_LABEL_STACK);
	    nh->labels, mls->len, nhp->labels, MPLS_MAX_LABEL_STACK);
	skip_nexthop++;
	continue;
      }
    }
    if (ipa_nonzero(nh->gw))
      nhp->gw = nh->gw;		/* Router nexthop */
    else if (ipa_nonzero(he->link))
      nhp->gw = he->link;		/* Device nexthop with link-local address known */
    else
      nhp->gw = he->addr;		/* Device nexthop with link-local address unknown */

      nhp = (nhp->next = lp_alloc(rte_update_pool, NEXTHOP_MAX_SIZE));
    }
  }

  memcpy(&(a->nh), nhp, nexthop_size(nhp));
  if (skip_nexthop)
    if (nhr)
      nhr->next = NULL;
    else
    {
      a->dest = RTD_UNREACHABLE;
      log(L_WARN "No valid nexthop remaining, setting route unreachable");
      goto no_nexthop;
    }
}

static inline rte *
@@ -1823,7 +1848,11 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
{
  rta *a = alloca(RTA_MAX_SIZE);
  memcpy(a, old->attrs, rta_size(old->attrs));
  rta_apply_hostentry(a, old->attrs->hostentry);

  mpls_label_stack mls = { .len = a->nh.labels_orig };
  memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));

  rta_apply_hostentry(a, old->attrs->hostentry, &mls);
  a->aflags = 0;

  rte *e = sl_alloc(rte_slab);
@@ -2387,13 +2416,25 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
	  goto done;
	}

      he->dest = a->dest;
      he->nexthop_linkable = 1;
      if (he->dest == RTD_UNICAST)
	{
	  for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
	    if (ipa_zero(nh->gw))
	      {
		if (if_local_addr(he->addr, nh->iface))
		  {
		    /* The host address is a local address, this is not valid */
		    log(L_WARN "Next hop address %I is a local address of iface %s",
			he->addr, nh->iface->name);
		    goto done;
		  }

		he->nexthop_linkable = 0;
		break;
	      }
	}

      he->src = rta_clone(a);
      he->igp_metric = rt_get_igp_metric(e);
@@ -2454,9 +2495,9 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
}

void
rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll)
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));
  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ipa_zero(ll) ? gw : ll, dep), mls);
}


+2 −0
Original line number Diff line number Diff line
@@ -337,6 +337,8 @@ struct bgp_parse_state {
  u32 mp_reach_af;
  u32 mp_unreach_af;

  mpls_label_stack mls;

  uint attr_len;
  uint ip_reach_len;
  uint ip_unreach_len;
Loading