]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
f8c06e2c OD |
2 | /* |
3 | * IS-IS Rout(e)ing protocol - isis_te.c | |
4 | * | |
68558b13 | 5 | * This is an implementation of RFC5305 & RFC 7810 |
f8c06e2c | 6 | * |
1b3f47d0 OD |
7 | * Author: Olivier Dugeon <olivier.dugeon@orange.com> |
8 | * | |
9 | * Copyright (C) 2014 - 2019 Orange Labs http://www.orange.com | |
f8c06e2c OD |
10 | */ |
11 | ||
12 | #include <zebra.h> | |
13 | #include <math.h> | |
14 | ||
15 | #include "linklist.h" | |
16 | #include "thread.h" | |
17 | #include "vty.h" | |
18 | #include "stream.h" | |
19 | #include "memory.h" | |
20 | #include "log.h" | |
21 | #include "prefix.h" | |
22 | #include "command.h" | |
23 | #include "hash.h" | |
24 | #include "if.h" | |
25 | #include "vrf.h" | |
26 | #include "checksum.h" | |
27 | #include "md5.h" | |
28 | #include "sockunion.h" | |
29 | #include "network.h" | |
af8ac8f9 | 30 | #include "sbuf.h" |
ed6189a9 OD |
31 | #include "link_state.h" |
32 | #include "lib/json.h" | |
f8c06e2c | 33 | |
f8c06e2c OD |
34 | #include "isisd/isis_constants.h" |
35 | #include "isisd/isis_common.h" | |
36 | #include "isisd/isis_flags.h" | |
37 | #include "isisd/isis_circuit.h" | |
1b3f47d0 | 38 | #include "isisd/isis_adjacency.h" |
f8c06e2c | 39 | #include "isisd/isisd.h" |
f8c06e2c OD |
40 | #include "isisd/isis_lsp.h" |
41 | #include "isisd/isis_pdu.h" | |
42 | #include "isisd/isis_dynhn.h" | |
43 | #include "isisd/isis_misc.h" | |
44 | #include "isisd/isis_csm.h" | |
45 | #include "isisd/isis_adjacency.h" | |
46 | #include "isisd/isis_spf.h" | |
ed6189a9 OD |
47 | #include "isisd/isis_tlvs.h" |
48 | #include "isisd/isis_mt.h" | |
f8c06e2c | 49 | #include "isisd/isis_te.h" |
1b3f47d0 | 50 | #include "isisd/isis_zebra.h" |
f8c06e2c | 51 | |
1fa63850 OD |
52 | DEFINE_MTYPE_STATIC(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters"); |
53 | ||
78d905be LS |
54 | static void isis_mpls_te_circuit_ip_update(struct isis_circuit *circuit); |
55 | ||
f8c06e2c | 56 | /*------------------------------------------------------------------------* |
78dfa0c7 | 57 | * Following are control functions for MPLS-TE parameters management. |
f8c06e2c OD |
58 | *------------------------------------------------------------------------*/ |
59 | ||
1fa63850 OD |
60 | /** |
61 | * Create MPLS Traffic Engineering structure which belongs to given area. | |
62 | * | |
63 | * @param area IS-IS Area | |
64 | */ | |
65 | void isis_mpls_te_create(struct isis_area *area) | |
66 | { | |
67 | struct listnode *node; | |
68 | struct isis_circuit *circuit; | |
69 | ||
70 | if (!area) | |
71 | return; | |
72 | ||
73 | if (area->mta == NULL) { | |
74 | ||
75 | struct mpls_te_area *new; | |
76 | ||
77 | zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering", | |
78 | area->area_tag); | |
79 | ||
80 | new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); | |
81 | ||
82 | /* Initialize MPLS_TE structure */ | |
83 | new->status = enable; | |
84 | new->level = 0; | |
85 | new->inter_as = off; | |
86 | new->interas_areaid.s_addr = 0; | |
87 | new->router_id.s_addr = 0; | |
88 | new->ted = ls_ted_new(1, "ISIS", 0); | |
89 | if (!new->ted) | |
90 | zlog_warn("Unable to create Link State Data Base"); | |
91 | ||
92 | area->mta = new; | |
93 | } else { | |
94 | area->mta->status = enable; | |
95 | } | |
96 | ||
97 | /* Initialize Link State Database */ | |
98 | if (area->mta->ted) | |
99 | isis_te_init_ted(area); | |
100 | ||
78d905be LS |
101 | /* Update Extended TLVs according to Interface link parameters |
102 | * and neighbor IP addresses | |
103 | */ | |
104 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { | |
1fa63850 | 105 | isis_link_params_update(circuit, circuit->interface); |
78d905be LS |
106 | isis_mpls_te_circuit_ip_update(circuit); |
107 | } | |
1fa63850 OD |
108 | } |
109 | ||
110 | /** | |
111 | * Disable MPLS Traffic Engineering structure which belongs to given area. | |
112 | * | |
113 | * @param area IS-IS Area | |
114 | */ | |
115 | void isis_mpls_te_disable(struct isis_area *area) | |
116 | { | |
117 | struct listnode *node; | |
118 | struct isis_circuit *circuit; | |
119 | ||
120 | if (!area->mta) | |
121 | return; | |
122 | ||
123 | area->mta->status = disable; | |
124 | ||
125 | /* Remove Link State Database */ | |
ca94e8d3 | 126 | ls_ted_clean(area->mta->ted); |
1fa63850 OD |
127 | |
128 | /* Disable Extended SubTLVs on all circuit */ | |
129 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { | |
130 | if (!IS_EXT_TE(circuit->ext)) | |
131 | continue; | |
132 | ||
133 | /* disable MPLS_TE Circuit keeping SR one's */ | |
134 | if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID)) | |
135 | circuit->ext->status = EXT_ADJ_SID; | |
136 | else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID)) | |
137 | circuit->ext->status = EXT_LAN_ADJ_SID; | |
138 | else | |
139 | circuit->ext->status = 0; | |
140 | } | |
141 | } | |
142 | ||
143 | void isis_mpls_te_term(struct isis_area *area) | |
144 | { | |
145 | struct listnode *node; | |
146 | struct isis_circuit *circuit; | |
147 | ||
148 | if (!area->mta) | |
149 | return; | |
150 | ||
151 | zlog_info("TE(%s): Terminate MPLS TE", __func__); | |
152 | /* Remove Link State Database */ | |
153 | ls_ted_del_all(&area->mta->ted); | |
154 | ||
155 | /* Remove Extended SubTLVs */ | |
156 | zlog_info(" |- Remove Extended SubTLVS for all circuit"); | |
157 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { | |
158 | zlog_info(" |- Call isis_del_ext_subtlvs()"); | |
159 | isis_del_ext_subtlvs(circuit->ext); | |
160 | circuit->ext = NULL; | |
161 | } | |
162 | ||
163 | zlog_info(" |- Free MTA structure at %p", area->mta); | |
164 | XFREE(MTYPE_ISIS_MPLS_TE, area->mta); | |
165 | } | |
166 | ||
f8c06e2c OD |
167 | /* Main initialization / update function of the MPLS TE Circuit context */ |
168 | /* Call when interface TE Link parameters are modified */ | |
d62a17ae | 169 | void isis_link_params_update(struct isis_circuit *circuit, |
170 | struct interface *ifp) | |
f8c06e2c | 171 | { |
d62a17ae | 172 | int i; |
173 | struct prefix_ipv4 *addr; | |
1b3f47d0 OD |
174 | struct prefix_ipv6 *addr6; |
175 | struct isis_ext_subtlvs *ext; | |
176 | ||
177 | /* Check if TE is enable or not */ | |
178 | if (!circuit->area || !IS_MPLS_TE(circuit->area->mta)) | |
179 | return; | |
d62a17ae | 180 | |
181 | /* Sanity Check */ | |
b53c5f1a | 182 | if ((ifp == NULL) || (circuit->state != C_STATE_UP)) |
d62a17ae | 183 | return; |
184 | ||
ed6189a9 OD |
185 | te_debug("ISIS-TE(%s): Update circuit parameters for interface %s", |
186 | circuit->area->area_tag, ifp->name); | |
d62a17ae | 187 | |
188 | /* Check if MPLS TE Circuit context has not been already created */ | |
1b3f47d0 OD |
189 | if (circuit->ext == NULL) { |
190 | circuit->ext = isis_alloc_ext_subtlvs(); | |
ed6189a9 OD |
191 | te_debug(" |- Allocated new Ext-subTLVs for interface %s", |
192 | ifp->name); | |
1b3f47d0 | 193 | } |
d62a17ae | 194 | |
1b3f47d0 | 195 | ext = circuit->ext; |
d62a17ae | 196 | |
1b3f47d0 | 197 | /* Fulfill Extended subTLVs from interface link parameters */ |
d62a17ae | 198 | if (HAS_LINK_PARAMS(ifp)) { |
d62a17ae | 199 | /* STD_TE metrics */ |
1b3f47d0 OD |
200 | if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) { |
201 | ext->adm_group = ifp->link_params->admin_grp; | |
202 | SET_SUBTLV(ext, EXT_ADM_GRP); | |
203 | } else | |
204 | UNSET_SUBTLV(ext, EXT_ADM_GRP); | |
fa1b95c3 LS |
205 | |
206 | if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) { | |
207 | admin_group_copy(&ext->ext_admin_group, | |
208 | &ifp->link_params->ext_admin_grp); | |
209 | SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); | |
210 | } else | |
211 | UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); | |
1b3f47d0 OD |
212 | |
213 | /* If known, register local IPv4 addr from ip_addr list */ | |
91a5bbc4 | 214 | if (listcount(circuit->ip_addrs) != 0) { |
1b3f47d0 OD |
215 | addr = (struct prefix_ipv4 *)listgetdata( |
216 | (struct listnode *)listhead(circuit->ip_addrs)); | |
217 | IPV4_ADDR_COPY(&ext->local_addr, &addr->prefix); | |
218 | SET_SUBTLV(ext, EXT_LOCAL_ADDR); | |
219 | } else | |
220 | UNSET_SUBTLV(ext, EXT_LOCAL_ADDR); | |
221 | ||
1b3f47d0 | 222 | /* If known, register local IPv6 addr from ip_addr list */ |
91a5bbc4 | 223 | if (listcount(circuit->ipv6_non_link) != 0) { |
1b3f47d0 OD |
224 | addr6 = (struct prefix_ipv6 *)listgetdata( |
225 | (struct listnode *)listhead( | |
226 | circuit->ipv6_non_link)); | |
227 | IPV6_ADDR_COPY(&ext->local_addr6, &addr6->prefix); | |
228 | SET_SUBTLV(ext, EXT_LOCAL_ADDR6); | |
229 | } else | |
230 | UNSET_SUBTLV(ext, EXT_LOCAL_ADDR6); | |
231 | ||
ed6189a9 OD |
232 | /* |
233 | * Remote IPv4 and IPv6 addresses are now added in | |
234 | * isis_mpls_te_adj_ip_enabled() to get the right IP address | |
235 | * in particular for IPv6 to get the global IPv6 address and | |
236 | * not the link-local IPv6 address. | |
237 | */ | |
1b3f47d0 OD |
238 | |
239 | if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) { | |
240 | ext->max_bw = ifp->link_params->max_bw; | |
241 | SET_SUBTLV(ext, EXT_MAX_BW); | |
242 | } else | |
243 | UNSET_SUBTLV(ext, EXT_MAX_BW); | |
244 | ||
245 | if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) { | |
246 | ext->max_rsv_bw = ifp->link_params->max_rsv_bw; | |
247 | SET_SUBTLV(ext, EXT_MAX_RSV_BW); | |
248 | } else | |
249 | UNSET_SUBTLV(ext, EXT_MAX_RSV_BW); | |
250 | ||
251 | if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) { | |
d62a17ae | 252 | for (i = 0; i < MAX_CLASS_TYPE; i++) |
1b3f47d0 OD |
253 | ext->unrsv_bw[i] = |
254 | ifp->link_params->unrsv_bw[i]; | |
255 | SET_SUBTLV(ext, EXT_UNRSV_BW); | |
256 | } else | |
257 | UNSET_SUBTLV(ext, EXT_UNRSV_BW); | |
258 | ||
259 | if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) { | |
260 | ext->te_metric = ifp->link_params->te_metric; | |
261 | SET_SUBTLV(ext, EXT_TE_METRIC); | |
262 | } else | |
263 | UNSET_SUBTLV(ext, EXT_TE_METRIC); | |
264 | ||
265 | /* TE metric extensions */ | |
266 | if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) { | |
267 | ext->delay = ifp->link_params->av_delay; | |
268 | SET_SUBTLV(ext, EXT_DELAY); | |
269 | } else | |
270 | UNSET_SUBTLV(ext, EXT_DELAY); | |
271 | ||
272 | if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) { | |
273 | ext->min_delay = ifp->link_params->min_delay; | |
274 | ext->max_delay = ifp->link_params->max_delay; | |
275 | SET_SUBTLV(ext, EXT_MM_DELAY); | |
276 | } else | |
277 | UNSET_SUBTLV(ext, EXT_MM_DELAY); | |
278 | ||
279 | if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) { | |
280 | ext->delay_var = ifp->link_params->delay_var; | |
281 | SET_SUBTLV(ext, EXT_DELAY_VAR); | |
282 | } else | |
283 | UNSET_SUBTLV(ext, EXT_DELAY_VAR); | |
284 | ||
285 | if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) { | |
286 | ext->pkt_loss = ifp->link_params->pkt_loss; | |
287 | SET_SUBTLV(ext, EXT_PKT_LOSS); | |
288 | } else | |
289 | UNSET_SUBTLV(ext, EXT_PKT_LOSS); | |
290 | ||
291 | if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) { | |
292 | ext->res_bw = ifp->link_params->res_bw; | |
293 | SET_SUBTLV(ext, EXT_RES_BW); | |
294 | } else | |
295 | UNSET_SUBTLV(ext, EXT_RES_BW); | |
296 | ||
297 | if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) { | |
298 | ext->ava_bw = ifp->link_params->ava_bw; | |
299 | SET_SUBTLV(ext, EXT_AVA_BW); | |
300 | } else | |
301 | UNSET_SUBTLV(ext, EXT_AVA_BW); | |
302 | ||
303 | if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) { | |
304 | ext->use_bw = ifp->link_params->use_bw; | |
305 | SET_SUBTLV(ext, EXT_USE_BW); | |
306 | } else | |
307 | UNSET_SUBTLV(ext, EXT_USE_BW); | |
d62a17ae | 308 | |
1b3f47d0 OD |
309 | /* INTER_AS */ |
310 | if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) { | |
311 | ext->remote_as = ifp->link_params->rmt_as; | |
312 | ext->remote_ip = ifp->link_params->rmt_ip; | |
313 | SET_SUBTLV(ext, EXT_RMT_AS); | |
314 | SET_SUBTLV(ext, EXT_RMT_IP); | |
315 | } else { | |
316 | /* reset inter-as TE params */ | |
317 | UNSET_SUBTLV(ext, EXT_RMT_AS); | |
318 | UNSET_SUBTLV(ext, EXT_RMT_IP); | |
319 | } | |
ed6189a9 OD |
320 | te_debug(" |- New MPLS-TE link parameters status 0x%x", |
321 | ext->status); | |
1b3f47d0 | 322 | } else { |
ed6189a9 OD |
323 | te_debug(" |- Reset Extended subTLVs status 0x%x", |
324 | ext->status); | |
1b3f47d0 OD |
325 | /* Reset TE subTLVs keeping SR one's */ |
326 | if (IS_SUBTLV(ext, EXT_ADJ_SID)) | |
327 | ext->status = EXT_ADJ_SID; | |
328 | else if (IS_SUBTLV(ext, EXT_LAN_ADJ_SID)) | |
329 | ext->status = EXT_LAN_ADJ_SID; | |
d62a17ae | 330 | else |
1b3f47d0 OD |
331 | ext->status = 0; |
332 | } | |
d62a17ae | 333 | |
1b3f47d0 OD |
334 | return; |
335 | } | |
d62a17ae | 336 | |
78d905be LS |
337 | static int _isis_mpls_te_adj_ip_enabled(struct isis_adjacency *adj, int family, |
338 | bool global) | |
1b3f47d0 | 339 | { |
173f8887 OD |
340 | struct isis_circuit *circuit; |
341 | struct isis_ext_subtlvs *ext; | |
342 | ||
173f8887 | 343 | circuit = adj->circuit; |
d62a17ae | 344 | |
173f8887 OD |
345 | /* Check that MPLS TE is enabled */ |
346 | if (!IS_MPLS_TE(circuit->area->mta) || !circuit->ext) | |
347 | return 0; | |
348 | ||
349 | ext = circuit->ext; | |
d62a17ae | 350 | |
173f8887 OD |
351 | /* Determine nexthop IP address */ |
352 | switch (family) { | |
353 | case AF_INET: | |
354 | if (!circuit->ip_router || !adj->ipv4_address_count) | |
355 | UNSET_SUBTLV(ext, EXT_NEIGH_ADDR); | |
356 | else { | |
357 | IPV4_ADDR_COPY(&ext->neigh_addr, | |
358 | &adj->ipv4_addresses[0]); | |
359 | SET_SUBTLV(ext, EXT_NEIGH_ADDR); | |
360 | } | |
361 | break; | |
362 | case AF_INET6: | |
78d905be LS |
363 | /* Nothing to do for link-local addresses - ie. not global. |
364 | * https://datatracker.ietf.org/doc/html/rfc6119#section-3.1.1 | |
365 | * Because the IPv6 traffic engineering TLVs present in LSPs are | |
366 | * propagated across networks, they MUST NOT use link-local | |
367 | * addresses. | |
368 | */ | |
173f8887 OD |
369 | if (!global) |
370 | return 0; | |
371 | ||
372 | if (!circuit->ipv6_router || !adj->global_ipv6_count) | |
373 | UNSET_SUBTLV(ext, EXT_NEIGH_ADDR6); | |
374 | else { | |
375 | IPV6_ADDR_COPY(&ext->neigh_addr6, | |
376 | &adj->global_ipv6_addrs[0]); | |
377 | SET_SUBTLV(ext, EXT_NEIGH_ADDR6); | |
378 | } | |
379 | break; | |
380 | default: | |
381 | return 0; | |
1b3f47d0 | 382 | } |
d62a17ae | 383 | |
173f8887 OD |
384 | return 0; |
385 | } | |
386 | ||
78d905be LS |
387 | static int isis_mpls_te_adj_ip_enabled(struct isis_adjacency *adj, int family, |
388 | bool global) | |
173f8887 | 389 | { |
78d905be | 390 | int ret; |
173f8887 OD |
391 | |
392 | /* Sanity Check */ | |
78d905be | 393 | if (!adj || !adj->circuit) |
173f8887 OD |
394 | return 0; |
395 | ||
78d905be LS |
396 | ret = _isis_mpls_te_adj_ip_enabled(adj, family, global); |
397 | ||
398 | /* Update LSP */ | |
399 | lsp_regenerate_schedule(adj->circuit->area, adj->circuit->is_type, 0); | |
400 | ||
401 | return ret; | |
402 | } | |
403 | ||
404 | static int _isis_mpls_te_adj_ip_disabled(struct isis_adjacency *adj, int family, | |
405 | bool global) | |
406 | { | |
407 | struct isis_circuit *circuit; | |
408 | struct isis_ext_subtlvs *ext; | |
409 | ||
173f8887 OD |
410 | circuit = adj->circuit; |
411 | ||
412 | /* Check that MPLS TE is enabled */ | |
413 | if (!IS_MPLS_TE(circuit->area->mta) || !circuit->ext) | |
414 | return 0; | |
415 | ||
416 | ext = circuit->ext; | |
417 | ||
418 | /* Update MPLS TE IP address parameters if possible */ | |
419 | if (!IS_MPLS_TE(circuit->area->mta) || !IS_EXT_TE(ext)) | |
420 | return 0; | |
421 | ||
422 | /* Determine nexthop IP address */ | |
423 | switch (family) { | |
424 | case AF_INET: | |
425 | UNSET_SUBTLV(ext, EXT_NEIGH_ADDR); | |
426 | break; | |
427 | case AF_INET6: | |
428 | if (global) | |
429 | UNSET_SUBTLV(ext, EXT_NEIGH_ADDR6); | |
430 | break; | |
431 | default: | |
432 | return 0; | |
1b3f47d0 | 433 | } |
d62a17ae | 434 | |
78d905be LS |
435 | return 0; |
436 | } | |
437 | ||
438 | static int isis_mpls_te_adj_ip_disabled(struct isis_adjacency *adj, int family, | |
439 | bool global) | |
440 | { | |
441 | int ret; | |
442 | ||
443 | /* Sanity Check */ | |
444 | if (!adj || !adj->circuit || !adj->circuit->ext) | |
445 | return 0; | |
446 | ||
447 | ret = _isis_mpls_te_adj_ip_disabled(adj, family, global); | |
448 | ||
173f8887 | 449 | /* Update LSP */ |
78d905be | 450 | lsp_regenerate_schedule(adj->circuit->area, adj->circuit->is_type, 0); |
173f8887 | 451 | |
78d905be | 452 | return ret; |
f8c06e2c OD |
453 | } |
454 | ||
78d905be LS |
455 | static void isis_mpls_te_circuit_ip_update(struct isis_circuit *circuit) |
456 | { | |
457 | struct isis_adjacency *adj; | |
458 | ||
459 | /* https://datatracker.ietf.org/doc/html/rfc6119#section-3.2.3 | |
460 | * This sub-TLV of the Extended IS Reachability TLV is used for point- | |
461 | * to-point links | |
462 | */ | |
463 | if (circuit->circ_type != CIRCUIT_T_P2P) | |
464 | return; | |
465 | ||
466 | adj = circuit->u.p2p.neighbor; | |
467 | ||
468 | if (!adj) | |
469 | return; | |
470 | ||
471 | /* Nothing to do for link-local addresses. | |
472 | * https://datatracker.ietf.org/doc/html/rfc6119#section-3.1.1 | |
473 | * Because the IPv6 traffic engineering TLVs present in LSPs are | |
474 | * propagated across networks, they MUST NOT use link-local addresses. | |
475 | */ | |
476 | if (adj->ipv4_address_count > 0) | |
477 | _isis_mpls_te_adj_ip_enabled(adj, AF_INET, false); | |
478 | else | |
479 | _isis_mpls_te_adj_ip_disabled(adj, AF_INET, false); | |
480 | ||
481 | if (adj->global_ipv6_count > 0) | |
482 | _isis_mpls_te_adj_ip_enabled(adj, AF_INET6, true); | |
483 | else | |
484 | _isis_mpls_te_adj_ip_disabled(adj, AF_INET6, true); | |
485 | } | |
486 | ||
487 | ||
1b3f47d0 | 488 | int isis_mpls_te_update(struct interface *ifp) |
f8c06e2c | 489 | { |
d62a17ae | 490 | struct isis_circuit *circuit; |
1b3f47d0 | 491 | uint8_t rc = 1; |
f8c06e2c | 492 | |
d62a17ae | 493 | /* Sanity Check */ |
494 | if (ifp == NULL) | |
1b3f47d0 | 495 | return rc; |
f8c06e2c | 496 | |
d62a17ae | 497 | /* Get circuit context from interface */ |
1b3f47d0 OD |
498 | circuit = circuit_scan_by_ifp(ifp); |
499 | if (circuit == NULL) | |
500 | return rc; | |
f8c06e2c | 501 | |
d62a17ae | 502 | /* Update TE TLVs ... */ |
503 | isis_link_params_update(circuit, ifp); | |
f8c06e2c | 504 | |
d62a17ae | 505 | /* ... and LSP */ |
2e2a8b91 | 506 | if (circuit->area && IS_MPLS_TE(circuit->area->mta)) |
d62a17ae | 507 | lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); |
f8c06e2c | 508 | |
1b3f47d0 OD |
509 | rc = 0; |
510 | return rc; | |
f8c06e2c OD |
511 | } |
512 | ||
ed6189a9 OD |
513 | |
514 | /** | |
515 | * Export Link State information to consumer daemon through ZAPI Link State | |
516 | * Opaque Message. | |
517 | * | |
518 | * @param type Type of Link State Element i.e. Vertex, Edge or Subnet | |
519 | * @param link_state Pointer to Link State Vertex, Edge or Subnet | |
520 | * | |
521 | * @return 0 if success, -1 otherwise | |
522 | */ | |
523 | static int isis_te_export(uint8_t type, void *link_state) | |
524 | { | |
525 | struct ls_message msg = {}; | |
526 | int rc = 0; | |
527 | ||
528 | switch (type) { | |
529 | case LS_MSG_TYPE_NODE: | |
530 | ls_vertex2msg(&msg, (struct ls_vertex *)link_state); | |
531 | rc = ls_send_msg(zclient, &msg, NULL); | |
532 | break; | |
533 | case LS_MSG_TYPE_ATTRIBUTES: | |
534 | ls_edge2msg(&msg, (struct ls_edge *)link_state); | |
535 | rc = ls_send_msg(zclient, &msg, NULL); | |
536 | break; | |
537 | case LS_MSG_TYPE_PREFIX: | |
538 | ls_subnet2msg(&msg, (struct ls_subnet *)link_state); | |
539 | rc = ls_send_msg(zclient, &msg, NULL); | |
540 | break; | |
541 | default: | |
542 | rc = -1; | |
543 | break; | |
544 | } | |
545 | ||
546 | return rc; | |
547 | } | |
548 | ||
549 | /** | |
550 | * Parse LSP and build corresponding vertex. If vertex doesn't exist in the | |
551 | * Link State Database it is created otherwise updated. | |
552 | * | |
553 | * @param ted Traffic Engineering Link State Database | |
554 | * @param lsp IS-IS Link State PDU | |
555 | * | |
556 | * @return Link State Vertex or NULL in case of error | |
557 | */ | |
558 | static struct ls_vertex *lsp_to_vertex(struct ls_ted *ted, struct isis_lsp *lsp) | |
559 | { | |
560 | struct ls_vertex *vertex = NULL; | |
561 | struct ls_node *old, lnode = {}; | |
562 | struct isis_tlvs *tlvs; | |
563 | const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; | |
564 | ||
565 | /* Sanity check */ | |
566 | if (!ted || !lsp) | |
567 | return NULL; | |
568 | ||
569 | /* Compute Link State Node ID from IS-IS sysID ... */ | |
570 | if (lsp->level == ISIS_LEVEL1) | |
571 | lnode.adv.origin = ISIS_L1; | |
572 | else | |
573 | lnode.adv.origin = ISIS_L2; | |
574 | memcpy(&lnode.adv.id.iso.sys_id, &lsp->hdr.lsp_id, ISIS_SYS_ID_LEN); | |
575 | lnode.adv.id.iso.level = lsp->level; | |
576 | /* ... and search the corresponding vertex */ | |
577 | vertex = ls_find_vertex_by_id(ted, lnode.adv); | |
578 | /* Create a new one if not found */ | |
579 | if (!vertex) { | |
580 | old = ls_node_new(lnode.adv, inaddr_any, in6addr_any); | |
581 | old->type = STANDARD; | |
582 | vertex = ls_vertex_add(ted, old); | |
583 | } | |
584 | old = vertex->node; | |
585 | te_debug(" |- %s Vertex (%" PRIu64 ") for node %s", | |
586 | vertex->status == NEW ? "Create" : "Found", vertex->key, | |
587 | print_sys_hostname(old->adv.id.iso.sys_id)); | |
588 | ||
589 | /* Fulfill Link State Node information */ | |
590 | tlvs = lsp->tlvs; | |
591 | if (tlvs) { | |
592 | if (tlvs->te_router_id) { | |
593 | IPV4_ADDR_COPY(&lnode.router_id, tlvs->te_router_id); | |
594 | SET_FLAG(lnode.flags, LS_NODE_ROUTER_ID); | |
595 | } | |
596 | if (tlvs->te_router_id_ipv6) { | |
597 | IPV6_ADDR_COPY(&lnode.router_id6, | |
598 | tlvs->te_router_id_ipv6); | |
599 | SET_FLAG(lnode.flags, LS_NODE_ROUTER_ID6); | |
600 | } | |
601 | if (tlvs->hostname) { | |
a976aa3c | 602 | strlcpy(lnode.name, tlvs->hostname, MAX_NAME_LENGTH); |
ed6189a9 OD |
603 | SET_FLAG(lnode.flags, LS_NODE_NAME); |
604 | } | |
605 | if (tlvs->router_cap) { | |
606 | struct isis_router_cap *cap = tlvs->router_cap; | |
607 | ||
608 | if (cap->srgb.lower_bound != 0 | |
609 | && cap->srgb.range_size != 0) { | |
610 | SET_FLAG(lnode.flags, LS_NODE_SR); | |
611 | lnode.srgb.flag = cap->srgb.flags; | |
612 | lnode.srgb.lower_bound = cap->srgb.lower_bound; | |
613 | lnode.srgb.range_size = cap->srgb.range_size; | |
614 | for (int i = 0; i < SR_ALGORITHM_COUNT; i++) | |
615 | lnode.algo[i] = cap->algo[i]; | |
616 | } | |
617 | ||
618 | if (cap->srlb.lower_bound != 0 | |
619 | && cap->srlb.range_size != 0) { | |
620 | lnode.srlb.lower_bound = cap->srlb.lower_bound; | |
621 | lnode.srlb.range_size = cap->srlb.range_size; | |
622 | SET_FLAG(lnode.flags, LS_NODE_SRLB); | |
623 | } | |
624 | if (cap->msd != 0) { | |
625 | lnode.msd = cap->msd; | |
626 | SET_FLAG(lnode.flags, LS_NODE_MSD); | |
627 | } | |
628 | } | |
629 | } | |
630 | ||
631 | /* Update Link State Node information */ | |
632 | if (!ls_node_same(old, &lnode)) { | |
633 | te_debug(" |- Update Link State Node information"); | |
634 | memcpy(old, &lnode, sizeof(struct ls_node)); | |
635 | if (vertex->status != NEW) | |
636 | vertex->status = UPDATE; | |
637 | } | |
638 | ||
639 | /* Set self TED vertex if LSP corresponds to the own router */ | |
640 | if (lsp->own_lsp) | |
641 | ted->self = vertex; | |
642 | ||
643 | return vertex; | |
644 | } | |
645 | ||
646 | /** | |
647 | * Get Link State Edge from Link State Attributes in TE Database. | |
648 | * Edge structure is dynamically allocated and fulfill with Link State | |
649 | * Attributes if not found. | |
650 | * | |
651 | * @param ted Link State Database | |
652 | * @param attr Link State Attributes | |
653 | * | |
654 | * @return New Link State Edge if success, NULL otherwise | |
655 | */ | |
656 | static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr) | |
657 | { | |
658 | struct ls_edge *edge; | |
659 | struct ls_standard *std; | |
660 | uint64_t key = 0; | |
661 | ||
662 | /* Check parameters */ | |
663 | if (!ted || !attr) | |
664 | return NULL; | |
665 | ||
666 | std = &attr->standard; | |
667 | ||
668 | /* Compute keys in function of local address (IPv4/v6) or identifier */ | |
669 | if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) | |
670 | key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff; | |
671 | else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) | |
672 | key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32 | |
673 | | (uint64_t)ntohl(std->local6.s6_addr32[3])); | |
674 | else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) | |
675 | key = ((uint64_t)std->remote_id << 32) | |
676 | | (((uint64_t)std->local_id) & 0xffffffff); | |
677 | else | |
678 | key = 0; | |
679 | ||
680 | /* Stop here if we don't got a valid key */ | |
681 | if (key == 0) | |
682 | return NULL; | |
683 | ||
684 | /* Get corresponding Edge by key from Link State Data Base */ | |
685 | edge = ls_find_edge_by_key(ted, key); | |
686 | ||
687 | /* and create new one if not exist */ | |
688 | if (!edge) { | |
689 | edge = ls_edge_add(ted, attr); | |
690 | /* | |
691 | * Edge could be Null if no local ID is found in Attributes. | |
692 | * Stop the processing as without any local ID it is not | |
693 | * possible to store Edge in the TED. | |
694 | */ | |
695 | if (!edge) | |
696 | return NULL; | |
697 | } | |
698 | ||
699 | if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR)) | |
700 | te_debug(" |- %s Edge (%" PRIu64 | |
701 | ") from Extended Reach. %pI4", | |
702 | edge->status == NEW ? "Create" : "Found", edge->key, | |
703 | &attr->standard.local); | |
704 | else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6)) | |
705 | te_debug(" |- %s Edge (%" PRIu64 | |
706 | ") from Extended Reach. %pI6", | |
707 | edge->status == NEW ? "Create" : "Found", edge->key, | |
708 | &attr->standard.local6); | |
709 | else | |
710 | te_debug(" |- %s Edge (%" PRIu64 ")", | |
711 | edge->status == NEW ? "Create" : "Found", edge->key); | |
712 | ||
713 | return edge; | |
714 | } | |
715 | ||
716 | /** | |
717 | * Get Link State Attributes from IS-IS Sub-TLVs. Structure is dynamically | |
718 | * allocated and should be free once not use anymore. | |
719 | * | |
720 | * @param adv Link State Node ID | |
721 | * @param tlvs IS-IS Sub TLVs | |
722 | * | |
723 | * @return New Link State attributes if success, NULL otherwise | |
724 | */ | |
725 | static struct ls_attributes *get_attributes(struct ls_node_id adv, | |
726 | struct isis_ext_subtlvs *tlvs) | |
727 | { | |
728 | struct ls_attributes *attr; | |
729 | struct in_addr local = {.s_addr = INADDR_ANY}; | |
730 | struct in6_addr local6 = in6addr_any; | |
731 | uint32_t local_id = 0; | |
732 | ||
733 | /* Got Local identifier */ | |
734 | if (CHECK_FLAG(tlvs->status, EXT_LOCAL_ADDR)) | |
735 | local.s_addr = tlvs->local_addr.s_addr; | |
736 | ||
737 | if (CHECK_FLAG(tlvs->status, EXT_LOCAL_ADDR6)) | |
738 | memcpy(&local6, &tlvs->local_addr6, IPV6_MAX_BYTELEN); | |
739 | ||
740 | if (CHECK_FLAG(tlvs->status, EXT_LLRI)) | |
741 | local_id = tlvs->local_llri; | |
742 | ||
743 | /* Create LS Attributes */ | |
744 | attr = ls_attributes_new(adv, local, local6, local_id); | |
745 | if (!attr) | |
746 | return NULL; | |
747 | ||
748 | /* Browse sub-TLV and fulfill Link State Attributes */ | |
749 | if (CHECK_FLAG(tlvs->status, EXT_ADM_GRP)) { | |
750 | attr->standard.admin_group = tlvs->adm_group; | |
751 | SET_FLAG(attr->flags, LS_ATTR_ADM_GRP); | |
752 | } | |
bd0581e4 LS |
753 | if (CHECK_FLAG(tlvs->status, EXT_EXTEND_ADM_GRP)) { |
754 | admin_group_copy(&attr->ext_admin_group, | |
755 | &tlvs->ext_admin_group); | |
756 | SET_FLAG(attr->flags, LS_ATTR_EXT_ADM_GRP); | |
757 | } | |
ed6189a9 OD |
758 | if (CHECK_FLAG(tlvs->status, EXT_LLRI)) { |
759 | attr->standard.local_id = tlvs->local_llri; | |
760 | attr->standard.remote_id = tlvs->remote_llri; | |
761 | SET_FLAG(attr->flags, LS_ATTR_LOCAL_ID); | |
762 | SET_FLAG(attr->flags, LS_ATTR_NEIGH_ID); | |
763 | } | |
764 | if (CHECK_FLAG(tlvs->status, EXT_NEIGH_ADDR)) { | |
765 | attr->standard.remote.s_addr = tlvs->neigh_addr.s_addr; | |
766 | SET_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR); | |
767 | } | |
768 | if (CHECK_FLAG(tlvs->status, EXT_NEIGH_ADDR6)) { | |
769 | memcpy(&attr->standard.remote6, &tlvs->neigh_addr6, | |
770 | IPV6_MAX_BYTELEN); | |
771 | SET_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6); | |
772 | } | |
773 | if (CHECK_FLAG(tlvs->status, EXT_MAX_BW)) { | |
774 | attr->standard.max_bw = tlvs->max_bw; | |
775 | SET_FLAG(attr->flags, LS_ATTR_MAX_BW); | |
776 | } | |
777 | if (CHECK_FLAG(tlvs->status, EXT_MAX_RSV_BW)) { | |
778 | attr->standard.max_rsv_bw = tlvs->max_rsv_bw; | |
779 | SET_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW); | |
780 | } | |
781 | if (CHECK_FLAG(tlvs->status, EXT_UNRSV_BW)) { | |
782 | memcpy(&attr->standard.unrsv_bw, tlvs->unrsv_bw, | |
783 | ISIS_SUBTLV_UNRSV_BW_SIZE); | |
784 | SET_FLAG(attr->flags, LS_ATTR_UNRSV_BW); | |
785 | } | |
786 | if (CHECK_FLAG(tlvs->status, EXT_TE_METRIC)) { | |
787 | attr->standard.te_metric = tlvs->te_metric; | |
788 | SET_FLAG(attr->flags, LS_ATTR_TE_METRIC); | |
789 | } | |
790 | if (CHECK_FLAG(tlvs->status, EXT_RMT_AS)) { | |
791 | attr->standard.remote_as = tlvs->remote_as; | |
792 | SET_FLAG(attr->flags, LS_ATTR_REMOTE_AS); | |
793 | } | |
794 | if (CHECK_FLAG(tlvs->status, EXT_RMT_IP)) { | |
795 | attr->standard.remote_addr = tlvs->remote_ip; | |
796 | SET_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR); | |
797 | } | |
798 | if (CHECK_FLAG(tlvs->status, EXT_DELAY)) { | |
799 | attr->extended.delay = tlvs->delay; | |
800 | SET_FLAG(attr->flags, LS_ATTR_DELAY); | |
801 | } | |
802 | if (CHECK_FLAG(tlvs->status, EXT_MM_DELAY)) { | |
803 | attr->extended.min_delay = tlvs->min_delay; | |
804 | attr->extended.max_delay = tlvs->max_delay; | |
805 | SET_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY); | |
806 | } | |
807 | if (CHECK_FLAG(tlvs->status, EXT_DELAY_VAR)) { | |
808 | attr->extended.jitter = tlvs->delay_var; | |
809 | SET_FLAG(attr->flags, LS_ATTR_JITTER); | |
810 | } | |
811 | if (CHECK_FLAG(tlvs->status, EXT_PKT_LOSS)) { | |
812 | attr->extended.pkt_loss = tlvs->pkt_loss; | |
813 | SET_FLAG(attr->flags, LS_ATTR_PACKET_LOSS); | |
814 | } | |
815 | if (CHECK_FLAG(tlvs->status, EXT_AVA_BW)) { | |
816 | attr->extended.ava_bw = tlvs->ava_bw; | |
817 | SET_FLAG(attr->flags, LS_ATTR_AVA_BW); | |
818 | } | |
819 | if (CHECK_FLAG(tlvs->status, EXT_RES_BW)) { | |
820 | attr->extended.rsv_bw = tlvs->res_bw; | |
821 | SET_FLAG(attr->flags, LS_ATTR_RSV_BW); | |
822 | } | |
823 | if (CHECK_FLAG(tlvs->status, EXT_USE_BW)) { | |
824 | attr->extended.used_bw = tlvs->use_bw; | |
825 | SET_FLAG(attr->flags, LS_ATTR_USE_BW); | |
826 | } | |
827 | if (CHECK_FLAG(tlvs->status, EXT_ADJ_SID)) { | |
828 | struct isis_adj_sid *adj = | |
829 | (struct isis_adj_sid *)tlvs->adj_sid.head; | |
830 | int i; | |
831 | for (; adj; adj = adj->next) { | |
832 | i = adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? 1 : 0; | |
833 | i += adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? 2 : 0; | |
834 | attr->adj_sid[i].flags = adj->flags; | |
835 | attr->adj_sid[i].weight = adj->weight; | |
836 | attr->adj_sid[i].sid = adj->sid; | |
837 | switch (i) { | |
838 | case ADJ_PRI_IPV4: | |
839 | SET_FLAG(attr->flags, LS_ATTR_ADJ_SID); | |
840 | break; | |
841 | case ADJ_BCK_IPV4: | |
842 | SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID); | |
843 | break; | |
844 | case ADJ_PRI_IPV6: | |
845 | SET_FLAG(attr->flags, LS_ATTR_ADJ_SID6); | |
846 | break; | |
847 | case ADJ_BCK_IPV6: | |
848 | SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID6); | |
849 | break; | |
850 | } | |
851 | } | |
852 | } | |
853 | if (CHECK_FLAG(tlvs->status, EXT_LAN_ADJ_SID)) { | |
854 | struct isis_lan_adj_sid *ladj = | |
855 | (struct isis_lan_adj_sid *)tlvs->lan_sid.head; | |
856 | int i; | |
857 | for (; ladj; ladj = ladj->next) { | |
858 | i = ladj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? 1 : 0; | |
859 | i += ladj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? 2 : 0; | |
860 | attr->adj_sid[i].flags = ladj->flags; | |
861 | attr->adj_sid[i].weight = ladj->weight; | |
862 | attr->adj_sid[i].sid = ladj->sid; | |
863 | memcpy(&attr->adj_sid[i].neighbor.sysid, | |
864 | &ladj->neighbor_id, ISIS_SYS_ID_LEN); | |
865 | switch (i) { | |
866 | case ADJ_PRI_IPV4: | |
867 | SET_FLAG(attr->flags, LS_ATTR_ADJ_SID); | |
868 | break; | |
869 | case ADJ_BCK_IPV4: | |
870 | SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID); | |
871 | break; | |
872 | case ADJ_PRI_IPV6: | |
873 | SET_FLAG(attr->flags, LS_ATTR_ADJ_SID6); | |
874 | break; | |
875 | case ADJ_BCK_IPV6: | |
876 | SET_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID6); | |
877 | break; | |
878 | } | |
879 | } | |
880 | } | |
881 | ||
882 | return attr; | |
883 | } | |
884 | ||
885 | /** | |
886 | * Parse Extended Reachability TLVs and create or update the corresponding | |
887 | * Link State Edge and Attributes. Vertex connections are also updated if | |
888 | * needed based on the remote IP address of the Edge and existing reverse Edge. | |
889 | * | |
890 | * @param id ID of Extended IS | |
891 | * @param metric Metric of the link | |
892 | * @param old_metric Boolean that indicate if it is an old metric (no TE) | |
893 | * @param tlvs SubTlvs that contains TE information | |
894 | * @param arg IS-IS TE argument (TED, Vertex, and export indication) | |
895 | * | |
896 | * @return 0 if success, -1 otherwise | |
897 | */ | |
898 | static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric, | |
899 | struct isis_ext_subtlvs *tlvs, void *arg) | |
900 | { | |
901 | struct isis_te_args *args = (struct isis_te_args *)arg; | |
902 | struct ls_vertex *vertex; | |
903 | struct ls_edge *edge, *dst; | |
904 | struct ls_attributes *attr; | |
905 | ||
906 | te_debug(" |- Process Extended IS for %s", sysid_print(id)); | |
907 | ||
908 | /* Check parameters */ | |
909 | if (old_metric || !args || !tlvs) | |
910 | return LSP_ITER_CONTINUE; | |
911 | ||
912 | /* Initialize Link State Attributes */ | |
913 | vertex = args->vertex; | |
914 | attr = get_attributes(vertex->node->adv, tlvs); | |
915 | /* | |
916 | * Attributes may be Null if no local ID has been found in the LSP. | |
917 | * Stop processing here as without any local ID it is not possible to | |
918 | * create corresponding Edge in the TED. | |
919 | */ | |
920 | if (!attr) | |
921 | return LSP_ITER_CONTINUE; | |
922 | ||
923 | attr->metric = metric; | |
29abd4e3 | 924 | SET_FLAG(attr->flags, LS_ATTR_METRIC); |
ed6189a9 OD |
925 | |
926 | /* Get corresponding Edge from Link State Data Base */ | |
927 | edge = get_edge(args->ted, attr); | |
928 | /* | |
929 | * Edge could be Null if no local ID has been found in Attributes. | |
930 | * Stop processing here as without any local ID it is not possible to | |
931 | * create corresponding Edge in the TED. | |
932 | */ | |
933 | if (!edge) { | |
934 | ls_attributes_del(attr); | |
935 | return LSP_ITER_CONTINUE; | |
936 | } | |
937 | ||
938 | /* Update Attribute fields if there are different */ | |
939 | if (edge->status != NEW) { | |
940 | if (!ls_attributes_same(edge->attributes, attr)) { | |
941 | te_debug(" |- Update Edge Attributes information"); | |
942 | ls_attributes_del(edge->attributes); | |
943 | edge->attributes = attr; | |
944 | edge->status = UPDATE; | |
945 | } else { | |
946 | if (edge->attributes != attr) | |
947 | ls_attributes_del(attr); | |
948 | edge->status = SYNC; | |
949 | } | |
950 | } | |
951 | ||
952 | /* Try to update remote Link from remote address or reachability ID */ | |
953 | te_debug(" |- Link Edge (%" PRIu64 ") to destination vertex (%s)", | |
954 | edge->key, print_sys_hostname(id)); | |
955 | dst = ls_find_edge_by_destination(args->ted, edge->attributes); | |
956 | if (dst) { | |
957 | /* Attach remote link if not set */ | |
958 | if (edge->source && dst->destination == NULL) { | |
959 | vertex = edge->source; | |
960 | if (vertex->incoming_edges) | |
961 | listnode_add_sort_nodup(vertex->incoming_edges, | |
962 | dst); | |
963 | dst->destination = vertex; | |
964 | } | |
965 | /* and destination vertex to this edge if not set */ | |
966 | if (dst->source && edge->destination == NULL) { | |
967 | vertex = dst->source; | |
968 | if (vertex->incoming_edges) | |
969 | listnode_add_sort_nodup(vertex->incoming_edges, | |
970 | edge); | |
971 | edge->destination = vertex; | |
972 | } | |
973 | } else { | |
974 | /* Search dst. Vertex by Extended Reach. ID if not found */ | |
975 | if (edge->destination == NULL) { | |
976 | vertex = ls_find_vertex_by_key(args->ted, | |
977 | sysid_to_key(id)); | |
978 | if (vertex && vertex->incoming_edges) | |
979 | listnode_add_sort_nodup(vertex->incoming_edges, | |
980 | edge); | |
981 | edge->destination = vertex; | |
982 | } | |
983 | } | |
984 | ||
985 | /* Update status and Export Link State Edge if needed */ | |
986 | if (edge->status != SYNC) { | |
987 | if (args->export) | |
988 | isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); | |
989 | edge->status = SYNC; | |
990 | } | |
991 | ||
992 | return LSP_ITER_CONTINUE; | |
993 | } | |
994 | ||
995 | /** | |
996 | * Parse Extended IP Reachability or MT IPv6 Reachability TLVs and create or | |
997 | * update the corresponding Link State Subnet and Prefix. | |
998 | * | |
999 | * @param prefix Prefix associated to this subnet | |
1000 | * @param metric Metric of this prefix | |
1001 | * @param external Boolean to indicate if the prefix is external | |
1002 | * @param subtlvs Subtlvs if any (mostly Segment Routing ID) | |
1003 | * @param arg IS-IS TE argument (TED, Vertex, and export indication) | |
1004 | * | |
1005 | * @return 0 if success, -1 otherwise | |
1006 | */ | |
1007 | static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, | |
1008 | bool external, struct isis_subtlvs *subtlvs, | |
1009 | void *arg) | |
1010 | { | |
1011 | struct isis_te_args *args = (struct isis_te_args *)arg; | |
1012 | struct ls_vertex *vertex; | |
1013 | struct ls_subnet *subnet; | |
1014 | struct ls_prefix *ls_pref; | |
1015 | struct listnode *node; | |
1016 | struct ls_edge *edge; | |
1017 | struct ls_standard *std = NULL; | |
1018 | struct prefix p; | |
1019 | ||
1020 | /* Sanity Check */ | |
1021 | if (!args || !prefix) | |
1022 | return LSP_ITER_CONTINUE; | |
1023 | ||
1024 | te_debug(" |- Process Extended %s Reachability %pFX", | |
1025 | prefix->family == AF_INET ? "IP" : "IPv6", prefix); | |
1026 | ||
1027 | vertex = args->vertex; | |
1028 | ||
1029 | /* | |
1030 | * Prefix with mask different from /32 or /128 are advertised by at | |
1031 | * least 2 nodes. To avoid subnet attached to undetermined vertex, and | |
1032 | * gives the possibility to send the information to client e.g. BGP for | |
1033 | * Link State advertisement, we adjust the prefix with the corresponding | |
1034 | * IP address of the belonging interface when it is available. Other | |
1035 | * prefixes are kept unchanged. | |
1036 | */ | |
1037 | if (prefix->family == AF_INET && prefix->prefixlen < IPV4_MAX_BITLEN) { | |
1038 | std = NULL; | |
1039 | for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) { | |
1040 | if (!CHECK_FLAG(edge->attributes->flags, | |
1041 | LS_ATTR_LOCAL_ADDR)) | |
1042 | continue; | |
1043 | ||
1044 | p.u.prefix4 = edge->attributes->standard.local; | |
1045 | p.family = AF_INET; | |
1046 | p.prefixlen = prefix->prefixlen; | |
1047 | apply_mask_ipv4((struct prefix_ipv4 *)&p); | |
1048 | if (IPV4_ADDR_SAME(&p.u.prefix4, &prefix->u.prefix4)) { | |
1049 | std = &edge->attributes->standard; | |
1050 | break; | |
1051 | } | |
1052 | } | |
1053 | if (std) | |
1054 | p.u.prefix4 = std->local; | |
1055 | ||
1056 | } else if (prefix->family == AF_INET6 | |
1057 | && prefix->prefixlen < IPV6_MAX_BITLEN) { | |
1058 | std = NULL; | |
1059 | for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) { | |
1060 | if (!CHECK_FLAG(edge->attributes->flags, | |
1061 | LS_ATTR_LOCAL_ADDR6)) | |
1062 | continue; | |
1063 | ||
1064 | p.u.prefix6 = edge->attributes->standard.local6; | |
1065 | p.family = AF_INET6; | |
1066 | p.prefixlen = prefix->prefixlen; | |
1067 | apply_mask_ipv6((struct prefix_ipv6 *)&p); | |
1068 | if (IPV6_ADDR_SAME(&p.u.prefix6, &prefix->u.prefix6)) { | |
1069 | std = &edge->attributes->standard; | |
1070 | break; | |
1071 | } | |
1072 | } | |
1073 | if (std) | |
1074 | p.u.prefix6 = std->local6; | |
1075 | } | |
1076 | if (!std) | |
14678bfa | 1077 | prefix_copy(&p, prefix); |
ed6189a9 OD |
1078 | else |
1079 | te_debug(" |- Adjust prefix %pFX with local address to: %pFX", | |
1080 | prefix, &p); | |
1081 | ||
1082 | /* Search existing Subnet in TED ... */ | |
1083 | subnet = ls_find_subnet(args->ted, p); | |
1084 | /* ... and create a new Subnet if not found */ | |
1085 | if (!subnet) { | |
1086 | ls_pref = ls_prefix_new(vertex->node->adv, p); | |
1087 | subnet = ls_subnet_add(args->ted, ls_pref); | |
1088 | if (!subnet) | |
1089 | return LSP_ITER_CONTINUE; | |
1090 | } | |
1091 | ls_pref = subnet->ls_pref; | |
1092 | ||
1093 | te_debug(" |- %s Subnet from prefix %pFX", | |
1094 | subnet->status == NEW ? "Create" : "Found", &p); | |
1095 | ||
1096 | /* Update Metric */ | |
1097 | if (!CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC) | |
1098 | || (ls_pref->metric != metric)) { | |
1099 | ls_pref->metric = metric; | |
1100 | SET_FLAG(ls_pref->flags, LS_PREF_METRIC); | |
1101 | if (subnet->status != NEW) | |
1102 | subnet->status = UPDATE; | |
1103 | } else { | |
1104 | if (subnet->status == ORPHAN) | |
1105 | subnet->status = SYNC; | |
1106 | } | |
1107 | ||
1108 | /* Update Prefix SID if any */ | |
1109 | if (subtlvs && subtlvs->prefix_sids.count != 0) { | |
1110 | struct isis_prefix_sid *psid; | |
1111 | struct ls_sid sr = {}; | |
1112 | ||
1113 | psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; | |
1114 | sr.algo = psid->algorithm; | |
1115 | sr.sid_flag = psid->flags; | |
1116 | sr.sid = psid->value; | |
1117 | ||
1118 | if (!CHECK_FLAG(ls_pref->flags, LS_PREF_SR) | |
1119 | || !memcmp(&ls_pref->sr, &sr, sizeof(struct ls_sid))) { | |
1120 | memcpy(&ls_pref->sr, &sr, sizeof(struct ls_sid)); | |
1121 | SET_FLAG(ls_pref->flags, LS_PREF_SR); | |
1122 | if (subnet->status != NEW) | |
1123 | subnet->status = UPDATE; | |
1124 | } else { | |
1125 | if (subnet->status == ORPHAN) | |
1126 | subnet->status = SYNC; | |
1127 | } | |
1128 | } else { | |
1129 | if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) { | |
1130 | UNSET_FLAG(ls_pref->flags, LS_PREF_SR); | |
1131 | if (subnet->status != NEW) | |
1132 | subnet->status = UPDATE; | |
1133 | } else { | |
1134 | if (subnet->status == ORPHAN) | |
1135 | subnet->status = SYNC; | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | /* Update status and Export Link State Edge if needed */ | |
1140 | if (subnet->status != SYNC) { | |
1141 | if (args->export) | |
1142 | isis_te_export(LS_MSG_TYPE_PREFIX, subnet); | |
1143 | subnet->status = SYNC; | |
1144 | } | |
1145 | ||
1146 | return LSP_ITER_CONTINUE; | |
1147 | } | |
1148 | ||
1149 | /** | |
1150 | * Parse ISIS LSP to fulfill the Link State Database | |
1151 | * | |
1152 | * @param ted Link State Database | |
1153 | * @param lsp ISIS Link State PDU | |
1154 | */ | |
1155 | static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) | |
1156 | { | |
1157 | struct ls_ted *ted; | |
1158 | struct ls_vertex *vertex; | |
1159 | struct ls_edge *edge; | |
1160 | struct ls_subnet *subnet; | |
1161 | struct listnode *node; | |
1162 | struct isis_te_args args; | |
1163 | ||
1164 | /* Sanity Check */ | |
1165 | if (!IS_MPLS_TE(mta) || !mta->ted || !lsp) | |
1166 | return; | |
1167 | ||
1168 | ted = mta->ted; | |
1169 | ||
1170 | te_debug("ISIS-TE(%s): Parse LSP %s", lsp->area->area_tag, | |
1171 | sysid_print(lsp->hdr.lsp_id)); | |
1172 | ||
1173 | /* First parse LSP to obtain the corresponding Vertex */ | |
1174 | vertex = lsp_to_vertex(ted, lsp); | |
1175 | if (!vertex) { | |
1176 | zlog_warn("Unable to build Vertex from LSP %s. Abort!", | |
1177 | sysid_print(lsp->hdr.lsp_id)); | |
1178 | return; | |
1179 | } | |
1180 | ||
1181 | /* Check if Vertex has been modified */ | |
1182 | if (vertex->status != SYNC) { | |
1183 | /* Vertex is out of sync: export it if requested */ | |
1184 | if (IS_EXPORT_TE(mta)) | |
1185 | isis_te_export(LS_MSG_TYPE_NODE, vertex); | |
1186 | vertex->status = SYNC; | |
1187 | } | |
1188 | ||
1189 | /* Mark outgoing Edges and Subnets as ORPHAN to detect deletion */ | |
1190 | for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) | |
1191 | edge->status = ORPHAN; | |
1192 | ||
1193 | for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) | |
1194 | subnet->status = ORPHAN; | |
1195 | ||
1196 | /* Process all Extended Reachability in LSP (all fragments) */ | |
1197 | args.ted = ted; | |
1198 | args.vertex = vertex; | |
1199 | args.export = mta->export; | |
1200 | isis_lsp_iterate_is_reach(lsp, ISIS_MT_IPV4_UNICAST, lsp_to_edge_cb, | |
1201 | &args); | |
1202 | ||
1203 | isis_lsp_iterate_is_reach(lsp, ISIS_MT_IPV6_UNICAST, lsp_to_edge_cb, | |
1204 | &args); | |
1205 | ||
1206 | /* Process all Extended IP (v4 & v6) in LSP (all fragments) */ | |
1207 | isis_lsp_iterate_ip_reach(lsp, AF_INET, ISIS_MT_IPV4_UNICAST, | |
1208 | lsp_to_subnet_cb, &args); | |
1209 | isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV6_UNICAST, | |
1210 | lsp_to_subnet_cb, &args); | |
1211 | isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV4_UNICAST, | |
1212 | lsp_to_subnet_cb, &args); | |
1213 | ||
1214 | /* Clean remaining Orphan Edges or Subnets */ | |
1215 | if (IS_EXPORT_TE(mta)) | |
1216 | ls_vertex_clean(ted, vertex, zclient); | |
1217 | else | |
1218 | ls_vertex_clean(ted, vertex, NULL); | |
1219 | } | |
1220 | ||
1221 | /** | |
1222 | * Delete Link State Database Vertex, Edge & Prefix that correspond to this | |
1223 | * ISIS Link State PDU | |
1224 | * | |
1225 | * @param ted Link State Database | |
1226 | * @param lsp ISIS Link State PDU | |
1227 | */ | |
1228 | static void isis_te_delete_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) | |
1229 | { | |
1230 | struct ls_ted *ted; | |
1231 | struct ls_vertex *vertex = NULL; | |
1232 | struct ls_node lnode = {}; | |
1233 | struct ls_edge *edge; | |
1234 | struct ls_subnet *subnet; | |
1235 | struct listnode *nnode, *node; | |
1236 | ||
1237 | /* Sanity Check */ | |
1238 | if (!IS_MPLS_TE(mta) || !mta->ted || !lsp) | |
1239 | return; | |
1240 | ||
1241 | te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %s", | |
1242 | lsp->area->area_tag, sysid_print(lsp->hdr.lsp_id)); | |
1243 | ||
1244 | /* Compute Link State Node ID from IS-IS sysID ... */ | |
1245 | if (lsp->level == ISIS_LEVEL1) | |
1246 | lnode.adv.origin = ISIS_L1; | |
1247 | else | |
1248 | lnode.adv.origin = ISIS_L2; | |
1249 | memcpy(&lnode.adv.id.iso.sys_id, &lsp->hdr.lsp_id, ISIS_SYS_ID_LEN); | |
1250 | lnode.adv.id.iso.level = lsp->level; | |
1251 | ted = mta->ted; | |
1252 | /* ... and search the corresponding vertex */ | |
1253 | vertex = ls_find_vertex_by_id(ted, lnode.adv); | |
1254 | if (!vertex) | |
1255 | return; | |
1256 | ||
1257 | te_debug(" |- Delete Vertex %s", vertex->node->name); | |
1258 | ||
1259 | /* | |
1260 | * We can't use the ls_vertex_del_all() function if export TE is set, | |
1261 | * as we must first advertise the client daemons of each removal. | |
1262 | */ | |
1263 | /* Remove outgoing Edges */ | |
1264 | for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) { | |
1265 | if (IS_EXPORT_TE(mta)) { | |
1266 | edge->status = DELETE; | |
1267 | isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); | |
1268 | } | |
1269 | ls_edge_del_all(ted, edge); | |
1270 | } | |
1271 | ||
1272 | /* Disconnect incoming Edges */ | |
1273 | for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) { | |
1274 | ls_disconnect(vertex, edge, false); | |
1275 | if (edge->source == NULL) { | |
1276 | if (IS_EXPORT_TE(mta)) { | |
1277 | edge->status = DELETE; | |
1278 | isis_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); | |
1279 | } | |
1280 | ls_edge_del_all(ted, edge); | |
1281 | } | |
1282 | } | |
1283 | ||
1284 | /* Remove subnets */ | |
1285 | for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) { | |
1286 | if (IS_EXPORT_TE(mta)) { | |
1287 | subnet->status = DELETE; | |
1288 | isis_te_export(LS_MSG_TYPE_PREFIX, subnet); | |
1289 | } | |
1290 | ls_subnet_del_all(ted, subnet); | |
1291 | } | |
1292 | ||
1293 | /* Then remove Link State Node */ | |
1294 | if (IS_EXPORT_TE(mta)) { | |
1295 | vertex->status = DELETE; | |
1296 | isis_te_export(LS_MSG_TYPE_NODE, vertex); | |
1297 | } | |
1298 | ls_node_del(vertex->node); | |
1299 | ||
1300 | /* Finally, remove Vertex */ | |
1301 | ls_vertex_del(ted, vertex); | |
1302 | } | |
1303 | ||
1304 | /** | |
1305 | * Process ISIS LSP according to the event to add, update or remove | |
1306 | * corresponding vertex, edge and prefix in the Link State database. | |
1307 | * Since LSP could be fragmented, the function starts by searching the root LSP | |
1308 | * to retrieve the complete LSP, including eventual fragment before processing | |
1309 | * all of them. | |
1310 | * | |
1311 | * @param lsp ISIS Link State PDU | |
1312 | * @param event LSP event: ADD, UPD, INC & DEL (TICK are ignored) | |
1313 | * | |
1314 | */ | |
1315 | void isis_te_lsp_event(struct isis_lsp *lsp, enum lsp_event event) | |
1316 | { | |
1317 | struct isis_area *area; | |
1318 | struct isis_lsp *lsp0; | |
1319 | ||
1320 | /* Sanity check */ | |
1321 | if (!lsp || !lsp->area) | |
1322 | return; | |
1323 | ||
1324 | area = lsp->area; | |
1325 | if (!IS_MPLS_TE(area->mta)) | |
1326 | return; | |
1327 | ||
1328 | /* Adjust LSP0 in case of fragment */ | |
1329 | if (LSP_FRAGMENT(lsp->hdr.lsp_id)) | |
1330 | lsp0 = lsp->lspu.zero_lsp; | |
1331 | else | |
1332 | lsp0 = lsp; | |
1333 | ||
1334 | /* Then process event */ | |
1335 | switch (event) { | |
1336 | case LSP_ADD: | |
1337 | case LSP_UPD: | |
1338 | case LSP_INC: | |
1339 | isis_te_parse_lsp(area->mta, lsp0); | |
1340 | break; | |
1341 | case LSP_DEL: | |
1342 | isis_te_delete_lsp(area->mta, lsp0); | |
1343 | break; | |
a348c945 DS |
1344 | case LSP_UNKNOWN: |
1345 | case LSP_TICK: | |
ed6189a9 OD |
1346 | break; |
1347 | } | |
1348 | } | |
1349 | ||
1350 | /** | |
1351 | * Send the whole Link State Traffic Engineering Database to the consumer that | |
1352 | * request it through a ZAPI Link State Synchronous Opaque Message. | |
1353 | * | |
1354 | * @param info ZAPI Opaque message | |
1355 | * | |
1356 | * @return 0 if success, -1 otherwise | |
1357 | */ | |
1358 | int isis_te_sync_ted(struct zapi_opaque_reg_info dst) | |
1359 | { | |
1360 | struct listnode *node, *inode; | |
1361 | struct isis *isis; | |
1362 | struct isis_area *area; | |
1363 | struct mpls_te_area *mta; | |
1364 | int rc = -1; | |
1365 | ||
1366 | te_debug("ISIS-TE(%s): Received TED synchro from client %d", __func__, | |
1367 | dst.proto); | |
1368 | /* For each area, send TED if TE distribution is enabled */ | |
1369 | for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { | |
1370 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { | |
1371 | mta = area->mta; | |
1372 | if (IS_MPLS_TE(mta) && IS_EXPORT_TE(mta)) { | |
1373 | te_debug(" |- Export TED from area %s", | |
1374 | area->area_tag); | |
1375 | rc = ls_sync_ted(mta->ted, zclient, &dst); | |
1376 | if (rc != 0) | |
1377 | return rc; | |
1378 | } | |
1379 | } | |
1380 | } | |
1381 | ||
1382 | return rc; | |
1383 | } | |
1384 | ||
1385 | /** | |
1386 | * Initialize the Link State database from the LSP already stored for this area | |
1387 | * | |
1388 | * @param area ISIS area | |
1389 | */ | |
1390 | void isis_te_init_ted(struct isis_area *area) | |
1391 | { | |
1392 | struct isis_lsp *lsp; | |
1393 | ||
1394 | /* Iterate over all lsp. */ | |
1395 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) | |
1396 | frr_each (lspdb, &area->lspdb[level - 1], lsp) | |
1397 | isis_te_parse_lsp(area->mta, lsp); | |
1398 | } | |
1399 | ||
78dfa0c7 | 1400 | /* Following are vty command functions */ |
ef020087 CF |
1401 | #ifndef FABRICD |
1402 | ||
173f8887 OD |
1403 | static void show_router_id(struct vty *vty, struct isis_area *area) |
1404 | { | |
1405 | bool no_match = true; | |
1406 | ||
1407 | vty_out(vty, "Area %s:\n", area->area_tag); | |
1408 | if (area->mta->router_id.s_addr != 0) { | |
1409 | vty_out(vty, " MPLS-TE IPv4 Router-Address: %pI4\n", | |
1410 | &area->mta->router_id); | |
1411 | no_match = false; | |
1412 | } | |
1413 | if (!IN6_IS_ADDR_UNSPECIFIED(&area->mta->router_id_ipv6)) { | |
1414 | vty_out(vty, " MPLS-TE IPv6 Router-Address: %pI6\n", | |
1415 | &area->mta->router_id_ipv6); | |
1416 | no_match = false; | |
1417 | } | |
1418 | if (no_match) | |
1419 | vty_out(vty, " N/A\n"); | |
1420 | } | |
1421 | ||
eab88f36 K |
1422 | DEFUN(show_isis_mpls_te_router, |
1423 | show_isis_mpls_te_router_cmd, | |
1424 | "show " PROTO_NAME " [vrf <NAME|all>] mpls-te router", | |
1425 | SHOW_STR | |
1426 | PROTO_HELP | |
1427 | VRF_CMD_HELP_STR "All VRFs\n" | |
1428 | MPLS_TE_STR "Router information\n") | |
f8c06e2c | 1429 | { |
d62a17ae | 1430 | |
36944791 | 1431 | struct listnode *anode, *inode; |
2e2a8b91 | 1432 | struct isis_area *area; |
eab88f36 K |
1433 | struct isis *isis = NULL; |
1434 | const char *vrf_name = VRF_DEFAULT_NAME; | |
1435 | bool all_vrf = false; | |
1436 | int idx_vrf = 0; | |
2e2a8b91 | 1437 | |
eab88f36 | 1438 | if (!im) { |
2e2a8b91 OD |
1439 | vty_out(vty, "IS-IS Routing Process not enabled\n"); |
1440 | return CMD_SUCCESS; | |
1441 | } | |
eab88f36 K |
1442 | ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); |
1443 | if (vrf_name) { | |
1444 | if (all_vrf) { | |
36944791 | 1445 | for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { |
eab88f36 K |
1446 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, |
1447 | anode, area)) { | |
1448 | if (!IS_MPLS_TE(area->mta)) | |
1449 | continue; | |
1450 | ||
173f8887 | 1451 | show_router_id(vty, area); |
eab88f36 K |
1452 | } |
1453 | } | |
1454 | return 0; | |
1455 | } | |
1456 | isis = isis_lookup_by_vrfname(vrf_name); | |
1457 | if (isis != NULL) { | |
1458 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, | |
1459 | area)) { | |
1460 | ||
1461 | if (!IS_MPLS_TE(area->mta)) | |
1462 | continue; | |
1463 | ||
173f8887 | 1464 | show_router_id(vty, area); |
eab88f36 K |
1465 | } |
1466 | } | |
2e2a8b91 | 1467 | } |
d62a17ae | 1468 | |
1469 | return CMD_SUCCESS; | |
f8c06e2c OD |
1470 | } |
1471 | ||
1b3f47d0 OD |
1472 | static void show_ext_sub(struct vty *vty, char *name, |
1473 | struct isis_ext_subtlvs *ext) | |
f8c06e2c | 1474 | { |
af8ac8f9 | 1475 | struct sbuf buf; |
1b3f47d0 | 1476 | char ibuf[PREFIX2STR_BUFFER]; |
af8ac8f9 CF |
1477 | |
1478 | sbuf_init(&buf, NULL, 0); | |
d62a17ae | 1479 | |
1b3f47d0 | 1480 | if (!ext || ext->status == EXT_DISABLE) |
2e2a8b91 | 1481 | return; |
d62a17ae | 1482 | |
2e2a8b91 | 1483 | vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name); |
d62a17ae | 1484 | |
2e2a8b91 | 1485 | sbuf_reset(&buf); |
231e94e3 | 1486 | |
1b3f47d0 | 1487 | if (IS_SUBTLV(ext, EXT_ADM_GRP)) |
6cde4b45 | 1488 | sbuf_push(&buf, 4, "Administrative Group: 0x%x\n", |
1b3f47d0 OD |
1489 | ext->adm_group); |
1490 | if (IS_SUBTLV(ext, EXT_LLRI)) { | |
6cde4b45 | 1491 | sbuf_push(&buf, 4, "Link Local ID: %u\n", |
1b3f47d0 | 1492 | ext->local_llri); |
6cde4b45 | 1493 | sbuf_push(&buf, 4, "Link Remote ID: %u\n", |
1b3f47d0 OD |
1494 | ext->remote_llri); |
1495 | } | |
1496 | if (IS_SUBTLV(ext, EXT_LOCAL_ADDR)) | |
a854ea43 MS |
1497 | sbuf_push(&buf, 4, "Local Interface IP Address(es): %pI4\n", |
1498 | &ext->local_addr); | |
1b3f47d0 | 1499 | if (IS_SUBTLV(ext, EXT_NEIGH_ADDR)) |
a854ea43 MS |
1500 | sbuf_push(&buf, 4, "Remote Interface IP Address(es): %pI4\n", |
1501 | &ext->neigh_addr); | |
1b3f47d0 OD |
1502 | if (IS_SUBTLV(ext, EXT_LOCAL_ADDR6)) |
1503 | sbuf_push(&buf, 4, "Local Interface IPv6 Address(es): %s\n", | |
1504 | inet_ntop(AF_INET6, &ext->local_addr6, ibuf, | |
1505 | PREFIX2STR_BUFFER)); | |
1506 | if (IS_SUBTLV(ext, EXT_NEIGH_ADDR6)) | |
1507 | sbuf_push(&buf, 4, "Remote Interface IPv6 Address(es): %s\n", | |
1508 | inet_ntop(AF_INET6, &ext->local_addr6, ibuf, | |
1509 | PREFIX2STR_BUFFER)); | |
1510 | if (IS_SUBTLV(ext, EXT_MAX_BW)) | |
1511 | sbuf_push(&buf, 4, "Maximum Bandwidth: %g (Bytes/sec)\n", | |
1512 | ext->max_bw); | |
1513 | if (IS_SUBTLV(ext, EXT_MAX_RSV_BW)) | |
1514 | sbuf_push(&buf, 4, | |
1515 | "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", | |
1516 | ext->max_rsv_bw); | |
1517 | if (IS_SUBTLV(ext, EXT_UNRSV_BW)) { | |
1518 | sbuf_push(&buf, 4, "Unreserved Bandwidth:\n"); | |
1519 | for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { | |
1520 | sbuf_push(&buf, 4 + 2, | |
1521 | "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", | |
1522 | j, ext->unrsv_bw[j], | |
1523 | j + 1, ext->unrsv_bw[j + 1]); | |
1524 | } | |
1525 | } | |
1526 | if (IS_SUBTLV(ext, EXT_TE_METRIC)) | |
1527 | sbuf_push(&buf, 4, "Traffic Engineering Metric: %u\n", | |
1528 | ext->te_metric); | |
1529 | if (IS_SUBTLV(ext, EXT_RMT_AS)) | |
1530 | sbuf_push(&buf, 4, | |
6cde4b45 | 1531 | "Inter-AS TE Remote AS number: %u\n", |
1b3f47d0 OD |
1532 | ext->remote_as); |
1533 | if (IS_SUBTLV(ext, EXT_RMT_IP)) | |
1534 | sbuf_push(&buf, 4, | |
a854ea43 MS |
1535 | "Inter-AS TE Remote ASBR IP address: %pI4\n", |
1536 | &ext->remote_ip); | |
1b3f47d0 OD |
1537 | if (IS_SUBTLV(ext, EXT_DELAY)) |
1538 | sbuf_push(&buf, 4, | |
6cde4b45 | 1539 | "%s Average Link Delay: %u (micro-sec)\n", |
1b3f47d0 | 1540 | IS_ANORMAL(ext->delay) ? "Anomalous" : "Normal", |
129ad38b | 1541 | ext->delay & TE_EXT_MASK); |
1b3f47d0 | 1542 | if (IS_SUBTLV(ext, EXT_MM_DELAY)) { |
6cde4b45 | 1543 | sbuf_push(&buf, 4, "%s Min/Max Link Delay: %u / %u (micro-sec)\n", |
1b3f47d0 OD |
1544 | IS_ANORMAL(ext->min_delay) ? "Anomalous" : "Normal", |
1545 | ext->min_delay & TE_EXT_MASK, | |
1546 | ext->max_delay & TE_EXT_MASK); | |
1547 | } | |
1548 | if (IS_SUBTLV(ext, EXT_DELAY_VAR)) | |
1549 | sbuf_push(&buf, 4, | |
6cde4b45 | 1550 | "Delay Variation: %u (micro-sec)\n", |
1b3f47d0 OD |
1551 | ext->delay_var & TE_EXT_MASK); |
1552 | if (IS_SUBTLV(ext, EXT_PKT_LOSS)) | |
1553 | sbuf_push(&buf, 4, "%s Link Packet Loss: %g (%%)\n", | |
1554 | IS_ANORMAL(ext->pkt_loss) ? "Anomalous" : "Normal", | |
1555 | (float)((ext->pkt_loss & TE_EXT_MASK) | |
1556 | * LOSS_PRECISION)); | |
1557 | if (IS_SUBTLV(ext, EXT_RES_BW)) | |
1558 | sbuf_push(&buf, 4, | |
1559 | "Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", | |
1560 | ext->res_bw); | |
1561 | if (IS_SUBTLV(ext, EXT_AVA_BW)) | |
1562 | sbuf_push(&buf, 4, | |
1563 | "Unidirectional Available Bandwidth: %g (Bytes/sec)\n", | |
1564 | ext->ava_bw); | |
1565 | if (IS_SUBTLV(ext, EXT_USE_BW)) | |
1566 | sbuf_push(&buf, 4, | |
1567 | "Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", | |
1568 | ext->use_bw); | |
af8ac8f9 | 1569 | |
2e2a8b91 OD |
1570 | vty_multiline(vty, "", "%s", sbuf_buf(&buf)); |
1571 | vty_out(vty, "---------------\n\n"); | |
d62a17ae | 1572 | |
af8ac8f9 | 1573 | sbuf_free(&buf); |
d62a17ae | 1574 | return; |
f8c06e2c OD |
1575 | } |
1576 | ||
1577 | DEFUN (show_isis_mpls_te_interface, | |
1578 | show_isis_mpls_te_interface_cmd, | |
7c0cbd0e | 1579 | "show " PROTO_NAME " mpls-te interface [INTERFACE]", |
f8c06e2c | 1580 | SHOW_STR |
7c0cbd0e | 1581 | PROTO_HELP |
f8c06e2c OD |
1582 | MPLS_TE_STR |
1583 | "Interface information\n" | |
1584 | "Interface name\n") | |
1585 | { | |
36944791 | 1586 | struct listnode *anode, *cnode, *inode; |
2e2a8b91 OD |
1587 | struct isis_area *area; |
1588 | struct isis_circuit *circuit; | |
231e94e3 | 1589 | struct interface *ifp; |
2e2a8b91 | 1590 | int idx_interface = 4; |
eab88f36 | 1591 | struct isis *isis = NULL; |
d62a17ae | 1592 | |
eab88f36 | 1593 | if (!im) { |
2e2a8b91 OD |
1594 | vty_out(vty, "IS-IS Routing Process not enabled\n"); |
1595 | return CMD_SUCCESS; | |
d62a17ae | 1596 | } |
2e2a8b91 OD |
1597 | |
1598 | if (argc == idx_interface) { | |
1599 | /* Show All Interfaces. */ | |
36944791 | 1600 | for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { |
eab88f36 K |
1601 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, |
1602 | area)) { | |
2e2a8b91 | 1603 | |
eab88f36 K |
1604 | if (!IS_MPLS_TE(area->mta)) |
1605 | continue; | |
2e2a8b91 | 1606 | |
eab88f36 | 1607 | vty_out(vty, "Area %s:\n", area->area_tag); |
2e2a8b91 | 1608 | |
eab88f36 K |
1609 | for (ALL_LIST_ELEMENTS_RO(area->circuit_list, |
1610 | cnode, circuit)) | |
1611 | show_ext_sub(vty, | |
1612 | circuit->interface->name, | |
1613 | circuit->ext); | |
1614 | } | |
2e2a8b91 OD |
1615 | } |
1616 | } else { | |
1617 | /* Interface name is specified. */ | |
a36898e7 | 1618 | ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); |
2e2a8b91 | 1619 | if (ifp == NULL) |
d62a17ae | 1620 | vty_out(vty, "No such interface name\n"); |
2e2a8b91 OD |
1621 | else { |
1622 | circuit = circuit_scan_by_ifp(ifp); | |
1623 | if (!circuit) | |
1624 | vty_out(vty, | |
1625 | "ISIS is not enabled on circuit %s\n", | |
1626 | ifp->name); | |
1627 | else | |
1b3f47d0 | 1628 | show_ext_sub(vty, ifp->name, circuit->ext); |
2e2a8b91 | 1629 | } |
d62a17ae | 1630 | } |
1631 | ||
1632 | return CMD_SUCCESS; | |
f8c06e2c | 1633 | } |
ed6189a9 OD |
1634 | |
1635 | /** | |
1636 | * Search Vertex in TED that corresponds to the given string that represent | |
1637 | * the ISO system ID in the forms <systemid/hostname>[.<pseudo-id>-<framenent>] | |
1638 | * | |
1639 | * @param ted Link State Database | |
1640 | * @param id ISO System ID | |
1641 | * @param isis Main reference to the isis daemon | |
1642 | * | |
1643 | * @return Vertex if found, NULL otherwise | |
1644 | */ | |
1645 | static struct ls_vertex *vertex_for_arg(struct ls_ted *ted, const char *id, | |
1646 | struct isis *isis) | |
1647 | { | |
1648 | char sysid[255] = {0}; | |
1649 | uint8_t number[3]; | |
1650 | const char *pos; | |
1651 | uint8_t lspid[ISIS_SYS_ID_LEN + 2] = {0}; | |
1652 | struct isis_dynhn *dynhn; | |
1653 | uint64_t key = 0; | |
1654 | ||
1655 | if (!id) | |
1656 | return NULL; | |
1657 | ||
1658 | /* | |
1659 | * extract fragment and pseudo id from the string argv | |
1660 | * in the forms: | |
1661 | * (a) <systemid/hostname>.<pseudo-id>-<framenent> or | |
1662 | * (b) <systemid/hostname>.<pseudo-id> or | |
1663 | * (c) <systemid/hostname> or | |
1664 | * Where systemid is in the form: | |
1665 | * xxxx.xxxx.xxxx | |
1666 | */ | |
1667 | strlcpy(sysid, id, sizeof(sysid)); | |
1668 | if (strlen(id) > 3) { | |
1669 | pos = id + strlen(id) - 3; | |
1670 | if (strncmp(pos, "-", 1) == 0) { | |
1671 | memcpy(number, ++pos, 2); | |
1672 | lspid[ISIS_SYS_ID_LEN + 1] = | |
1673 | (uint8_t)strtol((char *)number, NULL, 16); | |
1674 | pos -= 4; | |
1675 | if (strncmp(pos, ".", 1) != 0) | |
1676 | return NULL; | |
1677 | } | |
1678 | if (strncmp(pos, ".", 1) == 0) { | |
1679 | memcpy(number, ++pos, 2); | |
1680 | lspid[ISIS_SYS_ID_LEN] = | |
1681 | (uint8_t)strtol((char *)number, NULL, 16); | |
1682 | sysid[pos - id - 1] = '\0'; | |
1683 | } | |
1684 | } | |
1685 | ||
1686 | /* | |
1687 | * Try to find the lsp-id if the argv | |
1688 | * string is in | |
1689 | * the form | |
1690 | * hostname.<pseudo-id>-<fragment> | |
1691 | */ | |
1692 | if (sysid2buff(lspid, sysid)) { | |
1693 | key = sysid_to_key(lspid); | |
1694 | } else if ((dynhn = dynhn_find_by_name(isis, sysid))) { | |
1695 | memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN); | |
1696 | key = sysid_to_key(lspid); | |
1697 | } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { | |
1698 | memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); | |
1699 | key = sysid_to_key(lspid); | |
1700 | } | |
1701 | ||
1702 | if (key == 0) | |
1703 | return NULL; | |
1704 | ||
1705 | return ls_find_vertex_by_key(ted, key); | |
1706 | } | |
1707 | ||
1708 | /** | |
1709 | * Show Link State Traffic Engineering Database extracted from IS-IS LSP. | |
1710 | * | |
1711 | * @param vty VTY output console | |
1712 | * @param argv Command line argument | |
1713 | * @param argc Number of command line argument | |
1714 | * @param ted Traffic Engineering Database | |
1715 | * @param isis isis Main reference to the isis daemon | |
1716 | * | |
1717 | * @return Command Success if OK, Command Warning otherwise | |
1718 | */ | |
1719 | static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, | |
1720 | struct isis_area *area, struct isis *isis) | |
1721 | { | |
1722 | int idx; | |
1723 | char *id; | |
1724 | struct in_addr ip_addr; | |
1725 | struct in6_addr ip6_addr; | |
1726 | struct prefix pref; | |
1727 | struct ls_ted *ted; | |
1728 | struct ls_vertex *vertex; | |
1729 | struct ls_edge *edge; | |
1730 | struct ls_subnet *subnet; | |
1731 | uint64_t key; | |
1732 | bool detail = false; | |
1733 | bool uj = use_json(argc, argv); | |
1734 | json_object *json = NULL; | |
1735 | ||
1736 | if (!IS_MPLS_TE(area->mta) || !area->mta->ted) { | |
1737 | vty_out(vty, "MPLS-TE is disabled for Area %s\n", | |
1738 | area->area_tag ? area->area_tag : "null"); | |
1739 | return CMD_SUCCESS; | |
1740 | } | |
1741 | ||
1742 | ted = area->mta->ted; | |
1743 | ||
1744 | if (uj) | |
1745 | json = json_object_new_object(); | |
1746 | else | |
1747 | vty_out(vty, "Area %s:\n", | |
1748 | area->area_tag ? area->area_tag : "null"); | |
1749 | ||
1750 | if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "detail")) | |
1751 | detail = true; | |
1752 | ||
1753 | idx = 4; | |
1754 | if (argv_find(argv, argc, "vertex", &idx)) { | |
1755 | /* Show Vertex */ | |
1756 | id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg | |
1757 | : NULL; | |
1758 | if (!id) | |
1759 | vertex = NULL; | |
1760 | else if (!strncmp(id, "self", 4)) | |
1761 | vertex = ted->self; | |
1762 | else { | |
1763 | vertex = vertex_for_arg(ted, id, isis); | |
1764 | if (!vertex) { | |
1765 | vty_out(vty, "No vertex found for ID %s\n", id); | |
1766 | return CMD_WARNING; | |
1767 | } | |
1768 | } | |
1769 | ||
1770 | if (vertex) | |
1771 | ls_show_vertex(vertex, vty, json, detail); | |
1772 | else | |
1773 | ls_show_vertices(ted, vty, json, detail); | |
1774 | ||
1775 | } else if (argv_find(argv, argc, "edge", &idx)) { | |
1776 | /* Show Edge */ | |
1777 | if (argv_find(argv, argc, "A.B.C.D", &idx)) { | |
1778 | if (!inet_pton(AF_INET, argv[idx]->arg, &ip_addr)) { | |
1779 | vty_out(vty, | |
1780 | "Specified Edge ID %s is invalid\n", | |
1781 | argv[idx]->arg); | |
1782 | return CMD_WARNING_CONFIG_FAILED; | |
1783 | } | |
1784 | /* Get the Edge from the Link State Database */ | |
1785 | key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; | |
1786 | edge = ls_find_edge_by_key(ted, key); | |
1787 | if (!edge) { | |
1788 | vty_out(vty, "No edge found for ID %pI4\n", | |
1789 | &ip_addr); | |
1790 | return CMD_WARNING; | |
1791 | } | |
1792 | } else if (argv_find(argv, argc, "X:X::X:X", &idx)) { | |
1793 | if (!inet_pton(AF_INET6, argv[idx]->arg, &ip6_addr)) { | |
1794 | vty_out(vty, | |
1795 | "Specified Edge ID %s is invalid\n", | |
1796 | argv[idx]->arg); | |
1797 | return CMD_WARNING_CONFIG_FAILED; | |
1798 | } | |
1799 | /* Get the Edge from the Link State Database */ | |
1800 | key = (uint64_t)ntohl(ip6_addr.s6_addr32[3]) | |
1801 | | ((uint64_t)ntohl(ip6_addr.s6_addr32[2]) << 32); | |
1802 | edge = ls_find_edge_by_key(ted, key); | |
1803 | if (!edge) { | |
1804 | vty_out(vty, "No edge found for ID %pI6\n", | |
1805 | &ip6_addr); | |
1806 | return CMD_WARNING; | |
1807 | } | |
1808 | } else | |
1809 | edge = NULL; | |
1810 | ||
1811 | if (edge) | |
1812 | ls_show_edge(edge, vty, json, detail); | |
1813 | else | |
1814 | ls_show_edges(ted, vty, json, detail); | |
1815 | ||
1816 | } else if (argv_find(argv, argc, "subnet", &idx)) { | |
1817 | /* Show Subnet */ | |
1818 | if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { | |
1819 | if (!str2prefix(argv[idx]->arg, &pref)) { | |
1820 | vty_out(vty, "Invalid prefix format %s\n", | |
1821 | argv[idx]->arg); | |
1822 | return CMD_WARNING_CONFIG_FAILED; | |
1823 | } | |
1824 | /* Get the Subnet from the Link State Database */ | |
1825 | subnet = ls_find_subnet(ted, pref); | |
1826 | if (!subnet) { | |
1827 | vty_out(vty, "No subnet found for ID %pFX\n", | |
1828 | &pref); | |
1829 | return CMD_WARNING; | |
1830 | } | |
1831 | } else if (argv_find(argv, argc, "X:X::X:X/M", &idx)) { | |
1832 | if (!str2prefix(argv[idx]->arg, &pref)) { | |
1833 | vty_out(vty, "Invalid prefix format %s\n", | |
1834 | argv[idx]->arg); | |
1835 | return CMD_WARNING_CONFIG_FAILED; | |
1836 | } | |
1837 | /* Get the Subnet from the Link State Database */ | |
1838 | subnet = ls_find_subnet(ted, pref); | |
1839 | if (!subnet) { | |
1840 | vty_out(vty, "No subnet found for ID %pFX\n", | |
1841 | &pref); | |
1842 | return CMD_WARNING; | |
1843 | } | |
1844 | } else | |
1845 | subnet = NULL; | |
1846 | ||
1847 | if (subnet) | |
1848 | ls_show_subnet(subnet, vty, json, detail); | |
1849 | else | |
1850 | ls_show_subnets(ted, vty, json, detail); | |
1851 | ||
1852 | } else { | |
1853 | /* Show the complete TED */ | |
1854 | ls_show_ted(ted, vty, json, detail); | |
1855 | } | |
1856 | ||
3757f964 DA |
1857 | if (uj) |
1858 | vty_json(vty, json); | |
ed6189a9 OD |
1859 | |
1860 | return CMD_SUCCESS; | |
1861 | } | |
1862 | ||
1863 | /** | |
1864 | * Show ISIS Traffic Engineering Database | |
1865 | * | |
1866 | * @param vty VTY output console | |
1867 | * @param argv Command line argument | |
1868 | * @param argc Number of command line argument | |
1869 | * @param isis isis Main reference to the isis daemon | |
1870 | ||
1871 | * @return Command Success if OK, Command Warning otherwise | |
1872 | */ | |
1873 | static int show_isis_ted(struct vty *vty, struct cmd_token *argv[], int argc, | |
1874 | struct isis *isis) | |
1875 | { | |
1876 | struct listnode *node; | |
1877 | struct isis_area *area; | |
1878 | int rc; | |
1879 | ||
1880 | for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { | |
1881 | rc = show_ted(vty, argv, argc, area, isis); | |
1882 | if (rc != CMD_SUCCESS) | |
1883 | return rc; | |
1884 | } | |
1885 | return CMD_SUCCESS; | |
1886 | } | |
1887 | ||
1888 | DEFUN(show_isis_mpls_te_db, | |
1889 | show_isis_mpls_te_db_cmd, | |
1890 | "show " PROTO_NAME " [vrf <NAME|all>] mpls-te database [<vertex [WORD]|edge [A.B.C.D|X:X::X:X]|subnet [A.B.C.D/M|X:X::X:X/M]>] [detail|json]", | |
1891 | SHOW_STR PROTO_HELP VRF_CMD_HELP_STR | |
1892 | "All VRFs\n" | |
1893 | MPLS_TE_STR | |
1894 | "MPLS-TE database\n" | |
1895 | "MPLS-TE Vertex\n" | |
1896 | "MPLS-TE Vertex ID (as an ISO ID, hostname or \"self\")\n" | |
1897 | "MPLS-TE Edge\n" | |
1898 | "MPLS-TE Edge ID (as an IPv4 address)\n" | |
1899 | "MPLS-TE Edge ID (as an IPv6 address)\n" | |
1900 | "MPLS-TE Subnet\n" | |
1901 | "MPLS-TE Subnet ID (as an IPv4 prefix)\n" | |
1902 | "MPLS-TE Subnet ID (as an IPv6 prefix)\n" | |
1903 | "Detailed information\n" | |
1904 | JSON_STR) | |
1905 | { | |
1906 | int idx_vrf = 0; | |
1907 | const char *vrf_name = VRF_DEFAULT_NAME; | |
1908 | bool all_vrf = false; | |
1909 | struct listnode *node; | |
1910 | struct isis *isis; | |
1911 | int rc = CMD_WARNING; | |
1912 | ||
1913 | ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); | |
1914 | if (vrf_name) { | |
1915 | if (all_vrf) { | |
1916 | for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { | |
1917 | rc = show_isis_ted(vty, argv, argc, isis); | |
1918 | if (rc != CMD_SUCCESS) | |
1919 | return rc; | |
1920 | } | |
1921 | return CMD_SUCCESS; | |
1922 | } | |
1923 | isis = isis_lookup_by_vrfname(vrf_name); | |
1924 | if (isis) | |
1925 | rc = show_isis_ted(vty, argv, argc, isis); | |
1926 | } | |
1927 | ||
1928 | return rc; | |
1929 | } | |
1930 | ||
1931 | #endif /* #ifndef FRABRICD */ | |
f8c06e2c OD |
1932 | |
1933 | /* Initialize MPLS_TE */ | |
d62a17ae | 1934 | void isis_mpls_te_init(void) |
f8c06e2c OD |
1935 | { |
1936 | ||
1b3f47d0 OD |
1937 | /* Register Circuit and Adjacency hook */ |
1938 | hook_register(isis_if_new_hook, isis_mpls_te_update); | |
173f8887 OD |
1939 | hook_register(isis_adj_ip_enabled_hook, isis_mpls_te_adj_ip_enabled); |
1940 | hook_register(isis_adj_ip_disabled_hook, isis_mpls_te_adj_ip_disabled); | |
1b3f47d0 | 1941 | |
ef020087 | 1942 | #ifndef FABRICD |
d62a17ae | 1943 | /* Register new VTY commands */ |
1944 | install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); | |
1945 | install_element(VIEW_NODE, &show_isis_mpls_te_interface_cmd); | |
ed6189a9 | 1946 | install_element(VIEW_NODE, &show_isis_mpls_te_db_cmd); |
ef020087 | 1947 | #endif |
f8c06e2c | 1948 | |
d62a17ae | 1949 | return; |
f8c06e2c | 1950 | } |