mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Merge branch 'mq-aggregator-for-v3' into thread-next
This commit is contained in:
commit
30712a2bdf
@ -934,6 +934,10 @@ cf_symbol_class_name(struct symbol *sym)
|
||||
return "routing table";
|
||||
case SYM_ATTRIBUTE:
|
||||
return "custom attribute";
|
||||
case SYM_MPLS_DOMAIN:
|
||||
return "MPLS domain";
|
||||
case SYM_MPLS_RANGE:
|
||||
return "MPLS label range";
|
||||
case SYM_KEYWORD:
|
||||
return "symbol";
|
||||
case SYM_CONSTANT_RANGE:
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/mpls.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/event.h"
|
||||
@ -141,6 +142,7 @@ config_parse(struct config *c)
|
||||
cf_lex_init(0, c);
|
||||
sysdep_preconfig(c);
|
||||
protos_preconfig(c);
|
||||
mpls_preconfig(c);
|
||||
rt_preconfig(c);
|
||||
cf_parse();
|
||||
rt_postconfig(c);
|
||||
@ -300,6 +302,7 @@ config_do_commit(struct config *c, int type)
|
||||
int force_restart = sysdep_commit(c, old_config);
|
||||
DBG("global_commit\n");
|
||||
force_restart |= global_commit(c, old_config);
|
||||
mpls_commit(c, old_config);
|
||||
DBG("rt_commit\n");
|
||||
rt_commit(c, old_config);
|
||||
DBG("protos_commit\n");
|
||||
@ -548,6 +551,7 @@ order_shutdown(int gr)
|
||||
memcpy(c, config, sizeof(struct config));
|
||||
init_list(&c->protos);
|
||||
init_list(&c->tables);
|
||||
init_list(&c->mpls_domains);
|
||||
init_list(&c->symbols);
|
||||
memset(c->def_tables, 0, sizeof(c->def_tables));
|
||||
c->shutdown = 1;
|
||||
|
@ -21,6 +21,7 @@ struct config {
|
||||
linpool *mem; /* Linear pool containing configuration data */
|
||||
list protos; /* Configured protocol instances (struct proto_config) */
|
||||
list tables; /* Configured routing tables (struct rtable_config) */
|
||||
list mpls_domains; /* Configured MPLS domains (struct mpls_domain_config) */
|
||||
list logfiles; /* Configured log files (sysdep) */
|
||||
list tests; /* Configured unit tests (f_bt_test_suite) */
|
||||
list symbols; /* Configured symbols in config order */
|
||||
@ -133,6 +134,8 @@ struct symbol {
|
||||
const struct filter *filter; /* For SYM_FILTER */
|
||||
struct rtable_config *table; /* For SYM_TABLE */
|
||||
struct ea_class *attribute; /* For SYM_ATTRIBUTE */
|
||||
struct mpls_domain_config *mpls_domain; /* For SYM_MPLS_DOMAIN */
|
||||
struct mpls_range_config *mpls_range; /* For SYM_MPLS_RANGE */
|
||||
struct f_val *val; /* For SYM_CONSTANT */
|
||||
uint offset; /* For SYM_VARIABLE */
|
||||
const struct keyword *keyword; /* For SYM_KEYWORD */
|
||||
@ -173,6 +176,8 @@ extern linpool *global_root_scope_linpool;
|
||||
#define SYM_ATTRIBUTE 6
|
||||
#define SYM_KEYWORD 7
|
||||
#define SYM_METHOD 8
|
||||
#define SYM_MPLS_DOMAIN 9
|
||||
#define SYM_MPLS_RANGE 10
|
||||
|
||||
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
||||
#define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff)
|
||||
|
@ -44,6 +44,8 @@ static inline void cf_assert_symbol(const struct symbol *sym, uint class) {
|
||||
case SYM_FILTER: cf_assert(sym->class == SYM_FILTER, "Filter name required"); break;
|
||||
case SYM_TABLE: cf_assert(sym->class == SYM_TABLE, "Table name required"); break;
|
||||
case SYM_ATTRIBUTE: cf_assert(sym->class == SYM_ATTRIBUTE, "Custom attribute name required"); break;
|
||||
case SYM_MPLS_DOMAIN: cf_assert(sym->class == SYM_MPLS_DOMAIN, "MPLS domain name required"); break;
|
||||
case SYM_MPLS_RANGE: cf_assert(sym->class == SYM_MPLS_RANGE, "MPLS range name required"); break;
|
||||
case SYM_VARIABLE: cf_assert((sym->class & ~0xff) == SYM_VARIABLE, "Variable name required"); break;
|
||||
case SYM_CONSTANT: cf_assert((sym->class & ~0xff) == SYM_CONSTANT, "Constant name required"); break;
|
||||
default: bug("This shall not happen");
|
||||
|
8
lib/ip.h
8
lib/ip.h
@ -385,7 +385,13 @@ static inline ip6_addr ip6_hton(ip6_addr a)
|
||||
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 16
|
||||
#define MPLS_MAX_LABEL 0x100000
|
||||
|
||||
#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)
|
||||
|
@ -452,6 +452,12 @@ u32 rt_get_igp_metric(const rte *rt);
|
||||
/* From: Advertising router */
|
||||
extern struct ea_class ea_gen_from;
|
||||
|
||||
|
||||
/* MPLS Label, Policy and Class */
|
||||
extern struct ea_class ea_gen_mpls_label,
|
||||
ea_gen_mpls_policy, ea_gen_mpls_class;
|
||||
|
||||
|
||||
/* Source: An old method to devise the route source protocol and kind.
|
||||
* To be superseded in a near future by something more informative. */
|
||||
extern struct ea_class ea_gen_source;
|
||||
|
@ -91,6 +91,7 @@ enum btype {
|
||||
|
||||
T_ENUM_RTS = 0x31,
|
||||
T_ENUM_SCOPE = 0x33,
|
||||
T_ENUM_MPLS_POLICY = 0x35,
|
||||
T_ENUM_RTD = 0x37,
|
||||
T_ENUM_ROA = 0x39,
|
||||
T_ENUM_NETTYPE = 0x3b,
|
||||
|
1
nest/Doc
1
nest/Doc
@ -6,6 +6,7 @@ D proto.sgml
|
||||
S proto.c
|
||||
S proto-hooks.c
|
||||
S iface.c
|
||||
S mpls.c
|
||||
S neighbor.c
|
||||
S cli.c
|
||||
S locks.c
|
||||
|
@ -1,7 +1,8 @@
|
||||
src := cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
|
||||
src := cli.c cmds.c iface.c locks.c mpls.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
$(conf-y-targets): $(s)mpls.Y
|
||||
|
||||
$(o)proto-build.c: Makefile $(lastword $(MAKEFILE_LIST)) $(objdir)/.dir-stamp
|
||||
$(E)echo GEN $@
|
||||
|
@ -12,6 +12,7 @@ CF_HDR
|
||||
#include "nest/rt-dev.h"
|
||||
#include "nest/password.h"
|
||||
#include "nest/cmds.h"
|
||||
#include "nest/mpls.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/mac.h"
|
||||
|
||||
@ -164,6 +165,7 @@ CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
|
||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, ROA, ROUTE, REFRESH, SETTLE, TIME, GC, THRESHOLD, PERIOD)
|
||||
CF_KEYWORDS(MPLS_LABEL, MPLS_POLICY, MPLS_CLASS)
|
||||
|
||||
/* For r_args_channel */
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
@ -174,6 +176,7 @@ CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINE
|
||||
CF_ENUM(T_ENUM_RTD, RTD_, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
||||
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||
CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE)
|
||||
|
||||
%type <i32> idval
|
||||
%type <f> imexport
|
||||
@ -181,7 +184,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||
%type <s> optproto
|
||||
%type <ra> r_args
|
||||
%type <sd> sym_args
|
||||
%type <i> proto_start debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm
|
||||
%type <i> proto_start debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type net_type_base tos password_algorithm
|
||||
%type <ps> proto_patt proto_patt2
|
||||
%type <cc> channel_start proto_channel
|
||||
%type <cl> limit_spec
|
||||
@ -227,7 +230,7 @@ gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
|
||||
|
||||
/* Network types (for tables, channels) */
|
||||
|
||||
net_type:
|
||||
net_type_base:
|
||||
IPV4 { $$ = NET_IP4; }
|
||||
| IPV6 { $$ = NET_IP6; }
|
||||
| IPV6 SADR { $$ = NET_IP6_SADR; }
|
||||
@ -237,6 +240,10 @@ net_type:
|
||||
| ROA6 { $$ = NET_ROA6; }
|
||||
| FLOW4{ $$ = NET_FLOW4; }
|
||||
| FLOW6{ $$ = NET_FLOW6; }
|
||||
;
|
||||
|
||||
net_type:
|
||||
net_type_base
|
||||
| MPLS { $$ = NET_MPLS; }
|
||||
;
|
||||
|
||||
@ -340,7 +347,7 @@ proto_item:
|
||||
;
|
||||
|
||||
|
||||
channel_start: net_type
|
||||
channel_start: net_type_base
|
||||
{
|
||||
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
|
||||
};
|
||||
|
141
nest/mpls.Y
Normal file
141
nest/mpls.Y
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
|
||||
#include "nest/mpls.h"
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
static struct mpls_domain_config *this_mpls_domain;
|
||||
static struct mpls_range_config *this_mpls_range;
|
||||
|
||||
#define MPLS_CC ((struct mpls_channel_config *) this_channel)
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(MPLS, DOMAIN, LABEL, RANGE, STATIC, DYNAMIC, START, LENGTH, POLICY, PREFIX, AGGREGATE)
|
||||
|
||||
%type <i> mpls_label_policy
|
||||
%type <cc> mpls_channel_start mpls_channel
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
conf: mpls_domain;
|
||||
|
||||
mpls_domain: mpls_domain_start mpls_domain_opt_list mpls_domain_end;
|
||||
|
||||
mpls_domain_start: MPLS DOMAIN symbol { this_mpls_domain = mpls_domain_config_new($3); };
|
||||
|
||||
mpls_domain_opt:
|
||||
mpls_range
|
||||
;
|
||||
|
||||
mpls_domain_opts:
|
||||
/* empty */
|
||||
| mpls_domain_opts mpls_domain_opt ';'
|
||||
;
|
||||
|
||||
mpls_domain_opt_list:
|
||||
/* empty */
|
||||
| '{' mpls_domain_opts '}'
|
||||
;
|
||||
|
||||
mpls_domain_end: { mpls_domain_postconfig(this_mpls_domain); this_mpls_domain = NULL; };
|
||||
|
||||
|
||||
mpls_range: mpls_range_start mpls_range_opt_list mpls_range_end;
|
||||
|
||||
mpls_range_start: LABEL RANGE symbol
|
||||
{
|
||||
if (($3->class == SYM_KEYWORD) && ($3->keyword->value == STATIC))
|
||||
this_mpls_range = this_mpls_domain->static_range;
|
||||
else if (($3->class == SYM_KEYWORD) && ($3->keyword->value == DYNAMIC))
|
||||
this_mpls_range = this_mpls_domain->dynamic_range;
|
||||
else
|
||||
this_mpls_range = mpls_range_config_new(this_mpls_domain, $3);
|
||||
};
|
||||
|
||||
mpls_range_opt:
|
||||
START expr { this_mpls_range->start = $2; if ($2 >= MPLS_MAX_LABEL) cf_error("MPLS label range start must be less than 2^20"); }
|
||||
| LENGTH expr { this_mpls_range->length = $2; if ($2 >= MPLS_MAX_LABEL) cf_error("MPLS label range length must be less than 2^20"); if (!$2) cf_error("MPLS label range length must be nonzero"); }
|
||||
;
|
||||
|
||||
mpls_range_opts:
|
||||
/* empty */
|
||||
| mpls_range_opts mpls_range_opt ';'
|
||||
;
|
||||
|
||||
mpls_range_opt_list:
|
||||
/* empty */
|
||||
| '{' mpls_range_opts '}'
|
||||
;
|
||||
|
||||
mpls_range_end:
|
||||
{
|
||||
struct mpls_range_config *r = this_mpls_range;
|
||||
|
||||
if ((r->start == (uint) -1) || (r->length == (uint) -1))
|
||||
cf_error("MPLS label range start and length must be specified");
|
||||
|
||||
if (r->start + r->length > MPLS_MAX_LABEL)
|
||||
cf_error("MPLS label range end must be less than 2^20");
|
||||
|
||||
this_mpls_range = NULL;
|
||||
};
|
||||
|
||||
|
||||
mpls_channel: mpls_channel_start mpls_channel_opt_list mpls_channel_end;
|
||||
|
||||
mpls_channel_start: MPLS
|
||||
{
|
||||
$$ = this_channel = channel_config_get(&channel_mpls, net_label[NET_MPLS], NET_MPLS, this_proto);
|
||||
|
||||
if (EMPTY_LIST(new_config->mpls_domains))
|
||||
cf_error("No MPLS domain defined");
|
||||
|
||||
/* Default values for new channel */
|
||||
if (!MPLS_CC->domain)
|
||||
{
|
||||
MPLS_CC->domain = cf_default_mpls_domain(new_config);
|
||||
MPLS_CC->label_policy = MPLS_POLICY_PREFIX;
|
||||
}
|
||||
};
|
||||
|
||||
mpls_label_policy:
|
||||
STATIC { $$ = MPLS_POLICY_STATIC; }
|
||||
| PREFIX { $$ = MPLS_POLICY_PREFIX; }
|
||||
| AGGREGATE { $$ = MPLS_POLICY_AGGREGATE; }
|
||||
;
|
||||
|
||||
mpls_channel_opt:
|
||||
channel_item
|
||||
| DOMAIN CF_SYM_KNOWN { cf_assert_symbol($2, SYM_MPLS_DOMAIN); MPLS_CC->domain = $2->mpls_domain; }
|
||||
| LABEL RANGE CF_SYM_KNOWN { cf_assert_symbol($3, SYM_MPLS_RANGE); MPLS_CC->range = $3->mpls_range; }
|
||||
| LABEL RANGE STATIC { MPLS_CC->range = MPLS_CC->domain->static_range; }
|
||||
| LABEL RANGE DYNAMIC { MPLS_CC->range = MPLS_CC->domain->dynamic_range; }
|
||||
| LABEL POLICY mpls_label_policy { MPLS_CC->label_policy = $3; }
|
||||
;
|
||||
|
||||
mpls_channel_opts:
|
||||
/* empty */
|
||||
| mpls_channel_opts mpls_channel_opt ';'
|
||||
;
|
||||
|
||||
mpls_channel_opt_list:
|
||||
/* empty */
|
||||
| '{' mpls_channel_opts '}'
|
||||
;
|
||||
|
||||
mpls_channel_end: { mpls_channel_postconfig(this_channel); } channel_end;
|
||||
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
995
nest/mpls.c
Normal file
995
nest/mpls.c
Normal file
@ -0,0 +1,995 @@
|
||||
/*
|
||||
* 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, the_bird_domain.the_bird, "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
|
||||
#define RTA_NEXT(fec) fec->next_k
|
||||
#define RTA_EQ(r1,r2) r1 == r2
|
||||
#define RTA_FN(r) r->hash_key
|
||||
|
||||
#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 struct ea_storage * mpls_get_key_attrs(struct mpls_fec_map *m, ea_list *src);
|
||||
|
||||
struct mpls_fec_map *
|
||||
mpls_fec_map_new(pool *pp, struct channel *C, uint rts)
|
||||
{
|
||||
struct pool *p = rp_new(pp, the_bird_domain.the_bird, "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;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void
|
||||
mpls_fec_map_free(struct mpls_fec_map *m)
|
||||
{
|
||||
/* Free stored rtas */
|
||||
if (m->attrs_hash.data)
|
||||
{
|
||||
HASH_WALK(m->attrs_hash, next_k, fec)
|
||||
{
|
||||
ea_free(fec->rta->l);
|
||||
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_destination(struct mpls_fec_map *m, ea_list *dest)
|
||||
{
|
||||
if (!m->attrs_hash.data)
|
||||
HASH_INIT(m->attrs_hash, m->pool, 4);
|
||||
|
||||
struct ea_storage *rta = mpls_get_key_attrs(m, dest);
|
||||
u32 hash = rta->hash_key;
|
||||
struct mpls_fec *fec = HASH_FIND(m->attrs_hash, RTA, rta);
|
||||
|
||||
if (fec)
|
||||
{
|
||||
ea_free(rta->l);
|
||||
return fec;
|
||||
}
|
||||
|
||||
fec = sl_allocz(mpls_slab(m, 0));
|
||||
|
||||
fec->hash = hash;
|
||||
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->attrs_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:
|
||||
ea_free(fec->rta->l);
|
||||
HASH_REMOVE2(m->attrs_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); }
|
||||
|
||||
struct mpls_fec_tmp_lock {
|
||||
resource r;
|
||||
struct mpls_fec_map *m;
|
||||
struct mpls_fec *fec;
|
||||
};
|
||||
|
||||
static void
|
||||
mpls_fec_tmp_lock_free(resource *r)
|
||||
{
|
||||
struct mpls_fec_tmp_lock *l = SKIP_BACK(struct mpls_fec_tmp_lock, r, r);
|
||||
mpls_unlock_fec(l->m, l->fec);
|
||||
}
|
||||
|
||||
static void
|
||||
mpls_fec_tmp_lock_dump(resource *r, unsigned indent UNUSED)
|
||||
{
|
||||
struct mpls_fec_tmp_lock *l = SKIP_BACK(struct mpls_fec_tmp_lock, r, r);
|
||||
debug("map=%p fec=%p label=%u", l->m, l->fec, l->fec->label);
|
||||
}
|
||||
|
||||
static struct resclass mpls_fec_tmp_lock_class = {
|
||||
.name = "Temporary MPLS FEC Lock",
|
||||
.size = sizeof(struct mpls_fec_tmp_lock),
|
||||
.free = mpls_fec_tmp_lock_free,
|
||||
.dump = mpls_fec_tmp_lock_dump,
|
||||
};
|
||||
|
||||
static void
|
||||
mpls_lock_fec_tmp(struct mpls_fec_map *m, struct mpls_fec *fec)
|
||||
{
|
||||
if (!fec)
|
||||
return;
|
||||
|
||||
fec->uc++;
|
||||
|
||||
struct mpls_fec_tmp_lock *l = ralloc(tmp_res.pool, &mpls_fec_tmp_lock_class);
|
||||
l->m = m;
|
||||
l->fec = 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 struct ea_storage *
|
||||
mpls_get_key_attrs(struct mpls_fec_map *m, ea_list *src)
|
||||
{
|
||||
EA_LOCAL_LIST(4) ea = {
|
||||
.l.flags = EALF_SORTED,
|
||||
};
|
||||
|
||||
uint last_id = 0;
|
||||
#define PUT_ATTR(cls) do { \
|
||||
ASSERT_DIE(last_id < (cls)->id); \
|
||||
last_id = (cls)->id; \
|
||||
eattr *a = ea_find_by_class(src, (cls)); \
|
||||
if (a) ea.a[ea.l.count++] = *a; \
|
||||
} while (0)
|
||||
|
||||
PUT_ATTR(&ea_gen_nexthop);
|
||||
PUT_ATTR(&ea_gen_hostentry);
|
||||
ea.a[ea.l.count++] = EA_LITERAL_EMBEDDED(&ea_gen_source, 0, m->mpls_rts);
|
||||
PUT_ATTR(&ea_gen_mpls_class);
|
||||
|
||||
return ea_get_storage(ea_lookup(&ea.l, 0));
|
||||
}
|
||||
|
||||
static void
|
||||
mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, ea_list *src)
|
||||
{
|
||||
/* Check existence of hostentry */
|
||||
const struct eattr *heea = ea_find_by_class(src, &ea_gen_hostentry);
|
||||
if (heea) {
|
||||
/* The same hostentry, but different dependent table */
|
||||
struct hostentry_adata *head = SKIP_BACK(struct hostentry_adata, ad, heea->u.ad);
|
||||
struct hostentry *he = head->he;
|
||||
ea_set_hostentry(&src, m->channel->table, he->owner, he->addr, he->link,
|
||||
HOSTENTRY_LABEL_COUNT(head), head->labels);
|
||||
}
|
||||
|
||||
net_addr_mpls n = NET_ADDR_MPLS(fec->label);
|
||||
|
||||
rte e = {
|
||||
.src = m->channel->proto->main_source,
|
||||
.attrs = src,
|
||||
};
|
||||
|
||||
fec->state = MPLS_FEC_CLEAN;
|
||||
rte_update(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_update(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source);
|
||||
}
|
||||
|
||||
static void
|
||||
mpls_apply_fec(rte *r, struct mpls_fec *fec)
|
||||
{
|
||||
ea_set_attr_u32(&r->attrs, &ea_gen_mpls_label, 0, fec->label);
|
||||
ea_set_attr_u32(&r->attrs, &ea_gen_mpls_policy, 0, fec->policy);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r)
|
||||
{
|
||||
struct mpls_fec *fec = NULL;
|
||||
|
||||
/* Select FEC for route */
|
||||
uint policy = ea_get_int(r->attrs, &ea_gen_mpls_policy, 0);
|
||||
switch (policy)
|
||||
{
|
||||
case MPLS_POLICY_NONE:
|
||||
return;
|
||||
|
||||
case MPLS_POLICY_STATIC:;
|
||||
uint label = ea_get_int(r->attrs, &ea_gen_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:
|
||||
fec = mpls_get_fec_by_destination(m, r->attrs);
|
||||
break;
|
||||
|
||||
default:
|
||||
log(L_WARN "Route %N has invalid MPLS policy %u", n, policy);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Temporarily lock FEC */
|
||||
mpls_lock_fec_tmp(m, fec);
|
||||
|
||||
/* Apply FEC label to route */
|
||||
mpls_apply_fec(r, fec);
|
||||
|
||||
/* Announce MPLS rule for new/updated FEC */
|
||||
if (fec->state != MPLS_FEC_CLEAN)
|
||||
mpls_announce_fec(m, fec, r->attrs);
|
||||
}
|
||||
|
||||
static inline struct mpls_fec_tmp_lock
|
||||
mpls_rte_get_fec_lock(const rte *r)
|
||||
{
|
||||
struct mpls_fec_tmp_lock mt = {
|
||||
.m = SKIP_BACK(struct proto, sources, r->src->owner)->mpls_map,
|
||||
};
|
||||
|
||||
if (!mt.m)
|
||||
return mt;
|
||||
|
||||
uint label = ea_get_int(r->attrs, &ea_gen_mpls_label, 0);
|
||||
if (label < 16)
|
||||
return mt;
|
||||
|
||||
mt.fec = mpls_find_fec_by_label(mt.m, label);
|
||||
return mt;
|
||||
}
|
||||
|
||||
void
|
||||
mpls_rte_preimport(rte *new, const rte *old)
|
||||
{
|
||||
struct mpls_fec_tmp_lock new_mt = {}, old_mt = {};
|
||||
|
||||
if (new)
|
||||
new_mt = mpls_rte_get_fec_lock(new);
|
||||
|
||||
if (old)
|
||||
old_mt = mpls_rte_get_fec_lock(old);
|
||||
|
||||
if (new_mt.fec == old_mt.fec)
|
||||
return;
|
||||
|
||||
if (new_mt.fec)
|
||||
mpls_lock_fec(new_mt.m, new_mt.fec);
|
||||
|
||||
if (old_mt.fec)
|
||||
mpls_unlock_fec(old_mt.m, old_mt.fec);
|
||||
}
|
||||
|
||||
struct ea_class ea_gen_mpls_policy = {
|
||||
.name = "mpls_policy",
|
||||
.type = T_ENUM_MPLS_POLICY,
|
||||
};
|
||||
|
||||
struct ea_class ea_gen_mpls_class = {
|
||||
.name = "mpls_class",
|
||||
.type = T_INT,
|
||||
};
|
||||
|
||||
struct ea_class ea_gen_mpls_label = {
|
||||
.name = "mpls_label",
|
||||
.type = T_INT,
|
||||
};
|
166
nest/mpls.h
Normal file
166
nest/mpls.h
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_MPLS_H_
|
||||
#define _BIRD_MPLS_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/bitmap.h"
|
||||
#include "lib/hash.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
|
||||
|
||||
#define MPLS_POLICY_NONE 0
|
||||
#define MPLS_POLICY_STATIC 1
|
||||
#define MPLS_POLICY_PREFIX 2
|
||||
#define MPLS_POLICY_AGGREGATE 3
|
||||
|
||||
#define MPLS_FEC_DOWN 0
|
||||
#define MPLS_FEC_CLEAN 1
|
||||
#define MPLS_FEC_DIRTY 2
|
||||
|
||||
|
||||
struct mpls_domain_config {
|
||||
node n; /* Node in config.mpls_domains */
|
||||
struct mpls_domain *domain; /* Our instance */
|
||||
const char *name;
|
||||
|
||||
list ranges; /* List of label ranges (struct mpls_range_config) */
|
||||
struct mpls_range_config *static_range; /* Default static label range */
|
||||
struct mpls_range_config *dynamic_range; /* Default dynamic label range */
|
||||
};
|
||||
|
||||
struct mpls_domain {
|
||||
node n; /* Node in global list of MPLS domains (mpls_domains) */
|
||||
struct mpls_domain_config *cf; /* Our config */
|
||||
const char *name;
|
||||
pool *pool; /* Pool for the domain and associated objects */
|
||||
|
||||
struct lmap labels; /* Bitmap of allocated labels */
|
||||
uint label_count; /* Number of allocated labels */
|
||||
uint use_count; /* Reference counter */
|
||||
|
||||
struct config *removed; /* Deconfigured, waiting for zero use_count,
|
||||
while keeping config obstacle */
|
||||
|
||||
list ranges; /* List of label ranges (struct mpls_range) */
|
||||
list handles; /* List of label handles (struct mpls_handle) */
|
||||
};
|
||||
|
||||
struct mpls_range_config {
|
||||
node n; /* Node in mpls_domain_config.ranges */
|
||||
struct mpls_range *range; /* Our instance */
|
||||
struct mpls_domain_config *domain; /* Parent MPLS domain */
|
||||
const char *name;
|
||||
|
||||
uint start; /* Label range start, (uint) -1 for undefined */
|
||||
uint length; /* Label range length, (uint) -1 for undefined */
|
||||
};
|
||||
|
||||
struct mpls_range {
|
||||
node n; /* Node in mpls_domain.ranges */
|
||||
struct mpls_range_config *cf; /* Our config */
|
||||
const char *name;
|
||||
|
||||
uint lo, hi; /* Label range interval */
|
||||
uint label_count; /* Number of allocated labels */
|
||||
uint use_count; /* Reference counter */
|
||||
u8 removed; /* Deconfigured, waiting for zero use_count */
|
||||
};
|
||||
|
||||
struct mpls_handle {
|
||||
node n; /* Node in mpls_domain.handles */
|
||||
|
||||
struct mpls_range *range; /* Associated range, keeping reference */
|
||||
uint label_count; /* Number of allocated labels */
|
||||
};
|
||||
|
||||
|
||||
void mpls_init(void);
|
||||
struct mpls_domain_config * mpls_domain_config_new(struct symbol *s);
|
||||
void mpls_domain_postconfig(struct mpls_domain_config *cf);
|
||||
struct mpls_range_config * mpls_range_config_new(struct mpls_domain_config *m, struct symbol *s);
|
||||
void mpls_preconfig(struct config *c);
|
||||
void mpls_commit(struct config *new, struct config *old);
|
||||
uint mpls_new_label(struct mpls_domain *m, struct mpls_handle *h);
|
||||
void mpls_free_label(struct mpls_domain *m, struct mpls_handle *h, uint n);
|
||||
|
||||
static inline struct mpls_domain_config *cf_default_mpls_domain(struct config *cfg)
|
||||
{ return EMPTY_LIST(cfg->mpls_domains) ? NULL : HEAD(cfg->mpls_domains); }
|
||||
|
||||
|
||||
struct mpls_channel_config {
|
||||
struct channel_config c;
|
||||
|
||||
struct mpls_domain_config *domain;
|
||||
struct mpls_range_config *range;
|
||||
|
||||
uint label_policy;
|
||||
};
|
||||
|
||||
struct mpls_channel {
|
||||
struct channel c;
|
||||
|
||||
struct mpls_domain *domain;
|
||||
struct mpls_range *range;
|
||||
|
||||
uint label_policy;
|
||||
};
|
||||
|
||||
|
||||
void mpls_channel_postconfig(struct channel_config *CF);
|
||||
extern struct channel_class channel_mpls;
|
||||
|
||||
|
||||
struct mpls_fec {
|
||||
u32 label; /* Label for FEC */
|
||||
u32 hash; /* Hash for primary key (net / rta) */
|
||||
u32 uc; /* Number of LSPs for FEC */
|
||||
union { /* Extension part of key */
|
||||
u32 path_id; /* Source path_id */
|
||||
};
|
||||
|
||||
u8 state; /* FEC state (MPLS_FEC_*) */
|
||||
u8 policy; /* Label policy (MPLS_POLICY_*) */
|
||||
|
||||
struct mpls_fec *next_k; /* Next in mpls_fec.net_hash/rta_hash */
|
||||
struct mpls_fec *next_l; /* Next in mpls_fec.label_hash */
|
||||
union { /* Primary key */
|
||||
struct ea_storage *rta;
|
||||
net_addr net[0];
|
||||
};
|
||||
};
|
||||
|
||||
struct mpls_fec_map {
|
||||
pool *pool; /* Pool for FEC map */
|
||||
slab *slabs[4]; /* Slabs for FEC allocation */
|
||||
HASH(struct mpls_fec) net_hash; /* Hash table for MPLS_POLICY_PREFIX FECs */
|
||||
HASH(struct mpls_fec) attrs_hash; /* Hash table for MPLS_POLICY_AGGREGATE FECs */
|
||||
HASH(struct mpls_fec) label_hash; /* Hash table for FEC lookup by label */
|
||||
|
||||
struct channel *channel; /* MPLS channel for FEC announcement */
|
||||
struct mpls_domain *domain; /* MPLS domain, keeping reference */
|
||||
struct mpls_handle *handle; /* Handle for allocation of labels */
|
||||
|
||||
u8 mpls_rts; /* Source value used for MPLS routes (RTS_*) */
|
||||
};
|
||||
|
||||
|
||||
struct mpls_fec_map *mpls_fec_map_new(pool *p, struct channel *c, uint rts);
|
||||
void mpls_fec_map_free(struct mpls_fec_map *m);
|
||||
struct mpls_fec *mpls_find_fec_by_label(struct mpls_fec_map *x, u32 label);
|
||||
struct mpls_fec *mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label);
|
||||
struct mpls_fec *mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u32 path_id);
|
||||
struct mpls_fec *mpls_get_fec_by_destination(struct mpls_fec_map *m, ea_list *dest);
|
||||
void mpls_free_fec(struct mpls_fec_map *x, struct mpls_fec *fec);
|
||||
void mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r);
|
||||
void mpls_rte_preimport(rte *new, const rte *old);
|
||||
|
||||
#endif
|
69
nest/proto.c
69
nest/proto.c
@ -18,6 +18,7 @@
|
||||
#include "conf/conf.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/mpls.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
#include "filter/f-inst.h"
|
||||
@ -1283,7 +1284,7 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty
|
||||
if (!net_val_match(net_type, proto->protocol->channel_mask))
|
||||
cf_error("Unsupported channel type");
|
||||
|
||||
if (proto->net_type && (net_type != proto->net_type))
|
||||
if (proto->net_type && (net_type != proto->net_type) && (net_type != NET_MPLS))
|
||||
cf_error("Different channel type");
|
||||
|
||||
tab = rt_get_default_table(new_config, net_type);
|
||||
@ -1502,6 +1503,71 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* proto_setup_mpls_map - automatically setup FEC map for protocol
|
||||
* @p: affected protocol
|
||||
* @rts: RTS_* value for generated MPLS routes
|
||||
* @hooks: whether to update rte_insert / rte_remove hooks
|
||||
*
|
||||
* Add, remove or reconfigure MPLS FEC map of the protocol @p, depends on
|
||||
* whether MPLS channel exists, and setup rte_insert / rte_remove hooks with
|
||||
* default MPLS handlers. It is a convenience function supposed to be called
|
||||
* from the protocol start and configure hooks, after reconfiguration of
|
||||
* channels. For shutdown, use proto_shutdown_mpls_map(). If caller uses its own
|
||||
* rte_insert / rte_remove hooks, it is possible to disable updating hooks and
|
||||
* doing that manually.
|
||||
*/
|
||||
void
|
||||
proto_setup_mpls_map(struct proto *p, uint rts)
|
||||
{
|
||||
struct mpls_fec_map *m = p->mpls_map;
|
||||
struct channel *c = p->mpls_channel;
|
||||
|
||||
if (!m && c)
|
||||
{
|
||||
/*
|
||||
* Note that when called from a protocol start hook, it is called before
|
||||
* mpls_channel_start(). But FEC map locks MPLS domain internally so it does
|
||||
* not depend on lock from MPLS channel.
|
||||
*/
|
||||
p->mpls_map = mpls_fec_map_new(p->pool, c, rts);
|
||||
}
|
||||
else if (m && !c)
|
||||
{
|
||||
/*
|
||||
* Note that for reconfiguration, it is called after the MPLS channel has
|
||||
* been already removed. But removal of active MPLS channel would trigger
|
||||
* protocol restart anyways.
|
||||
*/
|
||||
mpls_fec_map_free(m);
|
||||
p->mpls_map = NULL;
|
||||
}
|
||||
else if (m && c)
|
||||
{
|
||||
// mpls_fec_map_reconfigure(m, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* proto_shutdown_mpls_map - automatically shutdown FEC map for protocol
|
||||
* @p: affected protocol
|
||||
* @hooks: whether to update rte_insert / rte_remove hooks
|
||||
*
|
||||
* Remove MPLS FEC map of the protocol @p during protocol shutdown.
|
||||
*/
|
||||
void
|
||||
proto_shutdown_mpls_map(struct proto *p)
|
||||
{
|
||||
struct mpls_fec_map *m = p->mpls_map;
|
||||
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
mpls_fec_map_free(m);
|
||||
p->mpls_map = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_cleanup(struct proto *p)
|
||||
{
|
||||
@ -1534,6 +1600,7 @@ proto_loop_stopped(void *ptr)
|
||||
proto_cleanup(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
proto_event(void *ptr)
|
||||
{
|
||||
|
@ -34,6 +34,7 @@ struct channel;
|
||||
struct ea_list;
|
||||
struct eattr;
|
||||
struct symbol;
|
||||
struct mpls_fec_map;
|
||||
|
||||
|
||||
/*
|
||||
@ -161,6 +162,8 @@ struct proto {
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
TLIST_LIST(proto_neigh) neighbors; /* List of neighbor structures */
|
||||
struct iface_subscription iface_sub; /* Interface notification subscription */
|
||||
struct channel *mpls_channel; /* MPLS channel, when used */
|
||||
struct mpls_fec_map *mpls_map; /* Maps protocol routes to FECs / labels */
|
||||
|
||||
const char *name; /* Name of this instance (== cf->name) */
|
||||
u32 debug; /* Debugging flags */
|
||||
@ -677,12 +680,16 @@ struct channel {
|
||||
struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type);
|
||||
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
|
||||
{ return proto_cf_find_channel(pc, pc->net_type); }
|
||||
static inline struct channel_config *proto_cf_mpls_channel(struct proto_config *pc)
|
||||
{ return proto_cf_find_channel(pc, NET_MPLS); }
|
||||
|
||||
struct channel *proto_find_channel_by_table(struct proto *p, rtable *t);
|
||||
struct channel *proto_find_channel_by_name(struct proto *p, const char *n);
|
||||
struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
|
||||
void proto_remove_channel(struct proto *p, struct channel *c);
|
||||
int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
|
||||
void proto_setup_mpls_map(struct proto *p, uint rts);
|
||||
void proto_shutdown_mpls_map(struct proto *p);
|
||||
|
||||
void channel_set_state(struct channel *c, uint state);
|
||||
void channel_schedule_reload(struct channel *c, struct channel_import_request *cir);
|
||||
|
@ -7,8 +7,8 @@
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_NEST_RT_H_
|
||||
#define _BIRD_NEST_RT_H_
|
||||
#ifndef _BIRD_ROUTE_H_
|
||||
#define _BIRD_ROUTE_H_
|
||||
|
||||
#include "lib/lists.h"
|
||||
#include "lib/bitmap.h"
|
||||
@ -161,6 +161,7 @@ struct rtable_private {
|
||||
struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
|
||||
|
||||
struct f_trie *flowspec_trie; /* Trie for evaluation of flowspec notifications */
|
||||
// struct mpls_domain *mpls_domain; /* Label allocator for MPLS */
|
||||
};
|
||||
|
||||
/* The final union private-public rtable structure */
|
||||
@ -717,6 +718,8 @@ struct hostentry_adata {
|
||||
u32 labels[0];
|
||||
};
|
||||
|
||||
#define HOSTENTRY_LABEL_COUNT(head) (head->ad.length + sizeof(struct adata) - sizeof(struct hostentry_adata)) / sizeof(u32)
|
||||
|
||||
void
|
||||
ea_set_hostentry(ea_list **to, rtable *dep, rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]);
|
||||
|
||||
|
@ -1715,6 +1715,11 @@ rta_init(void)
|
||||
ea_register_init(&ea_gen_from);
|
||||
ea_register_init(&ea_gen_source);
|
||||
ea_register_init(&ea_gen_flowspec_valid);
|
||||
|
||||
/* MPLS route attributes */
|
||||
ea_register_init(&ea_gen_mpls_policy);
|
||||
ea_register_init(&ea_gen_mpls_class);
|
||||
ea_register_init(&ea_gen_mpls_label);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -41,7 +41,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
||||
ea_list *a = e->attrs;
|
||||
int sync_error = d->tab->kernel ? krt_get_sync_error(d->tab->kernel, e) : 0;
|
||||
void (*get_route_info)(const rte *, byte *buf);
|
||||
eattr *nhea = net_type_match(e->net, NB_DEST) ?
|
||||
const eattr *nhea = net_type_match(e->net, NB_DEST) ?
|
||||
ea_find(a, &ea_gen_nexthop) : NULL;
|
||||
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
|
||||
int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
|
||||
@ -67,7 +67,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
||||
if (d->last_table != d->tab)
|
||||
rt_show_table(d);
|
||||
|
||||
eattr *heea;
|
||||
const eattr *heea;
|
||||
struct hostentry_adata *had = NULL;
|
||||
if (!net_is_flow(e->net) && (dest == RTD_NONE) && (heea = ea_find(a, &ea_gen_hostentry)))
|
||||
had = (struct hostentry_adata *) heea->u.ptr;
|
||||
@ -96,7 +96,7 @@ static void
|
||||
rt_show_net(struct rt_show_data *d, const net_addr *n, const rte **feed, uint count)
|
||||
{
|
||||
struct cli *c = d->cli;
|
||||
byte ia[NET_MAX_TEXT_LENGTH+1];
|
||||
byte ia[NET_MAX_TEXT_LENGTH+16+1];
|
||||
struct channel *ec = d->tab->export_channel;
|
||||
|
||||
/* The Clang static analyzer complains that ec may be NULL.
|
||||
@ -105,6 +105,7 @@ rt_show_net(struct rt_show_data *d, const net_addr *n, const rte **feed, uint co
|
||||
|
||||
int first = 1;
|
||||
int first_show = 1;
|
||||
uint last_label = 0;
|
||||
int pass = 0;
|
||||
|
||||
for (uint i = 0; i < count; i++)
|
||||
@ -186,13 +187,21 @@ rt_show_net(struct rt_show_data *d, const net_addr *n, const rte **feed, uint co
|
||||
|
||||
if (d->stats < 2)
|
||||
{
|
||||
if (first_show)
|
||||
net_format(n, ia, sizeof(ia));
|
||||
uint label = ea_get_int(e.attrs, &ea_gen_mpls_label, ~0U);
|
||||
|
||||
if (first_show || (last_label != label))
|
||||
{
|
||||
if (!~label)
|
||||
net_format(n, ia, sizeof(ia));
|
||||
else
|
||||
bsnprintf(ia, sizeof(ia), "%N mpls %d", n, label);
|
||||
}
|
||||
else
|
||||
ia[0] = 0;
|
||||
|
||||
rt_show_rte(c, ia, &e, d, !d->tab->prefilter && !i);
|
||||
first_show = 0;
|
||||
last_label = label;
|
||||
}
|
||||
|
||||
d->show_counter++;
|
||||
|
@ -96,6 +96,7 @@
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/mpls.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
@ -1894,21 +1895,22 @@ channel_preimport(struct rt_import_request *req, rte *new, const rte *old)
|
||||
|
||||
int new_in = new && !rte_is_filtered(new);
|
||||
int old_in = old && !rte_is_filtered(old);
|
||||
|
||||
int verdict = 1;
|
||||
|
||||
if (new_in && !old_in)
|
||||
if (CHANNEL_LIMIT_PUSH(c, IN))
|
||||
if (c->in_keep & RIK_REJECTED)
|
||||
{
|
||||
new->flags |= REF_FILTERED;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
verdict = 0;
|
||||
|
||||
if (!new_in && old_in)
|
||||
CHANNEL_LIMIT_POP(c, IN);
|
||||
|
||||
return 1;
|
||||
mpls_rte_preimport(new_in ? new : NULL, old_in ? old : NULL);
|
||||
|
||||
return verdict;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1951,6 +1953,9 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||
new = NULL;
|
||||
}
|
||||
|
||||
if (new && c->proto->mpls_map)
|
||||
mpls_handle_rte(c->proto->mpls_map, n, new);
|
||||
|
||||
if (new)
|
||||
if (net_is_flow(n))
|
||||
rt_flowspec_resolve_rte(new, c);
|
||||
@ -1963,7 +1968,6 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||
stats->updates_invalid++;
|
||||
new = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
stats->withdraws_received++;
|
||||
@ -3471,11 +3475,8 @@ rt_postconfig(struct config *c)
|
||||
void
|
||||
ea_set_hostentry(ea_list **to, rtable *dep, rtable *src, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum])
|
||||
{
|
||||
struct {
|
||||
struct adata ad;
|
||||
struct hostentry *he;
|
||||
u32 labels[0];
|
||||
} *head = (void *) tmp_alloc_adata(sizeof *head + sizeof(u32) * lnum - sizeof(struct adata));
|
||||
struct hostentry_adata *head = (struct hostentry_adata *) tmp_alloc_adata(
|
||||
sizeof *head + sizeof(u32) * lnum - sizeof(struct adata));
|
||||
|
||||
RT_LOCKED(src, tab)
|
||||
head->he = rt_get_hostentry(tab, gw, ll, dep);
|
||||
|
@ -33,6 +33,7 @@ CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTAR
|
||||
CF_KEYWORDS(INTERFACE, PREFERRED)
|
||||
|
||||
%type <i> kern_mp_limit
|
||||
%type <cc> kern_channel
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -53,9 +54,15 @@ kern_mp_limit:
|
||||
| LIMIT expr { $$ = $2; if (($2 <= 0) || ($2 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
|
||||
;
|
||||
|
||||
|
||||
kern_channel:
|
||||
proto_channel
|
||||
| mpls_channel
|
||||
;
|
||||
|
||||
kern_item:
|
||||
proto_item
|
||||
| proto_channel { this_proto->net_type = $1->net_type; }
|
||||
| kern_channel { this_proto->net_type = $1->net_type; }
|
||||
| PERSIST bool { THIS_KRT->persist = $2; }
|
||||
| SCAN TIME expr {
|
||||
/* Scan time of 0 means scan on startup only */
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/mpls.h"
|
||||
#include "nest/cli.h"
|
||||
#include "nest/locks.h"
|
||||
#include "conf/conf.h"
|
||||
@ -905,6 +906,7 @@ main(int argc, char **argv)
|
||||
rt_init();
|
||||
io_init();
|
||||
if_init();
|
||||
mpls_init();
|
||||
// roa_init();
|
||||
config_init();
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/mpls.h"
|
||||
|
||||
#include "sysdep/unix/unix.h"
|
||||
#include "sysdep/unix/krt.h"
|
||||
@ -66,6 +67,7 @@ bt_bird_init(void)
|
||||
log_switch(1, NULL, NULL);
|
||||
io_init();
|
||||
if_init();
|
||||
mpls_init();
|
||||
config_init();
|
||||
|
||||
protos_build();
|
||||
|
Loading…
Reference in New Issue
Block a user