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