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 if (adj
->bfd_session
->family
!= dst
->family
)
101 switch (adj
->bfd_session
->family
) {
103 if (!IPV4_ADDR_SAME(&adj
->bfd_session
->dst_ip
.ipv4
,
108 if (!IPV6_ADDR_SAME(&adj
->bfd_session
->dst_ip
.ipv6
,
113 flog_err(EC_LIB_DEVELOPMENT
, "%s: unknown address-family: %u",
114 __func__
, adj
->bfd_session
->family
);
118 int old_status
= adj
->bfd_session
->status
;
120 BFD_SET_CLIENT_STATUS(adj
->bfd_session
->status
, new_status
);
122 if (old_status
== new_status
)
126 char dst_str
[INET6_ADDRSTRLEN
];
128 inet_ntop(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
129 dst_str
, sizeof(dst_str
));
130 zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s",
131 dst_str
, adj
->circuit
->interface
->name
,
132 bfd_get_status_str(old_status
),
133 bfd_get_status_str(new_status
));
136 if (old_status
!= BFD_STATUS_UP
137 || new_status
!= BFD_STATUS_DOWN
) {
141 adj
->circuit
->area
->bfd_signalled_down
= true;
143 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
, "bfd session went down");
146 static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS
)
148 struct interface
*ifp
;
149 struct prefix dst_ip
;
152 ifp
= bfd_get_peer_info(zclient
->ibuf
, &dst_ip
, NULL
, &status
,
154 if (!ifp
|| (dst_ip
.family
!= AF_INET
&& dst_ip
.family
!= AF_INET6
))
158 char dst_buf
[INET6_ADDRSTRLEN
];
160 inet_ntop(dst_ip
.family
, &dst_ip
.u
.prefix
, dst_buf
,
163 zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
164 dst_buf
, ifp
->name
, bfd_get_status_str(status
));
167 struct isis_circuit
*circuit
= circuit_scan_by_ifp(ifp
);
172 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
173 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
174 struct list
*adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
176 struct listnode
*node
, *nnode
;
177 struct isis_adjacency
*adj
;
179 for (ALL_LIST_ELEMENTS(adjdb
, node
, nnode
, adj
))
180 bfd_adj_event(adj
, &dst_ip
, status
);
182 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
183 if (circuit
->u
.p2p
.neighbor
) {
184 bfd_adj_event(circuit
->u
.p2p
.neighbor
,
192 static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS
)
194 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, vrf_id
);
196 struct listnode
*anode
;
197 struct isis_area
*area
;
198 struct isis
*isis
= NULL
;
200 isis
= isis_lookup_by_vrfid(vrf_id
);
203 zlog_warn(" %s : ISIS routing instance not found", __func__
);
208 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
210 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, anode
, area
)) {
211 struct listnode
*cnode
;
212 struct isis_circuit
*circuit
;
214 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, cnode
, circuit
))
215 isis_bfd_circuit_cmd(circuit
, ZEBRA_BFD_DEST_UPDATE
);
219 zlog_debug("ISIS-BFD: Done with replay.");
224 static void (*orig_zebra_connected
)(struct zclient
*);
225 static void isis_bfd_zebra_connected(struct zclient
*zclient
)
227 if (orig_zebra_connected
)
228 orig_zebra_connected(zclient
);
230 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, VRF_DEFAULT
);
233 static void bfd_debug(int family
, union g_addr
*dst
, union g_addr
*src
,
234 const char *interface
, int command
)
239 char dst_str
[INET6_ADDRSTRLEN
];
240 char src_str
[INET6_ADDRSTRLEN
];
242 inet_ntop(family
, dst
, dst_str
, sizeof(dst_str
));
243 inet_ntop(family
, src
, src_str
, sizeof(src_str
));
245 const char *command_str
;
248 case ZEBRA_BFD_DEST_REGISTER
:
249 command_str
= "Register";
251 case ZEBRA_BFD_DEST_DEREGISTER
:
252 command_str
= "Deregister";
254 case ZEBRA_BFD_DEST_UPDATE
:
255 command_str
= "Update";
258 command_str
= "Unknown-Cmd";
262 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
263 command_str
, dst_str
, interface
, src_str
);
266 static void bfd_command(int command
, struct bfd_info
*bfd_info
, int family
,
267 const void *dst_ip
, const void *src_ip
,
270 struct bfd_session_arg args
= {};
274 args
.family
= family
;
275 args
.vrf_id
= VRF_DEFAULT
;
276 args
.command
= command
;
277 args
.bfd_info
= bfd_info
;
279 args
.min_rx
= bfd_info
->required_min_rx
;
280 args
.min_tx
= bfd_info
->desired_min_tx
;
281 args
.detection_multiplier
= bfd_info
->detect_mult
;
282 if (bfd_info
->profile
[0]) {
283 args
.profilelen
= strlen(bfd_info
->profile
);
284 strlcpy(args
.profile
, bfd_info
->profile
,
285 sizeof(args
.profile
));
289 addrlen
= family
== AF_INET
? sizeof(struct in_addr
)
290 : sizeof(struct in6_addr
);
291 memcpy(&args
.dst
, dst_ip
, addrlen
);
293 memcpy(&args
.src
, src_ip
, addrlen
);
296 strlcpy(args
.ifname
, if_name
, sizeof(args
.ifname
));
297 args
.ifnamelen
= strlen(args
.ifname
);
300 zclient_bfd_command(zclient
, &args
);
303 static void bfd_handle_adj_down(struct isis_adjacency
*adj
)
305 if (!adj
->bfd_session
)
308 bfd_debug(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
309 &adj
->bfd_session
->src_ip
, adj
->circuit
->interface
->name
,
310 ZEBRA_BFD_DEST_DEREGISTER
);
312 bfd_command(ZEBRA_BFD_DEST_DEREGISTER
, NULL
, adj
->bfd_session
->family
,
313 &adj
->bfd_session
->dst_ip
, &adj
->bfd_session
->src_ip
,
314 (adj
->circuit
->interface
) ? adj
->circuit
->interface
->name
317 bfd_session_free(&adj
->bfd_session
);
320 static void bfd_handle_adj_up(struct isis_adjacency
*adj
, int command
)
322 struct isis_circuit
*circuit
= adj
->circuit
;
326 struct list
*local_ips
;
327 struct prefix
*local_ip
;
329 if (!circuit
->bfd_info
)
333 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
334 * creating a BFD session over IPv6.
336 if (circuit
->ipv6_router
&& adj
->ipv6_address_count
) {
338 dst_ip
.ipv6
= adj
->ipv6_addresses
[0];
339 local_ips
= circuit
->ipv6_link
;
340 if (!local_ips
|| list_isempty(local_ips
))
342 local_ip
= listgetdata(listhead(local_ips
));
343 src_ip
.ipv6
= local_ip
->u
.prefix6
;
344 } else if (circuit
->ip_router
&& adj
->ipv4_address_count
) {
346 dst_ip
.ipv4
= adj
->ipv4_addresses
[0];
347 local_ips
= fabricd_ip_addrs(adj
->circuit
);
348 if (!local_ips
|| list_isempty(local_ips
))
350 local_ip
= listgetdata(listhead(local_ips
));
351 src_ip
.ipv4
= local_ip
->u
.prefix4
;
355 if (adj
->bfd_session
) {
356 if (bfd_session_same(adj
->bfd_session
, family
, &src_ip
,
358 bfd_handle_adj_down(adj
);
361 if (!adj
->bfd_session
)
362 adj
->bfd_session
= bfd_session_new(family
, &dst_ip
, &src_ip
);
364 bfd_debug(adj
->bfd_session
->family
, &adj
->bfd_session
->dst_ip
,
365 &adj
->bfd_session
->src_ip
, circuit
->interface
->name
, command
);
367 bfd_command(command
, circuit
->bfd_info
, family
,
368 &adj
->bfd_session
->dst_ip
, &adj
->bfd_session
->src_ip
,
369 (adj
->circuit
->interface
) ? adj
->circuit
->interface
->name
374 bfd_handle_adj_down(adj
);
377 static int bfd_handle_adj_state_change(struct isis_adjacency
*adj
)
379 if (adj
->adj_state
== ISIS_ADJ_UP
)
380 bfd_handle_adj_up(adj
, ZEBRA_BFD_DEST_REGISTER
);
382 bfd_handle_adj_down(adj
);
386 static void bfd_adj_cmd(struct isis_adjacency
*adj
, int command
)
388 if (adj
->adj_state
== ISIS_ADJ_UP
389 && command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
390 bfd_handle_adj_up(adj
, command
);
392 bfd_handle_adj_down(adj
);
396 void isis_bfd_circuit_cmd(struct isis_circuit
*circuit
, int command
)
398 switch (circuit
->circ_type
) {
399 case CIRCUIT_T_BROADCAST
:
400 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
401 struct list
*adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
403 struct listnode
*node
;
404 struct isis_adjacency
*adj
;
406 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
407 bfd_adj_cmd(adj
, command
);
411 if (circuit
->u
.p2p
.neighbor
)
412 bfd_adj_cmd(circuit
->u
.p2p
.neighbor
, command
);
419 void isis_bfd_circuit_param_set(struct isis_circuit
*circuit
, uint32_t min_rx
,
420 uint32_t min_tx
, uint32_t detect_mult
,
421 const char *profile
, int defaults
)
425 bfd_set_param(&circuit
->bfd_info
, min_rx
, min_tx
, detect_mult
, profile
,
429 isis_bfd_circuit_cmd(circuit
, command
);
433 static int bfd_circuit_write_settings(struct isis_circuit
*circuit
,
436 struct bfd_info
*bfd_info
= circuit
->bfd_info
;
441 vty_out(vty
, " %s bfd\n", PROTO_NAME
);
446 void isis_bfd_init(void)
450 orig_zebra_connected
= zclient
->zebra_connected
;
451 zclient
->zebra_connected
= isis_bfd_zebra_connected
;
452 zclient
->interface_bfd_dest_update
= isis_bfd_interface_dest_update
;
453 zclient
->bfd_dest_replay
= isis_bfd_nbr_replay
;
454 hook_register(isis_adj_state_change_hook
,
455 bfd_handle_adj_state_change
);
457 hook_register(isis_circuit_config_write
,
458 bfd_circuit_write_settings
);