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_STANDARD
:
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
, "standard"))
84 return ISIS_MT_STANDARD
;
85 if (!strcmp(name
, "ipv4-mgmt"))
86 return ISIS_MT_IPV4_MGMT
;
87 if (!strcmp(name
, "ipv6-unicast"))
88 return ISIS_MT_IPV6_UNICAST
;
89 if (!strcmp(name
, "ipv4-multicast"))
90 return ISIS_MT_IPV4_MULTICAST
;
91 if (!strcmp(name
, "ipv6-multicast"))
92 return ISIS_MT_IPV6_MULTICAST
;
93 if (!strcmp(name
, "ipv6-mgmt"))
94 return ISIS_MT_IPV6_MGMT
;
95 if (!strcmp(name
, "ipv6-dstsrc"))
96 return ISIS_MT_IPV6_DSTSRC
;
100 /* General MT settings api */
106 static void *lookup_mt_setting(struct list
*mt_list
, uint16_t mtid
)
108 struct listnode
*node
;
109 struct mt_setting
*setting
;
111 for (ALL_LIST_ELEMENTS_RO(mt_list
, node
, setting
)) {
112 if (setting
->mtid
== mtid
)
118 static void add_mt_setting(struct list
**mt_list
, void *setting
)
121 *mt_list
= list_new();
122 listnode_add(*mt_list
, setting
);
125 /* Area specific MT settings api */
127 struct isis_area_mt_setting
*area_lookup_mt_setting(struct isis_area
*area
,
130 return lookup_mt_setting(area
->mt_settings
, mtid
);
133 struct isis_area_mt_setting
*area_new_mt_setting(struct isis_area
*area
,
136 struct isis_area_mt_setting
*setting
;
138 setting
= XCALLOC(MTYPE_MT_AREA_SETTING
, sizeof(*setting
));
139 setting
->mtid
= mtid
;
143 static void area_free_mt_setting(void *setting
)
145 XFREE(MTYPE_MT_AREA_SETTING
, setting
);
148 void area_add_mt_setting(struct isis_area
*area
,
149 struct isis_area_mt_setting
*setting
)
151 add_mt_setting(&area
->mt_settings
, setting
);
154 void area_mt_init(struct isis_area
*area
)
156 struct isis_area_mt_setting
*v4_unicast_setting
;
158 /* MTID 0 is always enabled */
159 v4_unicast_setting
= area_new_mt_setting(area
, ISIS_MT_IPV4_UNICAST
);
160 v4_unicast_setting
->enabled
= true;
161 add_mt_setting(&area
->mt_settings
, v4_unicast_setting
);
162 area
->mt_settings
->del
= area_free_mt_setting
;
165 void area_mt_finish(struct isis_area
*area
)
167 list_delete(&area
->mt_settings
);
170 struct isis_area_mt_setting
*area_get_mt_setting(struct isis_area
*area
,
173 struct isis_area_mt_setting
*setting
;
175 setting
= area_lookup_mt_setting(area
, mtid
);
177 setting
= area_new_mt_setting(area
, mtid
);
178 area_add_mt_setting(area
, setting
);
183 int area_write_mt_settings(struct isis_area
*area
, struct vty
*vty
)
186 struct listnode
*node
;
187 struct isis_area_mt_setting
*setting
;
189 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
190 const char *name
= isis_mtid2str(setting
->mtid
);
191 if (name
&& setting
->enabled
) {
192 if (setting
->mtid
== ISIS_MT_IPV4_UNICAST
)
193 continue; /* always enabled, no need to write
195 vty_out(vty
, " topology %s%s\n", name
,
196 setting
->overload
? " overload" : "");
203 bool area_is_mt(struct isis_area
*area
)
205 struct listnode
*node
, *node2
;
206 struct isis_area_mt_setting
*setting
;
207 struct isis_circuit
*circuit
;
208 struct isis_circuit_mt_setting
*csetting
;
210 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
211 if (setting
->enabled
&& setting
->mtid
!= ISIS_MT_IPV4_UNICAST
)
214 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
)) {
215 for (ALL_LIST_ELEMENTS_RO(circuit
->mt_settings
, node2
,
217 if (!csetting
->enabled
218 && csetting
->mtid
== ISIS_MT_IPV4_UNICAST
)
226 struct isis_area_mt_setting
**area_mt_settings(struct isis_area
*area
,
227 unsigned int *mt_count
)
229 static unsigned int size
= 0;
230 static struct isis_area_mt_setting
**rv
= NULL
;
232 unsigned int count
= 0;
233 struct listnode
*node
;
234 struct isis_area_mt_setting
*setting
;
236 for (ALL_LIST_ELEMENTS_RO(area
->mt_settings
, node
, setting
)) {
237 if (!setting
->enabled
)
242 rv
= XREALLOC(MTYPE_TMP
, rv
, count
* sizeof(*rv
));
245 rv
[count
- 1] = setting
;
252 /* Circuit specific MT settings api */
254 struct isis_circuit_mt_setting
*
255 circuit_lookup_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
257 return lookup_mt_setting(circuit
->mt_settings
, mtid
);
260 struct isis_circuit_mt_setting
*
261 circuit_new_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
263 struct isis_circuit_mt_setting
*setting
;
265 setting
= XCALLOC(MTYPE_MT_CIRCUIT_SETTING
, sizeof(*setting
));
266 setting
->mtid
= mtid
;
267 setting
->enabled
= true; /* Enabled is default for circuit */
271 static void circuit_free_mt_setting(void *setting
)
273 XFREE(MTYPE_MT_CIRCUIT_SETTING
, setting
);
276 void circuit_add_mt_setting(struct isis_circuit
*circuit
,
277 struct isis_circuit_mt_setting
*setting
)
279 add_mt_setting(&circuit
->mt_settings
, setting
);
282 void circuit_mt_init(struct isis_circuit
*circuit
)
284 circuit
->mt_settings
= list_new();
285 circuit
->mt_settings
->del
= circuit_free_mt_setting
;
288 void circuit_mt_finish(struct isis_circuit
*circuit
)
290 list_delete(&circuit
->mt_settings
);
293 struct isis_circuit_mt_setting
*
294 circuit_get_mt_setting(struct isis_circuit
*circuit
, uint16_t mtid
)
296 struct isis_circuit_mt_setting
*setting
;
298 setting
= circuit_lookup_mt_setting(circuit
, mtid
);
300 setting
= circuit_new_mt_setting(circuit
, mtid
);
301 circuit_add_mt_setting(circuit
, setting
);
307 static int circuit_write_mt_settings(struct isis_circuit
*circuit
,
311 struct listnode
*node
;
312 struct isis_circuit_mt_setting
*setting
;
314 for (ALL_LIST_ELEMENTS_RO(circuit
->mt_settings
, node
, setting
)) {
315 const char *name
= isis_mtid2str(setting
->mtid
);
316 if (name
&& !setting
->enabled
) {
317 vty_out(vty
, " no " PROTO_NAME
" topology %s\n", name
);
325 struct isis_circuit_mt_setting
**
326 circuit_mt_settings(struct isis_circuit
*circuit
, unsigned int *mt_count
)
328 static unsigned int size
= 0;
329 static struct isis_circuit_mt_setting
**rv
= NULL
;
331 struct isis_area_mt_setting
**area_settings
;
332 unsigned int area_count
;
334 unsigned int count
= 0;
336 struct listnode
*node
;
337 struct isis_circuit_mt_setting
*setting
;
339 area_settings
= area_mt_settings(circuit
->area
, &area_count
);
341 for (unsigned int i
= 0; i
< area_count
; i
++) {
342 for (ALL_LIST_ELEMENTS_RO(circuit
->mt_settings
, node
,
344 if (setting
->mtid
!= area_settings
[i
]->mtid
)
349 setting
= circuit_get_mt_setting(
350 circuit
, area_settings
[i
]->mtid
);
352 if (!setting
->enabled
)
357 rv
= XREALLOC(MTYPE_TMP
, rv
, count
* sizeof(*rv
));
360 rv
[count
- 1] = setting
;
367 /* ADJ specific MT API */
368 static void adj_mt_set(struct isis_adjacency
*adj
, unsigned int index
,
371 if (adj
->mt_count
< index
+ 1) {
372 adj
->mt_set
= XREALLOC(MTYPE_MT_ADJ_INFO
, adj
->mt_set
,
373 (index
+ 1) * sizeof(*adj
->mt_set
));
374 adj
->mt_count
= index
+ 1;
376 adj
->mt_set
[index
] = mtid
;
379 bool tlvs_to_adj_mt_set(struct isis_tlvs
*tlvs
, bool v4_usable
, bool v6_usable
,
380 struct isis_adjacency
*adj
)
382 struct isis_circuit_mt_setting
**mt_settings
;
383 unsigned int circuit_mt_count
;
385 unsigned int intersect_count
= 0;
387 uint16_t *old_mt_set
= NULL
;
388 unsigned int old_mt_count
;
390 old_mt_count
= adj
->mt_count
;
393 XCALLOC(MTYPE_TMP
, old_mt_count
* sizeof(*old_mt_set
));
394 memcpy(old_mt_set
, adj
->mt_set
,
395 old_mt_count
* sizeof(*old_mt_set
));
398 mt_settings
= circuit_mt_settings(adj
->circuit
, &circuit_mt_count
);
399 for (unsigned int i
= 0; i
< circuit_mt_count
; i
++) {
400 if (!tlvs
->mt_router_info
.count
401 && !tlvs
->mt_router_info_empty
) {
402 /* Other end does not have MT enabled */
403 if (mt_settings
[i
]->mtid
== ISIS_MT_IPV4_UNICAST
404 && (v4_usable
|| v6_usable
))
405 adj_mt_set(adj
, intersect_count
++,
406 ISIS_MT_IPV4_UNICAST
);
408 struct isis_mt_router_info
*info_head
;
410 info_head
= (struct isis_mt_router_info
*)
411 tlvs
->mt_router_info
.head
;
412 for (struct isis_mt_router_info
*info
= info_head
; info
;
414 if (mt_settings
[i
]->mtid
== info
->mtid
) {
416 switch (info
->mtid
) {
417 case ISIS_MT_IPV4_UNICAST
:
418 case ISIS_MT_IPV4_MGMT
:
419 case ISIS_MT_IPV4_MULTICAST
:
422 case ISIS_MT_IPV6_UNICAST
:
423 case ISIS_MT_IPV6_MGMT
:
424 case ISIS_MT_IPV6_MULTICAST
:
439 adj
->mt_count
= intersect_count
;
441 bool changed
= false;
443 if (adj
->mt_count
!= old_mt_count
)
446 if (!changed
&& old_mt_count
447 && memcmp(adj
->mt_set
, old_mt_set
,
448 old_mt_count
* sizeof(*old_mt_set
)))
452 XFREE(MTYPE_TMP
, old_mt_set
);
457 bool adj_has_mt(struct isis_adjacency
*adj
, uint16_t mtid
)
459 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
460 if (adj
->mt_set
[i
] == mtid
)
465 void adj_mt_finish(struct isis_adjacency
*adj
)
467 XFREE(MTYPE_MT_ADJ_INFO
, adj
->mt_set
);
471 static void mt_set_add(uint16_t **mt_set
, unsigned int *size
,
472 unsigned int *index
, uint16_t mtid
)
474 for (unsigned int i
= 0; i
< *index
; i
++) {
475 if ((*mt_set
)[i
] == mtid
)
479 if (*index
>= *size
) {
480 *mt_set
= XREALLOC(MTYPE_TMP
, *mt_set
,
481 sizeof(**mt_set
) * ((*index
) + 1));
482 *size
= (*index
) + 1;
485 (*mt_set
)[*index
] = mtid
;
486 *index
= (*index
) + 1;
489 static uint16_t *circuit_bcast_mt_set(struct isis_circuit
*circuit
, int level
,
490 unsigned int *mt_count
)
493 static unsigned int size
;
494 struct listnode
*node
;
495 struct isis_adjacency
*adj
;
497 unsigned int count
= 0;
499 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
504 for (ALL_LIST_ELEMENTS_RO(circuit
->u
.bc
.adjdb
[level
- 1], node
, adj
)) {
505 if (adj
->adj_state
!= ISIS_ADJ_UP
)
507 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
508 mt_set_add(&rv
, &size
, &count
, adj
->mt_set
[i
]);
515 static void tlvs_add_mt_set(struct isis_area
*area
, struct isis_tlvs
*tlvs
,
516 unsigned int mt_count
, uint16_t *mt_set
,
517 uint8_t *id
, uint32_t metric
,
518 struct isis_ext_subtlvs
*ext
)
520 /* Check if MT is enable for this area */
521 if (!area_is_mt(area
)) {
523 "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)",
524 area
->area_tag
, sysid_print(id
), LSP_PSEUDO_ID(id
));
525 isis_tlvs_add_extended_reach(tlvs
, ISIS_MT_DISABLE
, id
, metric
,
530 /* Process Multi-Topology */
531 for (unsigned int i
= 0; i
< mt_count
; i
++) {
532 uint16_t mtid
= mt_set
[i
];
533 if (mt_set
[i
] == ISIS_MT_IPV4_UNICAST
) {
535 "ISIS (%s): Adding %s.%02x as te-style neighbor",
536 area
->area_tag
, sysid_print(id
),
540 "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
541 area
->area_tag
, sysid_print(id
),
542 LSP_PSEUDO_ID(id
), isis_mtid2str(mtid
));
544 isis_tlvs_add_extended_reach(tlvs
, mtid
, id
, metric
, ext
);
548 void tlvs_add_mt_bcast(struct isis_tlvs
*tlvs
, struct isis_circuit
*circuit
,
549 int level
, uint8_t *id
, uint32_t metric
)
551 unsigned int mt_count
;
552 uint16_t *mt_set
= circuit_bcast_mt_set(circuit
, level
, &mt_count
);
554 tlvs_add_mt_set(circuit
->area
, tlvs
, mt_count
, mt_set
, id
, metric
,
558 void tlvs_add_mt_p2p(struct isis_tlvs
*tlvs
, struct isis_circuit
*circuit
,
559 uint8_t *id
, uint32_t metric
)
561 struct isis_adjacency
*adj
= circuit
->u
.p2p
.neighbor
;
563 tlvs_add_mt_set(circuit
->area
, tlvs
, adj
->mt_count
, adj
->mt_set
, id
,
564 metric
, circuit
->ext
);
570 hook_register(isis_circuit_config_write
,
571 circuit_write_mt_settings
);