]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_mt.c
isisd: Add support for RFC6119 (IPv6 TE in IS-IS)
[mirror_frr.git] / isisd / isis_mt.c
1 /*
2 * IS-IS Rout(e)ing protocol - Multi Topology Support
3 *
4 * Copyright (C) 2017 Christian Franke
5 *
6 * This file is part of FRRouting (FRR)
7 *
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 #include <zebra.h>
23 #include "isisd/isisd.h"
24 #include "isisd/isis_circuit.h"
25 #include "isisd/isis_adjacency.h"
26 #include "isisd/isis_misc.h"
27 #include "isisd/isis_lsp.h"
28 #include "isisd/isis_mt.h"
29 #include "isisd/isis_tlvs.h"
30
31 DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting");
32 DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting");
33 DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info");
34
35 bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area)
36 {
37 struct isis_area_mt_setting *area_mt_setting;
38 area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_DSTSRC);
39
40 return (area_mt_setting && area_mt_setting->enabled);
41 }
42
43 uint16_t isis_area_ipv6_topology(struct isis_area *area)
44 {
45 struct isis_area_mt_setting *area_mt_setting;
46 area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST);
47
48 if (area_mt_setting && area_mt_setting->enabled)
49 return ISIS_MT_IPV6_UNICAST;
50 return ISIS_MT_IPV4_UNICAST;
51 }
52
53 /* MT naming api */
54 const char *isis_mtid2str(uint16_t mtid)
55 {
56 static char buf[sizeof("65535")];
57
58 switch (mtid) {
59 case ISIS_MT_IPV4_UNICAST:
60 return "ipv4-unicast";
61 case ISIS_MT_IPV4_MGMT:
62 return "ipv4-mgmt";
63 case ISIS_MT_IPV6_UNICAST:
64 return "ipv6-unicast";
65 case ISIS_MT_IPV4_MULTICAST:
66 return "ipv4-multicast";
67 case ISIS_MT_IPV6_MULTICAST:
68 return "ipv6-multicast";
69 case ISIS_MT_IPV6_MGMT:
70 return "ipv6-mgmt";
71 case ISIS_MT_IPV6_DSTSRC:
72 return "ipv6-dstsrc";
73 default:
74 snprintf(buf, sizeof(buf), "%hu", mtid);
75 return buf;
76 }
77 }
78
79 uint16_t isis_str2mtid(const char *name)
80 {
81 if (!strcmp(name, "ipv4-unicast"))
82 return ISIS_MT_IPV4_UNICAST;
83 if (!strcmp(name, "ipv4-mgmt"))
84 return ISIS_MT_IPV4_MGMT;
85 if (!strcmp(name, "ipv6-unicast"))
86 return ISIS_MT_IPV6_UNICAST;
87 if (!strcmp(name, "ipv4-multicast"))
88 return ISIS_MT_IPV4_MULTICAST;
89 if (!strcmp(name, "ipv6-multicast"))
90 return ISIS_MT_IPV6_MULTICAST;
91 if (!strcmp(name, "ipv6-mgmt"))
92 return ISIS_MT_IPV6_MGMT;
93 if (!strcmp(name, "ipv6-dstsrc"))
94 return ISIS_MT_IPV6_DSTSRC;
95 return -1;
96 }
97
98 /* General MT settings api */
99
100 struct mt_setting {
101 ISIS_MT_INFO_FIELDS;
102 };
103
104 static void *lookup_mt_setting(struct list *mt_list, uint16_t mtid)
105 {
106 struct listnode *node;
107 struct mt_setting *setting;
108
109 for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) {
110 if (setting->mtid == mtid)
111 return setting;
112 }
113 return NULL;
114 }
115
116 static void add_mt_setting(struct list **mt_list, void *setting)
117 {
118 if (!*mt_list)
119 *mt_list = list_new();
120 listnode_add(*mt_list, setting);
121 }
122
123 /* Area specific MT settings api */
124
125 struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
126 uint16_t mtid)
127 {
128 return lookup_mt_setting(area->mt_settings, mtid);
129 }
130
131 struct isis_area_mt_setting *area_new_mt_setting(struct isis_area *area,
132 uint16_t mtid)
133 {
134 struct isis_area_mt_setting *setting;
135
136 setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting));
137 setting->mtid = mtid;
138 return setting;
139 }
140
141 static void area_free_mt_setting(void *setting)
142 {
143 XFREE(MTYPE_MT_AREA_SETTING, setting);
144 }
145
146 void area_add_mt_setting(struct isis_area *area,
147 struct isis_area_mt_setting *setting)
148 {
149 add_mt_setting(&area->mt_settings, setting);
150 }
151
152 void area_mt_init(struct isis_area *area)
153 {
154 struct isis_area_mt_setting *v4_unicast_setting;
155
156 /* MTID 0 is always enabled */
157 v4_unicast_setting = area_new_mt_setting(area, ISIS_MT_IPV4_UNICAST);
158 v4_unicast_setting->enabled = true;
159 add_mt_setting(&area->mt_settings, v4_unicast_setting);
160 area->mt_settings->del = area_free_mt_setting;
161 }
162
163 void area_mt_finish(struct isis_area *area)
164 {
165 list_delete(&area->mt_settings);
166 }
167
168 struct isis_area_mt_setting *area_get_mt_setting(struct isis_area *area,
169 uint16_t mtid)
170 {
171 struct isis_area_mt_setting *setting;
172
173 setting = area_lookup_mt_setting(area, mtid);
174 if (!setting) {
175 setting = area_new_mt_setting(area, mtid);
176 area_add_mt_setting(area, setting);
177 }
178 return setting;
179 }
180
181 int area_write_mt_settings(struct isis_area *area, struct vty *vty)
182 {
183 int written = 0;
184 struct listnode *node;
185 struct isis_area_mt_setting *setting;
186
187 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
188 const char *name = isis_mtid2str(setting->mtid);
189 if (name && setting->enabled) {
190 if (setting->mtid == ISIS_MT_IPV4_UNICAST)
191 continue; /* always enabled, no need to write
192 out config */
193 vty_out(vty, " topology %s%s\n", name,
194 setting->overload ? " overload" : "");
195 written++;
196 }
197 }
198 return written;
199 }
200
201 bool area_is_mt(struct isis_area *area)
202 {
203 struct listnode *node, *node2;
204 struct isis_area_mt_setting *setting;
205 struct isis_circuit *circuit;
206 struct isis_circuit_mt_setting *csetting;
207
208 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
209 if (setting->enabled && setting->mtid != ISIS_MT_IPV4_UNICAST)
210 return true;
211 }
212 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
213 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node2,
214 csetting)) {
215 if (!csetting->enabled
216 && csetting->mtid == ISIS_MT_IPV4_UNICAST)
217 return true;
218 }
219 }
220
221 return false;
222 }
223
224 struct isis_area_mt_setting **area_mt_settings(struct isis_area *area,
225 unsigned int *mt_count)
226 {
227 static unsigned int size = 0;
228 static struct isis_area_mt_setting **rv = NULL;
229
230 unsigned int count = 0;
231 struct listnode *node;
232 struct isis_area_mt_setting *setting;
233
234 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
235 if (!setting->enabled)
236 continue;
237
238 count++;
239 if (count > size) {
240 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
241 size = count;
242 }
243 rv[count - 1] = setting;
244 }
245
246 *mt_count = count;
247 return rv;
248 }
249
250 /* Circuit specific MT settings api */
251
252 struct isis_circuit_mt_setting *
253 circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
254 {
255 return lookup_mt_setting(circuit->mt_settings, mtid);
256 }
257
258 struct isis_circuit_mt_setting *
259 circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
260 {
261 struct isis_circuit_mt_setting *setting;
262
263 setting = XCALLOC(MTYPE_MT_CIRCUIT_SETTING, sizeof(*setting));
264 setting->mtid = mtid;
265 setting->enabled = true; /* Enabled is default for circuit */
266 return setting;
267 }
268
269 static void circuit_free_mt_setting(void *setting)
270 {
271 XFREE(MTYPE_MT_CIRCUIT_SETTING, setting);
272 }
273
274 void circuit_add_mt_setting(struct isis_circuit *circuit,
275 struct isis_circuit_mt_setting *setting)
276 {
277 add_mt_setting(&circuit->mt_settings, setting);
278 }
279
280 void circuit_mt_init(struct isis_circuit *circuit)
281 {
282 circuit->mt_settings = list_new();
283 circuit->mt_settings->del = circuit_free_mt_setting;
284 }
285
286 void circuit_mt_finish(struct isis_circuit *circuit)
287 {
288 list_delete(&circuit->mt_settings);
289 }
290
291 struct isis_circuit_mt_setting *
292 circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
293 {
294 struct isis_circuit_mt_setting *setting;
295
296 setting = circuit_lookup_mt_setting(circuit, mtid);
297 if (!setting) {
298 setting = circuit_new_mt_setting(circuit, mtid);
299 circuit_add_mt_setting(circuit, setting);
300 }
301 return setting;
302 }
303
304 #ifdef FABRICD
305 static int circuit_write_mt_settings(struct isis_circuit *circuit,
306 struct vty *vty)
307 {
308 int written = 0;
309 struct listnode *node;
310 struct isis_circuit_mt_setting *setting;
311
312 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) {
313 const char *name = isis_mtid2str(setting->mtid);
314 if (name && !setting->enabled) {
315 vty_out(vty, " no " PROTO_NAME " topology %s\n", name);
316 written++;
317 }
318 }
319 return written;
320 }
321 #endif
322
323 struct isis_circuit_mt_setting **
324 circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
325 {
326 static unsigned int size = 0;
327 static struct isis_circuit_mt_setting **rv = NULL;
328
329 struct isis_area_mt_setting **area_settings;
330 unsigned int area_count;
331
332 unsigned int count = 0;
333
334 struct listnode *node;
335 struct isis_circuit_mt_setting *setting;
336
337 area_settings = area_mt_settings(circuit->area, &area_count);
338
339 for (unsigned int i = 0; i < area_count; i++) {
340 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node,
341 setting)) {
342 if (setting->mtid != area_settings[i]->mtid)
343 continue;
344 break;
345 }
346 if (!setting)
347 setting = circuit_get_mt_setting(
348 circuit, area_settings[i]->mtid);
349
350 if (!setting->enabled)
351 continue;
352
353 count++;
354 if (count > size) {
355 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
356 size = count;
357 }
358 rv[count - 1] = setting;
359 }
360
361 *mt_count = count;
362 return rv;
363 }
364
365 /* ADJ specific MT API */
366 static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
367 uint16_t mtid)
368 {
369 if (adj->mt_count < index + 1) {
370 adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set,
371 (index + 1) * sizeof(*adj->mt_set));
372 adj->mt_count = index + 1;
373 }
374 adj->mt_set[index] = mtid;
375 }
376
377 bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable,
378 struct isis_adjacency *adj)
379 {
380 struct isis_circuit_mt_setting **mt_settings;
381 unsigned int circuit_mt_count;
382
383 unsigned int intersect_count = 0;
384
385 uint16_t *old_mt_set = NULL;
386 unsigned int old_mt_count;
387
388 old_mt_count = adj->mt_count;
389 if (old_mt_count) {
390 old_mt_set =
391 XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set));
392 memcpy(old_mt_set, adj->mt_set,
393 old_mt_count * sizeof(*old_mt_set));
394 }
395
396 mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count);
397 for (unsigned int i = 0; i < circuit_mt_count; i++) {
398 if (!tlvs->mt_router_info.count
399 && !tlvs->mt_router_info_empty) {
400 /* Other end does not have MT enabled */
401 if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST
402 && (v4_usable || v6_usable))
403 adj_mt_set(adj, intersect_count++,
404 ISIS_MT_IPV4_UNICAST);
405 } else {
406 struct isis_mt_router_info *info_head;
407
408 info_head = (struct isis_mt_router_info *)
409 tlvs->mt_router_info.head;
410 for (struct isis_mt_router_info *info = info_head; info;
411 info = info->next) {
412 if (mt_settings[i]->mtid == info->mtid) {
413 bool usable;
414 switch (info->mtid) {
415 case ISIS_MT_IPV4_UNICAST:
416 case ISIS_MT_IPV4_MGMT:
417 case ISIS_MT_IPV4_MULTICAST:
418 usable = v4_usable;
419 break;
420 case ISIS_MT_IPV6_UNICAST:
421 case ISIS_MT_IPV6_MGMT:
422 case ISIS_MT_IPV6_MULTICAST:
423 usable = v6_usable;
424 break;
425 default:
426 usable = true;
427 break;
428 }
429 if (usable)
430 adj_mt_set(adj,
431 intersect_count++,
432 info->mtid);
433 }
434 }
435 }
436 }
437 adj->mt_count = intersect_count;
438
439 bool changed = false;
440
441 if (adj->mt_count != old_mt_count)
442 changed = true;
443
444 if (!changed && old_mt_count
445 && memcmp(adj->mt_set, old_mt_set,
446 old_mt_count * sizeof(*old_mt_set)))
447 changed = true;
448
449 if (old_mt_count)
450 XFREE(MTYPE_TMP, old_mt_set);
451
452 return changed;
453 }
454
455 bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid)
456 {
457 for (unsigned int i = 0; i < adj->mt_count; i++)
458 if (adj->mt_set[i] == mtid)
459 return true;
460 return false;
461 }
462
463 void adj_mt_finish(struct isis_adjacency *adj)
464 {
465 XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
466 adj->mt_count = 0;
467 }
468
469 static void mt_set_add(uint16_t **mt_set, unsigned int *size,
470 unsigned int *index, uint16_t mtid)
471 {
472 for (unsigned int i = 0; i < *index; i++) {
473 if ((*mt_set)[i] == mtid)
474 return;
475 }
476
477 if (*index >= *size) {
478 *mt_set = XREALLOC(MTYPE_TMP, *mt_set,
479 sizeof(**mt_set) * ((*index) + 1));
480 *size = (*index) + 1;
481 }
482
483 (*mt_set)[*index] = mtid;
484 *index = (*index) + 1;
485 }
486
487 static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
488 unsigned int *mt_count)
489 {
490 static uint16_t *rv;
491 static unsigned int size;
492 struct listnode *node;
493 struct isis_adjacency *adj;
494
495 unsigned int count = 0;
496
497 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
498 *mt_count = 0;
499 return NULL;
500 }
501
502 for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) {
503 if (adj->adj_state != ISIS_ADJ_UP)
504 continue;
505 for (unsigned int i = 0; i < adj->mt_count; i++)
506 mt_set_add(&rv, &size, &count, adj->mt_set[i]);
507 }
508
509 *mt_count = count;
510 return rv;
511 }
512
513 static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
514 unsigned int mt_count, uint16_t *mt_set,
515 uint8_t *id, uint32_t metric,
516 struct isis_ext_subtlvs *ext)
517 {
518 /* Check if MT is enable for this area */
519 if (!area_is_mt(area)) {
520 lsp_debug(
521 "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)",
522 area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id));
523 isis_tlvs_add_extended_reach(tlvs, ISIS_MT_DISABLE, id, metric,
524 ext);
525 return;
526 }
527
528 /* Process Multi-Topology */
529 for (unsigned int i = 0; i < mt_count; i++) {
530 uint16_t mtid = mt_set[i];
531 if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
532 lsp_debug(
533 "ISIS (%s): Adding %s.%02x as te-style neighbor",
534 area->area_tag, sysid_print(id),
535 LSP_PSEUDO_ID(id));
536 } else {
537 lsp_debug(
538 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
539 area->area_tag, sysid_print(id),
540 LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
541 }
542 isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext);
543 }
544 }
545
546 void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
547 int level, uint8_t *id, uint32_t metric)
548 {
549 unsigned int mt_count;
550 uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count);
551
552 tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric,
553 circuit->ext);
554 }
555
556 void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
557 uint8_t *id, uint32_t metric)
558 {
559 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
560
561 tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id,
562 metric, circuit->ext);
563 }
564
565 void mt_init(void)
566 {
567 #ifdef FABRICD
568 hook_register(isis_circuit_config_write,
569 circuit_write_mt_settings);
570 #endif
571 }