]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_mt.c
Merge pull request #2576 from pacovn/Coverity_1399228_Logically_dead_code
[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 uint16_t isis_area_ipv6_topology(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_UNICAST);
40
41 if (area_mt_setting && area_mt_setting->enabled)
42 return ISIS_MT_IPV6_UNICAST;
43 return ISIS_MT_IPV4_UNICAST;
44 }
45
46 /* MT naming api */
47 const char *isis_mtid2str(uint16_t mtid)
48 {
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 }
68 }
69
70 uint16_t isis_str2mtid(const char *name)
71 {
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;
85 }
86
87 /* General MT settings api */
88
89 struct mt_setting {
90 ISIS_MT_INFO_FIELDS;
91 };
92
93 static void *lookup_mt_setting(struct list *mt_list, uint16_t mtid)
94 {
95 struct listnode *node;
96 struct mt_setting *setting;
97
98 for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) {
99 if (setting->mtid == mtid)
100 return setting;
101 }
102 return NULL;
103 }
104
105 static void add_mt_setting(struct list **mt_list, void *setting)
106 {
107 if (!*mt_list)
108 *mt_list = list_new();
109 listnode_add(*mt_list, setting);
110 }
111
112 /* Area specific MT settings api */
113
114 struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
115 uint16_t mtid)
116 {
117 return lookup_mt_setting(area->mt_settings, mtid);
118 }
119
120 struct isis_area_mt_setting *area_new_mt_setting(struct isis_area *area,
121 uint16_t mtid)
122 {
123 struct isis_area_mt_setting *setting;
124
125 setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting));
126 setting->mtid = mtid;
127 return setting;
128 }
129
130 static void area_free_mt_setting(void *setting)
131 {
132 XFREE(MTYPE_MT_AREA_SETTING, setting);
133 }
134
135 void area_add_mt_setting(struct isis_area *area,
136 struct isis_area_mt_setting *setting)
137 {
138 add_mt_setting(&area->mt_settings, setting);
139 }
140
141 void area_mt_init(struct isis_area *area)
142 {
143 struct isis_area_mt_setting *v4_unicast_setting;
144
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;
150 }
151
152 void area_mt_finish(struct isis_area *area)
153 {
154 list_delete_and_null(&area->mt_settings);
155 }
156
157 struct isis_area_mt_setting *area_get_mt_setting(struct isis_area *area,
158 uint16_t mtid)
159 {
160 struct isis_area_mt_setting *setting;
161
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;
168 }
169
170 int area_write_mt_settings(struct isis_area *area, struct vty *vty)
171 {
172 int written = 0;
173 struct listnode *node;
174 struct isis_area_mt_setting *setting;
175
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;
188 }
189
190 bool area_is_mt(struct isis_area *area)
191 {
192 struct listnode *node, *node2;
193 struct isis_area_mt_setting *setting;
194 struct isis_circuit *circuit;
195 struct isis_circuit_mt_setting *csetting;
196
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 }
209
210 return false;
211 }
212
213 struct isis_area_mt_setting **area_mt_settings(struct isis_area *area,
214 unsigned int *mt_count)
215 {
216 static unsigned int size = 0;
217 static struct isis_area_mt_setting **rv = NULL;
218
219 unsigned int count = 0;
220 struct listnode *node;
221 struct isis_area_mt_setting *setting;
222
223 for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) {
224 if (!setting->enabled)
225 continue;
226
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 }
234
235 *mt_count = count;
236 return rv;
237 }
238
239 /* Circuit specific MT settings api */
240
241 struct isis_circuit_mt_setting *
242 circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
243 {
244 return lookup_mt_setting(circuit->mt_settings, mtid);
245 }
246
247 struct isis_circuit_mt_setting *
248 circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
249 {
250 struct isis_circuit_mt_setting *setting;
251
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;
256 }
257
258 static void circuit_free_mt_setting(void *setting)
259 {
260 XFREE(MTYPE_MT_CIRCUIT_SETTING, setting);
261 }
262
263 void circuit_add_mt_setting(struct isis_circuit *circuit,
264 struct isis_circuit_mt_setting *setting)
265 {
266 add_mt_setting(&circuit->mt_settings, setting);
267 }
268
269 void circuit_mt_init(struct isis_circuit *circuit)
270 {
271 circuit->mt_settings = list_new();
272 circuit->mt_settings->del = circuit_free_mt_setting;
273 }
274
275 void circuit_mt_finish(struct isis_circuit *circuit)
276 {
277 list_delete_and_null(&circuit->mt_settings);
278 }
279
280 struct isis_circuit_mt_setting *
281 circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid)
282 {
283 struct isis_circuit_mt_setting *setting;
284
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;
291 }
292
293 int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty)
294 {
295 int written = 0;
296 struct listnode *node;
297 struct isis_circuit_mt_setting *setting;
298
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;
307 }
308
309 struct isis_circuit_mt_setting **
310 circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count)
311 {
312 static unsigned int size = 0;
313 static struct isis_circuit_mt_setting **rv = NULL;
314
315 struct isis_area_mt_setting **area_settings;
316 unsigned int area_count;
317
318 unsigned int count = 0;
319
320 struct listnode *node;
321 struct isis_circuit_mt_setting *setting;
322
323 area_settings = area_mt_settings(circuit->area, &area_count);
324
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);
335
336 if (!setting->enabled)
337 continue;
338
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 }
346
347 *mt_count = count;
348 return rv;
349 }
350
351 /* ADJ specific MT API */
352 static void adj_mt_set(struct isis_adjacency *adj, unsigned int index,
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
363 bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable,
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++) {
384 if (!tlvs->mt_router_info.count
385 && !tlvs->mt_router_info_empty) {
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 {
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) {
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
441 bool 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
449 void adj_mt_finish(struct isis_adjacency *adj)
450 {
451 XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set);
452 adj->mt_count = 0;
453 }
454
455 static void mt_set_add(uint16_t **mt_set, unsigned int *size,
456 unsigned int *index, uint16_t mtid)
457 {
458 for (unsigned int i = 0; i < *index; i++) {
459 if ((*mt_set)[i] == mtid)
460 return;
461 }
462
463 if (*index >= *size) {
464 *mt_set = XREALLOC(MTYPE_TMP, *mt_set,
465 sizeof(**mt_set) * ((*index) + 1));
466 *size = (*index) + 1;
467 }
468
469 (*mt_set)[*index] = mtid;
470 *index = (*index) + 1;
471 }
472
473 static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level,
474 unsigned int *mt_count)
475 {
476 static uint16_t *rv;
477 static unsigned int size;
478 struct listnode *node;
479 struct isis_adjacency *adj;
480
481 unsigned int count = 0;
482
483 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
484 *mt_count = 0;
485 return NULL;
486 }
487
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 }
494
495 *mt_count = count;
496 return rv;
497 }
498
499 static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
500 unsigned int mt_count, uint16_t *mt_set,
501 uint8_t *id, uint32_t metric, uint8_t *subtlvs,
502 uint8_t subtlv_len)
503 {
504 for (unsigned int i = 0; i < mt_count; i++) {
505 uint16_t mtid = mt_set[i];
506 if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
507 lsp_debug(
508 "ISIS (%s): Adding %s.%02x as te-style neighbor",
509 area->area_tag, sysid_print(id),
510 LSP_PSEUDO_ID(id));
511 } else {
512 lsp_debug(
513 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
514 area->area_tag, sysid_print(id),
515 LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
516 }
517 isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, subtlvs,
518 subtlv_len);
519 }
520 }
521
522 void 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)
525 {
526 unsigned int mt_count;
527 uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count);
528
529 tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric,
530 subtlvs, subtlv_len);
531 }
532
533 void 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)
536 {
537 struct isis_adjacency *adj = circuit->u.p2p.neighbor;
538
539 tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id,
540 metric, subtlvs, subtlv_len);
541 }