]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_bfd.c
Merge pull request #8581 from qlyoung/bgp-fix-last-reset-buffer-size
[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
bf8d3d6a 35DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session");
20a42f01
CF
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{
980390ce
EDP
95 if (!adj->bfd_session) {
96 if (IS_DEBUG_BFD)
97 zlog_debug(
98 "ISIS-BFD: Ignoring update for adjacency with %s, could not find bfd session on the adjacency",
99 isis_adj_name(adj));
a5eba4e9 100 return;
980390ce 101 }
a5eba4e9 102
980390ce
EDP
103 if (adj->bfd_session->family != dst->family) {
104 if (IS_DEBUG_BFD)
105 zlog_debug(
106 "ISIS-BFD: Ignoring update for adjacency with %s, address family does not match the family on the adjacency",
107 isis_adj_name(adj));
a5eba4e9 108 return;
980390ce 109 }
a5eba4e9 110
e782cca7
RW
111 switch (adj->bfd_session->family) {
112 case AF_INET:
113 if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4,
980390ce
EDP
114 &dst->u.prefix4)) {
115 if (IS_DEBUG_BFD)
116 zlog_debug(
117 "ISIS-BFD: Ignoring update for adjacency with %s, IPv4 address does not match",
118 isis_adj_name(adj));
e782cca7 119 return;
980390ce 120 }
e782cca7
RW
121 break;
122 case AF_INET6:
123 if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6,
980390ce
EDP
124 &dst->u.prefix6)) {
125 if (IS_DEBUG_BFD)
126 zlog_debug(
127 "ISIS-BFD: Ignoring update for adjacency with %s, IPv6 address does not match",
128 isis_adj_name(adj));
e782cca7 129 return;
980390ce 130 }
e782cca7
RW
131 break;
132 default:
133 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
134 __func__, adj->bfd_session->family);
135 exit(1);
136 }
137
a5eba4e9 138 int old_status = adj->bfd_session->status;
a5eba4e9 139
7555dc61
S
140 BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status);
141
980390ce
EDP
142 if (old_status == new_status) {
143 if (IS_DEBUG_BFD)
144 zlog_debug(
145 "ISIS-BFD: Ignoring update for adjacency with %s, new status matches current known status",
146 isis_adj_name(adj));
a5eba4e9 147 return;
980390ce 148 }
a5eba4e9 149
e740f9c1 150 if (IS_DEBUG_BFD) {
a5eba4e9
CF
151 char dst_str[INET6_ADDRSTRLEN];
152
e782cca7 153 inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip,
a5eba4e9
CF
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));
159 }
160
161 if (old_status != BFD_STATUS_UP
162 || new_status != BFD_STATUS_DOWN) {
163 return;
164 }
165
690497fb
G
166 adj->circuit->area->bfd_signalled_down = true;
167
16167b31 168 isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "bfd session went down");
a5eba4e9
CF
169}
170
121f9dee 171static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
52df8228 172{
2815f817 173 struct interface *ifp;
37a74717 174 struct prefix dst_ip, src_ip;
2815f817
CF
175 int status;
176
37a74717
DS
177 ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, &src_ip, &status, NULL,
178 vrf_id);
e782cca7 179 if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6))
2815f817
CF
180 return 0;
181
e740f9c1 182 if (IS_DEBUG_BFD) {
2815f817 183 char dst_buf[INET6_ADDRSTRLEN];
5489eb45 184
e782cca7
RW
185 inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf,
186 sizeof(dst_buf));
2815f817
CF
187
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));
190 }
191
a5eba4e9 192 struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
5489eb45 193
980390ce
EDP
194 if (!circuit) {
195 if (IS_DEBUG_BFD)
196 zlog_debug(
197 "ISIS-BFD: Ignoring update, could not find circuit");
a5eba4e9 198 return 0;
980390ce 199 }
a5eba4e9
CF
200
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];
204
205 struct listnode *node, *nnode;
206 struct isis_adjacency *adj;
207
208 for (ALL_LIST_ELEMENTS(adjdb, node, nnode, adj))
209 bfd_adj_event(adj, &dst_ip, status);
210 }
211 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
212 if (circuit->u.p2p.neighbor) {
213 bfd_adj_event(circuit->u.p2p.neighbor,
214 &dst_ip, status);
215 }
216 }
217
52df8228
CF
218 return 0;
219}
220
121f9dee 221static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
52df8228 222{
0945d5ed 223 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
ab880eaf
CF
224
225 struct listnode *anode;
226 struct isis_area *area;
eab88f36
K
227 struct isis *isis = NULL;
228
229 isis = isis_lookup_by_vrfid(vrf_id);
230
231 if (isis == NULL) {
232 zlog_warn(" %s : ISIS routing instance not found", __func__);
233 return -1;
234 }
ab880eaf 235
e740f9c1 236 if (IS_DEBUG_BFD)
2815f817
CF
237 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
238
ab880eaf
CF
239 for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
240 struct listnode *cnode;
241 struct isis_circuit *circuit;
242
243 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
244 isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE);
245 }
246
e740f9c1 247 if (IS_DEBUG_BFD)
2815f817
CF
248 zlog_debug("ISIS-BFD: Done with replay.");
249
52df8228
CF
250 return 0;
251}
252
253static void (*orig_zebra_connected)(struct zclient *);
254static void isis_bfd_zebra_connected(struct zclient *zclient)
255{
256 if (orig_zebra_connected)
257 orig_zebra_connected(zclient);
258
0945d5ed 259 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
52df8228
CF
260}
261
e782cca7 262static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
2815f817
CF
263 const char *interface, int command)
264{
e740f9c1 265 if (!(IS_DEBUG_BFD))
2815f817
CF
266 return;
267
268 char dst_str[INET6_ADDRSTRLEN];
269 char src_str[INET6_ADDRSTRLEN];
270
e782cca7
RW
271 inet_ntop(family, dst, dst_str, sizeof(dst_str));
272 inet_ntop(family, src, src_str, sizeof(src_str));
2815f817
CF
273
274 const char *command_str;
275
276 switch (command) {
277 case ZEBRA_BFD_DEST_REGISTER:
278 command_str = "Register";
279 break;
280 case ZEBRA_BFD_DEST_DEREGISTER:
281 command_str = "Deregister";
282 break;
283 case ZEBRA_BFD_DEST_UPDATE:
284 command_str = "Update";
285 break;
286 default:
287 command_str = "Unknown-Cmd";
288 break;
289 }
290
291 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
292 command_str, dst_str, interface, src_str);
293}
294
bb99eb5d
G
295static void bfd_command(int command, struct bfd_info *bfd_info, int family,
296 const void *dst_ip, const void *src_ip,
297 const char *if_name)
298{
299 struct bfd_session_arg args = {};
300 size_t addrlen;
301
302 args.cbit = 1;
303 args.family = family;
304 args.vrf_id = VRF_DEFAULT;
305 args.command = command;
306 args.bfd_info = bfd_info;
307 if (args.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));
315 }
316 }
317
318 addrlen = family == AF_INET ? sizeof(struct in_addr)
319 : sizeof(struct in6_addr);
320 memcpy(&args.dst, dst_ip, addrlen);
321 if (src_ip)
322 memcpy(&args.src, src_ip, addrlen);
323
324 if (if_name) {
325 strlcpy(args.ifname, if_name, sizeof(args.ifname));
326 args.ifnamelen = strlen(args.ifname);
327 }
328
329 zclient_bfd_command(zclient, &args);
330}
331
20a42f01
CF
332static void bfd_handle_adj_down(struct isis_adjacency *adj)
333{
334 if (!adj->bfd_session)
335 return;
336
e782cca7
RW
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);
2815f817 340
bb99eb5d
G
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
344 : NULL);
345
20a42f01
CF
346 bfd_session_free(&adj->bfd_session);
347}
348
349static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
215eccb0 350{
20a42f01 351 struct isis_circuit *circuit = adj->circuit;
e782cca7
RW
352 int family;
353 union g_addr dst_ip;
354 union g_addr src_ip;
355 struct list *local_ips;
356 struct prefix *local_ip;
20a42f01 357
980390ce
EDP
358 if (!circuit->bfd_info) {
359 if (IS_DEBUG_BFD)
360 zlog_debug(
361 "ISIS-BFD: skipping BFD initialization on adjacency with %s because there is no bfd_info in the circuit",
362 isis_adj_name(adj));
20a42f01 363 goto out;
980390ce 364 }
20a42f01 365
2bec0447
KS
366 /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed
367 * before starting up BFD
368 */
0d5b1a3a
EDP
369 if (circuit->ipv6_router
370 && (listcount(circuit->ipv6_link) == 0
371 || adj->ipv6_address_count == 0)) {
372 if (IS_DEBUG_BFD)
373 zlog_debug(
374 "ISIS-BFD: skipping BFD initialization on adjacency with %s because IPv6 is enabled but not ready",
375 isis_adj_name(adj));
2bec0447 376 return;
0d5b1a3a 377 }
2bec0447 378
e782cca7
RW
379 /*
380 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
381 * creating a BFD session over IPv6.
382 */
383 if (circuit->ipv6_router && adj->ipv6_address_count) {
384 family = AF_INET6;
385 dst_ip.ipv6 = adj->ipv6_addresses[0];
386 local_ips = circuit->ipv6_link;
980390ce
EDP
387 if (!local_ips || list_isempty(local_ips)) {
388 if (IS_DEBUG_BFD)
389 zlog_debug(
390 "ISIS-BFD: skipping BFD initialization: IPv6 enabled and no local IPv6 addresses");
e782cca7 391 goto out;
980390ce 392 }
e782cca7
RW
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) {
396 family = AF_INET;
397 dst_ip.ipv4 = adj->ipv4_addresses[0];
398 local_ips = fabricd_ip_addrs(adj->circuit);
980390ce
EDP
399 if (!local_ips || list_isempty(local_ips)) {
400 if (IS_DEBUG_BFD)
401 zlog_debug(
402 "ISIS-BFD: skipping BFD initialization: IPv4 enabled and no local IPv4 addresses");
e782cca7 403 goto out;
980390ce 404 }
e782cca7
RW
405 local_ip = listgetdata(listhead(local_ips));
406 src_ip.ipv4 = local_ip->u.prefix4;
407 } else
20a42f01
CF
408 goto out;
409
20a42f01 410 if (adj->bfd_session) {
e782cca7
RW
411 if (bfd_session_same(adj->bfd_session, family, &src_ip,
412 &dst_ip))
20a42f01
CF
413 bfd_handle_adj_down(adj);
414 }
415
980390ce
EDP
416 if (!adj->bfd_session) {
417 if (IS_DEBUG_BFD)
418 zlog_debug(
419 "ISIS-BFD: creating BFD session for adjacency with %s",
420 isis_adj_name(adj));
e782cca7 421 adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip);
980390ce 422 }
20a42f01 423
e782cca7
RW
424 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
425 &adj->bfd_session->src_ip, circuit->interface->name, command);
bb99eb5d
G
426
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
430 : NULL);
431
215eccb0 432 return;
20a42f01
CF
433out:
434 bfd_handle_adj_down(adj);
435}
436
437static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
438{
439 if (adj->adj_state == ISIS_ADJ_UP)
440 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
441 else
442 bfd_handle_adj_down(adj);
443 return 0;
444}
445
446static void bfd_adj_cmd(struct isis_adjacency *adj, int command)
447{
448 if (adj->adj_state == ISIS_ADJ_UP
449 && command != ZEBRA_BFD_DEST_DEREGISTER) {
450 bfd_handle_adj_up(adj, command);
451 } else {
452 bfd_handle_adj_down(adj);
453 }
454}
455
456void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
457{
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];
462
463 struct listnode *node;
464 struct isis_adjacency *adj;
5489eb45 465
20a42f01
CF
466 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
467 bfd_adj_cmd(adj, command);
468 }
469 break;
470 case CIRCUIT_T_P2P:
471 if (circuit->u.p2p.neighbor)
472 bfd_adj_cmd(circuit->u.p2p.neighbor, command);
473 break;
474 default:
475 break;
476 }
215eccb0
CF
477}
478
4affdba7
G
479void 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)
215eccb0
CF
482{
483 int command = 0;
484
4affdba7
G
485 bfd_set_param(&circuit->bfd_info, min_rx, min_tx, detect_mult, profile,
486 defaults, &command);
215eccb0
CF
487
488 if (command)
489 isis_bfd_circuit_cmd(circuit, command);
490}
491
05e4ec37 492#ifdef FABRICD
3015e3d1
CF
493static int bfd_circuit_write_settings(struct isis_circuit *circuit,
494 struct vty *vty)
495{
496 struct bfd_info *bfd_info = circuit->bfd_info;
497
498 if (!bfd_info)
499 return 0;
500
490a6fc7 501 vty_out(vty, " %s bfd\n", PROTO_NAME);
3015e3d1
CF
502 return 1;
503}
05e4ec37 504#endif
3015e3d1 505
2bec0447
KS
506static int bfd_handle_adj_ip_enabled(struct isis_adjacency *adj, int family)
507{
508
509 if (family != AF_INET6)
510 return 0;
511
512 if (adj->bfd_session)
513 return 0;
514
515 if (adj->adj_state != ISIS_ADJ_UP)
516 return 0;
517
518 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
519
520 return 0;
521}
522
523static int bfd_handle_circuit_add_addr(struct isis_circuit *circuit)
524{
525 struct isis_adjacency *adj;
526 struct listnode *node;
527
528 if (circuit->area == 0)
529 return 0;
530
531 for (ALL_LIST_ELEMENTS_RO(circuit->area->adjacency_list, node, adj)) {
532 if (adj->bfd_session)
533 continue;
534
535 if (adj->adj_state != ISIS_ADJ_UP)
536 continue;
537
538 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
539 }
540
541 return 0;
542}
543
52df8228
CF
544void isis_bfd_init(void)
545{
546 bfd_gbl_init();
547
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;
20a42f01
CF
552 hook_register(isis_adj_state_change_hook,
553 bfd_handle_adj_state_change);
05e4ec37 554#ifdef FABRICD
3015e3d1
CF
555 hook_register(isis_circuit_config_write,
556 bfd_circuit_write_settings);
05e4ec37 557#endif
2bec0447
KS
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);
52df8228 560}