]>
Commit | Line | Data |
---|---|---|
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" | |
22 | #include "bfd.h" | |
23 | ||
24 | #include "isisd/isis_bfd.h" | |
25 | #include "isisd/isis_zebra.h" | |
215eccb0 CF |
26 | #include "isisd/isis_common.h" |
27 | #include "isisd/isis_constants.h" | |
28 | #include "isisd/isis_adjacency.h" | |
29 | #include "isisd/isis_circuit.h" | |
3015e3d1 | 30 | #include "isisd/isisd.h" |
215eccb0 | 31 | #include "isisd/fabricd.h" |
52df8228 | 32 | |
20a42f01 CF |
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; | |
a5eba4e9 | 38 | int status; |
20a42f01 CF |
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 | ||
a5eba4e9 | 46 | rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv)); |
20a42f01 CF |
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 | ||
a5eba4e9 CF |
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; | |
a5eba4e9 | 71 | |
5489eb45 | 72 | adj->bfd_session->status = new_status; |
a5eba4e9 CF |
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 | ||
121f9dee | 95 | static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) |
52df8228 | 96 | { |
2815f817 CF |
97 | struct interface *ifp; |
98 | struct prefix dst_ip; | |
99 | int status; | |
100 | ||
9beff0bd PG |
101 | ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, |
102 | NULL, vrf_id); | |
2815f817 CF |
103 | if (!ifp || dst_ip.family != AF_INET) |
104 | return 0; | |
105 | ||
106 | if (isis->debugs & DEBUG_BFD) { | |
107 | char dst_buf[INET6_ADDRSTRLEN]; | |
5489eb45 | 108 | |
2815f817 CF |
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 | ||
a5eba4e9 | 116 | struct isis_circuit *circuit = circuit_scan_by_ifp(ifp); |
5489eb45 | 117 | |
a5eba4e9 CF |
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 | ||
52df8228 CF |
138 | return 0; |
139 | } | |
140 | ||
121f9dee | 141 | static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) |
52df8228 | 142 | { |
0945d5ed | 143 | bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); |
ab880eaf CF |
144 | |
145 | struct listnode *anode; | |
146 | struct isis_area *area; | |
147 | ||
2815f817 CF |
148 | if (isis->debugs & DEBUG_BFD) |
149 | zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors."); | |
150 | ||
ab880eaf CF |
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 | ||
2815f817 CF |
159 | if (isis->debugs & DEBUG_BFD) |
160 | zlog_debug("ISIS-BFD: Done with replay."); | |
161 | ||
52df8228 CF |
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 | ||
0945d5ed | 171 | bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); |
52df8228 CF |
172 | } |
173 | ||
2815f817 CF |
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 | ||
20a42f01 CF |
207 | static void bfd_handle_adj_down(struct isis_adjacency *adj) |
208 | { | |
209 | if (!adj->bfd_session) | |
210 | return; | |
211 | ||
2815f817 CF |
212 | bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, |
213 | adj->circuit->interface->name, ZEBRA_BFD_DEST_DEREGISTER); | |
214 | ||
20a42f01 CF |
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 */ | |
9beff0bd | 221 | 1, /* control plane independent bit is on */ |
20a42f01 CF |
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) | |
215eccb0 | 229 | { |
20a42f01 CF |
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); | |
5489eb45 | 238 | |
20a42f01 CF |
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 | ||
2815f817 CF |
255 | bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, |
256 | circuit->interface->name, command); | |
20a42f01 CF |
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 */ | |
9beff0bd | 263 | 1, /* control plane independent bit is on */ |
20a42f01 CF |
264 | command, |
265 | 0, /* set flag */ | |
266 | VRF_DEFAULT); | |
215eccb0 | 267 | return; |
20a42f01 CF |
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; | |
5489eb45 | 300 | |
20a42f01 CF |
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 | } | |
215eccb0 CF |
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 | ||
3015e3d1 CF |
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 | ||
490a6fc7 | 335 | vty_out(vty, " %s bfd\n", PROTO_NAME); |
3015e3d1 CF |
336 | return 1; |
337 | } | |
338 | ||
52df8228 CF |
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; | |
20a42f01 CF |
347 | hook_register(isis_adj_state_change_hook, |
348 | bfd_handle_adj_state_change); | |
3015e3d1 CF |
349 | hook_register(isis_circuit_config_write, |
350 | bfd_circuit_write_settings); | |
52df8228 | 351 | } |