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