]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_bfd.c
bfdd, lib, bgpd: add bfd cbit usage
[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 "bfd.h"
23
24 #include "isisd/isis_bfd.h"
25 #include "isisd/isis_zebra.h"
26 #include "isisd/isis_common.h"
27 #include "isisd/isis_constants.h"
28 #include "isisd/isis_adjacency.h"
29 #include "isisd/isis_circuit.h"
30 #include "isisd/isisd.h"
31 #include "isisd/fabricd.h"
32
33 DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session")
34
35 struct bfd_session {
36 struct in_addr dst_ip;
37 struct in_addr src_ip;
38 int status;
39 };
40
41 static struct bfd_session *bfd_session_new(struct in_addr *dst_ip,
42 struct in_addr *src_ip)
43 {
44 struct bfd_session *rv;
45
46 rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv));
47 rv->dst_ip = *dst_ip;
48 rv->src_ip = *src_ip;
49 return rv;
50 }
51
52 static void bfd_session_free(struct bfd_session **session)
53 {
54 if (!*session)
55 return;
56
57 XFREE(MTYPE_BFD_SESSION, *session);
58 *session = NULL;
59 }
60
61 static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
62 int new_status)
63 {
64 if (!adj->bfd_session)
65 return;
66
67 if (adj->bfd_session->dst_ip.s_addr != dst->u.prefix4.s_addr)
68 return;
69
70 int old_status = adj->bfd_session->status;
71
72 adj->bfd_session->status = new_status;
73 if (old_status == new_status)
74 return;
75
76 if (isis->debugs & DEBUG_BFD) {
77 char dst_str[INET6_ADDRSTRLEN];
78
79 inet_ntop(AF_INET, &adj->bfd_session->dst_ip,
80 dst_str, sizeof(dst_str));
81 zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s",
82 dst_str, adj->circuit->interface->name,
83 bfd_get_status_str(old_status),
84 bfd_get_status_str(new_status));
85 }
86
87 if (old_status != BFD_STATUS_UP
88 || new_status != BFD_STATUS_DOWN) {
89 return;
90 }
91
92 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down");
93 }
94
95 static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
96 {
97 struct interface *ifp;
98 struct prefix dst_ip;
99 int status;
100
101 ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status,
102 NULL, vrf_id);
103 if (!ifp || dst_ip.family != AF_INET)
104 return 0;
105
106 if (isis->debugs & DEBUG_BFD) {
107 char dst_buf[INET6_ADDRSTRLEN];
108
109 inet_ntop(AF_INET, &dst_ip.u.prefix4,
110 dst_buf, sizeof(dst_buf));
111
112 zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
113 dst_buf, ifp->name, bfd_get_status_str(status));
114 }
115
116 struct isis_circuit *circuit = circuit_scan_by_ifp(ifp);
117
118 if (!circuit)
119 return 0;
120
121 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
122 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
123 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
124
125 struct listnode *node, *nnode;
126 struct isis_adjacency *adj;
127
128 for (ALL_LIST_ELEMENTS(adjdb, node, nnode, adj))
129 bfd_adj_event(adj, &dst_ip, status);
130 }
131 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
132 if (circuit->u.p2p.neighbor) {
133 bfd_adj_event(circuit->u.p2p.neighbor,
134 &dst_ip, status);
135 }
136 }
137
138 return 0;
139 }
140
141 static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
142 {
143 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
144
145 struct listnode *anode;
146 struct isis_area *area;
147
148 if (isis->debugs & DEBUG_BFD)
149 zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
150
151 for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
152 struct listnode *cnode;
153 struct isis_circuit *circuit;
154
155 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
156 isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE);
157 }
158
159 if (isis->debugs & DEBUG_BFD)
160 zlog_debug("ISIS-BFD: Done with replay.");
161
162 return 0;
163 }
164
165 static void (*orig_zebra_connected)(struct zclient *);
166 static void isis_bfd_zebra_connected(struct zclient *zclient)
167 {
168 if (orig_zebra_connected)
169 orig_zebra_connected(zclient);
170
171 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
172 }
173
174 static void bfd_debug(struct in_addr *dst, struct in_addr *src,
175 const char *interface, int command)
176 {
177 if (!(isis->debugs & DEBUG_BFD))
178 return;
179
180 char dst_str[INET6_ADDRSTRLEN];
181 char src_str[INET6_ADDRSTRLEN];
182
183 inet_ntop(AF_INET, dst, dst_str, sizeof(dst_str));
184 inet_ntop(AF_INET, src, src_str, sizeof(src_str));
185
186 const char *command_str;
187
188 switch (command) {
189 case ZEBRA_BFD_DEST_REGISTER:
190 command_str = "Register";
191 break;
192 case ZEBRA_BFD_DEST_DEREGISTER:
193 command_str = "Deregister";
194 break;
195 case ZEBRA_BFD_DEST_UPDATE:
196 command_str = "Update";
197 break;
198 default:
199 command_str = "Unknown-Cmd";
200 break;
201 }
202
203 zlog_debug("ISIS-BFD: %s peer %s on %s (src %s)",
204 command_str, dst_str, interface, src_str);
205 }
206
207 static void bfd_handle_adj_down(struct isis_adjacency *adj)
208 {
209 if (!adj->bfd_session)
210 return;
211
212 bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
213 adj->circuit->interface->name, ZEBRA_BFD_DEST_DEREGISTER);
214
215 bfd_peer_sendmsg(zclient, NULL, AF_INET,
216 &adj->bfd_session->dst_ip,
217 &adj->bfd_session->src_ip,
218 adj->circuit->interface->name,
219 0, /* ttl */
220 0, /* multihop */
221 1, /* control plane independent bit is on */
222 ZEBRA_BFD_DEST_DEREGISTER,
223 0, /* set_flag */
224 VRF_DEFAULT);
225 bfd_session_free(&adj->bfd_session);
226 }
227
228 static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
229 {
230 struct isis_circuit *circuit = adj->circuit;
231
232 if (!circuit->bfd_info
233 || !circuit->ip_router
234 || !adj->ipv4_address_count)
235 goto out;
236
237 struct list *local_ips = fabricd_ip_addrs(adj->circuit);
238
239 if (!local_ips)
240 goto out;
241
242 struct in_addr *dst_ip = &adj->ipv4_addresses[0];
243 struct prefix_ipv4 *local_ip = listgetdata(listhead(local_ips));
244 struct in_addr *src_ip = &local_ip->prefix;
245
246 if (adj->bfd_session) {
247 if (adj->bfd_session->dst_ip.s_addr != dst_ip->s_addr
248 || adj->bfd_session->src_ip.s_addr != src_ip->s_addr)
249 bfd_handle_adj_down(adj);
250 }
251
252 if (!adj->bfd_session)
253 adj->bfd_session = bfd_session_new(dst_ip, src_ip);
254
255 bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
256 circuit->interface->name, command);
257 bfd_peer_sendmsg(zclient, circuit->bfd_info, AF_INET,
258 &adj->bfd_session->dst_ip,
259 &adj->bfd_session->src_ip,
260 circuit->interface->name,
261 0, /* ttl */
262 0, /* multihop */
263 1, /* control plane independent bit is on */
264 command,
265 0, /* set flag */
266 VRF_DEFAULT);
267 return;
268 out:
269 bfd_handle_adj_down(adj);
270 }
271
272 static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
273 {
274 if (adj->adj_state == ISIS_ADJ_UP)
275 bfd_handle_adj_up(adj, ZEBRA_BFD_DEST_REGISTER);
276 else
277 bfd_handle_adj_down(adj);
278 return 0;
279 }
280
281 static void bfd_adj_cmd(struct isis_adjacency *adj, int command)
282 {
283 if (adj->adj_state == ISIS_ADJ_UP
284 && command != ZEBRA_BFD_DEST_DEREGISTER) {
285 bfd_handle_adj_up(adj, command);
286 } else {
287 bfd_handle_adj_down(adj);
288 }
289 }
290
291 void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
292 {
293 switch (circuit->circ_type) {
294 case CIRCUIT_T_BROADCAST:
295 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
296 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
297
298 struct listnode *node;
299 struct isis_adjacency *adj;
300
301 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
302 bfd_adj_cmd(adj, command);
303 }
304 break;
305 case CIRCUIT_T_P2P:
306 if (circuit->u.p2p.neighbor)
307 bfd_adj_cmd(circuit->u.p2p.neighbor, command);
308 break;
309 default:
310 break;
311 }
312 }
313
314 void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
315 uint32_t min_rx, uint32_t min_tx,
316 uint32_t detect_mult, int defaults)
317 {
318 int command = 0;
319
320 bfd_set_param(&circuit->bfd_info, min_rx,
321 min_tx, detect_mult, defaults, &command);
322
323 if (command)
324 isis_bfd_circuit_cmd(circuit, command);
325 }
326
327 static int bfd_circuit_write_settings(struct isis_circuit *circuit,
328 struct vty *vty)
329 {
330 struct bfd_info *bfd_info = circuit->bfd_info;
331
332 if (!bfd_info)
333 return 0;
334
335 vty_out(vty, " %s bfd\n", PROTO_NAME);
336 return 1;
337 }
338
339 void isis_bfd_init(void)
340 {
341 bfd_gbl_init();
342
343 orig_zebra_connected = zclient->zebra_connected;
344 zclient->zebra_connected = isis_bfd_zebra_connected;
345 zclient->interface_bfd_dest_update = isis_bfd_interface_dest_update;
346 zclient->bfd_dest_replay = isis_bfd_nbr_replay;
347 hook_register(isis_adj_state_change_hook,
348 bfd_handle_adj_state_change);
349 hook_register(isis_circuit_config_write,
350 bfd_circuit_write_settings);
351 }