]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_mt.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[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 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 *
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_memory.h"
25 #include "isisd/isis_circuit.h"
26 #include "isisd/isis_adjacency.h"
27 #include "isisd/isis_misc.h"
28 #include "isisd/isis_lsp.h"
29 #include "isisd/isis_mt.h"
30 #include "isisd/isis_tlvs.h"
31
32 DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting")
33 DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
34 DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
35
36 bool 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
44 uint16_t isis_area_ipv6_topology(struct isis_area *area)
45 {
46 struct isis_area_mt_setting *area_mt_setting;
47 area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST);
48
49 if (area_mt_setting && area_mt_setting->enabled)
50 return ISIS_MT_IPV6_UNICAST;
51 return ISIS_MT_IPV4_UNICAST;
52 }
53
54 /* MT naming api */
55 const char *isis_mtid2str(uint16_t mtid)
56 {
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";
72 case ISIS_MT_IPV6_DSTSRC:
73 return "ipv6-dstsrc";
74 default:
75 snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
76 return buf;
77 }
78 }
79
80 uint16_t isis_str2mtid(const char *name)
81 {
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;
94 if (!strcmp(name, "ipv6-dstsrc"))
95 return ISIS_MT_IPV6_DSTSRC;
96 return -1;
97 }
98
99 /* General MT settings api */
100
101 struct mt_setting {
102 ISIS_MT_INFO_FIELDS;
103 };
104
105 static void *lookup_mt_setting(struct list *mt_list, uint16_t mtid)
106 {
107 struct listnode *node;
108 struct mt_setting *setting;
109
110 for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) {
111 if (setting->mtid == mtid)
112 return setting;
113 }
114 return NULL;
115 }
116
117 static void add_mt_setting(struct list **mt_list, void *setting)
118 {
119 if (!*mt_list)
120 *mt_list = list_new();
121 listnode_add(*mt_list, setting);
122 }
123
124 /* Area specific MT settings api */
125
126 struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
127 uint16_t mtid)
128 {
129 return lookup_mt_setting(area->mt_settings, mtid);
130 }
131
132 struct isis_area_mt_setting *area_new_mt_setting(struct isis_area *area,
133 uint16_t mtid)
134 {
135 struct isis_area_mt_setting *setting;
136
137 setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting));
138 setting->mtid = mtid;
139 return setting;
140 }
141
142 static void area_free_mt_setting(void *setting)
143 {
144 XFREE(MTYPE_MT_AREA_SETTING, setting);
145 }
146
147 void area_add_mt_setting(struct isis_area *area,
148 struct isis_area_mt_setting *setting)
149 {
150 add_mt_setting(&area->mt_settings, setting);
151 }
152
153 void area_mt_init(struct isis_area *area)
154 {
155 struct isis_area_mt_setting *v4_unicast_setting;
156
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;
162 }
163
164 void area_mt_finish(struct isis_area *area)
165 {
166 list_delete(&area->mt_settings);
167 }
168
169 struct isis_area_mt_setting *area_get_mt_setting(struct isis_area *area,
170 uint16_t mtid)
171 {
172 struct isis_area_mt_setting *setting;
173
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;
180 }
181
182 int area_write_mt_settings(struct isis_area *area, struct vty *vty)
183 {
184 int written = 0;
185 struct listnode *node;
186 struct isis_area_mt_setting *setting;
187
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;
200 }
201
202 bool area_is_mt(struct isis_area *area)
203 {
204 struct listnode *node, *node2;
205 struct isis_area_mt_setting *setting;
206 struct isis_circuit *circuit;
207 struct isis_circuit_mt_setting *csetting;
208
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 }
221
222 return false;
223 }
224
225 struct isis_area_mt_setting **area_mt_settings(struct isis_area *area,
226 unsigned int *mt_count)
227 {
228 static unsigned int size = 0;
229 static struct isis_area_mt_setting **rv = NULL;
230
231 unsigned int count = 0;
232 struct listnode *node;
233 struct isis_area_mt_setting *setting;
234
235 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
236 if (!setting->enabled)
237 continue;
238
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 }
246
247 *mt_count = count;
248 return rv;
249 }
250
251 /* Circuit specific MT settings api */
252
253 struct isis_circuit_mt_setting *
254 circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
255 {
256 return lookup_mt_setting(circuit->mt_settings, mtid);
257 }
258
259 struct isis_circuit_mt_setting *
260 circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
261 {
262 struct isis_circuit_mt_setting *setting;
263
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;
268 }
269
270 static void circuit_free_mt_setting(void *setting)
271 {
272 XFREE(MTYPE_MT_CIRCUIT_SETTING, setting);
273 }
274
275 void circuit_add_mt_setting(struct isis_circuit *circuit,
276 struct isis_circuit_mt_setting *setting)
277 {
278 add_mt_setting(&circuit->mt_settings, setting);
279 }
280
281 void circuit_mt_init(struct isis_circuit *circuit)
282 {
283 circuit->mt_settings = list_new();
284 circuit->mt_settings->del = circuit_free_mt_setting;
285 }
286
287 void circuit_mt_finish(struct isis_circuit *circuit)
288 {
289 list_delete(&circuit->mt_settings);
290 }
291
292 struct isis_circuit_mt_setting *
293 circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
294 {
295 struct isis_circuit_mt_setting *setting;
296
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;
303 }
304
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
322 struct isis_circuit_mt_setting **
323 circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
324 {
325 static unsigned int size = 0;
326 static struct isis_circuit_mt_setting **rv = NULL;
327
328 struct isis_area_mt_setting **area_settings;
329 unsigned int area_count;
330
331 unsigned int count = 0;
332
333 struct listnode *node;
334 struct isis_circuit_mt_setting *setting;
335
336 area_settings = area_mt_settings(circuit->area, &area_count);
337
338 for (unsigned int i = 0; i < area_count; i++) {
339 for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node,
340 setting)) {
341 if (setting->mtid != area_settings[i]->mtid)
342 continue;
343 break;
344 }
345 if (!setting)
346 setting = circuit_get_mt_setting(
347 circuit, area_settings[i]->mtid);
348
349 if (!setting->enabled)
350 continue;
351
352 count++;
353 if (count > size) {
354 rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv));
355 size = count;
356 }
357 rv[count - 1] = setting;
358 }
359
360 *mt_count = count;
361 return rv;
362 }
363
364 /* ADJ specific MT API */
365 static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
366 uint16_t mtid)
367 {
368 if (adj->mt_count < index + 1) {
369 adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set,
370 (index + 1) * sizeof(*adj->mt_set));
371 adj->mt_count = index + 1;
372 }
373 adj->mt_set[index] = mtid;
374 }
375
376 bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable,
377 struct isis_adjacency *adj)
378 {
379 struct isis_circuit_mt_setting **mt_settings;
380 unsigned int circuit_mt_count;
381
382 unsigned int intersect_count = 0;
383
384 uint16_t *old_mt_set = NULL;
385 unsigned int old_mt_count;
386
387 old_mt_count = adj->mt_count;
388 if (old_mt_count) {
389 old_mt_set =
390 XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set));
391 memcpy(old_mt_set, adj->mt_set,
392 old_mt_count * sizeof(*old_mt_set));
393 }
394
395 mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count);
396 for (unsigned int i = 0; i < circuit_mt_count; i++) {
397 if (!tlvs->mt_router_info.count
398 && !tlvs->mt_router_info_empty) {
399 /* Other end does not have MT enabled */
400 if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST
401 && (v4_usable || v6_usable))
402 adj_mt_set(adj, intersect_count++,
403 ISIS_MT_IPV4_UNICAST);
404 } else {
405 struct isis_mt_router_info *info_head;
406
407 info_head = (struct isis_mt_router_info *)
408 tlvs->mt_router_info.head;
409 for (struct isis_mt_router_info *info = info_head; info;
410 info = info->next) {
411 if (mt_settings[i]->mtid == info->mtid) {
412 bool usable;
413 switch (info->mtid) {
414 case ISIS_MT_IPV4_UNICAST:
415 case ISIS_MT_IPV4_MGMT:
416 case ISIS_MT_IPV4_MULTICAST:
417 usable = v4_usable;
418 break;
419 case ISIS_MT_IPV6_UNICAST:
420 case ISIS_MT_IPV6_MGMT:
421 case ISIS_MT_IPV6_MULTICAST:
422 usable = v6_usable;
423 break;
424 default:
425 usable = true;
426 break;
427 }
428 if (usable)
429 adj_mt_set(adj,
430 intersect_count++,
431 info->mtid);
432 }
433 }
434 }
435 }
436 adj->mt_count = intersect_count;
437
438 bool changed = false;
439
440 if (adj->mt_count != old_mt_count)
441 changed = true;
442
443 if (!changed && old_mt_count
444 && memcmp(adj->mt_set, old_mt_set,
445 old_mt_count * sizeof(*old_mt_set)))
446 changed = true;
447
448 if (old_mt_count)
449 XFREE(MTYPE_TMP, old_mt_set);
450
451 return changed;
452 }
453
454 bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid)
455 {
456 for (unsigned int i = 0; i < adj->mt_count; i++)
457 if (adj->mt_set[i] == mtid)
458 return true;
459 return false;
460 }
461
462 void adj_mt_finish(struct isis_adjacency *adj)
463 {
464 XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
465 adj->mt_count = 0;
466 }
467
468 static void mt_set_add(uint16_t **mt_set, unsigned int *size,
469 unsigned int *index, uint16_t mtid)
470 {
471 for (unsigned int i = 0; i < *index; i++) {
472 if ((*mt_set)[i] == mtid)
473 return;
474 }
475
476 if (*index >= *size) {
477 *mt_set = XREALLOC(MTYPE_TMP, *mt_set,
478 sizeof(**mt_set) * ((*index) + 1));
479 *size = (*index) + 1;
480 }
481
482 (*mt_set)[*index] = mtid;
483 *index = (*index) + 1;
484 }
485
486 static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
487 unsigned int *mt_count)
488 {
489 static uint16_t *rv;
490 static unsigned int size;
491 struct listnode *node;
492 struct isis_adjacency *adj;
493
494 unsigned int count = 0;
495
496 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
497 *mt_count = 0;
498 return NULL;
499 }
500
501 for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) {
502 if (adj->adj_state != ISIS_ADJ_UP)
503 continue;
504 for (unsigned int i = 0; i < adj->mt_count; i++)
505 mt_set_add(&rv, &size, &count, adj->mt_set[i]);
506 }
507
508 *mt_count = count;
509 return rv;
510 }
511
512 static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
513 unsigned int mt_count, uint16_t *mt_set,
514 uint8_t *id, uint32_t metric, uint8_t *subtlvs,
515 uint8_t subtlv_len)
516 {
517 for (unsigned int i = 0; i < mt_count; i++) {
518 uint16_t mtid = mt_set[i];
519 if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
520 lsp_debug(
521 "ISIS (%s): Adding %s.%02x as te-style neighbor",
522 area->area_tag, sysid_print(id),
523 LSP_PSEUDO_ID(id));
524 } else {
525 lsp_debug(
526 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
527 area->area_tag, sysid_print(id),
528 LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
529 }
530 isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, subtlvs,
531 subtlv_len);
532 }
533 }
534
535 void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
536 int level, uint8_t *id, uint32_t metric,
537 uint8_t *subtlvs, uint8_t subtlv_len)
538 {
539 unsigned int mt_count;
540 uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count);
541
542 tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric,
543 subtlvs, subtlv_len);
544 }
545
546 void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit,
547 uint8_t *id, uint32_t metric, uint8_t *subtlvs,
548 uint8_t subtlv_len)
549 {
550 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
551
552 tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id,
553 metric, subtlvs, subtlv_len);
554 }
555
556 void mt_init(void)
557 {
558 hook_register(isis_circuit_config_write,
559 circuit_write_mt_settings);
560 }