]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_bfd.c
Merge pull request #8284 from mjstapp/fix_bgp_zero_timers
[mirror_frr.git] / isisd / isis_bfd.c
CommitLineData
52df8228
CF
1/*
2 * IS-IS Rout(e)ing protocol - BFD support
52df8228
CF
3 * Copyright (C) 2018 Christian Franke
4 *
d04f9fbf
CF
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)
8 * any later version.
52df8228 9 *
d04f9fbf
CF
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
13 * more details.
52df8228
CF
14 *
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
18 */
19#include <zebra.h>
20
21#include "zclient.h"
e782cca7 22#include "nexthop.h"
52df8228 23#include "bfd.h"
e782cca7 24#include "lib_errors.h"
52df8228
CF
25
26#include "isisd/isis_bfd.h"
27#include "isisd/isis_zebra.h"
215eccb0
CF
28#include "isisd/isis_common.h"
29#include "isisd/isis_constants.h"
30#include "isisd/isis_adjacency.h"
31#include "isisd/isis_circuit.h"
3015e3d1 32#include "isisd/isisd.h"
215eccb0 33#include "isisd/fabricd.h"
52df8228 34
20a42f01
CF
35DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session")
36
37struct bfd_session {
e782cca7
RW
38 int family;
39 union g_addr dst_ip;
40 union g_addr src_ip;
a5eba4e9 41 int status;
20a42f01
CF
42};
43
e782cca7
RW
44static struct bfd_session *bfd_session_new(int family, union g_addr *dst_ip,
45 union g_addr *src_ip)
20a42f01
CF
46{
47 struct bfd_session *rv;
48
a5eba4e9 49 rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv));
e782cca7 50 rv->family = family;
20a42f01
CF
51 rv->dst_ip = *dst_ip;
52 rv->src_ip = *src_ip;
53 return rv;
54}
55
56static void bfd_session_free(struct bfd_session **session)
57{
58 if (!*session)
59 return;
60
61 XFREE(MTYPE_BFD_SESSION, *session);
20a42f01
CF
62}
63
e782cca7
RW
64static bool bfd_session_same(const struct bfd_session *session, int family,
65 const union g_addr *src, const union g_addr *dst)
66{
67 if (session->family != family)
68 return false;
69
70 switch (session->family) {
71 case AF_INET:
72 if (!IPV4_ADDR_SAME(&session->dst_ip.ipv4, &dst->ipv4))
73 return false;
74 if (!IPV4_ADDR_SAME(&session->src_ip.ipv4, &src->ipv4))
75 return false;
76 break;
77 case AF_INET6:
78 if (!IPV6_ADDR_SAME(&session->dst_ip.ipv6, &dst->ipv6))
79 return false;
80 if (!IPV6_ADDR_SAME(&session->src_ip.ipv6, &src->ipv6))
81 return false;
82 break;
83 default:
84 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
85 __func__, session->family);
86 exit(1);
87 }
88
89 return true;
90}
91
a5eba4e9
CF
92static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
93 int new_status)
94{
95 if (!adj->bfd_session)
96 return;
97
e782cca7 98 if (adj->bfd_session->family != dst->family)
a5eba4e9
CF
99 return;
100
e782cca7
RW
101 switch (adj->bfd_session->family) {
102 case AF_INET:
103 if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4,
104 &dst->u.prefix4))
105 return;
106 break;
107 case AF_INET6:
108 if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6,
109 &dst->u.prefix6))
110 return;
111 break;
112 default:
113 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
114 __func__, adj->bfd_session->family);
115 exit(1);
116 }
117
a5eba4e9 118 int old_status = adj->bfd_session->status;
a5eba4e9 119
7555dc61
S
120 BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status);
121
a5eba4e9
CF
122 if (old_status == new_status)
123 return;
124
e740f9c1 125 if (IS_DEBUG_BFD) {
a5eba4e9
CF
126 char dst_str[INET6_ADDRSTRLEN];
127
e782cca7 128 inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip,
a5eba4e9
CF
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));
134 }
135
136 if (old_status != BFD_STATUS_UP
137 || new_status != BFD_STATUS_DOWN) {
138 return;
139 }
140
690497fb
G
141 adj->circuit->area->bfd_signalled_down = true;
142
16167b31 143 isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "bfd session went down");
a5eba4e9
CF
144}
145
121f9dee 146static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
52df8228 147{
2815f817 148 struct interface *ifp;
37a74717 149 struct prefix dst_ip, src_ip;
2815f817
CF
150 int status;
151
37a74717
DS
152 ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, &src_ip, &status, NULL,
153 vrf_id);
e782cca7 154 if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6))
2815f817
CF
155 return 0;
156
e740f9c1 157 if (IS_DEBUG_BFD) {
2815f817 158 char dst_buf[INET6_ADDRSTRLEN];
5489eb45 159
e782cca7
RW
160 inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf,
161 sizeof(dst_buf));
2815f817
CF
162
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));
165 }
166
a5eba4e9 167 struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
5489eb45 168
a5eba4e9
CF
169 if (!circuit)
170 return 0;
171
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];
175
176 struct listnode *node, *nnode;
177 struct isis_adjacency *adj;
178
179 for (ALL_LIST_ELEMENTS(adjdb, node, nnode, adj))
180 bfd_adj_event(adj, &dst_ip, status);
181 }
182 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
183 if (circuit->u.p2p.neighbor) {
184 bfd_adj_event(circuit->u.p2p.neighbor,
185 &dst_ip, status);
186 }
187 }
188
52df8228
CF
189 return 0;
190}
191
121f9dee 192static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
52df8228 193{
0945d5ed 194 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
ab880eaf
CF
195
196 struct listnode *anode;
197 struct isis_area *area;
eab88f36
K
198 struct isis *isis = NULL;
199
200 isis = isis_lookup_by_vrfid(vrf_id);
201
202 if (isis == NULL) {
203 zlog_warn(" %s : ISIS routing instance not found", __func__);
204 return -1;
205 }
ab880eaf 206
e740f9c1 207 if (IS_DEBUG_BFD)
2815f817
CF
208 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
209
ab880eaf
CF
210 for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
211 struct listnode *cnode;
212 struct isis_circuit *circuit;
213
214 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
215 isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE);
216 }
217
e740f9c1 218 if (IS_DEBUG_BFD)
2815f817
CF
219 zlog_debug("ISIS-BFD: Done with replay.");
220
52df8228
CF
221 return 0;
222}
223
224static void (*orig_zebra_connected)(struct zclient *);
225static void isis_bfd_zebra_connected(struct zclient *zclient)
226{
227 if (orig_zebra_connected)
228 orig_zebra_connected(zclient);
229
0945d5ed 230 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
52df8228
CF
231}
232
e782cca7 233static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
2815f817
CF
234 const char *interface, int command)
235{
e740f9c1 236 if (!(IS_DEBUG_BFD))
2815f817
CF
237 return;
238
239 char dst_str[INET6_ADDRSTRLEN];
240 char src_str[INET6_ADDRSTRLEN];
241
e782cca7
RW
242 inet_ntop(family, dst, dst_str, sizeof(dst_str));
243 inet_ntop(family, src, src_str, sizeof(src_str));
2815f817
CF
244
245 const char *command_str;
246
247 switch (command) {
248 case ZEBRA_BFD_DEST_REGISTER:
249 command_str = "Register";
250 break;
251 case ZEBRA_BFD_DEST_DEREGISTER:
252 command_str = "Deregister";
253 break;
254 case ZEBRA_BFD_DEST_UPDATE:
255 command_str = "Update";
256 break;
257 default:
258 command_str = "Unknown-Cmd";
259 break;
260 }
261
262 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
263 command_str, dst_str, interface, src_str);
264}
265
bb99eb5d
G
266static void bfd_command(int command, struct bfd_info *bfd_info, int family,
267 const void *dst_ip, const void *src_ip,
268 const char *if_name)
269{
270 struct bfd_session_arg args = {};
271 size_t addrlen;
272
273 args.cbit = 1;
274 args.family = family;
275 args.vrf_id = VRF_DEFAULT;
276 args.command = command;
277 args.bfd_info = bfd_info;
278 if (args.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));
286 }
287 }
288
289 addrlen = family == AF_INET ? sizeof(struct in_addr)
290 : sizeof(struct in6_addr);
291 memcpy(&args.dst, dst_ip, addrlen);
292 if (src_ip)
293 memcpy(&args.src, src_ip, addrlen);
294
295 if (if_name) {
296 strlcpy(args.ifname, if_name, sizeof(args.ifname));
297 args.ifnamelen = strlen(args.ifname);
298 }
299
300 zclient_bfd_command(zclient, &args);
301}
302
20a42f01
CF
303static void bfd_handle_adj_down(struct isis_adjacency *adj)
304{
305 if (!adj->bfd_session)
306 return;
307
e782cca7
RW
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);
2815f817 311
bb99eb5d
G
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
315 : NULL);
316
20a42f01
CF
317 bfd_session_free(&adj->bfd_session);
318}
319
320static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
215eccb0 321{
20a42f01 322 struct isis_circuit *circuit = adj->circuit;
e782cca7
RW
323 int family;
324 union g_addr dst_ip;
325 union g_addr src_ip;
326 struct list *local_ips;
327 struct prefix *local_ip;
20a42f01 328
e782cca7 329 if (!circuit->bfd_info)
20a42f01
CF
330 goto out;
331
2bec0447
KS
332 /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed
333 * before starting up BFD
334 */
335 if ((circuit->ipv6_router && listcount(circuit->ipv6_link) == 0)
336 || adj->ipv6_address_count == 0)
337 return;
338
e782cca7
RW
339 /*
340 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
341 * creating a BFD session over IPv6.
342 */
343 if (circuit->ipv6_router && adj->ipv6_address_count) {
344 family = AF_INET6;
345 dst_ip.ipv6 = adj->ipv6_addresses[0];
346 local_ips = circuit->ipv6_link;
347 if (!local_ips || list_isempty(local_ips))
348 goto out;
349 local_ip = listgetdata(listhead(local_ips));
350 src_ip.ipv6 = local_ip->u.prefix6;
351 } else if (circuit->ip_router && adj->ipv4_address_count) {
352 family = AF_INET;
353 dst_ip.ipv4 = adj->ipv4_addresses[0];
354 local_ips = fabricd_ip_addrs(adj->circuit);
355 if (!local_ips || list_isempty(local_ips))
356 goto out;
357 local_ip = listgetdata(listhead(local_ips));
358 src_ip.ipv4 = local_ip->u.prefix4;
359 } else
20a42f01
CF
360 goto out;
361
20a42f01 362 if (adj->bfd_session) {
e782cca7
RW
363 if (bfd_session_same(adj->bfd_session, family, &src_ip,
364 &dst_ip))
20a42f01
CF
365 bfd_handle_adj_down(adj);
366 }
367
368 if (!adj->bfd_session)
e782cca7 369 adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip);
20a42f01 370
e782cca7
RW
371 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
372 &adj->bfd_session->src_ip, circuit->interface->name, command);
bb99eb5d
G
373
374 bfd_command(command, circuit->bfd_info, family,
375 &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
376 (adj->circuit->interface) ? adj->circuit->interface->name
377 : NULL);
378
215eccb0 379 return;
20a42f01
CF
380out:
381 bfd_handle_adj_down(adj);
382}
383
384static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
385{
386 if (adj->adj_state == ISIS_ADJ_UP)
387 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
388 else
389 bfd_handle_adj_down(adj);
390 return 0;
391}
392
393static void bfd_adj_cmd(struct isis_adjacency *adj, int command)
394{
395 if (adj->adj_state == ISIS_ADJ_UP
396 && command != ZEBRA_BFD_DEST_DEREGISTER) {
397 bfd_handle_adj_up(adj, command);
398 } else {
399 bfd_handle_adj_down(adj);
400 }
401}
402
403void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
404{
405 switch (circuit->circ_type) {
406 case CIRCUIT_T_BROADCAST:
407 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
408 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
409
410 struct listnode *node;
411 struct isis_adjacency *adj;
5489eb45 412
20a42f01
CF
413 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
414 bfd_adj_cmd(adj, command);
415 }
416 break;
417 case CIRCUIT_T_P2P:
418 if (circuit->u.p2p.neighbor)
419 bfd_adj_cmd(circuit->u.p2p.neighbor, command);
420 break;
421 default:
422 break;
423 }
215eccb0
CF
424}
425
4affdba7
G
426void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx,
427 uint32_t min_tx, uint32_t detect_mult,
428 const char *profile, int defaults)
215eccb0
CF
429{
430 int command = 0;
431
4affdba7
G
432 bfd_set_param(&circuit->bfd_info, min_rx, min_tx, detect_mult, profile,
433 defaults, &command);
215eccb0
CF
434
435 if (command)
436 isis_bfd_circuit_cmd(circuit, command);
437}
438
05e4ec37 439#ifdef FABRICD
3015e3d1
CF
440static int bfd_circuit_write_settings(struct isis_circuit *circuit,
441 struct vty *vty)
442{
443 struct bfd_info *bfd_info = circuit->bfd_info;
444
445 if (!bfd_info)
446 return 0;
447
490a6fc7 448 vty_out(vty, " %s bfd\n", PROTO_NAME);
3015e3d1
CF
449 return 1;
450}
05e4ec37 451#endif
3015e3d1 452
2bec0447
KS
453static int bfd_handle_adj_ip_enabled(struct isis_adjacency *adj, int family)
454{
455
456 if (family != AF_INET6)
457 return 0;
458
459 if (adj->bfd_session)
460 return 0;
461
462 if (adj->adj_state != ISIS_ADJ_UP)
463 return 0;
464
465 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
466
467 return 0;
468}
469
470static int bfd_handle_circuit_add_addr(struct isis_circuit *circuit)
471{
472 struct isis_adjacency *adj;
473 struct listnode *node;
474
475 if (circuit->area == 0)
476 return 0;
477
478 for (ALL_LIST_ELEMENTS_RO(circuit->area->adjacency_list, node, adj)) {
479 if (adj->bfd_session)
480 continue;
481
482 if (adj->adj_state != ISIS_ADJ_UP)
483 continue;
484
485 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
486 }
487
488 return 0;
489}
490
52df8228
CF
491void isis_bfd_init(void)
492{
493 bfd_gbl_init();
494
495 orig_zebra_connected = zclient->zebra_connected;
496 zclient->zebra_connected = isis_bfd_zebra_connected;
497 zclient->interface_bfd_dest_update = isis_bfd_interface_dest_update;
498 zclient->bfd_dest_replay = isis_bfd_nbr_replay;
20a42f01
CF
499 hook_register(isis_adj_state_change_hook,
500 bfd_handle_adj_state_change);
05e4ec37 501#ifdef FABRICD
3015e3d1
CF
502 hook_register(isis_circuit_config_write,
503 bfd_circuit_write_settings);
05e4ec37 504#endif
2bec0447
KS
505 hook_register(isis_adj_ip_enabled_hook, bfd_handle_adj_ip_enabled);
506 hook_register(isis_circuit_add_addr_hook, bfd_handle_circuit_add_addr);
52df8228 507}