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