]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_mt.c
Merge pull request #11355 from patrasar/pim6reg
[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_STANDARD:
60 return "standard";
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, "standard"))
84 return ISIS_MT_STANDARD;
85 if (!strcmp(name, "ipv4-mgmt"))
86 return ISIS_MT_IPV4_MGMT;
87 if (!strcmp(name, "ipv6-unicast"))
88 return ISIS_MT_IPV6_UNICAST;
89 if (!strcmp(name, "ipv4-multicast"))
90 return ISIS_MT_IPV4_MULTICAST;
91 if (!strcmp(name, "ipv6-multicast"))
92 return ISIS_MT_IPV6_MULTICAST;
93 if (!strcmp(name, "ipv6-mgmt"))
94 return ISIS_MT_IPV6_MGMT;
95 if (!strcmp(name, "ipv6-dstsrc"))
96 return ISIS_MT_IPV6_DSTSRC;
97 return -1;
98 }
99
100 /* General MT settings api */
101
102 struct mt_setting {
103 ISIS_MT_INFO_FIELDS;
104 };
105
106 static void *lookup_mt_setting(struct list *mt_list, uint16_t mtid)
107 {
108 struct listnode *node;
109 struct mt_setting *setting;
110
111 for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) {
112 if (setting->mtid == mtid)
113 return setting;
114 }
115 return NULL;
116 }
117
118 static void add_mt_setting(struct list **mt_list, void *setting)
119 {
120 if (!*mt_list)
121 *mt_list = list_new();
122 listnode_add(*mt_list, setting);
123 }
124
125 /* Area specific MT settings api */
126
127 struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
128 uint16_t mtid)
129 {
130 return lookup_mt_setting(area->mt_settings, mtid);
131 }
132
133 struct isis_area_mt_setting *area_new_mt_setting(struct isis_area *area,
134 uint16_t mtid)
135 {
136 struct isis_area_mt_setting *setting;
137
138 setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting));
139 setting->mtid = mtid;
140 return setting;
141 }
142
143 static void area_free_mt_setting(void *setting)
144 {
145 XFREE(MTYPE_MT_AREA_SETTING, setting);
146 }
147
148 void area_add_mt_setting(struct isis_area *area,
149 struct isis_area_mt_setting *setting)
150 {
151 add_mt_setting(&area->mt_settings, setting);
152 }
153
154 void area_mt_init(struct isis_area *area)
155 {
156 struct isis_area_mt_setting *v4_unicast_setting;
157
158 /* MTID 0 is always enabled */
159 v4_unicast_setting = area_new_mt_setting(area, ISIS_MT_IPV4_UNICAST);
160 v4_unicast_setting->enabled = true;
161 add_mt_setting(&area->mt_settings, v4_unicast_setting);
162 area->mt_settings->del = area_free_mt_setting;
163 }
164
165 void area_mt_finish(struct isis_area *area)
166 {
167 list_delete(&area->mt_settings);
168 }
169
170 struct isis_area_mt_setting *area_get_mt_setting(struct isis_area *area,
171 uint16_t mtid)
172 {
173 struct isis_area_mt_setting *setting;
174
175 setting = area_lookup_mt_setting(area, mtid);
176 if (!setting) {
177 setting = area_new_mt_setting(area, mtid);
178 area_add_mt_setting(area, setting);
179 }
180 return setting;
181 }
182
183 int area_write_mt_settings(struct isis_area *area, struct vty *vty)
184 {
185 int written = 0;
186 struct listnode *node;
187 struct isis_area_mt_setting *setting;
188
189 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
190 const char *name = isis_mtid2str(setting->mtid);
191 if (name && setting->enabled) {
192 if (setting->mtid == ISIS_MT_IPV4_UNICAST)
193 continue; /* always enabled, no need to write
194 out config */
195 vty_out(vty, " topology %s%s\n", name,
196 setting->overload ? " overload" : "");
197 written++;
198 }
199 }
200 return written;
201 }
202
203 bool area_is_mt(struct isis_area *area)
204 {
205 struct listnode *node, *node2;
206 struct isis_area_mt_setting *setting;
207 struct isis_circuit *circuit;
208 struct isis_circuit_mt_setting *csetting;
209
210 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
211 if (setting->enabled && setting->mtid != ISIS_MT_IPV4_UNICAST)
212 return true;
213 }
214 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
215 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node2,
216 csetting)) {
217 if (!csetting->enabled
218 && csetting->mtid == ISIS_MT_IPV4_UNICAST)
219 return true;
220 }
221 }
222
223 return false;
224 }
225
226 struct isis_area_mt_setting **area_mt_settings(struct isis_area *area,
227 unsigned int *mt_count)
228 {
229 static unsigned int size = 0;
230 static struct isis_area_mt_setting **rv = NULL;
231
232 unsigned int count = 0;
233 struct listnode *node;
234 struct isis_area_mt_setting *setting;
235
236 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
237 if (!setting->enabled)
238 continue;
239
240 count++;
241 if (count > size) {
242 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
243 size = count;
244 }
245 rv[count - 1] = setting;
246 }
247
248 *mt_count = count;
249 return rv;
250 }
251
252 /* Circuit specific MT settings api */
253
254 struct isis_circuit_mt_setting *
255 circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
256 {
257 return lookup_mt_setting(circuit->mt_settings, mtid);
258 }
259
260 struct isis_circuit_mt_setting *
261 circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
262 {
263 struct isis_circuit_mt_setting *setting;
264
265 setting = XCALLOC(MTYPE_MT_CIRCUIT_SETTING, sizeof(*setting));
266 setting->mtid = mtid;
267 setting->enabled = true; /* Enabled is default for circuit */
268 return setting;
269 }
270
271 static void circuit_free_mt_setting(void *setting)
272 {
273 XFREE(MTYPE_MT_CIRCUIT_SETTING, setting);
274 }
275
276 void circuit_add_mt_setting(struct isis_circuit *circuit,
277 struct isis_circuit_mt_setting *setting)
278 {
279 add_mt_setting(&circuit->mt_settings, setting);
280 }
281
282 void circuit_mt_init(struct isis_circuit *circuit)
283 {
284 circuit->mt_settings = list_new();
285 circuit->mt_settings->del = circuit_free_mt_setting;
286 }
287
288 void circuit_mt_finish(struct isis_circuit *circuit)
289 {
290 list_delete(&circuit->mt_settings);
291 }
292
293 struct isis_circuit_mt_setting *
294 circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
295 {
296 struct isis_circuit_mt_setting *setting;
297
298 setting = circuit_lookup_mt_setting(circuit, mtid);
299 if (!setting) {
300 setting = circuit_new_mt_setting(circuit, mtid);
301 circuit_add_mt_setting(circuit, setting);
302 }
303 return setting;
304 }
305
306 #ifdef FABRICD
307 static int circuit_write_mt_settings(struct isis_circuit *circuit,
308 struct vty *vty)
309 {
310 int written = 0;
311 struct listnode *node;
312 struct isis_circuit_mt_setting *setting;
313
314 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) {
315 const char *name = isis_mtid2str(setting->mtid);
316 if (name && !setting->enabled) {
317 vty_out(vty, " no " PROTO_NAME " topology %s\n", name);
318 written++;
319 }
320 }
321 return written;
322 }
323 #endif
324
325 struct isis_circuit_mt_setting **
326 circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
327 {
328 static unsigned int size = 0;
329 static struct isis_circuit_mt_setting **rv = NULL;
330
331 struct isis_area_mt_setting **area_settings;
332 unsigned int area_count;
333
334 unsigned int count = 0;
335
336 struct listnode *node;
337 struct isis_circuit_mt_setting *setting;
338
339 area_settings = area_mt_settings(circuit->area, &area_count);
340
341 for (unsigned int i = 0; i < area_count; i++) {
342 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node,
343 setting)) {
344 if (setting->mtid != area_settings[i]->mtid)
345 continue;
346 break;
347 }
348 if (!setting)
349 setting = circuit_get_mt_setting(
350 circuit, area_settings[i]->mtid);
351
352 if (!setting->enabled)
353 continue;
354
355 count++;
356 if (count > size) {
357 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
358 size = count;
359 }
360 rv[count - 1] = setting;
361 }
362
363 *mt_count = count;
364 return rv;
365 }
366
367 /* ADJ specific MT API */
368 static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
369 uint16_t mtid)
370 {
371 if (adj->mt_count < index + 1) {
372 adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set,
373 (index + 1) * sizeof(*adj->mt_set));
374 adj->mt_count = index + 1;
375 }
376 adj->mt_set[index] = mtid;
377 }
378
379 bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable,
380 struct isis_adjacency *adj)
381 {
382 struct isis_circuit_mt_setting **mt_settings;
383 unsigned int circuit_mt_count;
384
385 unsigned int intersect_count = 0;
386
387 uint16_t *old_mt_set = NULL;
388 unsigned int old_mt_count;
389
390 old_mt_count = adj->mt_count;
391 if (old_mt_count) {
392 old_mt_set =
393 XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set));
394 memcpy(old_mt_set, adj->mt_set,
395 old_mt_count * sizeof(*old_mt_set));
396 }
397
398 mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count);
399 for (unsigned int i = 0; i < circuit_mt_count; i++) {
400 if (!tlvs->mt_router_info.count
401 && !tlvs->mt_router_info_empty) {
402 /* Other end does not have MT enabled */
403 if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST
404 && (v4_usable || v6_usable))
405 adj_mt_set(adj, intersect_count++,
406 ISIS_MT_IPV4_UNICAST);
407 } else {
408 struct isis_mt_router_info *info_head;
409
410 info_head = (struct isis_mt_router_info *)
411 tlvs->mt_router_info.head;
412 for (struct isis_mt_router_info *info = info_head; info;
413 info = info->next) {
414 if (mt_settings[i]->mtid == info->mtid) {
415 bool usable;
416 switch (info->mtid) {
417 case ISIS_MT_IPV4_UNICAST:
418 case ISIS_MT_IPV4_MGMT:
419 case ISIS_MT_IPV4_MULTICAST:
420 usable = v4_usable;
421 break;
422 case ISIS_MT_IPV6_UNICAST:
423 case ISIS_MT_IPV6_MGMT:
424 case ISIS_MT_IPV6_MULTICAST:
425 usable = v6_usable;
426 break;
427 default:
428 usable = true;
429 break;
430 }
431 if (usable)
432 adj_mt_set(adj,
433 intersect_count++,
434 info->mtid);
435 }
436 }
437 }
438 }
439 adj->mt_count = intersect_count;
440
441 bool changed = false;
442
443 if (adj->mt_count != old_mt_count)
444 changed = true;
445
446 if (!changed && old_mt_count
447 && memcmp(adj->mt_set, old_mt_set,
448 old_mt_count * sizeof(*old_mt_set)))
449 changed = true;
450
451 if (old_mt_count)
452 XFREE(MTYPE_TMP, old_mt_set);
453
454 return changed;
455 }
456
457 bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid)
458 {
459 for (unsigned int i = 0; i < adj->mt_count; i++)
460 if (adj->mt_set[i] == mtid)
461 return true;
462 return false;
463 }
464
465 void adj_mt_finish(struct isis_adjacency *adj)
466 {
467 XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
468 adj->mt_count = 0;
469 }
470
471 static void mt_set_add(uint16_t **mt_set, unsigned int *size,
472 unsigned int *index, uint16_t mtid)
473 {
474 for (unsigned int i = 0; i < *index; i++) {
475 if ((*mt_set)[i] == mtid)
476 return;
477 }
478
479 if (*index >= *size) {
480 *mt_set = XREALLOC(MTYPE_TMP, *mt_set,
481 sizeof(**mt_set) * ((*index) + 1));
482 *size = (*index) + 1;
483 }
484
485 (*mt_set)[*index] = mtid;
486 *index = (*index) + 1;
487 }
488
489 static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
490 unsigned int *mt_count)
491 {
492 static uint16_t *rv;
493 static unsigned int size;
494 struct listnode *node;
495 struct isis_adjacency *adj;
496
497 unsigned int count = 0;
498
499 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
500 *mt_count = 0;
501 return NULL;
502 }
503
504 for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) {
505 if (adj->adj_state != ISIS_ADJ_UP)
506 continue;
507 for (unsigned int i = 0; i < adj->mt_count; i++)
508 mt_set_add(&rv, &size, &count, adj->mt_set[i]);
509 }
510
511 *mt_count = count;
512 return rv;
513 }
514
515 static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
516 unsigned int mt_count, uint16_t *mt_set,
517 uint8_t *id, uint32_t metric,
518 struct isis_ext_subtlvs *ext)
519 {
520 /* Check if MT is enable for this area */
521 if (!area_is_mt(area)) {
522 lsp_debug(
523 "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)",
524 area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id));
525 isis_tlvs_add_extended_reach(tlvs, ISIS_MT_DISABLE, id, metric,
526 ext);
527 return;
528 }
529
530 /* Process Multi-Topology */
531 for (unsigned int i = 0; i < mt_count; i++) {
532 uint16_t mtid = mt_set[i];
533 if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
534 lsp_debug(
535 "ISIS (%s): Adding %s.%02x as te-style neighbor",
536 area->area_tag, sysid_print(id),
537 LSP_PSEUDO_ID(id));
538 } else {
539 lsp_debug(
540 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
541 area->area_tag, sysid_print(id),
542 LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
543 }
544 isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext);
545 }
546 }
547
548 void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
549 int level, uint8_t *id, uint32_t metric)
550 {
551 unsigned int mt_count;
552 uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count);
553
554 tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric,
555 circuit->ext);
556 }
557
558 void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
559 uint8_t *id, uint32_t metric)
560 {
561 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
562
563 tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id,
564 metric, circuit->ext);
565 }
566
567 void mt_init(void)
568 {
569 #ifdef FABRICD
570 hook_register(isis_circuit_config_write,
571 circuit_write_mt_settings);
572 #endif
573 }