]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/mpls.c
MPLS: Fix issue with recursive MPLS routes
[thirdparty/bird.git] / nest / mpls.c
CommitLineData
333ddd4f
OZ
1/*
2 * BIRD Internet Routing Daemon -- MPLS Structures
3 *
4 * (c) 2022 Ondrej Zajicek <santiago@crfreenet.org>
5 * (c) 2022 CZ.NIC z.s.p.o.
6 *
7 * Can be freely distributed and used under the terms of the GNU GPL.
8 */
9
10/**
11 * DOC: MPLS
12 *
13 * The MPLS subsystem manages MPLS labels and handles their allocation to
14 * MPLS-aware routing protocols. These labels are then attached to IP or VPN
15 * routes representing label switched paths -- LSPs. MPLS labels are also used
16 * in special MPLS routes (which use labels as network address) that are
17 * exported to MPLS routing table in kernel. The MPLS subsystem consists of MPLS
18 * domains (struct &mpls_domain), MPLS channels (struct &mpls_channel) and FEC
19 * maps (struct &mpls_fec_map).
20 *
21 * The MPLS domain represents one MPLS label address space, implements the label
22 * allocator, and handles associated configuration and management. The domain is
23 * declared in the configuration (struct &mpls_domain_config). There might be
24 * multiple MPLS domains representing separate label spaces, but in most cases
25 * one domain is enough. MPLS-aware protocols and routing tables are associated
26 * with a specific MPLS domain.
27 *
28 * The MPLS domain has configurable label ranges (struct &mpls_range), by
29 * default it has two ranges: static (16-1000) and dynamic (1000-10000). When
30 * a protocol wants to allocate labels, it first acquires a handle (struct
31 * &mpls_handle) for a specific range using mpls_new_handle(), and then it
32 * allocates labels from that with mpls_new_label(). When not needed, labels are
33 * freed by mpls_free_label() and the handle is released by mpls_free_handle().
34 * Note that all labels and handles must be freed manually.
35 *
36 * Both MPLS domain and MPLS range are reference counted, so when deconfigured
37 * they could be freed just after all labels and ranges are freed. Users are
38 * expected to hold a reference to a MPLS domain for whole time they use
39 * something from that domain (e.g. &mpls_handle), but releasing reference to
40 * a range while holding associated handle is OK.
41 *
42 * The MPLS channel is subclass of a generic protocol channel. It has two
43 * distinct purposes - to handle per-protocol MPLS configuration (e.g. which
44 * MPLS domain is associated with the protocol, which label range is used by the
45 * protocol), and to announce MPLS routes to a routing table (as a regular
46 * protocol channel).
47 *
48 * The FEC map is a helper structure that maps forwarding equivalent classes
49 * (FECs) to MPLS labels. It is an internal matter of a routing protocol how to
50 * assign meaning to allocated labels, announce LSP routes and associated MPLS
51 * routes (i.e. ILM entries). But the common behavior is implemented in the FEC
52 * map, which can be used by the protocols that work with IP-prefix-based FECs.
53 *
54 * The FEC map keeps hash tables of FECs (struct &mpls_fec) based on network
9ca86ef6 55 * prefix, next hop eattr and assigned label. It has three general labeling policies:
333ddd4f
OZ
56 * static assignment (%MPLS_POLICY_STATIC), per-prefix policy (%MPLS_POLICY_PREFIX),
57 * and aggregating policy (%MPLS_POLICY_AGGREGATE). In per-prefix policy, each
58 * distinct LSP is a separate FEC and uses a separate label, which is kept even
59 * if the next hop of the LSP changes. In aggregating policy, LSPs with a same
60 * next hop form one FEC and use one label, but when a next hop (or remote
61 * label) of such LSP changes then the LSP must be moved to a different FEC and
9ca86ef6
OZ
62 * assigned a different label. There is also a special VRF policy (%MPLS_POLICY_VRF)
63 * applicable for L3VPN protocols, which uses one label for all routes from a VRF,
64 * while replacing the original next hop with lookup in the VRF.
333ddd4f
OZ
65 *
66 * The overall process works this way: A protocol wants to announce a LSP route,
67 * it does that by announcing e.g. IP route with %EA_MPLS_POLICY attribute.
68 * After the route is accepted by filters (which may also change the policy
69 * attribute or set a static label), the mpls_handle_rte() is called from
70 * rte_update2(), which applies selected labeling policy, finds existing FEC or
71 * creates a new FEC (which includes allocating new label and announcing related
72 * MPLS route by mpls_announce_fec()), and attach FEC label to the LSP route.
73 * After that, the LSP route is stored in routing table by rte_recalculate().
74 * Changes in routing tables trigger mpls_rte_insert() and mpls_rte_remove()
75 * hooks, which refcount FEC structures and possibly trigger removal of FECs
76 * and withdrawal of MPLS routes.
77 *
78 * TODO:
333ddd4f
OZ
79 * - special handling of reserved labels
80 */
81
35726051
OZ
82#include <stdlib.h>
83
333ddd4f
OZ
84#include "nest/bird.h"
85#include "nest/route.h"
86#include "nest/mpls.h"
8e9e013b 87#include "nest/cli.h"
333ddd4f
OZ
88
89static struct mpls_range *mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf);
90static struct mpls_range *mpls_find_range_(list *l, const char *name);
91static int mpls_reconfigure_range(struct mpls_domain *m, struct mpls_range *r, struct mpls_range_config *cf);
92static void mpls_remove_range(struct mpls_range *r);
93
94
95/*
96 * MPLS domain
97 */
98
99list mpls_domains;
100
101void
102mpls_init(void)
103{
104 init_list(&mpls_domains);
105}
106
107struct mpls_domain_config *
108mpls_domain_config_new(struct symbol *s)
109{
110 struct mpls_domain_config *mc = cfg_allocz(sizeof(struct mpls_domain_config));
111 struct mpls_range_config *rc;
112
113 cf_define_symbol(new_config, s, SYM_MPLS_DOMAIN, mpls_domain, mc);
114 mc->name = s->name;
115 init_list(&mc->ranges);
116
117 /* Predefined static range */
118 rc = mpls_range_config_new(mc, NULL);
119 rc->name = "static";
120 rc->start = 16;
121 rc->length = 984;
35726051 122 rc->implicit = 1;
333ddd4f
OZ
123 mc->static_range = rc;
124
125 /* Predefined dynamic range */
126 rc = mpls_range_config_new(mc, NULL);
127 rc->name = "dynamic";
128 rc->start = 1000;
129 rc->length = 9000;
35726051 130 rc->implicit = 1;
333ddd4f
OZ
131 mc->dynamic_range = rc;
132
133 add_tail(&new_config->mpls_domains, &mc->n);
134
135 return mc;
136}
137
35726051
OZ
138static int
139mpls_compare_range_configs(const void *r1_, const void *r2_)
140{
141 const struct mpls_range_config * const *r1 = r1_;
142 const struct mpls_range_config * const *r2 = r2_;
143
144 return uint_cmp((*r1)->start, (*r2)->start);
145}
146
333ddd4f
OZ
147void
148mpls_domain_postconfig(struct mpls_domain_config *cf UNUSED)
149{
35726051
OZ
150 /* Label range non-intersection check */
151
152 int num_ranges = list_length(&cf->ranges);
153 struct mpls_range_config **ranges = tmp_alloc(num_ranges * sizeof(struct mpls_range_config *));
154
155 {
156 int i = 0;
157 struct mpls_range_config *r;
158 WALK_LIST(r, cf->ranges)
159 ranges[i++] = r;
160 }
161
162 qsort(ranges, num_ranges, sizeof(struct mpls_range_config *), mpls_compare_range_configs);
163
164 struct mpls_range_config *max_range = NULL;
165 uint max_hi = 0;
166
167 for (int i = 0; i < num_ranges; i++)
168 {
169 struct mpls_range_config *r = ranges[i];
170 uint hi = r->start + r->length;
171
172 if (r->implicit)
173 continue;
174
175 if (r->start < max_hi)
176 cf_warn("MPLS label ranges %s and %s intersect", max_range->name, r->name);
177
178 if (hi > max_hi)
179 {
180 max_range = r;
181 max_hi = hi;
182 }
183 }
333ddd4f
OZ
184}
185
186static struct mpls_domain *
187mpls_new_domain(struct mpls_domain_config *cf)
188{
189 struct pool *p = rp_new(&root_pool, "MPLS domain");
190 struct mpls_domain *m = mb_allocz(p, sizeof(struct mpls_domain));
191
192 m->cf = cf;
193 m->name = cf->name;
194 m->pool = p;
195
196 lmap_init(&m->labels, p);
197 lmap_set(&m->labels, 0);
198
199 init_list(&m->ranges);
200 init_list(&m->handles);
201
202 struct mpls_range_config *rc;
203 WALK_LIST(rc, cf->ranges)
204 mpls_new_range(m, rc);
205
206 add_tail(&mpls_domains, &m->n);
207 cf->domain = m;
208
209 return m;
210}
211
212static struct mpls_domain *
213mpls_find_domain_(list *l, const char *name)
214{
215 struct mpls_domain *m;
216
217 WALK_LIST(m, *l)
218 if (!strcmp(m->name, name))
219 return m;
220
221 return NULL;
222}
223
224static int
225mpls_reconfigure_domain(struct mpls_domain *m, struct mpls_domain_config *cf)
226{
227 cf->domain = m;
228 m->cf->domain = NULL;
229 m->cf = cf;
230 m->name = cf->name;
231
232 /* Reconfigure label ranges */
233 list old_ranges;
234 init_list(&old_ranges);
235 add_tail_list(&old_ranges, &m->ranges);
236 init_list(&m->ranges);
237
238 struct mpls_range_config *rc;
239 WALK_LIST(rc, cf->ranges)
240 {
241 struct mpls_range *r = mpls_find_range_(&old_ranges, rc->name);
242
243 if (r && mpls_reconfigure_range(m, r, rc))
244 {
245 rem_node(&r->n);
246 add_tail(&m->ranges, &r->n);
247 continue;
248 }
249
250 mpls_new_range(m, rc);
251 }
252
253 struct mpls_range *r, *r2;
254 WALK_LIST_DELSAFE(r, r2, old_ranges)
a7a9df86
OZ
255 if (!r->removed)
256 mpls_remove_range(r);
333ddd4f
OZ
257
258 add_tail_list(&m->ranges, &old_ranges);
259
260 return 1;
261}
262
263static void
264mpls_free_domain(struct mpls_domain *m)
265{
266 ASSERT(m->use_count == 0);
267 ASSERT(m->label_count == 0);
268 ASSERT(EMPTY_LIST(m->handles));
269
270 struct config *cfg = m->removed;
271
272 m->cf->domain = NULL;
273 rem_node(&m->n);
274 rfree(m->pool);
275
276 config_del_obstacle(cfg);
277}
278
279static void
280mpls_remove_domain(struct mpls_domain *m, struct config *cfg)
281{
282 m->removed = cfg;
283 config_add_obstacle(cfg);
284
285 if (!m->use_count)
286 mpls_free_domain(m);
287}
288
289void
290mpls_lock_domain(struct mpls_domain *m)
291{
292 m->use_count++;
293}
294
295void
296mpls_unlock_domain(struct mpls_domain *m)
297{
298 ASSERT(m->use_count > 0);
299
300 m->use_count--;
301 if (!m->use_count && m->removed)
302 mpls_free_domain(m);
303}
304
305void
306mpls_preconfig(struct config *c)
307{
308 init_list(&c->mpls_domains);
309}
310
311void
312mpls_commit(struct config *new, struct config *old)
313{
314 list old_domains;
315 init_list(&old_domains);
316 add_tail_list(&old_domains, &mpls_domains);
317 init_list(&mpls_domains);
318
319 struct mpls_domain_config *mc;
320 WALK_LIST(mc, new->mpls_domains)
321 {
322 struct mpls_domain *m = mpls_find_domain_(&old_domains, mc->name);
323
324 if (m && mpls_reconfigure_domain(m, mc))
325 {
326 rem_node(&m->n);
327 add_tail(&mpls_domains, &m->n);
328 continue;
329 }
330
331 mpls_new_domain(mc);
332 }
333
334 struct mpls_domain *m, *m2;
335 WALK_LIST_DELSAFE(m, m2, old_domains)
336 mpls_remove_domain(m, old);
337
338 add_tail_list(&mpls_domains, &old_domains);
339}
340
341
342/*
343 * MPLS range
344 */
345
346struct mpls_range_config *
347mpls_range_config_new(struct mpls_domain_config *mc, struct symbol *s)
348{
349 struct mpls_range_config *rc = cfg_allocz(sizeof(struct mpls_range_config));
350
351 if (s)
352 cf_define_symbol(new_config, s, SYM_MPLS_RANGE, mpls_range, rc);
353
354 rc->domain = mc;
355 rc->name = s ? s->name : NULL;
356 rc->start = (uint) -1;
357 rc->length = (uint) -1;
358
359 add_tail(&mc->ranges, &rc->n);
360
361 return rc;
362}
363
364static struct mpls_range *
365mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf)
366{
367 struct mpls_range *r = mb_allocz(m->pool, sizeof(struct mpls_range));
368
369 r->cf = cf;
370 r->name = cf->name;
371 r->lo = cf->start;
372 r->hi = cf->start + cf->length;
373
374 add_tail(&m->ranges, &r->n);
375 cf->range = r;
376
377 return r;
378}
379
380static struct mpls_range *
381mpls_find_range_(list *l, const char *name)
382{
383 struct mpls_range *r;
384
385 WALK_LIST(r, *l)
a7a9df86 386 if (!strcmp(r->name, name) && !r->removed)
333ddd4f
OZ
387 return r;
388
389 return NULL;
390}
391
392static int
fcf22586 393mpls_reconfigure_range(struct mpls_domain *m, struct mpls_range *r, struct mpls_range_config *cf)
333ddd4f 394{
fcf22586
OZ
395 uint last = lmap_last_one_in_range(&m->labels, r->lo, r->hi);
396 if (last == r->hi) last = 0;
397
398 if ((cf->start > r->lo) || (cf->start + cf->length <= last))
333ddd4f
OZ
399 return 0;
400
401 cf->range = r;
402 r->cf->range = NULL;
403 r->cf = cf;
404 r->name = cf->name;
405 r->lo = cf->start;
406 r->hi = cf->start + cf->length;
407
408 return 1;
409}
410
411static void
412mpls_free_range(struct mpls_range *r)
413{
414 ASSERT(r->use_count == 0);
415 ASSERT(r->label_count == 0);
416
333ddd4f
OZ
417 rem_node(&r->n);
418 mb_free(r);
419}
420
421static void
422mpls_remove_range(struct mpls_range *r)
423{
a7a9df86
OZ
424 ASSERT(!r->removed);
425
333ddd4f 426 r->removed = 1;
a7a9df86
OZ
427 r->cf->range = NULL;
428 r->cf = NULL;
333ddd4f
OZ
429
430 if (!r->use_count)
431 mpls_free_range(r);
432}
433
434void
435mpls_lock_range(struct mpls_range *r)
436{
437 r->use_count++;
438}
439
440void
441mpls_unlock_range(struct mpls_range *r)
442{
443 ASSERT(r->use_count > 0);
444
445 r->use_count--;
446 if (!r->use_count && r->removed)
447 mpls_free_range(r);
448}
449
450
451/*
452 * MPLS handle
453 */
454
455struct mpls_handle *
456mpls_new_handle(struct mpls_domain *m, struct mpls_range *r)
457{
458 struct mpls_handle *h = mb_allocz(m->pool, sizeof(struct mpls_handle));
459
460 h->range = r;
461 mpls_lock_range(h->range);
462
463 add_tail(&m->handles, &h->n);
464
465 return h;
466}
467
468void
469mpls_free_handle(struct mpls_domain *m UNUSED, struct mpls_handle *h)
470{
471 ASSERT(h->label_count == 0);
472
473 mpls_unlock_range(h->range);
474 rem_node(&h->n);
475 mb_free(h);
476}
477
478
479/*
480 * MPLS label
481 */
482
483uint
b6385dec 484mpls_new_label(struct mpls_domain *m, struct mpls_handle *h, uint n)
333ddd4f
OZ
485{
486 struct mpls_range *r = h->range;
333ddd4f 487
b6385dec
OZ
488 if (!n)
489 n = lmap_first_zero_in_range(&m->labels, r->lo, r->hi);
490
491 if ((n < r->lo) || (n >= r->hi) || lmap_test(&m->labels, n))
333ddd4f
OZ
492 return 0;
493
494 m->label_count++;
495 r->label_count++;
496 h->label_count++;
497
498 lmap_set(&m->labels, n);
499 return n;
500}
501
502void
503mpls_free_label(struct mpls_domain *m, struct mpls_handle *h, uint n)
504{
505 struct mpls_range *r = h->range;
506
507 ASSERT(lmap_test(&m->labels, n));
508 lmap_clear(&m->labels, n);
509
510 ASSERT(m->label_count);
511 m->label_count--;
512
513 ASSERT(r->label_count);
514 r->label_count--;
515
516 ASSERT(h->label_count);
517 h->label_count--;
518}
519
a7a9df86
OZ
520void
521mpls_move_label(struct mpls_domain *m, struct mpls_handle *fh, struct mpls_handle *th, uint n)
522{
523 struct mpls_range *fr = fh->range;
524 struct mpls_range *tr = th->range;
525
526 ASSERT(lmap_test(&m->labels, n));
527 ASSERT((n >= fr->lo) && (n < fr->hi));
528 ASSERT((n >= tr->lo) && (n < tr->hi));
529
530 ASSERT(fr->label_count);
531 fr->label_count--;
532
533 ASSERT(fh->label_count);
534 fh->label_count--;
535
536 tr->label_count++;
537 th->label_count++;
538}
539
333ddd4f
OZ
540
541/*
542 * MPLS channel
543 */
544
545static void
546mpls_channel_init(struct channel *C, struct channel_config *CC)
547{
548 struct mpls_channel *c = (void *) C;
549 struct mpls_channel_config *cc = (void *) CC;
550
551 c->domain = cc->domain->domain;
552 c->range = cc->range->range;
553 c->label_policy = cc->label_policy;
554}
555
556static int
557mpls_channel_start(struct channel *C)
558{
559 struct mpls_channel *c = (void *) C;
560
561 mpls_lock_domain(c->domain);
562 mpls_lock_range(c->range);
563
564 return 0;
565}
566
567/*
568static void
569mpls_channel_shutdown(struct channel *C)
570{
571 struct mpls_channel *c = (void *) C;
572
573}
574*/
575
576static void
577mpls_channel_cleanup(struct channel *C)
578{
579 struct mpls_channel *c = (void *) C;
580
581 mpls_unlock_range(c->range);
582 mpls_unlock_domain(c->domain);
583}
584
585static int
a7a9df86 586mpls_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed, int *export_changed UNUSED)
333ddd4f
OZ
587{
588 struct mpls_channel *c = (void *) C;
589 struct mpls_channel_config *new = (void *) CC;
590
a7a9df86 591 if (new->domain->domain != c->domain)
333ddd4f
OZ
592 return 0;
593
a7a9df86
OZ
594 if (new->range->range != c->range)
595 {
596 if (c->c.channel_state != CS_DOWN)
597 mpls_unlock_range(c->range);
598
599 c->range = new->range->range;
600 *import_changed = 1;
601
602 if (c->c.channel_state != CS_DOWN)
603 mpls_lock_range(c->range);
604 }
605
606 if (new->label_policy != c->label_policy)
607 {
608 c->label_policy = new->label_policy;
609 *import_changed = 1;
610 }
611
333ddd4f
OZ
612 return 1;
613}
614
615void
616mpls_channel_postconfig(struct channel_config *CC)
617{
618 struct mpls_channel_config *cc = (void *) CC;
619
620 if (!cc->domain)
621 cf_error("MPLS domain not specified");
622
623 if (!cc->range)
b6385dec 624 cc->range = cc->domain->dynamic_range;
333ddd4f
OZ
625
626 if (cc->range->domain != cc->domain)
627 cf_error("MPLS label range from different MPLS domain");
628
629 if (!cc->c.table)
630 cf_error("Routing table not specified");
631}
632
633struct channel_class channel_mpls = {
634 .channel_size = sizeof(struct mpls_channel),
635 .config_size = sizeof(struct mpls_channel_config),
636 .init = mpls_channel_init,
637 .start = mpls_channel_start,
638// .shutdown = mpls_channel_shutdown,
639 .cleanup = mpls_channel_cleanup,
640 .reconfigure = mpls_channel_reconfigure,
641};
642
643
644/*
645 * MPLS FEC map
646 */
647
648#define NET_KEY(fec) fec->net, fec->path_id, fec->hash
649#define NET_NEXT(fec) fec->next_k
650#define NET_EQ(n1,i1,h1,n2,i2,h2) h1 == h2 && i1 == i2 && net_equal(n1, n2)
651#define NET_FN(n,i,h) h
652
653#define NET_REHASH mpls_net_rehash
654#define NET_PARAMS /8, *2, 2, 2, 8, 24
655
656
657#define RTA_KEY(fec) fec->rta, fec->class_id, fec->hash
658#define RTA_NEXT(fec) fec->next_k
659#define RTA_EQ(r1,i1,h1,r2,i2,h2) h1 == h2 && r1 == r2 && i1 == i2
660#define RTA_FN(r,i,h) h
661
662#define RTA_REHASH mpls_rta_rehash
663#define RTA_PARAMS /8, *2, 2, 2, 8, 24
664
665
666#define LABEL_KEY(fec) fec->label
667#define LABEL_NEXT(fec) fec->next_l
668#define LABEL_EQ(l1,l2) l1 == l2
669#define LABEL_FN(l) u32_hash(l)
670
671#define LABEL_REHASH mpls_label_rehash
672#define LABEL_PARAMS /8, *2, 2, 2, 8, 24
673
674
675HASH_DEFINE_REHASH_FN(NET, struct mpls_fec)
676HASH_DEFINE_REHASH_FN(RTA, struct mpls_fec)
677HASH_DEFINE_REHASH_FN(LABEL, struct mpls_fec)
678
679
a7a9df86 680static void mpls_unlink_fec(struct mpls_fec_map *m, struct mpls_fec *fec);
333ddd4f
OZ
681static void mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec);
682static rta * mpls_get_key_rta(struct mpls_fec_map *m, const rta *src);
683
684struct mpls_fec_map *
685mpls_fec_map_new(pool *pp, struct channel *C, uint rts)
686{
687 struct pool *p = rp_new(pp, "MPLS FEC map");
688 struct mpls_fec_map *m = mb_allocz(p, sizeof(struct mpls_fec_map));
689 struct mpls_channel *c = (void *) C;
690
691 m->pool = p;
692 m->channel = C;
693
694 m->domain = c->domain;
695 mpls_lock_domain(m->domain);
696
697 m->handle = mpls_new_handle(c->domain, c->range);
698
699 /* net_hash and rta_hash are initialized on-demand */
700 HASH_INIT(m->label_hash, m->pool, 4);
701
702 m->mpls_rts = rts;
703 m->mpls_scope = SCOPE_UNIVERSE;
704
705 return m;
706}
707
a7a9df86
OZ
708void
709mpls_fec_map_reconfigure(struct mpls_fec_map *m, struct channel *C)
710{
711 struct mpls_channel *c = (void *) C;
712
713 struct mpls_handle *old_d = NULL;
714 struct mpls_handle *old_s = NULL;
715
716 /* Reallocate dynamic handle */
717 if (m->handle->range != c->range)
718 {
719 old_d = m->handle;
720 m->handle = mpls_new_handle(m->domain, c->range);
721 }
722
723 /* Reallocate static handle */
724 if (m->static_handle && (m->static_handle->range != m->domain->cf->static_range->range))
725 {
726 old_s = m->static_handle;
727 m->static_handle = mpls_new_handle(m->domain, m->domain->cf->static_range->range);
728 }
729
730 /* Skip rest if there is no change */
731 if (!old_d && !old_s)
732 return;
733
734 /* Process existing FECs */
735 HASH_WALK(m->label_hash, next_l, fec)
736 {
737 /* Skip already dead FECs */
738 if (fec->policy == MPLS_POLICY_NONE)
739 continue;
740
741 /* Skip FECs with valid handle */
742 if ((fec->handle == m->handle) || (fec->handle == m->static_handle))
743 continue;
744
745 /* Try new handle for the FEC */
746 struct mpls_handle *new = (fec->policy != MPLS_POLICY_STATIC) ? m->handle : m->static_handle;
747 if ((fec->label >= new->range->lo) && (fec->label < new->range->hi))
748 {
749 mpls_move_label(m->domain, fec->handle, new, fec->label);
750 fec->handle = new;
751 continue;
752 }
753
754 /* Unlink the FEC while keep it in the label hash */
755 mpls_unlink_fec(m, fec);
756 fec->policy = MPLS_POLICY_NONE;
757 }
758 HASH_WALK_END;
759
760 /* Remove old unused handles */
761
762 if (old_d && !old_d->label_count)
763 mpls_free_handle(m->domain, old_d);
764
765 if (old_s && !old_s->label_count)
766 mpls_free_handle(m->domain, old_s);
767}
768
333ddd4f
OZ
769void
770mpls_fec_map_free(struct mpls_fec_map *m)
771{
772 /* Free stored rtas */
773 if (m->rta_hash.data)
774 {
775 HASH_WALK(m->rta_hash, next_k, fec)
776 {
777 rta_free(fec->rta);
778 fec->rta = NULL;
779 }
780 HASH_WALK_END;
781 }
782
783 /* Free allocated labels */
784 HASH_WALK(m->label_hash, next_l, fec)
785 {
a7a9df86
OZ
786 mpls_free_label(m->domain, fec->handle, fec->label);
787
788 if (!fec->policy && !fec->handle->label_count)
789 mpls_free_handle(m->domain, fec->handle);
333ddd4f
OZ
790 }
791 HASH_WALK_END;
792
b6385dec
OZ
793 if (m->static_handle)
794 mpls_free_handle(m->domain, m->static_handle);
795
333ddd4f
OZ
796 mpls_free_handle(m->domain, m->handle);
797 mpls_unlock_domain(m->domain);
798
799 rfree(m->pool);
800}
801
802static slab *
803mpls_slab(struct mpls_fec_map *m, uint type)
804{
805 ASSERT(type <= NET_VPN6);
806 int pos = type ? (type - 1) : 0;
807
808 if (!m->slabs[pos])
809 m->slabs[pos] = sl_new(m->pool, sizeof(struct mpls_fec) + net_addr_length[pos + 1]);
810
811 return m->slabs[pos];
812}
813
814struct mpls_fec *
815mpls_find_fec_by_label(struct mpls_fec_map *m, u32 label)
816{
817 return HASH_FIND(m->label_hash, LABEL, label);
818}
819
820struct mpls_fec *
821mpls_get_fec_by_label(struct mpls_fec_map *m, u32 label)
822{
823 struct mpls_fec *fec = HASH_FIND(m->label_hash, LABEL, label);
9b775859 824
333ddd4f 825 if (fec)
9b775859 826 return (fec->policy == MPLS_POLICY_STATIC) ? fec : NULL;
333ddd4f 827
a7a9df86
OZ
828 if (!m->static_handle)
829 m->static_handle = mpls_new_handle(m->domain, m->domain->cf->static_range->range);
830
9b775859 831 label = mpls_new_label(m->domain, m->static_handle, label);
333ddd4f 832
9b775859
OZ
833 if (!label)
834 return NULL;
835
836 fec = sl_allocz(mpls_slab(m, 0));
b6385dec 837
9b775859 838 fec->label = label;
333ddd4f 839 fec->policy = MPLS_POLICY_STATIC;
a7a9df86 840 fec->handle = m->static_handle;
333ddd4f
OZ
841
842 DBG("New FEC lab %u\n", fec->label);
843
844 HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
845
846 return fec;
847}
848
849struct mpls_fec *
be09b030 850mpls_get_fec_by_net(struct mpls_fec_map *m, const net_addr *net, u64 path_id)
333ddd4f
OZ
851{
852 if (!m->net_hash.data)
853 HASH_INIT(m->net_hash, m->pool, 4);
854
be09b030 855 u32 hash = net_hash(net) ^ u64_hash(path_id);
333ddd4f
OZ
856 struct mpls_fec *fec = HASH_FIND(m->net_hash, NET, net, path_id, hash);
857
858 if (fec)
859 return fec;
860
9b775859
OZ
861 u32 label = mpls_new_label(m->domain, m->handle, 0);
862
863 if (!label)
864 return NULL;
865
333ddd4f
OZ
866 fec = sl_allocz(mpls_slab(m, net->type));
867
868 fec->hash = hash;
869 fec->path_id = path_id;
870 net_copy(fec->net, net);
871
9b775859 872 fec->label = label;
333ddd4f 873 fec->policy = MPLS_POLICY_PREFIX;
a7a9df86 874 fec->handle = m->handle;
333ddd4f
OZ
875
876 DBG("New FEC net %u\n", fec->label);
877
878 HASH_INSERT2(m->net_hash, NET, m->pool, fec);
879 HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
880
881 return fec;
882}
883
884struct mpls_fec *
885mpls_get_fec_by_rta(struct mpls_fec_map *m, const rta *src, u32 class_id)
886{
887 if (!m->rta_hash.data)
888 HASH_INIT(m->rta_hash, m->pool, 4);
889
890 rta *rta = mpls_get_key_rta(m, src);
891 u32 hash = rta->hash_key ^ u32_hash(class_id);
892 struct mpls_fec *fec = HASH_FIND(m->rta_hash, RTA, rta, class_id, hash);
893
894 if (fec)
895 {
896 rta_free(rta);
897 return fec;
898 }
899
9b775859
OZ
900 u32 label = mpls_new_label(m->domain, m->handle, 0);
901
902 if (!label)
903 {
904 rta_free(rta);
905 return NULL;
906 }
907
333ddd4f
OZ
908 fec = sl_allocz(mpls_slab(m, 0));
909
910 fec->hash = hash;
911 fec->class_id = class_id;
912 fec->rta = rta;
913
9b775859 914 fec->label = label;
333ddd4f 915 fec->policy = MPLS_POLICY_AGGREGATE;
a7a9df86 916 fec->handle = m->handle;
333ddd4f
OZ
917
918 DBG("New FEC rta %u\n", fec->label);
919
920 HASH_INSERT2(m->rta_hash, RTA, m->pool, fec);
921 HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
922
923 return fec;
924}
925
9ca86ef6
OZ
926struct mpls_fec *
927mpls_get_fec_for_vrf(struct mpls_fec_map *m)
928{
929 struct mpls_fec *fec = m->vrf_fec;
930
931 if (fec)
932 return fec;
933
9b775859
OZ
934 u32 label = mpls_new_label(m->domain, m->handle, 0);
935
936 if (!label)
937 return NULL;
938
9ca86ef6
OZ
939 fec = sl_allocz(mpls_slab(m, 0));
940
9b775859 941 fec->label = label;
9ca86ef6 942 fec->policy = MPLS_POLICY_VRF;
a7a9df86 943 fec->handle = m->handle;
9ca86ef6
OZ
944 fec->iface = m->vrf_iface;
945
946 DBG("New FEC vrf %u\n", fec->label);
947
948 m->vrf_fec = fec;
949 HASH_INSERT2(m->label_hash, LABEL, m->pool, fec);
950
951 return fec;
952}
953
a7a9df86
OZ
954static void
955mpls_unlink_fec(struct mpls_fec_map *m, struct mpls_fec *fec)
333ddd4f 956{
333ddd4f
OZ
957 switch (fec->policy)
958 {
a7a9df86 959 case MPLS_POLICY_NONE:
333ddd4f
OZ
960 case MPLS_POLICY_STATIC:
961 break;
962
963 case MPLS_POLICY_PREFIX:
964 HASH_REMOVE2(m->net_hash, NET, m->pool, fec);
965 break;
966
967 case MPLS_POLICY_AGGREGATE:
968 rta_free(fec->rta);
969 HASH_REMOVE2(m->rta_hash, RTA, m->pool, fec);
970 break;
971
9ca86ef6
OZ
972 case MPLS_POLICY_VRF:
973 ASSERT(m->vrf_fec == fec);
974 m->vrf_fec = NULL;
975 break;
976
333ddd4f
OZ
977 default:
978 bug("Unknown fec type");
979 }
a7a9df86
OZ
980}
981
982void
983mpls_free_fec(struct mpls_fec_map *m, struct mpls_fec *fec)
984{
985 if (fec->state != MPLS_FEC_DOWN)
986 mpls_withdraw_fec(m, fec);
987
988 DBG("Free FEC %u\n", fec->label);
989
990 mpls_free_label(m->domain, fec->handle, fec->label);
991
992 if (!fec->policy && !fec->handle->label_count)
993 mpls_free_handle(m->domain, fec->handle);
994
995 HASH_REMOVE2(m->label_hash, LABEL, m->pool, fec);
996
997 mpls_unlink_fec(m, fec);
333ddd4f
OZ
998
999 sl_free(fec);
1000}
1001
1002static inline void mpls_lock_fec(struct mpls_fec_map *x UNUSED, struct mpls_fec *fec)
1003{ if (fec) fec->uc++; }
1004
1005static inline void mpls_unlock_fec(struct mpls_fec_map *x, struct mpls_fec *fec)
1006{ if (fec && !--fec->uc) mpls_free_fec(x, fec); }
1007
1008static inline void
1009mpls_damage_fec(struct mpls_fec_map *m UNUSED, struct mpls_fec *fec)
1010{
9b775859 1011 if (fec && (fec->state == MPLS_FEC_CLEAN))
333ddd4f
OZ
1012 fec->state = MPLS_FEC_DIRTY;
1013}
1014
1015static rta *
1016mpls_get_key_rta(struct mpls_fec_map *m, const rta *src)
1017{
1018 rta *a = allocz(RTA_MAX_SIZE);
1019
1020 a->source = m->mpls_rts;
1021 a->scope = m->mpls_scope;
1022
1023 if (!src->hostentry)
1024 {
1025 /* Just copy the nexthop */
1026 a->dest = src->dest;
1027 nexthop_link(a, &src->nh);
1028 }
1029 else
1030 {
1031 /* Keep the hostentry */
1032 a->hostentry = src->hostentry;
1033
1034 /* Keep the original labelstack */
1035 const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig];
1036 a->nh.labels = a->nh.labels_orig = src->nh.labels_orig;
1037 memcpy(a->nh.label, labels, src->nh.labels_orig * sizeof(u32));
1038 }
1039
1040 return rta_lookup(a);
1041}
1042
1043static void
1044mpls_announce_fec(struct mpls_fec_map *m, struct mpls_fec *fec, const rta *src)
1045{
1046 rta *a = allocz(RTA_MAX_SIZE);
1047
1048 a->source = m->mpls_rts;
1049 a->scope = m->mpls_scope;
1050
1051 if (!src->hostentry)
1052 {
1053 /* Just copy the nexthop */
1054 a->dest = src->dest;
1055 nexthop_link(a, &src->nh);
1056 }
1057 else
1058 {
1059 const u32 *labels = &src->nh.label[src->nh.labels - src->nh.labels_orig];
1060 mpls_label_stack ms;
1061
b5e9e519 1062 /* Reconstruct the original labelstack */
333ddd4f
OZ
1063 ms.len = src->nh.labels_orig;
1064 memcpy(ms.stack, labels, src->nh.labels_orig * sizeof(u32));
b5e9e519
OZ
1065
1066 /* The same hostentry, but different dependent table */
1067 struct hostentry *s = src->hostentry;
1068 rta_set_recursive_next_hop(m->channel->table, a, s->owner, s->addr, s->link, &ms);
333ddd4f
OZ
1069 }
1070
1071 net_addr_mpls n = NET_ADDR_MPLS(fec->label);
1072
1073 rte *e = rte_get_temp(rta_lookup(a), m->channel->proto->main_source);
1074 e->pflags = 0;
1075
1076 fec->state = MPLS_FEC_CLEAN;
1077 rte_update2(m->channel, (net_addr *) &n, e, m->channel->proto->main_source);
1078}
1079
1080static void
1081mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec)
1082{
1083 net_addr_mpls n = NET_ADDR_MPLS(fec->label);
1084
1085 fec->state = MPLS_FEC_DOWN;
1086 rte_update2(m->channel, (net_addr *) &n, NULL, m->channel->proto->main_source);
1087}
1088
1089static void
1090mpls_apply_fec(rte *r, struct mpls_fec *fec, linpool *lp)
1091{
1092 struct ea_list *ea = lp_allocz(lp, sizeof(struct ea_list) + 2 * sizeof(eattr));
1093
1094 rta *old_attrs = r->attrs;
1095
1096 if (rta_is_cached(old_attrs))
1097 r->attrs = rta_do_cow(r->attrs, lp);
1098
1099 *ea = (struct ea_list) {
1100 .next = r->attrs->eattrs,
1101 .flags = EALF_SORTED,
1102 .count = 2,
1103 };
1104
1105 ea->attrs[0] = (struct eattr) {
1106 .id = EA_MPLS_LABEL,
1107 .type = EAF_TYPE_INT,
1108 .u.data = fec->label,
1109 };
1110
1111 ea->attrs[1] = (struct eattr) {
1112 .id = EA_MPLS_POLICY,
1113 .type = EAF_TYPE_INT,
1114 .u.data = fec->policy,
1115 };
1116
1117 r->attrs->eattrs = ea;
1118
9ca86ef6
OZ
1119 if (fec->policy == MPLS_POLICY_VRF)
1120 {
1121 r->attrs->hostentry = NULL;
1122 r->attrs->dest = RTD_UNICAST;
1123 r->attrs->nh = (struct nexthop) { .iface = fec->iface };
1124 }
1125
333ddd4f
OZ
1126 if (rta_is_cached(old_attrs))
1127 {
1128 r->attrs = rta_lookup(r->attrs);
1129 rta_free(old_attrs);
1130 }
1131}
1132
1133
9b775859 1134int
333ddd4f
OZ
1135mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r, linpool *lp, struct mpls_fec **locked_fec)
1136{
1137 ASSERT(!(r->flags & REF_COW));
1138
1139 struct mpls_fec *fec = NULL;
1140
333ddd4f
OZ
1141 /* Select FEC for route */
1142 uint policy = ea_get_int(r->attrs->eattrs, EA_MPLS_POLICY, 0);
1143 switch (policy)
1144 {
1145 case MPLS_POLICY_NONE:
9b775859 1146 return 0;
333ddd4f
OZ
1147
1148 case MPLS_POLICY_STATIC:;
1149 uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
1150
1151 if (label < 16)
9b775859 1152 return 0;
333ddd4f
OZ
1153
1154 fec = mpls_get_fec_by_label(m, label);
9b775859
OZ
1155 if (!fec)
1156 {
1157 log(L_WARN "Static label %u failed for %N from %s",
1158 label, n, r->sender->proto->name);
1159 return -1;
1160 }
1161
333ddd4f
OZ
1162 mpls_damage_fec(m, fec);
1163 break;
1164
1165 case MPLS_POLICY_PREFIX:
1166 fec = mpls_get_fec_by_net(m, n, r->src->private_id);
1167 mpls_damage_fec(m, fec);
1168 break;
1169
1170 case MPLS_POLICY_AGGREGATE:;
1171 uint class = ea_get_int(r->attrs->eattrs, EA_MPLS_CLASS, 0);
1172 fec = mpls_get_fec_by_rta(m, r->attrs, class);
1173 break;
1174
9ca86ef6
OZ
1175 case MPLS_POLICY_VRF:
1176 if (!m->vrf_iface)
9b775859 1177 return 0;
9ca86ef6
OZ
1178
1179 fec = mpls_get_fec_for_vrf(m);
1180 break;
1181
333ddd4f
OZ
1182 default:
1183 log(L_WARN "Route %N has invalid MPLS policy %u", n, policy);
9b775859
OZ
1184 return -1;
1185 }
1186
1187 /* Label allocation failure */
1188 if (!fec)
1189 {
1190 log(L_WARN "Label allocation in range %s failed for %N from %s",
1191 m->handle->range->name, n, r->sender->proto->name);
1192 return -1;
333ddd4f
OZ
1193 }
1194
1195 /* Temporarily lock FEC */
1196 mpls_lock_fec(m, fec);
1197 *locked_fec = fec;
1198
1199 /* Apply FEC label to route */
1200 mpls_apply_fec(r, fec, lp);
1201
1202 /* Announce MPLS rule for new/updated FEC */
1203 if (fec->state != MPLS_FEC_CLEAN)
1204 mpls_announce_fec(m, fec, r->attrs);
9b775859
OZ
1205
1206 return 0;
333ddd4f
OZ
1207}
1208
1209void
1210mpls_handle_rte_cleanup(struct mpls_fec_map *m, struct mpls_fec **locked_fec)
1211{
1212 /* Unlock temporarily locked FEC from mpls_handle_rte() */
1213 if (*locked_fec)
1214 {
1215 mpls_unlock_fec(m, *locked_fec);
1216 *locked_fec = NULL;
1217 }
1218}
1219
1220void
1221mpls_rte_insert(net *n UNUSED, rte *r)
1222{
1223 struct proto *p = r->src->proto;
1224 struct mpls_fec_map *m = p->mpls_map;
1225
1226 uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
1227 if (label < 16)
1228 return;
1229
1230 struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
1231 if (!fec)
1232 return;
1233
1234 mpls_lock_fec(m, fec);
1235}
1236
1237void
1238mpls_rte_remove(net *n UNUSED, rte *r)
1239{
1240 struct proto *p = r->src->proto;
1241 struct mpls_fec_map *m = p->mpls_map;
1242
1243 uint label = ea_get_int(r->attrs->eattrs, EA_MPLS_LABEL, 0);
1244 if (label < 16)
1245 return;
1246
1247 struct mpls_fec *fec = mpls_find_fec_by_label(m, label);
1248 if (!fec)
1249 return;
1250
1251 mpls_unlock_fec(m, fec);
1252}
8e9e013b
OZ
1253
1254static void
1255mpls_show_ranges_rng(struct mpls_show_ranges_cmd *cmd, struct mpls_range *r)
1256{
1257 uint last = lmap_last_one_in_range(&cmd->dom->labels, r->lo, r->hi);
1258 if (last == r->hi) last = 0;
1259
1260 cli_msg(-1026, "%-11s %7u %7u %7u %7u %7u",
1261 r->name, r->lo, r->hi - r->lo, r->hi, r->label_count, last);
1262}
1263
1264void
1265mpls_show_ranges_dom(struct mpls_show_ranges_cmd *cmd, struct mpls_domain *m)
1266{
1267 if (cmd->dom)
1268 cli_msg(-1026, "");
1269
1270 cmd->dom = m;
1271 cli_msg(-1026, "MPLS domain %s:", m->name);
1272 cli_msg(-1026, "%-11s %7s %7s %7s %7s %7s",
1273 "Range", "Start", "Length", "End", "Labels", "Last");
1274
1275 if (cmd->range)
1276 mpls_show_ranges_rng(cmd, cmd->range->range);
1277 else
1278 {
1279 struct mpls_range *r;
1280 WALK_LIST(r, m->ranges)
1281 if (!r->removed)
1282 mpls_show_ranges_rng(cmd, r);
1283 }
1284}
1285
1286void
1287mpls_show_ranges(struct mpls_show_ranges_cmd *cmd)
1288{
1289 if (cmd->domain)
1290 mpls_show_ranges_dom(cmd, cmd->domain->domain);
1291 else
1292 {
1293 struct mpls_domain *m;
1294 WALK_LIST(m, mpls_domains)
1295 mpls_show_ranges_dom(cmd, m);
1296 }
1297
1298 cli_msg(0, "");
1299}