]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_mt.c
Merge pull request #2758 from donaldsharp/pim_join
[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 *
6 * This file is part of FreeRangeRouting (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 *
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
CF
31
32DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting")
33DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
d8fba7d9 34DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
c3ae3127
CF
35
36uint16_t isis_area_ipv6_topology(struct isis_area *area)
37{
d62a17ae 38 struct isis_area_mt_setting *area_mt_setting;
39 area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST);
c3ae3127 40
d62a17ae 41 if (area_mt_setting && area_mt_setting->enabled)
42 return ISIS_MT_IPV6_UNICAST;
43 return ISIS_MT_IPV4_UNICAST;
c3ae3127 44}
064f4896
CF
45
46/* MT naming api */
47const char *isis_mtid2str(uint16_t mtid)
48{
d62a17ae 49 static char buf[sizeof("65535")];
50
51 switch (mtid) {
52 case ISIS_MT_IPV4_UNICAST:
53 return "ipv4-unicast";
54 case ISIS_MT_IPV4_MGMT:
55 return "ipv4-mgmt";
56 case ISIS_MT_IPV6_UNICAST:
57 return "ipv6-unicast";
58 case ISIS_MT_IPV4_MULTICAST:
59 return "ipv4-multicast";
60 case ISIS_MT_IPV6_MULTICAST:
61 return "ipv6-multicast";
62 case ISIS_MT_IPV6_MGMT:
63 return "ipv6-mgmt";
64 default:
65 snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
66 return buf;
67 }
064f4896
CF
68}
69
70uint16_t isis_str2mtid(const char *name)
71{
d62a17ae 72 if (!strcmp(name, "ipv4-unicast"))
73 return ISIS_MT_IPV4_UNICAST;
74 if (!strcmp(name, "ipv4-mgmt"))
75 return ISIS_MT_IPV4_MGMT;
76 if (!strcmp(name, "ipv6-unicast"))
77 return ISIS_MT_IPV6_UNICAST;
78 if (!strcmp(name, "ipv4-multicast"))
79 return ISIS_MT_IPV4_MULTICAST;
80 if (!strcmp(name, "ipv6-multicast"))
81 return ISIS_MT_IPV6_MULTICAST;
82 if (!strcmp(name, "ipv6-mgmt"))
83 return ISIS_MT_IPV6_MGMT;
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{
affe9e99 154 list_delete_and_null(&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{
affe9e99 277 list_delete_and_null(&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
d62a17ae 293int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty)
064f4896 294{
d62a17ae 295 int written = 0;
296 struct listnode *node;
297 struct isis_circuit_mt_setting *setting;
064f4896 298
d62a17ae 299 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) {
300 const char *name = isis_mtid2str(setting->mtid);
301 if (name && !setting->enabled) {
302 vty_out(vty, " no isis topology %s\n", name);
303 written++;
304 }
305 }
306 return written;
064f4896 307}
99894f9a 308
d62a17ae 309struct isis_circuit_mt_setting **
99894f9a
CF
310circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
311{
d62a17ae 312 static unsigned int size = 0;
313 static struct isis_circuit_mt_setting **rv = NULL;
99894f9a 314
d62a17ae 315 struct isis_area_mt_setting **area_settings;
316 unsigned int area_count;
99894f9a 317
d62a17ae 318 unsigned int count = 0;
99894f9a 319
d62a17ae 320 struct listnode *node;
321 struct isis_circuit_mt_setting *setting;
99894f9a 322
d62a17ae 323 area_settings = area_mt_settings(circuit->area, &area_count);
99894f9a 324
d62a17ae 325 for (unsigned int i = 0; i < area_count; i++) {
326 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node,
327 setting)) {
328 if (setting->mtid != area_settings[i]->mtid)
329 continue;
330 break;
331 }
332 if (!setting)
333 setting = circuit_get_mt_setting(
334 circuit, area_settings[i]->mtid);
99894f9a 335
d62a17ae 336 if (!setting->enabled)
337 continue;
99894f9a 338
d62a17ae 339 count++;
340 if (count > size) {
341 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
342 size = count;
343 }
344 rv[count - 1] = setting;
345 }
99894f9a 346
d62a17ae 347 *mt_count = count;
348 return rv;
99894f9a 349}
d8fba7d9 350
206f4aae 351/* ADJ specific MT API */
d8fba7d9 352static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
d62a17ae 353 uint16_t mtid)
354{
355 if (adj->mt_count < index + 1) {
356 adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set,
357 (index + 1) * sizeof(*adj->mt_set));
358 adj->mt_count = index + 1;
359 }
360 adj->mt_set[index] = mtid;
361}
362
0c1bd758 363bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable,
d62a17ae 364 struct isis_adjacency *adj)
365{
366 struct isis_circuit_mt_setting **mt_settings;
367 unsigned int circuit_mt_count;
368
369 unsigned int intersect_count = 0;
370
371 uint16_t *old_mt_set = NULL;
372 unsigned int old_mt_count;
373
374 old_mt_count = adj->mt_count;
375 if (old_mt_count) {
376 old_mt_set =
377 XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set));
378 memcpy(old_mt_set, adj->mt_set,
379 old_mt_count * sizeof(*old_mt_set));
380 }
381
382 mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count);
383 for (unsigned int i = 0; i < circuit_mt_count; i++) {
af8ac8f9
CF
384 if (!tlvs->mt_router_info.count
385 && !tlvs->mt_router_info_empty) {
d62a17ae 386 /* Other end does not have MT enabled */
387 if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST
388 && v4_usable)
389 adj_mt_set(adj, intersect_count++,
390 ISIS_MT_IPV4_UNICAST);
391 } else {
0c1bd758
CF
392 struct isis_mt_router_info *info_head;
393
394 info_head = (struct isis_mt_router_info *)
395 tlvs->mt_router_info.head;
396 for (struct isis_mt_router_info *info = info_head; info;
397 info = info->next) {
d62a17ae 398 if (mt_settings[i]->mtid == info->mtid) {
399 bool usable;
400 switch (info->mtid) {
401 case ISIS_MT_IPV4_UNICAST:
402 case ISIS_MT_IPV4_MGMT:
403 case ISIS_MT_IPV4_MULTICAST:
404 usable = v4_usable;
405 break;
406 case ISIS_MT_IPV6_UNICAST:
407 case ISIS_MT_IPV6_MGMT:
408 case ISIS_MT_IPV6_MULTICAST:
409 usable = v6_usable;
410 break;
411 default:
412 usable = true;
413 break;
414 }
415 if (usable)
416 adj_mt_set(adj,
417 intersect_count++,
418 info->mtid);
419 }
420 }
421 }
422 }
423 adj->mt_count = intersect_count;
424
425 bool changed = false;
426
427 if (adj->mt_count != old_mt_count)
428 changed = true;
429
430 if (!changed && old_mt_count
431 && memcmp(adj->mt_set, old_mt_set,
432 old_mt_count * sizeof(*old_mt_set)))
433 changed = true;
434
435 if (old_mt_count)
436 XFREE(MTYPE_TMP, old_mt_set);
437
438 return changed;
439}
440
441bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid)
442{
443 for (unsigned int i = 0; i < adj->mt_count; i++)
444 if (adj->mt_set[i] == mtid)
445 return true;
446 return false;
447}
448
449void adj_mt_finish(struct isis_adjacency *adj)
450{
451 XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
452 adj->mt_count = 0;
d8fba7d9 453}
206f4aae 454
d62a17ae 455static void mt_set_add(uint16_t **mt_set, unsigned int *size,
456 unsigned int *index, uint16_t mtid)
206f4aae 457{
d62a17ae 458 for (unsigned int i = 0; i < *index; i++) {
459 if ((*mt_set)[i] == mtid)
460 return;
461 }
206f4aae 462
d62a17ae 463 if (*index >= *size) {
464 *mt_set = XREALLOC(MTYPE_TMP, *mt_set,
465 sizeof(**mt_set) * ((*index) + 1));
466 *size = (*index) + 1;
467 }
206f4aae 468
d62a17ae 469 (*mt_set)[*index] = mtid;
470 *index = (*index) + 1;
206f4aae
CF
471}
472
d62a17ae 473static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
474 unsigned int *mt_count)
206f4aae 475{
d62a17ae 476 static uint16_t *rv;
477 static unsigned int size;
478 struct listnode *node;
479 struct isis_adjacency *adj;
206f4aae 480
d62a17ae 481 unsigned int count = 0;
206f4aae 482
d62a17ae 483 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
484 *mt_count = 0;
485 return NULL;
486 }
206f4aae 487
d62a17ae 488 for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) {
489 if (adj->adj_state != ISIS_ADJ_UP)
490 continue;
491 for (unsigned int i = 0; i < adj->mt_count; i++)
492 mt_set_add(&rv, &size, &count, adj->mt_set[i]);
493 }
206f4aae 494
d62a17ae 495 *mt_count = count;
496 return rv;
206f4aae
CF
497}
498
af8ac8f9 499static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
d62a17ae 500 unsigned int mt_count, uint16_t *mt_set,
af8ac8f9
CF
501 uint8_t *id, uint32_t metric, uint8_t *subtlvs,
502 uint8_t subtlv_len)
206f4aae 503{
d62a17ae 504 for (unsigned int i = 0; i < mt_count; i++) {
505 uint16_t mtid = mt_set[i];
d62a17ae 506 if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
d62a17ae 507 lsp_debug(
508 "ISIS (%s): Adding %s.%02x as te-style neighbor",
af8ac8f9
CF
509 area->area_tag, sysid_print(id),
510 LSP_PSEUDO_ID(id));
d62a17ae 511 } else {
d62a17ae 512 lsp_debug(
513 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
af8ac8f9
CF
514 area->area_tag, sysid_print(id),
515 LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
d62a17ae 516 }
af8ac8f9
CF
517 isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, subtlvs,
518 subtlv_len);
d62a17ae 519 }
206f4aae
CF
520}
521
af8ac8f9
CF
522void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
523 int level, uint8_t *id, uint32_t metric,
524 uint8_t *subtlvs, uint8_t subtlv_len)
206f4aae 525{
d62a17ae 526 unsigned int mt_count;
527 uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count);
206f4aae 528
af8ac8f9
CF
529 tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric,
530 subtlvs, subtlv_len);
d62a17ae 531}
532
af8ac8f9
CF
533void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
534 uint8_t *id, uint32_t metric, uint8_t *subtlvs,
535 uint8_t subtlv_len)
206f4aae 536{
d62a17ae 537 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
206f4aae 538
af8ac8f9
CF
539 tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id,
540 metric, subtlvs, subtlv_len);
206f4aae 541}