0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-09 12:48:43 +00:00
bird/nest/mpls.c
Ondrej Zajicek 333ddd4f98 MPLS subsystem
The MPLS subsystem manages MPLS labels and handles their allocation to
MPLS-aware routing protocols. These labels are then attached to IP or VPN
routes representing label switched paths -- LSPs.

There was already a preliminary MPLS support consisting of MPLS label
net_addr, MPLS routing tables with static MPLS routes, remote labels in
next hops, and kernel protocol support.

This patch adds the MPLS domain as a basic structure representing local
label space with dynamic label allocator and configurable label ranges.
To represent LSPs, allocated local labels can be attached as route
attributes to IP or VPN routes with local labels as attributes.

There are several steps for handling LSP routes in routing protocols --
deciding to which forwarding equivalence class (FEC) the LSP route
belongs, allocating labels for new FECs, announcing MPLS routes for new
FECs, attaching labels to LSP routes. The FEC map structure implements
basic code for managing FECs in routing protocols, therefore existing
protocols can be made MPLS-aware by adding FEC map and delegating
most work related to local label management to it.
2023-10-04 13:01:21 +02:00

999 lines
23 KiB
C

/*
* BIRD Internet Routing Daemon -- MPLS Structures
*
* (c) 2022 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2022 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: MPLS
*
* The MPLS subsystem manages MPLS labels and handles their allocation to
* MPLS-aware routing protocols. These labels are then attached to IP or VPN
* routes representing label switched paths -- LSPs. MPLS labels are also used
* in special MPLS routes (which use labels as network address) that are
* exported to MPLS routing table in kernel. The MPLS subsystem consists of MPLS
* domains (struct &mpls_domain), MPLS channels (struct &mpls_channel) and FEC
* maps (struct &mpls_fec_map).
*
* The MPLS domain represents one MPLS label address space, implements the label
* allocator, and handles associated configuration and management. The domain is
* declared in the configuration (struct &mpls_domain_config). There might be
* multiple MPLS domains representing separate label spaces, but in most cases
* one domain is enough. MPLS-aware protocols and routing tables are associated
* with a specific MPLS domain.
*
* The MPLS domain has configurable label ranges (struct &mpls_range), by
* default it has two ranges: static (16-1000) and dynamic (1000-10000). When
* a protocol wants to allocate labels, it first acquires a handle (struct
* &mpls_handle) for a specific range using mpls_new_handle(), and then it
* allocates labels from that with mpls_new_label(). When not needed, labels are
* freed by mpls_free_label() and the handle is released by mpls_free_handle().
* Note that all labels and handles must be freed manually.
*
* Both MPLS domain and MPLS range are reference counted, so when deconfigured
* they could be freed just after all labels and ranges are freed. Users are
* expected to hold a reference to a MPLS domain for whole time they use
* something from that domain (e.g. &mpls_handle), but releasing reference to
* a range while holding associated handle is OK.
*
* The MPLS channel is subclass of a generic protocol channel. It has two
* distinct purposes - to handle per-protocol MPLS configuration (e.g. which
* MPLS domain is associated with the protocol, which label range is used by the
* protocol), and to announce MPLS routes to a routing table (as a regular
* protocol channel).
*
* The FEC map is a helper structure that maps forwarding equivalent classes
* (FECs) to MPLS labels. It is an internal matter of a routing protocol how to
* assign meaning to allocated labels, announce LSP routes and associated MPLS
* routes (i.e. ILM entries). But the common behavior is implemented in the FEC
* map, which can be used by the protocols that work with IP-prefix-based FECs.
*
* The FEC map keeps hash tables of FECs (struct &mpls_fec) based on network
* prefix, next hop eattr and assigned label. It has three labeling policies:
* static assignment (%MPLS_POLICY_STATIC), per-prefix policy (%MPLS_POLICY_PREFIX),
* and aggregating policy (%MPLS_POLICY_AGGREGATE). In per-prefix policy, each
* distinct LSP is a separate FEC and uses a separate label, which is kept even
* if the next hop of the LSP changes. In aggregating policy, LSPs with a same
* next hop form one FEC and use one label, but when a next hop (or remote
* label) of such LSP changes then the LSP must be moved to a different FEC and
* assigned a different label.
*
* The overall process works this way: A protocol wants to announce a LSP route,
* it does that by announcing e.g. IP route with %EA_MPLS_POLICY attribute.
* After the route is accepted by filters (which may also change the policy
* attribute or set a static label), the mpls_handle_rte() is called from
* rte_update2(), which applies selected labeling policy, finds existing FEC or
* creates a new FEC (which includes allocating new label and announcing related
* MPLS route by mpls_announce_fec()), and attach FEC label to the LSP route.
* After that, the LSP route is stored in routing table by rte_recalculate().
* Changes in routing tables trigger mpls_rte_insert() and mpls_rte_remove()
* hooks, which refcount FEC structures and possibly trigger removal of FECs
* and withdrawal of MPLS routes.
*
* TODO:
* - show mpls labels CLI command
* - label range non-intersection check
* - better range reconfigurations (allow reduce ranges over unused labels)
* - protocols should do route refresh instead of resetart when reconfiguration
* requires changing labels (e.g. different label range)
* - registering static allocations
* - checking range in static allocations
* - special handling of reserved labels
*/
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/mpls.h"
static struct mpls_range *mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf);
static struct mpls_range *mpls_find_range_(list *l, const char *name);
static int mpls_reconfigure_range(struct mpls_domain *m, struct mpls_range *r, struct mpls_range_config *cf);
static void mpls_remove_range(struct mpls_range *r);
/*
* MPLS domain
*/
list mpls_domains;
void
mpls_init(void)
{
init_list(&mpls_domains);
}
struct mpls_domain_config *
mpls_domain_config_new(struct symbol *s)
{
struct mpls_domain_config *mc = cfg_allocz(sizeof(struct mpls_domain_config));
struct mpls_range_config *rc;
cf_define_symbol(new_config, s, SYM_MPLS_DOMAIN, mpls_domain, mc);
mc->name = s->name;
init_list(&mc->ranges);
/* Predefined static range */
rc = mpls_range_config_new(mc, NULL);
rc->name = "static";
rc->start = 16;
rc->length = 984;
mc->static_range = rc;
/* Predefined dynamic range */
rc = mpls_range_config_new(mc, NULL);
rc->name = "dynamic";
rc->start = 1000;
rc->length = 9000;
mc->dynamic_range = rc;
add_tail(&new_config->mpls_domains, &mc->n);
return mc;
}
void
mpls_domain_postconfig(struct mpls_domain_config *cf UNUSED)
{
/* Add label range non-intersection check */
}
static struct mpls_domain *
mpls_new_domain(struct mpls_domain_config *cf)
{
struct pool *p = rp_new(&root_pool, "MPLS domain");
struct mpls_domain *m = mb_allocz(p, sizeof(struct mpls_domain));
m->cf = cf;
m->name = cf->name;
m->pool = p;
lmap_init(&m->labels, p);
lmap_set(&m->labels, 0);
init_list(&m->ranges);
init_list(&m->handles);
struct mpls_range_config *rc;
WALK_LIST(rc, cf->ranges)
mpls_new_range(m, rc);
add_tail(&mpls_domains, &m->n);
cf->domain = m;
return m;
}
static struct mpls_domain *
mpls_find_domain_(list *l, const char *name)
{
struct mpls_domain *m;
WALK_LIST(m, *l)
if (!strcmp(m->name, name))
return m;
return NULL;
}
static int
mpls_reconfigure_domain(struct mpls_domain *m, struct mpls_domain_config *cf)
{
cf->domain = m;
m->cf->domain = NULL;
m->cf = cf;
m->name = cf->name;
/* Reconfigure label ranges */
list old_ranges;
init_list(&old_ranges);
add_tail_list(&old_ranges, &m->ranges);
init_list(&m->ranges);
struct mpls_range_config *rc;
WALK_LIST(rc, cf->ranges)
{
struct mpls_range *r = mpls_find_range_(&old_ranges, rc->name);
if (r && mpls_reconfigure_range(m, r, rc))
{
rem_node(&r->n);
add_tail(&m->ranges, &r->n);
continue;
}
mpls_new_range(m, rc);
}
struct mpls_range *r, *r2;
WALK_LIST_DELSAFE(r, r2, old_ranges)
mpls_remove_range(r);
add_tail_list(&m->ranges, &old_ranges);
return 1;
}
static void
mpls_free_domain(struct mpls_domain *m)
{
ASSERT(m->use_count == 0);
ASSERT(m->label_count == 0);
ASSERT(EMPTY_LIST(m->handles));
struct config *cfg = m->removed;
m->cf->domain = NULL;
rem_node(&m->n);
rfree(m->pool);
config_del_obstacle(cfg);
}
static void
mpls_remove_domain(struct mpls_domain *m, struct config *cfg)
{
m->removed = cfg;
config_add_obstacle(cfg);
if (!m->use_count)
mpls_free_domain(m);
}
void
mpls_lock_domain(struct mpls_domain *m)
{
m->use_count++;
}
void
mpls_unlock_domain(struct mpls_domain *m)
{
ASSERT(m->use_count > 0);
m->use_count--;
if (!m->use_count && m->removed)
mpls_free_domain(m);
}
void
mpls_preconfig(struct config *c)
{
init_list(&c->mpls_domains);
}
void
mpls_commit(struct config *new, struct config *old)
{
list old_domains;
init_list(&old_domains);
add_tail_list(&old_domains, &mpls_domains);
init_list(&mpls_domains);
struct mpls_domain_config *mc;
WALK_LIST(mc, new->mpls_domains)
{
struct mpls_domain *m = mpls_find_domain_(&old_domains, mc->name);
if (m && mpls_reconfigure_domain(m, mc))
{
rem_node(&m->n);
add_tail(&mpls_domains, &m->n);
continue;
}
mpls_new_domain(mc);
}
struct mpls_domain *m, *m2;
WALK_LIST_DELSAFE(m, m2, old_domains)
mpls_remove_domain(m, old);
add_tail_list(&mpls_domains, &old_domains);
}
/*
* MPLS range
*/
struct mpls_range_config *
mpls_range_config_new(struct mpls_domain_config *mc, struct symbol *s)
{
struct mpls_range_config *rc = cfg_allocz(sizeof(struct mpls_range_config));
if (s)
cf_define_symbol(new_config, s, SYM_MPLS_RANGE, mpls_range, rc);
rc->domain = mc;
rc->name = s ? s->name : NULL;
rc->start = (uint) -1;
rc->length = (uint) -1;
add_tail(&mc->ranges, &rc->n);
return rc;
}
static struct mpls_range *
mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf)
{
struct mpls_range *r = mb_allocz(m->pool, sizeof(struct mpls_range));
r->cf = cf;
r->name = cf->name;
r->lo = cf->start;
r->hi = cf->start + cf->length;
add_tail(&m->ranges, &r->n);
cf->range = r;
return r;
}
static struct mpls_range *
mpls_find_range_(list *l, const char *name)
{
struct mpls_range *r;
WALK_LIST(r, *l)
if (!strcmp(r->name, name))
return r;
return NULL;
}
static int
mpls_reconfigure_range(struct mpls_domain *m UNUSED, struct mpls_range *r, struct mpls_range_config *cf)
{
if ((cf->start > r->lo) || (cf->start + cf->length < r->hi))
return 0;
cf->range = r;
r->cf->range = NULL;
r->cf = cf;
r->name = cf->name;
r->lo = cf->start;
r->hi = cf->start + cf->length;
return 1;
}
static void
mpls_free_range(struct mpls_range *r)
{
ASSERT(r->use_count == 0);
ASSERT(r->label_count == 0);
r->cf->range = NULL;
rem_node(&r->n);
mb_free(r);
}
static void
mpls_remove_range(struct mpls_range *r)
{
r->removed = 1;
if (!r->use_count)
mpls_free_range(r);
}
void
mpls_lock_range(struct mpls_range *r)
{
r->use_count++;
}
void
mpls_unlock_range(struct mpls_range *r)
{
ASSERT(r->use_count > 0);
r->use_count--;
if (!r->use_count && r->removed)
mpls_free_range(r);
}
/*
* MPLS handle
*/
struct mpls_handle *
mpls_new_handle(struct mpls_domain *m, struct mpls_range *r)
{
struct mpls_handle *h = mb_allocz(m->pool, sizeof(struct mpls_handle));
h->range = r;
mpls_lock_range(h->range);
add_tail(&m->handles, &h->n);
return h;
}
void
mpls_free_handle(struct mpls_domain *m UNUSED, struct mpls_handle *h)
{
ASSERT(h->label_count == 0);
mpls_unlock_range(h->range);
rem_node(&h->n);
mb_free(h);
}
/*
* MPLS label
*/
uint
mpls_new_label(struct mpls_domain *m, struct mpls_handle *h)
{
struct mpls_range *r = h->range;
uint n = lmap_first_zero_in_range(&m->labels, r->lo, r->hi);
if (n >= r->hi)
return 0;
m->label_count++;
r->label_count++;
h->label_count++;
lmap_set(&m->labels, n);
return n;
}
void
mpls_free_label(struct mpls_domain *m, struct mpls_handle *h, uint n)
{
struct mpls_range *r = h->range;
ASSERT(lmap_test(&m->labels, n));
lmap_clear(&m->labels, n);
ASSERT(m->label_count);
m->label_count--;
ASSERT(r->label_count);
r->label_count--;
ASSERT(h->label_count);
h->label_count--;
}
/*
* MPLS channel
*/
static void
mpls_channel_init(struct channel *C, struct channel_config *CC)
{
struct mpls_channel *c = (void *) C;
struct mpls_channel_config *cc = (void *) CC;
c->domain = cc->domain->domain;
c->range = cc->range->range;
c->label_policy = cc->label_policy;
}
static int
mpls_channel_start(struct channel *C)
{
struct mpls_channel *c = (void *) C;
mpls_lock_domain(c->domain);
mpls_lock_range(c->range);
return 0;
}
/*
static void
mpls_channel_shutdown(struct channel *C)
{
struct mpls_channel *c = (void *) C;
}
*/
static void
mpls_channel_cleanup(struct channel *C)
{
struct mpls_channel *c = (void *) C;
mpls_unlock_range(c->range);
mpls_unlock_domain(c->domain);
}
static int
mpls_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed UNUSED, int *export_changed UNUSED)
{
struct mpls_channel *c = (void *) C;
struct mpls_channel_config *new = (void *) CC;
if ((new->domain->domain != c->domain) ||
(new->range->range != c->range) ||
(new->label_policy != c->label_policy))
return 0;
return 1;
}
void
mpls_channel_postconfig(struct channel_config *CC)
{
struct mpls_channel_config *cc = (void *) CC;
if (!cc->domain)
cf_error("MPLS domain not specified");
if (!cc->range)
cc->range = (cc->label_policy == MPLS_POLICY_STATIC) ?
cc->domain->static_range : cc->domain->dynamic_range;
if (cc->range->domain != cc->domain)
cf_error("MPLS label range from different MPLS domain");
if (!cc->c.table)
cf_error("Routing table not specified");
}
struct channel_class channel_mpls = {
.channel_size = sizeof(struct mpls_channel),
.config_size = sizeof(struct mpls_channel_config),
.init = mpls_channel_init,
.start = mpls_channel_start,
// .shutdown = mpls_channel_shutdown,
.cleanup = mpls_channel_cleanup,
.reconfigure = mpls_channel_reconfigure,
};
/*
* MPLS FEC map
*/
#define NET_KEY(fec) fec->net, fec->path_id, fec->hash
#define NET_NEXT(fec) fec->next_k
#define NET_EQ(n1,i1,h1,n2,i2,h2) h1 == h2 && i1 == i2 && net_equal(n1, n2)
#define NET_FN(n,i,h) h
#define NET_REHASH mpls_net_rehash
#define NET_PARAMS /8, *2, 2, 2, 8, 24
#define RTA_KEY(fec) fec->rta, fec->class_id, fec->hash
#define RTA_NEXT(fec) fec->next_k
#define RTA_EQ(r1,i1,h1,r2,i2,h2) h1 == h2 && r1 == r2 && i1 == i2
#define RTA_FN(r,i,h) h
#define RTA_REHASH mpls_rta_rehash
#define RTA_PARAMS /8, *2, 2, 2, 8, 24
#define LABEL_KEY(fec) fec->label
#define LABEL_NEXT(fec) fec->next_l
#define LABEL_EQ(l1,l2) l1 == l2
#define LABEL_FN(l) u32_hash(l)
#define LABEL_REHASH mpls_label_rehash
#define LABEL_PARAMS /8, *2, 2, 2, 8, 24
HASH_DEFINE_REHASH_FN(NET, struct mpls_fec)
HASH_DEFINE_REHASH_FN(RTA, struct mpls_fec)
HASH_DEFINE_REHASH_FN(LABEL, struct mpls_fec)
static void mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec);
static rta * mpls_get_key_rta(struct mpls_fec_map *m, const rta *src);
struct mpls_fec_map *
mpls_fec_map_new(pool *pp, struct channel *C, uint rts)
{
struct pool *p = rp_new(pp, "MPLS FEC map");
struct mpls_fec_map *m = mb_allocz(p, sizeof(struct mpls_fec_map));
struct mpls_channel *c = (void *) C;
m->pool = p;
m->channel = C;
m->domain = c->domain;
mpls_lock_domain(m->domain);
m->handle = mpls_new_handle(c->domain, c->range);
/* net_hash and rta_hash are initialized on-demand */
HASH_INIT(m->label_hash, m->pool, 4);
m->mpls_rts = rts;
m->mpls_scope = SCOPE_UNIVERSE;
return m;
}
void
mpls_fec_map_free(struct mpls_fec_map *m)
{
/* Free stored rtas */
if (m->rta_hash.data)
{
HASH_WALK(m->rta_hash, next_k, fec)
{
rta_free(fec->rta);
fec->rta = NULL;
}
HASH_WALK_END;
}
/* Free allocated labels */
HASH_WALK(m->label_hash, next_l, fec)
{
if (fec->policy != MPLS_POLICY_STATIC)
mpls_free_label(m->domain, m->handle, fec->label);
}
HASH_WALK_END;
mpls_free_handle(m->domain, m->handle);
mpls_unlock_domain(m->domain);
rfree(m->pool);
}
static slab *
mpls_slab(struct mpls_fec_map *m, uint type)
{
ASSERT(type <= NET_VPN6);
int pos = type ? (type - 1) : 0;
if (!m->slabs[pos])
m->slabs[pos] = sl_new(m->pool, sizeof(struct mpls_fec) + net_addr_length[pos + 1]);
return m->slabs[pos];
}
struct mpls_fec *
mpls_find_fec_by_label(struct mpls_fec_map *m, u32 label)
{
return HASH_FIND(m->label_hash, LABEL, label);
}
struct mpls_fec *
mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label)
{
struct mpls_fec *fec = HASH_FIND(m->label_hash, LABEL, label);
if (fec)
return fec;
fec = sl_allocz(mpls_slab(m, 0));
fec->label = label;
fec->policy = MPLS_POLICY_STATIC;
DBG("New FEC lab %u\n", fec->label);
HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
return fec;
}
struct mpls_fec *
mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id)
{
if (!m->net_hash.data)
HASH_INIT(m->net_hash, m->pool, 4);
u32 hash = net_hash(net) ^ u32_hash(path_id);
struct mpls_fec *fec = HASH_FIND(m->net_hash, NET, net, path_id, hash);
if (fec)
return fec;
fec = sl_allocz(mpls_slab(m, net->type));
fec->hash = hash;
fec->path_id = path_id;
net_copy(fec->net, net);
fec->label = mpls_new_label(m->domain, m->handle);
fec->policy = MPLS_POLICY_PREFIX;
DBG("New FEC net %u\n", fec->label);
HASH_INSERT2(m->net_hash, NET, m->pool, fec);
HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
return fec;
}
struct mpls_fec *
mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id)
{
if (!m->rta_hash.data)
HASH_INIT(m->rta_hash, m->pool, 4);
rta *rta = mpls_get_key_rta(m, src);
u32 hash = rta->hash_key ^ u32_hash(class_id);
struct mpls_fec *fec = HASH_FIND(m->rta_hash, RTA, rta, class_id, hash);
if (fec)
{
rta_free(rta);
return fec;
}
fec = sl_allocz(mpls_slab(m, 0));
fec->hash = hash;
fec->class_id = class_id;
fec->rta = rta;
fec->label = mpls_new_label(m->domain, m->handle);
fec->policy = MPLS_POLICY_AGGREGATE;
DBG("New FEC rta %u\n", fec->label);
HASH_INSERT2(m->rta_hash, RTA, m->pool, fec);
HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
return fec;
}
void
mpls_free_fec(struct mpls_fec_map *m, struct mpls_fec *fec)
{
if (fec->state != MPLS_FEC_DOWN)
mpls_withdraw_fec(m, fec);
DBG("Free FEC %u\n", fec->label);
mpls_free_label(m->domain, m->handle, fec->label);
HASH_REMOVE2(m->label_hash, LABEL, m->pool, fec);
switch (fec->policy)
{
case MPLS_POLICY_STATIC:
break;
case MPLS_POLICY_PREFIX:
HASH_REMOVE2(m->net_hash, NET, m->pool, fec);
break;
case MPLS_POLICY_AGGREGATE:
rta_free(fec->rta);
HASH_REMOVE2(m->rta_hash, RTA, m->pool, fec);
break;
default:
bug("Unknown fec type");
}
sl_free(fec);
}
static inline void mpls_lock_fec(struct mpls_fec_map *x UNUSED, struct mpls_fec *fec)
{ if (fec) fec->uc++; }
static inline void mpls_unlock_fec(struct mpls_fec_map *x, struct mpls_fec *fec)
{ if (fec && !--fec->uc) mpls_free_fec(x, fec); }
static inline void
mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *fec)
{
if (fec->state == MPLS_FEC_CLEAN)
fec->state = MPLS_FEC_DIRTY;
}
static rta *
mpls_get_key_rta(struct mpls_fec_map *m, const rta *src)
{
rta *a = allocz(RTA_MAX_SIZE);
a->source = m->mpls_rts;
a->scope = m->mpls_scope;
if (!src->hostentry)
{
/* Just copy the nexthop */
a->dest = src->dest;
nexthop_link(a, &src->nh);
}
else
{
/* Keep the hostentry */
a->hostentry = src->hostentry;
/* Keep the original labelstack */
const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig];
a->nh.labels = a->nh.labels_orig = src->nh.labels_orig;
memcpy(a->nh.label, labels, src->nh.labels_orig * sizeof(u32));
}
return rta_lookup(a);
}
static void
mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src)
{
rta *a = allocz(RTA_MAX_SIZE);
a->source = m->mpls_rts;
a->scope = m->mpls_scope;
if (!src->hostentry)
{
/* Just copy the nexthop */
a->dest = src->dest;
nexthop_link(a, &src->nh);
}
else
{
const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig];
mpls_label_stack ms;
/* Apply the hostentry with the original labelstack */
ms.len = src->nh.labels_orig;
memcpy(ms.stack, labels, src->nh.labels_orig * sizeof(u32));
rta_apply_hostentry(a, src->hostentry, &ms);
}
net_addr_mpls n = NET_ADDR_MPLS(fec->label);
rte *e = rte_get_temp(rta_lookup(a), m->channel->proto->main_source);
e->pflags = 0;
fec->state = MPLS_FEC_CLEAN;
rte_update2(m->channel, (net_addr *) &n, e, m->channel->proto->main_source);
}
static void
mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec)
{
net_addr_mpls n = NET_ADDR_MPLS(fec->label);
fec->state = MPLS_FEC_DOWN;
rte_update2(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source);
}
static void
mpls_apply_fec(rte *r, struct mpls_fec *fec, linpool *lp)
{
struct ea_list *ea = lp_allocz(lp, sizeof(struct ea_list) + 2 * sizeof(eattr));
rta *old_attrs = r->attrs;
if (rta_is_cached(old_attrs))
r->attrs = rta_do_cow(r->attrs, lp);
*ea = (struct ea_list) {
.next = r->attrs->eattrs,
.flags = EALF_SORTED,
.count = 2,
};
ea->attrs[0] = (struct eattr) {
.id = EA_MPLS_LABEL,
.type = EAF_TYPE_INT,
.u.data = fec->label,
};
ea->attrs[1] = (struct eattr) {
.id = EA_MPLS_POLICY,
.type = EAF_TYPE_INT,
.u.data = fec->policy,
};
r->attrs->eattrs = ea;
if (rta_is_cached(old_attrs))
{
r->attrs = rta_lookup(r->attrs);
rta_free(old_attrs);
}
}
void
mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec)
{
ASSERT(!(r->flags & REF_COW));
struct mpls_fec *fec = NULL;
/* Select FEC for route */
uint policy = ea_get_int(r->attrs->eattrs, EA_MPLS_POLICY, 0);
switch (policy)
{
case MPLS_POLICY_NONE:
return;
case MPLS_POLICY_STATIC:;
uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
if (label < 16)
return;
fec = mpls_get_fec_by_label(m, label);
mpls_damage_fec(m, fec);
break;
case MPLS_POLICY_PREFIX:
fec = mpls_get_fec_by_net(m, n, r->src->private_id);
mpls_damage_fec(m, fec);
break;
case MPLS_POLICY_AGGREGATE:;
uint class = ea_get_int(r->attrs->eattrs, EA_MPLS_CLASS, 0);
fec = mpls_get_fec_by_rta(m, r->attrs, class);
break;
default:
log(L_WARN "Route %N has invalid MPLS policy %u", n, policy);
return;
}
/* Temporarily lock FEC */
mpls_lock_fec(m, fec);
*locked_fec = fec;
/* Apply FEC label to route */
mpls_apply_fec(r, fec, lp);
/* Announce MPLS rule for new/updated FEC */
if (fec->state != MPLS_FEC_CLEAN)
mpls_announce_fec(m, fec, r->attrs);
}
void
mpls_handle_rte_cleanup(struct mpls_fec_map *m, struct mpls_fec **locked_fec)
{
/* Unlock temporarily locked FEC from mpls_handle_rte() */
if (*locked_fec)
{
mpls_unlock_fec(m, *locked_fec);
*locked_fec = NULL;
}
}
void
mpls_rte_insert(net *n UNUSED, rte *r)
{
struct proto *p = r->src->proto;
struct mpls_fec_map *m = p->mpls_map;
uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
if (label < 16)
return;
struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
if (!fec)
return;
mpls_lock_fec(m, fec);
}
void
mpls_rte_remove(net *n UNUSED, rte *r)
{
struct proto *p = r->src->proto;
struct mpls_fec_map *m = p->mpls_map;
uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
if (label < 16)
return;
struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
if (!fec)
return;
mpls_unlock_fec(m, fec);
}