]>
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; | |
7f342629 DS |
182 | case AF_INET6: |
183 | stream_put(s, dst_ip, 16); | |
184 | break; | |
7f342629 DS |
185 | default: |
186 | break; | |
187 | } | |
188 | ||
189 | if (command != ZEBRA_BFD_DEST_DEREGISTER) | |
190 | { | |
191 | stream_putl(s, bfd_info->required_min_rx); | |
192 | stream_putl(s, bfd_info->desired_min_tx); | |
193 | stream_putc(s, bfd_info->detect_mult); | |
194 | } | |
195 | ||
196 | if (multihop) | |
197 | { | |
198 | stream_putc(s, 1); | |
199 | /* Multi-hop destination send the source IP address to BFD */ | |
200 | if (src_ip) | |
201 | { | |
202 | stream_putw(s, family); | |
203 | switch (family) | |
204 | { | |
205 | case AF_INET: | |
206 | stream_put_in_addr (s, (struct in_addr *) src_ip); | |
207 | break; | |
7f342629 DS |
208 | case AF_INET6: |
209 | stream_put(s, src_ip, 16); | |
210 | break; | |
7f342629 DS |
211 | default: |
212 | break; | |
213 | } | |
214 | } | |
215 | stream_putc(s, ttl); | |
216 | } | |
217 | else | |
218 | { | |
219 | stream_putc(s, 0); | |
7f342629 DS |
220 | if ((family == AF_INET6) && (src_ip)) |
221 | { | |
222 | stream_putw(s, family); | |
223 | stream_put(s, src_ip, 16); | |
224 | } | |
7f342629 DS |
225 | if (if_name) |
226 | { | |
227 | len = strlen(if_name); | |
228 | stream_putc(s, len); | |
229 | stream_put(s, if_name, len); | |
230 | } | |
231 | else | |
232 | { | |
233 | stream_putc(s, 0); | |
234 | } | |
235 | } | |
236 | ||
237 | stream_putw_at (s, 0, stream_get_endp (s)); | |
238 | ||
239 | ret = zclient_send_message(zclient); | |
240 | ||
241 | if (ret < 0) | |
242 | { | |
588e90ec DS |
243 | if (bfd_debug) |
244 | zlog_debug("bfd_peer_sendmsg: zclient_send_message() failed"); | |
7f342629 DS |
245 | return; |
246 | } | |
247 | ||
248 | if (set_flag) | |
249 | { | |
250 | if (command == ZEBRA_BFD_DEST_REGISTER) | |
251 | SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
252 | else if (command == ZEBRA_BFD_DEST_DEREGISTER) | |
253 | UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
254 | } | |
255 | ||
256 | return; | |
257 | } | |
258 | ||
259 | /* | |
260 | * bfd_get_command_dbg_str - Convert command to a debug string. | |
261 | */ | |
262 | const char * | |
263 | bfd_get_command_dbg_str(int command) | |
264 | { | |
265 | switch (command) | |
266 | { | |
267 | case ZEBRA_BFD_DEST_REGISTER: | |
268 | return "Register"; | |
269 | case ZEBRA_BFD_DEST_DEREGISTER: | |
270 | return "Deregister"; | |
271 | case ZEBRA_BFD_DEST_UPDATE: | |
272 | return "Update"; | |
273 | default: | |
274 | return "Unknown"; | |
275 | } | |
276 | } | |
277 | ||
278 | /* | |
279 | * bfd_get_peer_info - Extract the Peer information for which the BFD session | |
280 | * went down from the message sent from Zebra to clients. | |
281 | */ | |
282 | struct interface * | |
68fe91d6 | 283 | bfd_get_peer_info (struct stream *s, struct prefix *dp, struct prefix *sp, |
1e22a2af | 284 | int *status, vrf_id_t vrf_id) |
7f342629 DS |
285 | { |
286 | unsigned int ifindex; | |
287 | struct interface *ifp = NULL; | |
288 | int plen; | |
289 | ||
290 | /* Get interface index. */ | |
291 | ifindex = stream_getl (s); | |
292 | ||
293 | /* Lookup index. */ | |
294 | if (ifindex != 0) | |
295 | { | |
1e22a2af | 296 | ifp = if_lookup_by_index_vrf (ifindex, vrf_id); |
7f342629 DS |
297 | if (ifp == NULL) |
298 | { | |
588e90ec DS |
299 | if (bfd_debug) |
300 | zlog_debug ("zebra_interface_bfd_read: " | |
301 | "Can't find interface by ifindex: %d ", ifindex); | |
7f342629 DS |
302 | return NULL; |
303 | } | |
304 | } | |
305 | ||
306 | /* Fetch destination address. */ | |
307 | dp->family = stream_getc (s); | |
308 | ||
309 | plen = prefix_blen (dp); | |
310 | stream_get (&dp->u.prefix, s, plen); | |
311 | dp->prefixlen = stream_getc (s); | |
312 | ||
68fe91d6 | 313 | /* Get BFD status. */ |
314 | *status = stream_getl (s); | |
315 | ||
7f342629 DS |
316 | if (sp) |
317 | { | |
318 | sp->family = stream_getc (s); | |
319 | ||
320 | plen = prefix_blen (sp); | |
321 | stream_get (&sp->u.prefix, s, plen); | |
322 | sp->prefixlen = stream_getc (s); | |
323 | } | |
324 | return ifp; | |
325 | } | |
68fe91d6 | 326 | |
327 | /* | |
328 | * bfd_get_status_str - Convert BFD status to a display string. | |
329 | */ | |
330 | const char * | |
331 | bfd_get_status_str(int status) | |
332 | { | |
333 | switch (status) | |
334 | { | |
335 | case BFD_STATUS_DOWN: | |
336 | return "Down"; | |
337 | case BFD_STATUS_UP: | |
338 | return "Up"; | |
339 | case BFD_STATUS_UNKNOWN: | |
340 | default: | |
341 | return "Unknown"; | |
342 | } | |
343 | } | |
344 | ||
345 | /* | |
346 | * bfd_last_update - Calculate the last BFD update time and convert it | |
347 | * into a dd:hh:mm:ss display format. | |
348 | */ | |
349 | static void | |
350 | bfd_last_update (time_t last_update, char *buf, size_t len) | |
351 | { | |
352 | time_t curr; | |
353 | time_t diff; | |
354 | struct tm *tm; | |
355 | struct timeval tv; | |
356 | ||
357 | /* If no BFD satatus update has ever been received, print `never'. */ | |
358 | if (last_update == 0) | |
359 | { | |
360 | snprintf (buf, len, "never"); | |
361 | return; | |
362 | } | |
363 | ||
364 | /* Get current time. */ | |
cf672a86 | 365 | monotime(&tv); |
68fe91d6 | 366 | curr = tv.tv_sec; |
367 | diff = curr - last_update; | |
368 | tm = gmtime (&diff); | |
369 | ||
370 | snprintf (buf, len, "%d:%02d:%02d:%02d", | |
371 | tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec); | |
372 | } | |
373 | ||
374 | /* | |
375 | * bfd_show_param - Show the BFD parameter information. | |
376 | */ | |
377 | void | |
378 | bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag, | |
379 | int extra_space, u_char use_json, json_object *json_obj) | |
380 | { | |
381 | json_object *json_bfd = NULL; | |
382 | ||
383 | if (!bfd_info) | |
384 | return; | |
385 | ||
386 | if (use_json) | |
387 | { | |
388 | if (bfd_tag) | |
389 | json_bfd = json_object_new_object(); | |
390 | else | |
391 | json_bfd = json_obj; | |
392 | ||
393 | json_object_int_add(json_bfd, "detectMultiplier", bfd_info->detect_mult); | |
394 | json_object_int_add(json_bfd, "rxMinInterval", bfd_info->required_min_rx); | |
395 | json_object_int_add(json_bfd, "txMinInterval", bfd_info->desired_min_tx); | |
396 | if (bfd_tag) | |
397 | json_object_object_add(json_obj, "peerBfdInfo", json_bfd); | |
398 | } | |
399 | else | |
400 | { | |
401 | vty_out (vty, " %s%sDetect Mul: %d, Min Rx interval: %d," | |
402 | " Min Tx interval: %d%s", | |
403 | (extra_space) ? " ": "", (bfd_tag) ? "BFD: " : " ", | |
404 | bfd_info->detect_mult, bfd_info->required_min_rx, | |
405 | bfd_info->desired_min_tx, VTY_NEWLINE); | |
406 | } | |
407 | } | |
408 | ||
409 | /* | |
410 | * bfd_show_status - Show the BFD status information. | |
411 | */ | |
412 | static void | |
413 | bfd_show_status(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag, | |
414 | int extra_space, u_char use_json, json_object *json_bfd) | |
415 | { | |
416 | char time_buf[32]; | |
417 | ||
418 | if (!bfd_info) | |
419 | return; | |
420 | ||
421 | bfd_last_update(bfd_info->last_update, time_buf, 32); | |
422 | if (use_json) | |
423 | { | |
424 | json_object_string_add(json_bfd, "status", | |
425 | bfd_get_status_str(bfd_info->status)); | |
426 | json_object_string_add(json_bfd, "lastUpdate", time_buf); | |
427 | } | |
428 | else | |
429 | { | |
430 | vty_out (vty, " %s%sStatus: %s, Last update: %s%s", | |
431 | (extra_space) ? " ": "", (bfd_tag) ? "BFD: " : " ", | |
432 | bfd_get_status_str(bfd_info->status), time_buf, VTY_NEWLINE); | |
433 | } | |
434 | } | |
435 | ||
436 | /* | |
437 | * bfd_show_info - Show the BFD information. | |
438 | */ | |
439 | void | |
440 | bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, | |
441 | int extra_space, u_char use_json, json_object *json_obj) | |
442 | { | |
443 | json_object *json_bfd = NULL; | |
444 | ||
445 | if (!bfd_info) | |
446 | return; | |
447 | ||
448 | if (use_json) | |
449 | { | |
450 | json_bfd = json_object_new_object(); | |
451 | if (multihop) | |
452 | json_object_string_add(json_bfd, "type", "multi hop"); | |
453 | else | |
454 | json_object_string_add(json_bfd, "type", "single hop"); | |
455 | } | |
456 | else | |
457 | { | |
458 | vty_out (vty, " %sBFD: Type: %s%s", (extra_space) ? " " : "", | |
459 | (multihop) ? "multi hop" : "single hop", VTY_NEWLINE); | |
460 | } | |
461 | ||
462 | bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
463 | bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
464 | ||
465 | if (use_json) | |
466 | json_object_object_add(json_obj, "peerBfdInfo", json_bfd); | |
467 | else | |
468 | vty_out (vty, "%s", VTY_NEWLINE); | |
469 | } | |
055c4dfc | 470 | |
471 | /* | |
472 | * bfd_client_sendmsg - Format and send a client register | |
473 | * command to Zebra to be forwarded to BFD | |
474 | */ | |
475 | void | |
476 | bfd_client_sendmsg (struct zclient *zclient, int command) | |
477 | { | |
478 | struct stream *s; | |
479 | int ret; | |
480 | ||
481 | /* Check socket. */ | |
482 | if (!zclient || zclient->sock < 0) | |
483 | { | |
588e90ec DS |
484 | if (bfd_debug) |
485 | zlog_debug ("%s: Can't send BFD client register, Zebra client not " | |
486 | "established", __FUNCTION__); | |
055c4dfc | 487 | return; |
488 | } | |
489 | ||
490 | s = zclient->obuf; | |
491 | stream_reset (s); | |
492 | zclient_create_header (s, command, VRF_DEFAULT); | |
493 | ||
494 | stream_putl(s, getpid()); | |
495 | ||
496 | stream_putw_at (s, 0, stream_get_endp (s)); | |
497 | ||
498 | ret = zclient_send_message(zclient); | |
499 | ||
500 | if (ret < 0) | |
501 | { | |
588e90ec | 502 | if (bfd_debug) |
ea8b7c71 RW |
503 | zlog_debug ("bfd_client_sendmsg %ld: zclient_send_message() failed", |
504 | (long) getpid()); | |
055c4dfc | 505 | return; |
506 | } | |
507 | ||
508 | return; | |
509 | } |