2 * IS-IS Rout(e)ing protocol - BFD support
3 * Copyright (C) 2018 Christian Franke
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "lib_errors.h"
26 #include "isisd/isis_bfd.h"
27 #include "isisd/isis_zebra.h"
28 #include "isisd/isis_common.h"
29 #include "isisd/isis_constants.h"
30 #include "isisd/isis_adjacency.h"
31 #include "isisd/isis_circuit.h"
32 #include "isisd/isisd.h"
33 #include "isisd/fabricd.h"
35 DEFINE_MTYPE_STATIC(ISISD
, BFD_SESSION
, "ISIS BFD Session")
44 static struct bfd_session
*bfd_session_new(int family
, union g_addr
*dst_ip
,
47 struct bfd_session
*rv
;
49 rv
= XCALLOC(MTYPE_BFD_SESSION
, sizeof(*rv
));
56 static void bfd_session_free(struct bfd_session
**session
)
61 XFREE(MTYPE_BFD_SESSION
, *session
);
64 static bool bfd_session_same(const struct bfd_session
*session
, int family
,
65 const union g_addr
*src
, const union g_addr
*dst
)
67 if (session
->family
!= family
)
70 switch (session
->family
) {
72 if (!IPV4_ADDR_SAME(&session
->dst_ip
.ipv4
, &dst
->ipv4
))
74 if (!IPV4_ADDR_SAME(&session
->src_ip
.ipv4
, &src
->ipv4
))
78 if (!IPV6_ADDR_SAME(&session
->dst_ip
.ipv6
, &dst
->ipv6
))
80 if (!IPV6_ADDR_SAME(&session
->src_ip
.ipv6
, &src
->ipv6
))
84 flog_err(EC_LIB_DEVELOPMENT
, "%s: unknown address-family: %u",
85 __func__
, session
->family
);
92 static void bfd_adj_event(struct isis_adjacency
*adj
, struct prefix
*dst
,
95 if (!adj
->bfd_session
) {
98 "ISIS-BFD: Ignoring update for adjacency with %s, could not find bfd session on the adjacency",
103 if (adj
->bfd_session
->family
!= dst
->family
) {
106 "ISIS-BFD: Ignoring update for adjacency with %s, address family does not match the family on the adjacency",
111 switch (adj
->bfd_session
->family
) {
113 if (!IPV4_ADDR_SAME(&adj
->bfd_session
->dst_ip
.ipv4
,
117 "ISIS-BFD: Ignoring update for adjacency with %s, IPv4 address does not match",
123 if (!IPV6_ADDR_SAME(&adj
->bfd_session
->dst_ip
.ipv6
,
127 "ISIS-BFD: Ignoring update for adjacency with %s, IPv6 address does not match",
133 flog_err(EC_LIB_DEVELOPMENT
, "%s: unknown address-family: %u",
134 __func__
, adj
->bfd_session
->family
);
138 int old_status
= adj
->bfd_session
->status
;
140 BFD_SET_CLIENT_STATUS(adj
->bfd_session
->status
, new_status
);
142 if (old_status
== new_status
) {
145 "ISIS-BFD: Ignoring update for adjacency with %s, new status matches current known status",
151 char dst_str
[INET6_ADDRSTRLEN
];
153 inet_ntop(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
154 dst_str
, sizeof(dst_str
));
155 zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s",
156 dst_str
, adj
->circuit
->interface
->name
,
157 bfd_get_status_str(old_status
),
158 bfd_get_status_str(new_status
));
161 if (old_status
!= BFD_STATUS_UP
162 || new_status
!= BFD_STATUS_DOWN
) {
166 adj
->circuit
->area
->bfd_signalled_down
= true;
168 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
, "bfd session went down");
171 static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS
)
173 struct interface
*ifp
;
174 struct prefix dst_ip
, src_ip
;
177 ifp
= bfd_get_peer_info(zclient
->ibuf
, &dst_ip
, &src_ip
, &status
, NULL
,
179 if (!ifp
|| (dst_ip
.family
!= AF_INET
&& dst_ip
.family
!= AF_INET6
))
183 char dst_buf
[INET6_ADDRSTRLEN
];
185 inet_ntop(dst_ip
.family
, &dst_ip
.u
.prefix
, dst_buf
,
188 zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
189 dst_buf
, ifp
->name
, bfd_get_status_str(status
));
192 struct isis_circuit
*circuit
= circuit_scan_by_ifp(ifp
);
197 "ISIS-BFD: Ignoring update, could not find circuit");
201 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
202 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
203 struct list
*adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
205 struct listnode
*node
, *nnode
;
206 struct isis_adjacency
*adj
;
208 for (ALL_LIST_ELEMENTS(adjdb
, node
, nnode
, adj
))
209 bfd_adj_event(adj
, &dst_ip
, status
);
211 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
212 if (circuit
->u
.p2p
.neighbor
) {
213 bfd_adj_event(circuit
->u
.p2p
.neighbor
,
221 static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS
)
223 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, vrf_id
);
225 struct listnode
*anode
;
226 struct isis_area
*area
;
227 struct isis
*isis
= NULL
;
229 isis
= isis_lookup_by_vrfid(vrf_id
);
232 zlog_warn(" %s : ISIS routing instance not found", __func__
);
237 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
239 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
240 struct listnode
*cnode
;
241 struct isis_circuit
*circuit
;
243 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
244 isis_bfd_circuit_cmd(circuit
, ZEBRA_BFD_DEST_UPDATE
);
248 zlog_debug("ISIS-BFD: Done with replay.");
253 static void (*orig_zebra_connected
)(struct zclient
*);
254 static void isis_bfd_zebra_connected(struct zclient
*zclient
)
256 if (orig_zebra_connected
)
257 orig_zebra_connected(zclient
);
259 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, VRF_DEFAULT
);
262 static void bfd_debug(int family
, union g_addr
*dst
, union g_addr
*src
,
263 const char *interface
, int command
)
268 char dst_str
[INET6_ADDRSTRLEN
];
269 char src_str
[INET6_ADDRSTRLEN
];
271 inet_ntop(family
, dst
, dst_str
, sizeof(dst_str
));
272 inet_ntop(family
, src
, src_str
, sizeof(src_str
));
274 const char *command_str
;
277 case ZEBRA_BFD_DEST_REGISTER
:
278 command_str
= "Register";
280 case ZEBRA_BFD_DEST_DEREGISTER
:
281 command_str
= "Deregister";
283 case ZEBRA_BFD_DEST_UPDATE
:
284 command_str
= "Update";
287 command_str
= "Unknown-Cmd";
291 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
292 command_str
, dst_str
, interface
, src_str
);
295 static void bfd_command(int command
, struct bfd_info
*bfd_info
, int family
,
296 const void *dst_ip
, const void *src_ip
,
299 struct bfd_session_arg args
= {};
303 args
.family
= family
;
304 args
.vrf_id
= VRF_DEFAULT
;
305 args
.command
= command
;
306 args
.bfd_info
= bfd_info
;
308 args
.min_rx
= bfd_info
->required_min_rx
;
309 args
.min_tx
= bfd_info
->desired_min_tx
;
310 args
.detection_multiplier
= bfd_info
->detect_mult
;
311 if (bfd_info
->profile
[0]) {
312 args
.profilelen
= strlen(bfd_info
->profile
);
313 strlcpy(args
.profile
, bfd_info
->profile
,
314 sizeof(args
.profile
));
318 addrlen
= family
== AF_INET
? sizeof(struct in_addr
)
319 : sizeof(struct in6_addr
);
320 memcpy(&args
.dst
, dst_ip
, addrlen
);
322 memcpy(&args
.src
, src_ip
, addrlen
);
325 strlcpy(args
.ifname
, if_name
, sizeof(args
.ifname
));
326 args
.ifnamelen
= strlen(args
.ifname
);
329 zclient_bfd_command(zclient
, &args
);
332 static void bfd_handle_adj_down(struct isis_adjacency
*adj
)
334 if (!adj
->bfd_session
)
337 bfd_debug(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
338 &adj
->bfd_session
->src_ip
, adj
->circuit
->interface
->name
,
339 ZEBRA_BFD_DEST_DEREGISTER
);
341 bfd_command(ZEBRA_BFD_DEST_DEREGISTER
, NULL
, adj
->bfd_session
->family
,
342 &adj
->bfd_session
->dst_ip
, &adj
->bfd_session
->src_ip
,
343 (adj
->circuit
->interface
) ? adj
->circuit
->interface
->name
346 bfd_session_free(&adj
->bfd_session
);
349 static void bfd_handle_adj_up(struct isis_adjacency
*adj
, int command
)
351 struct isis_circuit
*circuit
= adj
->circuit
;
355 struct list
*local_ips
;
356 struct prefix
*local_ip
;
358 if (!circuit
->bfd_info
) {
361 "ISIS-BFD: skipping BFD initialization on adjacency with %s because there is no bfd_info in the circuit",
366 /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed
367 * before starting up BFD
369 if (circuit
->ipv6_router
370 && (listcount(circuit
->ipv6_link
) == 0
371 || adj
->ipv6_address_count
== 0)) {
374 "ISIS-BFD: skipping BFD initialization on adjacency with %s because IPv6 is enabled but not ready",
380 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
381 * creating a BFD session over IPv6.
383 if (circuit
->ipv6_router
&& adj
->ipv6_address_count
) {
385 dst_ip
.ipv6
= adj
->ipv6_addresses
[0];
386 local_ips
= circuit
->ipv6_link
;
387 if (!local_ips
|| list_isempty(local_ips
)) {
390 "ISIS-BFD: skipping BFD initialization: IPv6 enabled and no local IPv6 addresses");
393 local_ip
= listgetdata(listhead(local_ips
));
394 src_ip
.ipv6
= local_ip
->u
.prefix6
;
395 } else if (circuit
->ip_router
&& adj
->ipv4_address_count
) {
397 dst_ip
.ipv4
= adj
->ipv4_addresses
[0];
398 local_ips
= fabricd_ip_addrs(adj
->circuit
);
399 if (!local_ips
|| list_isempty(local_ips
)) {
402 "ISIS-BFD: skipping BFD initialization: IPv4 enabled and no local IPv4 addresses");
405 local_ip
= listgetdata(listhead(local_ips
));
406 src_ip
.ipv4
= local_ip
->u
.prefix4
;
410 if (adj
->bfd_session
) {
411 if (bfd_session_same(adj
->bfd_session
, family
, &src_ip
,
413 bfd_handle_adj_down(adj
);
416 if (!adj
->bfd_session
) {
419 "ISIS-BFD: creating BFD session for adjacency with %s",
421 adj
->bfd_session
= bfd_session_new(family
, &dst_ip
, &src_ip
);
424 bfd_debug(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
425 &adj
->bfd_session
->src_ip
, circuit
->interface
->name
, command
);
427 bfd_command(command
, circuit
->bfd_info
, family
,
428 &adj
->bfd_session
->dst_ip
, &adj
->bfd_session
->src_ip
,
429 (adj
->circuit
->interface
) ? adj
->circuit
->interface
->name
434 bfd_handle_adj_down(adj
);
437 static int bfd_handle_adj_state_change(struct isis_adjacency
*adj
)
439 if (adj
->adj_state
== ISIS_ADJ_UP
)
440 bfd_handle_adj_up(adj
, ZEBRA_BFD_DEST_REGISTER
);
442 bfd_handle_adj_down(adj
);
446 static void bfd_adj_cmd(struct isis_adjacency
*adj
, int command
)
448 if (adj
->adj_state
== ISIS_ADJ_UP
449 && command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
450 bfd_handle_adj_up(adj
, command
);
452 bfd_handle_adj_down(adj
);
456 void isis_bfd_circuit_cmd(struct isis_circuit
*circuit
, int command
)
458 switch (circuit
->circ_type
) {
459 case CIRCUIT_T_BROADCAST
:
460 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
461 struct list
*adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
463 struct listnode
*node
;
464 struct isis_adjacency
*adj
;
466 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
467 bfd_adj_cmd(adj
, command
);
471 if (circuit
->u
.p2p
.neighbor
)
472 bfd_adj_cmd(circuit
->u
.p2p
.neighbor
, command
);
479 void isis_bfd_circuit_param_set(struct isis_circuit
*circuit
, uint32_t min_rx
,
480 uint32_t min_tx
, uint32_t detect_mult
,
481 const char *profile
, int defaults
)
485 bfd_set_param(&circuit
->bfd_info
, min_rx
, min_tx
, detect_mult
, profile
,
489 isis_bfd_circuit_cmd(circuit
, command
);
493 static int bfd_circuit_write_settings(struct isis_circuit
*circuit
,
496 struct bfd_info
*bfd_info
= circuit
->bfd_info
;
501 vty_out(vty
, " %s bfd\n", PROTO_NAME
);
506 static int bfd_handle_adj_ip_enabled(struct isis_adjacency
*adj
, int family
)
509 if (family
!= AF_INET6
)
512 if (adj
->bfd_session
)
515 if (adj
->adj_state
!= ISIS_ADJ_UP
)
518 bfd_handle_adj_up(adj
, ZEBRA_BFD_DEST_REGISTER
);
523 static int bfd_handle_circuit_add_addr(struct isis_circuit
*circuit
)
525 struct isis_adjacency
*adj
;
526 struct listnode
*node
;
528 if (circuit
->area
== 0)
531 for (ALL_LIST_ELEMENTS_RO(circuit
->area
->adjacency_list
, node
, adj
)) {
532 if (adj
->bfd_session
)
535 if (adj
->adj_state
!= ISIS_ADJ_UP
)
538 bfd_handle_adj_up(adj
, ZEBRA_BFD_DEST_REGISTER
);
544 void isis_bfd_init(void)
548 orig_zebra_connected
= zclient
->zebra_connected
;
549 zclient
->zebra_connected
= isis_bfd_zebra_connected
;
550 zclient
->interface_bfd_dest_update
= isis_bfd_interface_dest_update
;
551 zclient
->bfd_dest_replay
= isis_bfd_nbr_replay
;
552 hook_register(isis_adj_state_change_hook
,
553 bfd_handle_adj_state_change
);
555 hook_register(isis_circuit_config_write
,
556 bfd_circuit_write_settings
);
558 hook_register(isis_adj_ip_enabled_hook
, bfd_handle_adj_ip_enabled
);
559 hook_register(isis_circuit_add_addr_hook
, bfd_handle_circuit_add_addr
);