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