2 * IS-IS Rout(e)ing protocol - Multi Topology Support
4 * Copyright (C) 2017 Christian Franke
6 * This file is part of FRRouting (FRR)
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
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.
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
23 #include "isisd/isisd.h"
24 #include "isisd/isis_circuit.h"
25 #include "isisd/isis_adjacency.h"
26 #include "isisd/isis_misc.h"
27 #include "isisd/isis_lsp.h"
28 #include "isisd/isis_mt.h"
29 #include "isisd/isis_tlvs.h"
31 DEFINE_MTYPE_STATIC(ISISD
, MT_AREA_SETTING
, "ISIS MT Area Setting");
32 DEFINE_MTYPE_STATIC(ISISD
, MT_CIRCUIT_SETTING
, "ISIS MT Circuit Setting");
33 DEFINE_MTYPE_STATIC(ISISD
, MT_ADJ_INFO
, "ISIS MT Adjacency Info");
35 bool isis_area_ipv6_dstsrc_enabled(struct isis_area
*area
)
37 struct isis_area_mt_setting
*area_mt_setting
;
38 area_mt_setting
= area_lookup_mt_setting(area
, ISIS_MT_IPV6_DSTSRC
);
40 return (area_mt_setting
&& area_mt_setting
->enabled
);
43 uint16_t isis_area_ipv6_topology(struct isis_area
*area
)
45 struct isis_area_mt_setting
*area_mt_setting
;
46 area_mt_setting
= area_lookup_mt_setting(area
, ISIS_MT_IPV6_UNICAST
);
48 if (area_mt_setting
&& area_mt_setting
->enabled
)
49 return ISIS_MT_IPV6_UNICAST
;
50 return ISIS_MT_IPV4_UNICAST
;
54 const char *isis_mtid2str(uint16_t mtid
)
56 static char buf
[sizeof("65535")];
59 case ISIS_MT_IPV4_UNICAST
:
60 return "ipv4-unicast";
61 case ISIS_MT_IPV4_MGMT
:
63 case ISIS_MT_IPV6_UNICAST
:
64 return "ipv6-unicast";
65 case ISIS_MT_IPV4_MULTICAST
:
66 return "ipv4-multicast";
67 case ISIS_MT_IPV6_MULTICAST
:
68 return "ipv6-multicast";
69 case ISIS_MT_IPV6_MGMT
:
71 case ISIS_MT_IPV6_DSTSRC
:
74 snprintf(buf
, sizeof(buf
), "%hu", mtid
);
79 uint16_t isis_str2mtid(const char *name
)
81 if (!strcmp(name
, "ipv4-unicast"))
82 return ISIS_MT_IPV4_UNICAST
;
83 if (!strcmp(name
, "ipv4-mgmt"))
84 return ISIS_MT_IPV4_MGMT
;
85 if (!strcmp(name
, "ipv6-unicast"))
86 return ISIS_MT_IPV6_UNICAST
;
87 if (!strcmp(name
, "ipv4-multicast"))
88 return ISIS_MT_IPV4_MULTICAST
;
89 if (!strcmp(name
, "ipv6-multicast"))
90 return ISIS_MT_IPV6_MULTICAST
;
91 if (!strcmp(name
, "ipv6-mgmt"))
92 return ISIS_MT_IPV6_MGMT
;
93 if (!strcmp(name
, "ipv6-dstsrc"))
94 return ISIS_MT_IPV6_DSTSRC
;
98 /* General MT settings api */
104 static void *lookup_mt_setting(struct list
*mt_list
, uint16_t mtid
)
106 struct listnode
*node
;
107 struct mt_setting
*setting
;
109 for (ALL_LIST_ELEMENTS_RO(mt_list
, node
, setting
)) {
110 if (setting
->mtid
== mtid
)
116 static void add_mt_setting(struct list
**mt_list
, void *setting
)
119 *mt_list
= list_new();
120 listnode_add(*mt_list
, setting
);
123 /* Area specific MT settings api */
125 struct isis_area_mt_setting
*area_lookup_mt_setting(struct isis_area
*area
,
128 return lookup_mt_setting(area
->mt_settings
, mtid
);
131 struct isis_area_mt_setting
*area_new_mt_setting(struct isis_area
*area
,
134 struct isis_area_mt_setting
*setting
;
136 setting
= XCALLOC(MTYPE_MT_AREA_SETTING
, sizeof(*setting
));
137 setting
->mtid
= mtid
;
141 static void area_free_mt_setting(void *setting
)
143 XFREE(MTYPE_MT_AREA_SETTING
, setting
);
146 void area_add_mt_setting(struct isis_area
*area
,
147 struct isis_area_mt_setting
*setting
)
149 add_mt_setting(&area
->mt_settings
, setting
);
152 void area_mt_init(struct isis_area
*area
)
154 struct isis_area_mt_setting
*v4_unicast_setting
;
156 /* MTID 0 is always enabled */
157 v4_unicast_setting
= area_new_mt_setting(area
, ISIS_MT_IPV4_UNICAST
);
158 v4_unicast_setting
->enabled
= true;
159 add_mt_setting(&area
->mt_settings
, v4_unicast_setting
);
160 area
->mt_settings
->del
= area_free_mt_setting
;
163 void area_mt_finish(struct isis_area
*area
)
165 list_delete(&area
->mt_settings
);
168 struct isis_area_mt_setting
*area_get_mt_setting(struct isis_area
*area
,
171 struct isis_area_mt_setting
*setting
;
173 setting
= area_lookup_mt_setting(area
, mtid
);
175 setting
= area_new_mt_setting(area
, mtid
);
176 area_add_mt_setting(area
, setting
);
181 int area_write_mt_settings(struct isis_area
*area
, struct vty
*vty
)
184 struct listnode
*node
;
185 struct isis_area_mt_setting
*setting
;
187 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
188 const char *name
= isis_mtid2str(setting
->mtid
);
189 if (name
&& setting
->enabled
) {
190 if (setting
->mtid
== ISIS_MT_IPV4_UNICAST
)
191 continue; /* always enabled, no need to write
193 vty_out(vty
, " topology %s%s\n", name
,
194 setting
->overload
? " overload" : "");
201 bool area_is_mt(struct isis_area
*area
)
203 struct listnode
*node
, *node2
;
204 struct isis_area_mt_setting
*setting
;
205 struct isis_circuit
*circuit
;
206 struct isis_circuit_mt_setting
*csetting
;
208 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
209 if (setting
->enabled
&& setting
->mtid
!= ISIS_MT_IPV4_UNICAST
)
212 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
)) {
213 for (ALL_LIST_ELEMENTS_RO(circuit
->mt_settings
, node2
,
215 if (!csetting
->enabled
216 && csetting
->mtid
== ISIS_MT_IPV4_UNICAST
)
224 struct isis_area_mt_setting
**area_mt_settings(struct isis_area
*area
,
225 unsigned int *mt_count
)
227 static unsigned int size
= 0;
228 static struct isis_area_mt_setting
**rv
= NULL
;
230 unsigned int count
= 0;
231 struct listnode
*node
;
232 struct isis_area_mt_setting
*setting
;
234 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
235 if (!setting
->enabled
)
240 rv
= XREALLOC(MTYPE_TMP
, rv
, count
* sizeof(*rv
));
243 rv
[count
- 1] = setting
;
250 /* Circuit specific MT settings api */
252 struct isis_circuit_mt_setting
*
253 circuit_lookup_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
255 return lookup_mt_setting(circuit
->mt_settings
, mtid
);
258 struct isis_circuit_mt_setting
*
259 circuit_new_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
261 struct isis_circuit_mt_setting
*setting
;
263 setting
= XCALLOC(MTYPE_MT_CIRCUIT_SETTING
, sizeof(*setting
));
264 setting
->mtid
= mtid
;
265 setting
->enabled
= true; /* Enabled is default for circuit */
269 static void circuit_free_mt_setting(void *setting
)
271 XFREE(MTYPE_MT_CIRCUIT_SETTING
, setting
);
274 void circuit_add_mt_setting(struct isis_circuit
*circuit
,
275 struct isis_circuit_mt_setting
*setting
)
277 add_mt_setting(&circuit
->mt_settings
, setting
);
280 void circuit_mt_init(struct isis_circuit
*circuit
)
282 circuit
->mt_settings
= list_new();
283 circuit
->mt_settings
->del
= circuit_free_mt_setting
;
286 void circuit_mt_finish(struct isis_circuit
*circuit
)
288 list_delete(&circuit
->mt_settings
);
291 struct isis_circuit_mt_setting
*
292 circuit_get_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
294 struct isis_circuit_mt_setting
*setting
;
296 setting
= circuit_lookup_mt_setting(circuit
, mtid
);
298 setting
= circuit_new_mt_setting(circuit
, mtid
);
299 circuit_add_mt_setting(circuit
, setting
);
305 static int circuit_write_mt_settings(struct isis_circuit
*circuit
,
309 struct listnode
*node
;
310 struct isis_circuit_mt_setting
*setting
;
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
);
323 struct isis_circuit_mt_setting
**
324 circuit_mt_settings(struct isis_circuit
*circuit
, unsigned int *mt_count
)
326 static unsigned int size
= 0;
327 static struct isis_circuit_mt_setting
**rv
= NULL
;
329 struct isis_area_mt_setting
**area_settings
;
330 unsigned int area_count
;
332 unsigned int count
= 0;
334 struct listnode
*node
;
335 struct isis_circuit_mt_setting
*setting
;
337 area_settings
= area_mt_settings(circuit
->area
, &area_count
);
339 for (unsigned int i
= 0; i
< area_count
; i
++) {
340 for (ALL_LIST_ELEMENTS_RO(circuit
->mt_settings
, node
,
342 if (setting
->mtid
!= area_settings
[i
]->mtid
)
347 setting
= circuit_get_mt_setting(
348 circuit
, area_settings
[i
]->mtid
);
350 if (!setting
->enabled
)
355 rv
= XREALLOC(MTYPE_TMP
, rv
, count
* sizeof(*rv
));
358 rv
[count
- 1] = setting
;
365 /* ADJ specific MT API */
366 static void adj_mt_set(struct isis_adjacency
*adj
, unsigned int index
,
369 if (adj
->mt_count
< index
+ 1) {
370 adj
->mt_set
= XREALLOC(MTYPE_MT_ADJ_INFO
, adj
->mt_set
,
371 (index
+ 1) * sizeof(*adj
->mt_set
));
372 adj
->mt_count
= index
+ 1;
374 adj
->mt_set
[index
] = mtid
;
377 bool tlvs_to_adj_mt_set(struct isis_tlvs
*tlvs
, bool v4_usable
, bool v6_usable
,
378 struct isis_adjacency
*adj
)
380 struct isis_circuit_mt_setting
**mt_settings
;
381 unsigned int circuit_mt_count
;
383 unsigned int intersect_count
= 0;
385 uint16_t *old_mt_set
= NULL
;
386 unsigned int old_mt_count
;
388 old_mt_count
= adj
->mt_count
;
391 XCALLOC(MTYPE_TMP
, old_mt_count
* sizeof(*old_mt_set
));
392 memcpy(old_mt_set
, adj
->mt_set
,
393 old_mt_count
* sizeof(*old_mt_set
));
396 mt_settings
= circuit_mt_settings(adj
->circuit
, &circuit_mt_count
);
397 for (unsigned int i
= 0; i
< circuit_mt_count
; i
++) {
398 if (!tlvs
->mt_router_info
.count
399 && !tlvs
->mt_router_info_empty
) {
400 /* Other end does not have MT enabled */
401 if (mt_settings
[i
]->mtid
== ISIS_MT_IPV4_UNICAST
402 && (v4_usable
|| v6_usable
))
403 adj_mt_set(adj
, intersect_count
++,
404 ISIS_MT_IPV4_UNICAST
);
406 struct isis_mt_router_info
*info_head
;
408 info_head
= (struct isis_mt_router_info
*)
409 tlvs
->mt_router_info
.head
;
410 for (struct isis_mt_router_info
*info
= info_head
; info
;
412 if (mt_settings
[i
]->mtid
== info
->mtid
) {
414 switch (info
->mtid
) {
415 case ISIS_MT_IPV4_UNICAST
:
416 case ISIS_MT_IPV4_MGMT
:
417 case ISIS_MT_IPV4_MULTICAST
:
420 case ISIS_MT_IPV6_UNICAST
:
421 case ISIS_MT_IPV6_MGMT
:
422 case ISIS_MT_IPV6_MULTICAST
:
437 adj
->mt_count
= intersect_count
;
439 bool changed
= false;
441 if (adj
->mt_count
!= old_mt_count
)
444 if (!changed
&& old_mt_count
445 && memcmp(adj
->mt_set
, old_mt_set
,
446 old_mt_count
* sizeof(*old_mt_set
)))
450 XFREE(MTYPE_TMP
, old_mt_set
);
455 bool adj_has_mt(struct isis_adjacency
*adj
, uint16_t mtid
)
457 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
458 if (adj
->mt_set
[i
] == mtid
)
463 void adj_mt_finish(struct isis_adjacency
*adj
)
465 XFREE(MTYPE_MT_ADJ_INFO
, adj
->mt_set
);
469 static void mt_set_add(uint16_t **mt_set
, unsigned int *size
,
470 unsigned int *index
, uint16_t mtid
)
472 for (unsigned int i
= 0; i
< *index
; i
++) {
473 if ((*mt_set
)[i
] == mtid
)
477 if (*index
>= *size
) {
478 *mt_set
= XREALLOC(MTYPE_TMP
, *mt_set
,
479 sizeof(**mt_set
) * ((*index
) + 1));
480 *size
= (*index
) + 1;
483 (*mt_set
)[*index
] = mtid
;
484 *index
= (*index
) + 1;
487 static uint16_t *circuit_bcast_mt_set(struct isis_circuit
*circuit
, int level
,
488 unsigned int *mt_count
)
491 static unsigned int size
;
492 struct listnode
*node
;
493 struct isis_adjacency
*adj
;
495 unsigned int count
= 0;
497 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
502 for (ALL_LIST_ELEMENTS_RO(circuit
->u
.bc
.adjdb
[level
- 1], node
, adj
)) {
503 if (adj
->adj_state
!= ISIS_ADJ_UP
)
505 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
506 mt_set_add(&rv
, &size
, &count
, adj
->mt_set
[i
]);
513 static void tlvs_add_mt_set(struct isis_area
*area
, struct isis_tlvs
*tlvs
,
514 unsigned int mt_count
, uint16_t *mt_set
,
515 uint8_t *id
, uint32_t metric
,
516 struct isis_ext_subtlvs
*ext
)
518 for (unsigned int i
= 0; i
< mt_count
; i
++) {
519 uint16_t mtid
= mt_set
[i
];
520 if (mt_set
[i
] == ISIS_MT_IPV4_UNICAST
) {
522 "ISIS (%s): Adding %s.%02x as te-style neighbor",
523 area
->area_tag
, sysid_print(id
),
527 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
528 area
->area_tag
, sysid_print(id
),
529 LSP_PSEUDO_ID(id
), isis_mtid2str(mtid
));
531 isis_tlvs_add_extended_reach(tlvs
, mtid
, id
, metric
, ext
);
535 void tlvs_add_mt_bcast(struct isis_tlvs
*tlvs
, struct isis_circuit
*circuit
,
536 int level
, uint8_t *id
, uint32_t metric
)
538 unsigned int mt_count
;
539 uint16_t *mt_set
= circuit_bcast_mt_set(circuit
, level
, &mt_count
);
541 tlvs_add_mt_set(circuit
->area
, tlvs
, mt_count
, mt_set
, id
, metric
,
545 void tlvs_add_mt_p2p(struct isis_tlvs
*tlvs
, struct isis_circuit
*circuit
,
546 uint8_t *id
, uint32_t metric
)
548 struct isis_adjacency
*adj
= circuit
->u
.p2p
.neighbor
;
550 tlvs_add_mt_set(circuit
->area
, tlvs
, adj
->mt_count
, adj
->mt_set
, id
,
551 metric
, circuit
->ext
);
557 hook_register(isis_circuit_config_write
,
558 circuit_write_mt_settings
);