]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_bfd.c
Merge pull request #5580 from mjstapp/zebra_nhg_debug_category
[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);
62 *session = NULL;
63}
64
e782cca7
RW
65static bool bfd_session_same(const struct bfd_session *session, int family,
66 const union g_addr *src, const union g_addr *dst)
67{
68 if (session->family != family)
69 return false;
70
71 switch (session->family) {
72 case AF_INET:
73 if (!IPV4_ADDR_SAME(&session->dst_ip.ipv4, &dst->ipv4))
74 return false;
75 if (!IPV4_ADDR_SAME(&session->src_ip.ipv4, &src->ipv4))
76 return false;
77 break;
78 case AF_INET6:
79 if (!IPV6_ADDR_SAME(&session->dst_ip.ipv6, &dst->ipv6))
80 return false;
81 if (!IPV6_ADDR_SAME(&session->src_ip.ipv6, &src->ipv6))
82 return false;
83 break;
84 default:
85 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
86 __func__, session->family);
87 exit(1);
88 }
89
90 return true;
91}
92
a5eba4e9
CF
93static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
94 int new_status)
95{
96 if (!adj->bfd_session)
97 return;
98
e782cca7 99 if (adj->bfd_session->family != dst->family)
a5eba4e9
CF
100 return;
101
e782cca7
RW
102 switch (adj->bfd_session->family) {
103 case AF_INET:
104 if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4,
105 &dst->u.prefix4))
106 return;
107 break;
108 case AF_INET6:
109 if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6,
110 &dst->u.prefix6))
111 return;
112 break;
113 default:
114 flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
115 __func__, adj->bfd_session->family);
116 exit(1);
117 }
118
a5eba4e9 119 int old_status = adj->bfd_session->status;
a5eba4e9 120
7555dc61
S
121 BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status);
122
a5eba4e9
CF
123 if (old_status == new_status)
124 return;
125
126 if (isis->debugs & DEBUG_BFD) {
127 char dst_str[INET6_ADDRSTRLEN];
128
e782cca7 129 inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip,
a5eba4e9
CF
130 dst_str, sizeof(dst_str));
131 zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s",
132 dst_str, adj->circuit->interface->name,
133 bfd_get_status_str(old_status),
134 bfd_get_status_str(new_status));
135 }
136
137 if (old_status != BFD_STATUS_UP
138 || new_status != BFD_STATUS_DOWN) {
139 return;
140 }
141
142 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down");
143}
144
121f9dee 145static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
52df8228 146{
2815f817
CF
147 struct interface *ifp;
148 struct prefix dst_ip;
149 int status;
150
9beff0bd
PG
151 ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status,
152 NULL, vrf_id);
e782cca7 153 if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6))
2815f817
CF
154 return 0;
155
156 if (isis->debugs & DEBUG_BFD) {
157 char dst_buf[INET6_ADDRSTRLEN];
5489eb45 158
e782cca7
RW
159 inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf,
160 sizeof(dst_buf));
2815f817
CF
161
162 zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
163 dst_buf, ifp->name, bfd_get_status_str(status));
164 }
165
a5eba4e9 166 struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
5489eb45 167
a5eba4e9
CF
168 if (!circuit)
169 return 0;
170
171 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
172 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
173 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
174
175 struct listnode *node, *nnode;
176 struct isis_adjacency *adj;
177
178 for (ALL_LIST_ELEMENTS(adjdb, node, nnode, adj))
179 bfd_adj_event(adj, &dst_ip, status);
180 }
181 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
182 if (circuit->u.p2p.neighbor) {
183 bfd_adj_event(circuit->u.p2p.neighbor,
184 &dst_ip, status);
185 }
186 }
187
52df8228
CF
188 return 0;
189}
190
121f9dee 191static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
52df8228 192{
0945d5ed 193 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
ab880eaf
CF
194
195 struct listnode *anode;
196 struct isis_area *area;
197
2815f817
CF
198 if (isis->debugs & DEBUG_BFD)
199 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
200
ab880eaf
CF
201 for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
202 struct listnode *cnode;
203 struct isis_circuit *circuit;
204
205 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
206 isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE);
207 }
208
2815f817
CF
209 if (isis->debugs & DEBUG_BFD)
210 zlog_debug("ISIS-BFD: Done with replay.");
211
52df8228
CF
212 return 0;
213}
214
215static void (*orig_zebra_connected)(struct zclient *);
216static void isis_bfd_zebra_connected(struct zclient *zclient)
217{
218 if (orig_zebra_connected)
219 orig_zebra_connected(zclient);
220
0945d5ed 221 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
52df8228
CF
222}
223
e782cca7 224static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
2815f817
CF
225 const char *interface, int command)
226{
227 if (!(isis->debugs & DEBUG_BFD))
228 return;
229
230 char dst_str[INET6_ADDRSTRLEN];
231 char src_str[INET6_ADDRSTRLEN];
232
e782cca7
RW
233 inet_ntop(family, dst, dst_str, sizeof(dst_str));
234 inet_ntop(family, src, src_str, sizeof(src_str));
2815f817
CF
235
236 const char *command_str;
237
238 switch (command) {
239 case ZEBRA_BFD_DEST_REGISTER:
240 command_str = "Register";
241 break;
242 case ZEBRA_BFD_DEST_DEREGISTER:
243 command_str = "Deregister";
244 break;
245 case ZEBRA_BFD_DEST_UPDATE:
246 command_str = "Update";
247 break;
248 default:
249 command_str = "Unknown-Cmd";
250 break;
251 }
252
253 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
254 command_str, dst_str, interface, src_str);
255}
256
20a42f01
CF
257static void bfd_handle_adj_down(struct isis_adjacency *adj)
258{
259 if (!adj->bfd_session)
260 return;
261
e782cca7
RW
262 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
263 &adj->bfd_session->src_ip, adj->circuit->interface->name,
264 ZEBRA_BFD_DEST_DEREGISTER);
2815f817 265
e782cca7 266 bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family,
20a42f01
CF
267 &adj->bfd_session->dst_ip,
268 &adj->bfd_session->src_ip,
269 adj->circuit->interface->name,
270 0, /* ttl */
271 0, /* multihop */
9beff0bd 272 1, /* control plane independent bit is on */
20a42f01
CF
273 ZEBRA_BFD_DEST_DEREGISTER,
274 0, /* set_flag */
275 VRF_DEFAULT);
276 bfd_session_free(&adj->bfd_session);
277}
278
279static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
215eccb0 280{
20a42f01 281 struct isis_circuit *circuit = adj->circuit;
e782cca7
RW
282 int family;
283 union g_addr dst_ip;
284 union g_addr src_ip;
285 struct list *local_ips;
286 struct prefix *local_ip;
20a42f01 287
e782cca7 288 if (!circuit->bfd_info)
20a42f01
CF
289 goto out;
290
e782cca7
RW
291 /*
292 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
293 * creating a BFD session over IPv6.
294 */
295 if (circuit->ipv6_router && adj->ipv6_address_count) {
296 family = AF_INET6;
297 dst_ip.ipv6 = adj->ipv6_addresses[0];
298 local_ips = circuit->ipv6_link;
299 if (!local_ips || list_isempty(local_ips))
300 goto out;
301 local_ip = listgetdata(listhead(local_ips));
302 src_ip.ipv6 = local_ip->u.prefix6;
303 } else if (circuit->ip_router && adj->ipv4_address_count) {
304 family = AF_INET;
305 dst_ip.ipv4 = adj->ipv4_addresses[0];
306 local_ips = fabricd_ip_addrs(adj->circuit);
307 if (!local_ips || list_isempty(local_ips))
308 goto out;
309 local_ip = listgetdata(listhead(local_ips));
310 src_ip.ipv4 = local_ip->u.prefix4;
311 } else
20a42f01
CF
312 goto out;
313
20a42f01 314 if (adj->bfd_session) {
e782cca7
RW
315 if (bfd_session_same(adj->bfd_session, family, &src_ip,
316 &dst_ip))
20a42f01
CF
317 bfd_handle_adj_down(adj);
318 }
319
320 if (!adj->bfd_session)
e782cca7 321 adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip);
20a42f01 322
e782cca7
RW
323 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
324 &adj->bfd_session->src_ip, circuit->interface->name, command);
325 bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family,
20a42f01
CF
326 &adj->bfd_session->dst_ip,
327 &adj->bfd_session->src_ip,
328 circuit->interface->name,
329 0, /* ttl */
330 0, /* multihop */
9beff0bd 331 1, /* control plane independent bit is on */
20a42f01
CF
332 command,
333 0, /* set flag */
334 VRF_DEFAULT);
215eccb0 335 return;
20a42f01
CF
336out:
337 bfd_handle_adj_down(adj);
338}
339
340static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
341{
342 if (adj->adj_state == ISIS_ADJ_UP)
343 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
344 else
345 bfd_handle_adj_down(adj);
346 return 0;
347}
348
349static void bfd_adj_cmd(struct isis_adjacency *adj, int command)
350{
351 if (adj->adj_state == ISIS_ADJ_UP
352 && command != ZEBRA_BFD_DEST_DEREGISTER) {
353 bfd_handle_adj_up(adj, command);
354 } else {
355 bfd_handle_adj_down(adj);
356 }
357}
358
359void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
360{
361 switch (circuit->circ_type) {
362 case CIRCUIT_T_BROADCAST:
363 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
364 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
365
366 struct listnode *node;
367 struct isis_adjacency *adj;
5489eb45 368
20a42f01
CF
369 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
370 bfd_adj_cmd(adj, command);
371 }
372 break;
373 case CIRCUIT_T_P2P:
374 if (circuit->u.p2p.neighbor)
375 bfd_adj_cmd(circuit->u.p2p.neighbor, command);
376 break;
377 default:
378 break;
379 }
215eccb0
CF
380}
381
382void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
383 uint32_t min_rx, uint32_t min_tx,
384 uint32_t detect_mult, int defaults)
385{
386 int command = 0;
387
388 bfd_set_param(&circuit->bfd_info, min_rx,
389 min_tx, detect_mult, defaults, &command);
390
391 if (command)
392 isis_bfd_circuit_cmd(circuit, command);
393}
394
3015e3d1
CF
395static int bfd_circuit_write_settings(struct isis_circuit *circuit,
396 struct vty *vty)
397{
398 struct bfd_info *bfd_info = circuit->bfd_info;
399
400 if (!bfd_info)
401 return 0;
402
490a6fc7 403 vty_out(vty, " %s bfd\n", PROTO_NAME);
3015e3d1
CF
404 return 1;
405}
406
52df8228
CF
407void isis_bfd_init(void)
408{
409 bfd_gbl_init();
410
411 orig_zebra_connected = zclient->zebra_connected;
412 zclient->zebra_connected = isis_bfd_zebra_connected;
413 zclient->interface_bfd_dest_update = isis_bfd_interface_dest_update;
414 zclient->bfd_dest_replay = isis_bfd_nbr_replay;
20a42f01
CF
415 hook_register(isis_adj_state_change_hook,
416 bfd_handle_adj_state_change);
3015e3d1
CF
417 hook_register(isis_circuit_config_write,
418 bfd_circuit_write_settings);
52df8228 419}