]>
Commit | Line | Data |
---|---|---|
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 | |
89 | static struct mpls_range *mpls_new_range(struct mpls_domain *m, struct mpls_range_config *cf); | |
90 | static struct mpls_range *mpls_find_range_(list *l, const char *name); | |
91 | static int mpls_reconfigure_range(struct mpls_domain *m, struct mpls_range *r, struct mpls_range_config *cf); | |
92 | static void mpls_remove_range(struct mpls_range *r); | |
93 | ||
94 | ||
95 | /* | |
96 | * MPLS domain | |
97 | */ | |
98 | ||
99 | list mpls_domains; | |
100 | ||
101 | void | |
102 | mpls_init(void) | |
103 | { | |
104 | init_list(&mpls_domains); | |
105 | } | |
106 | ||
107 | struct mpls_domain_config * | |
108 | mpls_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 |
138 | static int |
139 | mpls_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 |
147 | void |
148 | mpls_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 | ||
186 | static struct mpls_domain * | |
187 | mpls_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 | ||
212 | static struct mpls_domain * | |
213 | mpls_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 | ||
224 | static int | |
225 | mpls_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 | ||
263 | static void | |
264 | mpls_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 | ||
279 | static void | |
280 | mpls_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 | ||
289 | void | |
290 | mpls_lock_domain(struct mpls_domain *m) | |
291 | { | |
292 | m->use_count++; | |
293 | } | |
294 | ||
295 | void | |
296 | mpls_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 | ||
305 | void | |
306 | mpls_preconfig(struct config *c) | |
307 | { | |
308 | init_list(&c->mpls_domains); | |
309 | } | |
310 | ||
311 | void | |
312 | mpls_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 | ||
346 | struct mpls_range_config * | |
347 | mpls_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 | ||
364 | static struct mpls_range * | |
365 | mpls_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 | ||
380 | static struct mpls_range * | |
381 | mpls_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 | ||
392 | static int | |
fcf22586 | 393 | mpls_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 | ||
411 | static void | |
412 | mpls_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 | ||
421 | static void | |
422 | mpls_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 | ||
434 | void | |
435 | mpls_lock_range(struct mpls_range *r) | |
436 | { | |
437 | r->use_count++; | |
438 | } | |
439 | ||
440 | void | |
441 | mpls_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 | ||
455 | struct mpls_handle * | |
456 | mpls_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 | ||
468 | void | |
469 | mpls_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 | ||
483 | uint | |
b6385dec | 484 | mpls_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 | ||
502 | void | |
503 | mpls_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 |
520 | void |
521 | mpls_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 | ||
545 | static void | |
546 | mpls_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 | ||
556 | static int | |
557 | mpls_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 | /* | |
568 | static void | |
569 | mpls_channel_shutdown(struct channel *C) | |
570 | { | |
571 | struct mpls_channel *c = (void *) C; | |
572 | ||
573 | } | |
574 | */ | |
575 | ||
576 | static void | |
577 | mpls_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 | ||
585 | static int | |
a7a9df86 | 586 | mpls_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 | ||
615 | void | |
616 | mpls_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 | ||
633 | struct 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 | ||
675 | HASH_DEFINE_REHASH_FN(NET, struct mpls_fec) | |
676 | HASH_DEFINE_REHASH_FN(RTA, struct mpls_fec) | |
677 | HASH_DEFINE_REHASH_FN(LABEL, struct mpls_fec) | |
678 | ||
679 | ||
a7a9df86 | 680 | static void mpls_unlink_fec(struct mpls_fec_map *m, struct mpls_fec *fec); |
333ddd4f OZ |
681 | static void mpls_withdraw_fec(struct mpls_fec_map *m, struct mpls_fec *fec); |
682 | static rta * mpls_get_key_rta(struct mpls_fec_map *m, const rta *src); | |
683 | ||
684 | struct mpls_fec_map * | |
685 | mpls_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 |
708 | void |
709 | mpls_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 |
769 | void |
770 | mpls_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 | ||
802 | static slab * | |
803 | mpls_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 | ||
814 | struct mpls_fec * | |
815 | mpls_find_fec_by_label(struct mpls_fec_map *m, u32 label) | |
816 | { | |
817 | return HASH_FIND(m->label_hash, LABEL, label); | |
818 | } | |
819 | ||
820 | struct mpls_fec * | |
821 | mpls_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 | ||
849 | struct mpls_fec * | |
be09b030 | 850 | mpls_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 | ||
884 | struct mpls_fec * | |
885 | mpls_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 |
926 | struct mpls_fec * |
927 | mpls_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 |
954 | static void |
955 | mpls_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 | ||
982 | void | |
983 | mpls_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 | ||
1002 | static inline void mpls_lock_fec(struct mpls_fec_map *x UNUSED, struct mpls_fec *fec) | |
1003 | { if (fec) fec->uc++; } | |
1004 | ||
1005 | static 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 | ||
1008 | static inline void | |
1009 | mpls_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 | ||
1015 | static rta * | |
1016 | mpls_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 | ||
1043 | static void | |
1044 | mpls_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 | ||
1080 | static void | |
1081 | mpls_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 | ||
1089 | static void | |
1090 | mpls_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 | 1134 | int |
333ddd4f OZ |
1135 | mpls_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 | ||
1209 | void | |
1210 | mpls_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 | ||
1220 | void | |
1221 | mpls_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 | ||
1237 | void | |
1238 | mpls_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 | |
1254 | static void | |
1255 | mpls_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 | ||
1264 | void | |
1265 | mpls_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 | ||
1286 | void | |
1287 | mpls_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 | } |