]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_bfd.c
README.md: Add FRR Icon
[mirror_frr.git] / isisd / isis_bfd.c
1 /*
2 * IS-IS Rout(e)ing protocol - BFD support
3 * Copyright (C) 2018 Christian Franke
4 *
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.
9 *
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.
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"
22 #include "nexthop.h"
23 #include "bfd.h"
24 #include "lib_errors.h"
25
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"
34
35 DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session")
36
37 struct bfd_session {
38 int family;
39 union g_addr dst_ip;
40 union g_addr src_ip;
41 int status;
42 };
43
44 static struct bfd_session *bfd_session_new(int family, union g_addr *dst_ip,
45 union g_addr *src_ip)
46 {
47 struct bfd_session *rv;
48
49 rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv));
50 rv->family = family;
51 rv->dst_ip = *dst_ip;
52 rv->src_ip = *src_ip;
53 return rv;
54 }
55
56 static 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
65 static 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
93 static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
94 int new_status)
95 {
96 if (!adj->bfd_session)
97 return;
98
99 if (adj->bfd_session->family != dst->family)
100 return;
101
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
119 int old_status = adj->bfd_session->status;
120
121 adj->bfd_session->status = new_status;
122 if (old_status == new_status)
123 return;
124
125 if (isis->debugs & DEBUG_BFD) {
126 char dst_str[INET6_ADDRSTRLEN];
127
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));
134 }
135
136 if (old_status != BFD_STATUS_UP
137 || new_status != BFD_STATUS_DOWN) {
138 return;
139 }
140
141 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down");
142 }
143
144 static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
145 {
146 struct interface *ifp;
147 struct prefix dst_ip;
148 int status;
149
150 ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status,
151 NULL, vrf_id);
152 if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6))
153 return 0;
154
155 if (isis->debugs & DEBUG_BFD) {
156 char dst_buf[INET6_ADDRSTRLEN];
157
158 inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf,
159 sizeof(dst_buf));
160
161 zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
162 dst_buf, ifp->name, bfd_get_status_str(status));
163 }
164
165 struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
166
167 if (!circuit)
168 return 0;
169
170 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
171 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
172 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
173
174 struct listnode *node, *nnode;
175 struct isis_adjacency *adj;
176
177 for (ALL_LIST_ELEMENTS(adjdb, node, nnode, adj))
178 bfd_adj_event(adj, &dst_ip, status);
179 }
180 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
181 if (circuit->u.p2p.neighbor) {
182 bfd_adj_event(circuit->u.p2p.neighbor,
183 &dst_ip, status);
184 }
185 }
186
187 return 0;
188 }
189
190 static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
191 {
192 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
193
194 struct listnode *anode;
195 struct isis_area *area;
196
197 if (isis->debugs & DEBUG_BFD)
198 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
199
200 for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
201 struct listnode *cnode;
202 struct isis_circuit *circuit;
203
204 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
205 isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE);
206 }
207
208 if (isis->debugs & DEBUG_BFD)
209 zlog_debug("ISIS-BFD: Done with replay.");
210
211 return 0;
212 }
213
214 static void (*orig_zebra_connected)(struct zclient *);
215 static void isis_bfd_zebra_connected(struct zclient *zclient)
216 {
217 if (orig_zebra_connected)
218 orig_zebra_connected(zclient);
219
220 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
221 }
222
223 static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
224 const char *interface, int command)
225 {
226 if (!(isis->debugs & DEBUG_BFD))
227 return;
228
229 char dst_str[INET6_ADDRSTRLEN];
230 char src_str[INET6_ADDRSTRLEN];
231
232 inet_ntop(family, dst, dst_str, sizeof(dst_str));
233 inet_ntop(family, src, src_str, sizeof(src_str));
234
235 const char *command_str;
236
237 switch (command) {
238 case ZEBRA_BFD_DEST_REGISTER:
239 command_str = "Register";
240 break;
241 case ZEBRA_BFD_DEST_DEREGISTER:
242 command_str = "Deregister";
243 break;
244 case ZEBRA_BFD_DEST_UPDATE:
245 command_str = "Update";
246 break;
247 default:
248 command_str = "Unknown-Cmd";
249 break;
250 }
251
252 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
253 command_str, dst_str, interface, src_str);
254 }
255
256 static void bfd_handle_adj_down(struct isis_adjacency *adj)
257 {
258 if (!adj->bfd_session)
259 return;
260
261 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
262 &adj->bfd_session->src_ip, adj->circuit->interface->name,
263 ZEBRA_BFD_DEST_DEREGISTER);
264
265 bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family,
266 &adj->bfd_session->dst_ip,
267 &adj->bfd_session->src_ip,
268 adj->circuit->interface->name,
269 0, /* ttl */
270 0, /* multihop */
271 1, /* control plane independent bit is on */
272 ZEBRA_BFD_DEST_DEREGISTER,
273 0, /* set_flag */
274 VRF_DEFAULT);
275 bfd_session_free(&adj->bfd_session);
276 }
277
278 static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
279 {
280 struct isis_circuit *circuit = adj->circuit;
281 int family;
282 union g_addr dst_ip;
283 union g_addr src_ip;
284 struct list *local_ips;
285 struct prefix *local_ip;
286
287 if (!circuit->bfd_info)
288 goto out;
289
290 /*
291 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
292 * creating a BFD session over IPv6.
293 */
294 if (circuit->ipv6_router && adj->ipv6_address_count) {
295 family = AF_INET6;
296 dst_ip.ipv6 = adj->ipv6_addresses[0];
297 local_ips = circuit->ipv6_link;
298 if (!local_ips || list_isempty(local_ips))
299 goto out;
300 local_ip = listgetdata(listhead(local_ips));
301 src_ip.ipv6 = local_ip->u.prefix6;
302 } else if (circuit->ip_router && adj->ipv4_address_count) {
303 family = AF_INET;
304 dst_ip.ipv4 = adj->ipv4_addresses[0];
305 local_ips = fabricd_ip_addrs(adj->circuit);
306 if (!local_ips || list_isempty(local_ips))
307 goto out;
308 local_ip = listgetdata(listhead(local_ips));
309 src_ip.ipv4 = local_ip->u.prefix4;
310 } else
311 goto out;
312
313 if (adj->bfd_session) {
314 if (bfd_session_same(adj->bfd_session, family, &src_ip,
315 &dst_ip))
316 bfd_handle_adj_down(adj);
317 }
318
319 if (!adj->bfd_session)
320 adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip);
321
322 bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
323 &adj->bfd_session->src_ip, circuit->interface->name, command);
324 bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family,
325 &adj->bfd_session->dst_ip,
326 &adj->bfd_session->src_ip,
327 circuit->interface->name,
328 0, /* ttl */
329 0, /* multihop */
330 1, /* control plane independent bit is on */
331 command,
332 0, /* set flag */
333 VRF_DEFAULT);
334 return;
335 out:
336 bfd_handle_adj_down(adj);
337 }
338
339 static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
340 {
341 if (adj->adj_state == ISIS_ADJ_UP)
342 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
343 else
344 bfd_handle_adj_down(adj);
345 return 0;
346 }
347
348 static void bfd_adj_cmd(struct isis_adjacency *adj, int command)
349 {
350 if (adj->adj_state == ISIS_ADJ_UP
351 && command != ZEBRA_BFD_DEST_DEREGISTER) {
352 bfd_handle_adj_up(adj, command);
353 } else {
354 bfd_handle_adj_down(adj);
355 }
356 }
357
358 void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
359 {
360 switch (circuit->circ_type) {
361 case CIRCUIT_T_BROADCAST:
362 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
363 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
364
365 struct listnode *node;
366 struct isis_adjacency *adj;
367
368 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
369 bfd_adj_cmd(adj, command);
370 }
371 break;
372 case CIRCUIT_T_P2P:
373 if (circuit->u.p2p.neighbor)
374 bfd_adj_cmd(circuit->u.p2p.neighbor, command);
375 break;
376 default:
377 break;
378 }
379 }
380
381 void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
382 uint32_t min_rx, uint32_t min_tx,
383 uint32_t detect_mult, int defaults)
384 {
385 int command = 0;
386
387 bfd_set_param(&circuit->bfd_info, min_rx,
388 min_tx, detect_mult, defaults, &command);
389
390 if (command)
391 isis_bfd_circuit_cmd(circuit, command);
392 }
393
394 static int bfd_circuit_write_settings(struct isis_circuit *circuit,
395 struct vty *vty)
396 {
397 struct bfd_info *bfd_info = circuit->bfd_info;
398
399 if (!bfd_info)
400 return 0;
401
402 vty_out(vty, " %s bfd\n", PROTO_NAME);
403 return 1;
404 }
405
406 void isis_bfd_init(void)
407 {
408 bfd_gbl_init();
409
410 orig_zebra_connected = zclient->zebra_connected;
411 zclient->zebra_connected = isis_bfd_zebra_connected;
412 zclient->interface_bfd_dest_update = isis_bfd_interface_dest_update;
413 zclient->bfd_dest_replay = isis_bfd_nbr_replay;
414 hook_register(isis_adj_state_change_hook,
415 bfd_handle_adj_state_change);
416 hook_register(isis_circuit_config_write,
417 bfd_circuit_write_settings);
418 }