]>
Commit | Line | Data |
---|---|---|
7f342629 DS |
1 | /** |
2 | * bfd.c: BFD handling routines | |
3 | * | |
4 | * @copyright Copyright (C) 2015 Cumulus Networks, Inc. | |
5 | * | |
6 | * This file is part of GNU Zebra. | |
7 | * | |
8 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2, or (at your option) any | |
11 | * later version. | |
12 | * | |
13 | * GNU Zebra is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
896014f4 DL |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; see the file COPYING; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
7f342629 DS |
21 | */ |
22 | ||
23 | #include <zebra.h> | |
24 | ||
25 | #include "command.h" | |
26 | #include "memory.h" | |
27 | #include "prefix.h" | |
28 | #include "thread.h" | |
29 | #include "stream.h" | |
30 | #include "zclient.h" | |
31 | #include "table.h" | |
32 | #include "vty.h" | |
33 | #include "bfd.h" | |
34 | ||
4a1ab8e4 DL |
35 | DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info") |
36 | ||
588e90ec | 37 | int bfd_debug = 0; |
567b877d | 38 | struct bfd_gbl bfd_gbl; |
39 | ||
40 | /* | |
41 | * bfd_gbl_init - Initialize the BFD global structure | |
42 | */ | |
d62a17ae | 43 | void bfd_gbl_init(void) |
567b877d | 44 | { |
d62a17ae | 45 | memset(&bfd_gbl, 0, sizeof(struct bfd_gbl)); |
567b877d | 46 | } |
47 | ||
48 | /* | |
49 | * bfd_gbl_exit - Called when daemon exits | |
50 | */ | |
d62a17ae | 51 | void bfd_gbl_exit(void) |
567b877d | 52 | { |
d62a17ae | 53 | SET_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN); |
567b877d | 54 | } |
588e90ec | 55 | |
7f342629 DS |
56 | /* |
57 | * bfd_info_create - Allocate the BFD information | |
58 | */ | |
d62a17ae | 59 | struct bfd_info *bfd_info_create(void) |
7f342629 | 60 | { |
d62a17ae | 61 | struct bfd_info *bfd_info; |
7f342629 | 62 | |
d62a17ae | 63 | bfd_info = XCALLOC(MTYPE_BFD_INFO, sizeof(struct bfd_info)); |
64 | assert(bfd_info); | |
7f342629 | 65 | |
d62a17ae | 66 | bfd_info->status = BFD_STATUS_UNKNOWN; |
67 | bfd_info->type = BFD_TYPE_NOT_CONFIGURED; | |
68 | bfd_info->last_update = 0; | |
69 | return bfd_info; | |
7f342629 DS |
70 | } |
71 | ||
72 | /* | |
73 | * bfd_info_free - Free the BFD information. | |
74 | */ | |
d62a17ae | 75 | void bfd_info_free(struct bfd_info **bfd_info) |
7f342629 | 76 | { |
d62a17ae | 77 | if (*bfd_info) { |
78 | XFREE(MTYPE_BFD_INFO, *bfd_info); | |
79 | *bfd_info = NULL; | |
80 | } | |
7f342629 DS |
81 | } |
82 | ||
83 | /* | |
84 | * bfd_validate_param - Validate the BFD paramter information. | |
85 | */ | |
d62a17ae | 86 | int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str, |
87 | const char *tx_str, u_int8_t *dm_val, u_int32_t *rx_val, | |
88 | u_int32_t *tx_val) | |
7f342629 | 89 | { |
d62a17ae | 90 | *dm_val = strtoul(dm_str, NULL, 10); |
91 | *rx_val = strtoul(rx_str, NULL, 10); | |
92 | *tx_val = strtoul(tx_str, NULL, 10); | |
93 | return CMD_SUCCESS; | |
7f342629 DS |
94 | } |
95 | ||
96 | /* | |
97 | * bfd_set_param - Set the configured BFD paramter values | |
98 | */ | |
d62a17ae | 99 | void bfd_set_param(struct bfd_info **bfd_info, u_int32_t min_rx, |
100 | u_int32_t min_tx, u_int8_t detect_mult, int defaults, | |
101 | int *command) | |
7f342629 | 102 | { |
d62a17ae | 103 | if (!*bfd_info) { |
104 | *bfd_info = bfd_info_create(); | |
105 | *command = ZEBRA_BFD_DEST_REGISTER; | |
106 | } else { | |
107 | if (((*bfd_info)->required_min_rx != min_rx) | |
108 | || ((*bfd_info)->desired_min_tx != min_tx) | |
109 | || ((*bfd_info)->detect_mult != detect_mult)) | |
110 | *command = ZEBRA_BFD_DEST_UPDATE; | |
111 | } | |
112 | ||
113 | if (*command) { | |
114 | (*bfd_info)->required_min_rx = min_rx; | |
115 | (*bfd_info)->desired_min_tx = min_tx; | |
116 | (*bfd_info)->detect_mult = detect_mult; | |
117 | } | |
118 | ||
119 | if (!defaults) | |
120 | SET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG); | |
121 | else | |
122 | UNSET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG); | |
7f342629 DS |
123 | } |
124 | ||
125 | /* | |
126 | * bfd_peer_sendmsg - Format and send a peer register/Unregister | |
127 | * command to Zebra to be forwarded to BFD | |
128 | */ | |
d62a17ae | 129 | void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, |
130 | int family, void *dst_ip, void *src_ip, char *if_name, | |
131 | int ttl, int multihop, int command, int set_flag, | |
132 | vrf_id_t vrf_id) | |
7f342629 | 133 | { |
d62a17ae | 134 | struct stream *s; |
135 | int ret; | |
136 | int len; | |
137 | ||
138 | /* Individual reg/dereg messages are supressed during shutdown. */ | |
139 | if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) { | |
140 | if (bfd_debug) | |
141 | zlog_debug( | |
142 | "%s: Suppressing BFD peer reg/dereg messages", | |
143 | __FUNCTION__); | |
144 | return; | |
145 | } | |
146 | ||
147 | /* Check socket. */ | |
148 | if (!zclient || zclient->sock < 0) { | |
149 | if (bfd_debug) | |
150 | zlog_debug( | |
151 | "%s: Can't send BFD peer register, Zebra client not " | |
152 | "established", | |
153 | __FUNCTION__); | |
154 | return; | |
155 | } | |
156 | ||
157 | s = zclient->obuf; | |
158 | stream_reset(s); | |
159 | zclient_create_header(s, command, vrf_id); | |
160 | ||
161 | stream_putl(s, getpid()); | |
162 | ||
163 | stream_putw(s, family); | |
164 | switch (family) { | |
165 | case AF_INET: | |
166 | stream_put_in_addr(s, (struct in_addr *)dst_ip); | |
167 | break; | |
168 | case AF_INET6: | |
169 | stream_put(s, dst_ip, 16); | |
170 | break; | |
171 | default: | |
172 | break; | |
173 | } | |
174 | ||
175 | if (command != ZEBRA_BFD_DEST_DEREGISTER) { | |
176 | stream_putl(s, bfd_info->required_min_rx); | |
177 | stream_putl(s, bfd_info->desired_min_tx); | |
178 | stream_putc(s, bfd_info->detect_mult); | |
179 | } | |
180 | ||
181 | if (multihop) { | |
182 | stream_putc(s, 1); | |
183 | /* Multi-hop destination send the source IP address to BFD */ | |
184 | if (src_ip) { | |
185 | stream_putw(s, family); | |
186 | switch (family) { | |
187 | case AF_INET: | |
188 | stream_put_in_addr(s, (struct in_addr *)src_ip); | |
189 | break; | |
190 | case AF_INET6: | |
191 | stream_put(s, src_ip, 16); | |
192 | break; | |
193 | default: | |
194 | break; | |
195 | } | |
196 | } | |
197 | stream_putc(s, ttl); | |
198 | } else { | |
199 | stream_putc(s, 0); | |
200 | if ((family == AF_INET6) && (src_ip)) { | |
201 | stream_putw(s, family); | |
202 | stream_put(s, src_ip, 16); | |
203 | } | |
204 | if (if_name) { | |
205 | len = strlen(if_name); | |
206 | stream_putc(s, len); | |
207 | stream_put(s, if_name, len); | |
208 | } else { | |
209 | stream_putc(s, 0); | |
210 | } | |
211 | } | |
212 | ||
213 | stream_putw_at(s, 0, stream_get_endp(s)); | |
214 | ||
215 | ret = zclient_send_message(zclient); | |
216 | ||
217 | if (ret < 0) { | |
218 | if (bfd_debug) | |
219 | zlog_debug( | |
220 | "bfd_peer_sendmsg: zclient_send_message() failed"); | |
221 | return; | |
222 | } | |
223 | ||
224 | if (set_flag) { | |
225 | if (command == ZEBRA_BFD_DEST_REGISTER) | |
226 | SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
227 | else if (command == ZEBRA_BFD_DEST_DEREGISTER) | |
228 | UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
229 | } | |
230 | ||
231 | return; | |
7f342629 DS |
232 | } |
233 | ||
234 | /* | |
235 | * bfd_get_command_dbg_str - Convert command to a debug string. | |
236 | */ | |
d62a17ae | 237 | const char *bfd_get_command_dbg_str(int command) |
7f342629 | 238 | { |
d62a17ae | 239 | switch (command) { |
240 | case ZEBRA_BFD_DEST_REGISTER: | |
241 | return "Register"; | |
242 | case ZEBRA_BFD_DEST_DEREGISTER: | |
243 | return "Deregister"; | |
244 | case ZEBRA_BFD_DEST_UPDATE: | |
245 | return "Update"; | |
246 | default: | |
247 | return "Unknown"; | |
248 | } | |
7f342629 DS |
249 | } |
250 | ||
251 | /* | |
252 | * bfd_get_peer_info - Extract the Peer information for which the BFD session | |
253 | * went down from the message sent from Zebra to clients. | |
254 | */ | |
d62a17ae | 255 | struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, |
256 | struct prefix *sp, int *status, | |
257 | vrf_id_t vrf_id) | |
7f342629 | 258 | { |
d62a17ae | 259 | unsigned int ifindex; |
260 | struct interface *ifp = NULL; | |
261 | int plen; | |
262 | ||
263 | /* Get interface index. */ | |
264 | ifindex = stream_getl(s); | |
265 | ||
266 | /* Lookup index. */ | |
267 | if (ifindex != 0) { | |
268 | ifp = if_lookup_by_index(ifindex, vrf_id); | |
269 | if (ifp == NULL) { | |
270 | if (bfd_debug) | |
271 | zlog_debug( | |
272 | "zebra_interface_bfd_read: " | |
273 | "Can't find interface by ifindex: %d ", | |
274 | ifindex); | |
275 | return NULL; | |
276 | } | |
277 | } | |
278 | ||
279 | /* Fetch destination address. */ | |
280 | dp->family = stream_getc(s); | |
281 | ||
282 | plen = prefix_blen(dp); | |
283 | stream_get(&dp->u.prefix, s, plen); | |
284 | dp->prefixlen = stream_getc(s); | |
285 | ||
286 | /* Get BFD status. */ | |
287 | *status = stream_getl(s); | |
288 | ||
289 | if (sp) { | |
290 | sp->family = stream_getc(s); | |
291 | ||
292 | plen = prefix_blen(sp); | |
293 | stream_get(&sp->u.prefix, s, plen); | |
294 | sp->prefixlen = stream_getc(s); | |
295 | } | |
296 | return ifp; | |
7f342629 | 297 | } |
68fe91d6 | 298 | |
299 | /* | |
300 | * bfd_get_status_str - Convert BFD status to a display string. | |
301 | */ | |
d62a17ae | 302 | const char *bfd_get_status_str(int status) |
68fe91d6 | 303 | { |
d62a17ae | 304 | switch (status) { |
305 | case BFD_STATUS_DOWN: | |
306 | return "Down"; | |
307 | case BFD_STATUS_UP: | |
308 | return "Up"; | |
309 | case BFD_STATUS_UNKNOWN: | |
310 | default: | |
311 | return "Unknown"; | |
312 | } | |
68fe91d6 | 313 | } |
314 | ||
315 | /* | |
316 | * bfd_last_update - Calculate the last BFD update time and convert it | |
317 | * into a dd:hh:mm:ss display format. | |
318 | */ | |
d62a17ae | 319 | static void bfd_last_update(time_t last_update, char *buf, size_t len) |
68fe91d6 | 320 | { |
d62a17ae | 321 | time_t curr; |
322 | time_t diff; | |
323 | struct tm *tm; | |
324 | struct timeval tv; | |
325 | ||
326 | /* If no BFD satatus update has ever been received, print `never'. */ | |
327 | if (last_update == 0) { | |
328 | snprintf(buf, len, "never"); | |
329 | return; | |
330 | } | |
331 | ||
332 | /* Get current time. */ | |
333 | monotime(&tv); | |
334 | curr = tv.tv_sec; | |
335 | diff = curr - last_update; | |
336 | tm = gmtime(&diff); | |
337 | ||
338 | snprintf(buf, len, "%d:%02d:%02d:%02d", tm->tm_yday, tm->tm_hour, | |
339 | tm->tm_min, tm->tm_sec); | |
68fe91d6 | 340 | } |
341 | ||
342 | /* | |
343 | * bfd_show_param - Show the BFD parameter information. | |
344 | */ | |
d62a17ae | 345 | void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag, |
346 | int extra_space, u_char use_json, json_object *json_obj) | |
68fe91d6 | 347 | { |
d62a17ae | 348 | json_object *json_bfd = NULL; |
349 | ||
350 | if (!bfd_info) | |
351 | return; | |
352 | ||
353 | if (use_json) { | |
354 | if (bfd_tag) | |
355 | json_bfd = json_object_new_object(); | |
356 | else | |
357 | json_bfd = json_obj; | |
358 | ||
359 | json_object_int_add(json_bfd, "detectMultiplier", | |
360 | bfd_info->detect_mult); | |
361 | json_object_int_add(json_bfd, "rxMinInterval", | |
362 | bfd_info->required_min_rx); | |
363 | json_object_int_add(json_bfd, "txMinInterval", | |
364 | bfd_info->desired_min_tx); | |
365 | if (bfd_tag) | |
366 | json_object_object_add(json_obj, "peerBfdInfo", | |
367 | json_bfd); | |
368 | } else { | |
369 | vty_out(vty, | |
b077b2e6 | 370 | " %s%sDetect Multiplier: %d, Min Rx interval: %d," |
d62a17ae | 371 | " Min Tx interval: %d\n", |
372 | (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ", | |
373 | bfd_info->detect_mult, bfd_info->required_min_rx, | |
374 | bfd_info->desired_min_tx); | |
375 | } | |
68fe91d6 | 376 | } |
377 | ||
378 | /* | |
379 | * bfd_show_status - Show the BFD status information. | |
380 | */ | |
d62a17ae | 381 | static void bfd_show_status(struct vty *vty, struct bfd_info *bfd_info, |
382 | int bfd_tag, int extra_space, u_char use_json, | |
383 | json_object *json_bfd) | |
68fe91d6 | 384 | { |
d62a17ae | 385 | char time_buf[32]; |
386 | ||
387 | if (!bfd_info) | |
388 | return; | |
389 | ||
390 | bfd_last_update(bfd_info->last_update, time_buf, 32); | |
391 | if (use_json) { | |
392 | json_object_string_add(json_bfd, "status", | |
393 | bfd_get_status_str(bfd_info->status)); | |
394 | json_object_string_add(json_bfd, "lastUpdate", time_buf); | |
395 | } else { | |
396 | vty_out(vty, " %s%sStatus: %s, Last update: %s\n", | |
397 | (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ", | |
398 | bfd_get_status_str(bfd_info->status), time_buf); | |
399 | } | |
68fe91d6 | 400 | } |
401 | ||
402 | /* | |
403 | * bfd_show_info - Show the BFD information. | |
404 | */ | |
d62a17ae | 405 | void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, |
406 | int extra_space, u_char use_json, json_object *json_obj) | |
68fe91d6 | 407 | { |
d62a17ae | 408 | json_object *json_bfd = NULL; |
409 | ||
410 | if (!bfd_info) | |
411 | return; | |
412 | ||
413 | if (use_json) { | |
414 | json_bfd = json_object_new_object(); | |
415 | if (multihop) | |
416 | json_object_string_add(json_bfd, "type", "multi hop"); | |
417 | else | |
418 | json_object_string_add(json_bfd, "type", "single hop"); | |
419 | } else { | |
420 | vty_out(vty, " %sBFD: Type: %s\n", (extra_space) ? " " : "", | |
421 | (multihop) ? "multi hop" : "single hop"); | |
422 | } | |
423 | ||
424 | bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
425 | bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
426 | ||
427 | if (use_json) | |
428 | json_object_object_add(json_obj, "peerBfdInfo", json_bfd); | |
429 | else | |
430 | vty_out(vty, "\n"); | |
68fe91d6 | 431 | } |
055c4dfc | 432 | |
433 | /* | |
434 | * bfd_client_sendmsg - Format and send a client register | |
435 | * command to Zebra to be forwarded to BFD | |
436 | */ | |
d62a17ae | 437 | void bfd_client_sendmsg(struct zclient *zclient, int command) |
055c4dfc | 438 | { |
d62a17ae | 439 | struct stream *s; |
440 | int ret; | |
441 | ||
442 | /* Check socket. */ | |
443 | if (!zclient || zclient->sock < 0) { | |
444 | if (bfd_debug) | |
445 | zlog_debug( | |
446 | "%s: Can't send BFD client register, Zebra client not " | |
447 | "established", | |
448 | __FUNCTION__); | |
449 | return; | |
450 | } | |
451 | ||
452 | s = zclient->obuf; | |
453 | stream_reset(s); | |
454 | zclient_create_header(s, command, VRF_DEFAULT); | |
455 | ||
456 | stream_putl(s, getpid()); | |
457 | ||
458 | stream_putw_at(s, 0, stream_get_endp(s)); | |
459 | ||
460 | ret = zclient_send_message(zclient); | |
461 | ||
462 | if (ret < 0) { | |
463 | if (bfd_debug) | |
464 | zlog_debug( | |
465 | "bfd_client_sendmsg %ld: zclient_send_message() failed", | |
466 | (long)getpid()); | |
467 | return; | |
468 | } | |
469 | ||
470 | return; | |
055c4dfc | 471 | } |