]>
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 | ||
c17faa4b QY |
37 | static int bfd_debug = 0; |
38 | static struct bfd_gbl bfd_gbl; | |
567b877d | 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 | { |
e1b36e13 | 77 | XFREE(MTYPE_BFD_INFO, *bfd_info); |
7f342629 DS |
78 | } |
79 | ||
80 | /* | |
81 | * bfd_validate_param - Validate the BFD paramter information. | |
82 | */ | |
d62a17ae | 83 | int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str, |
d7c0a89a QY |
84 | const char *tx_str, uint8_t *dm_val, uint32_t *rx_val, |
85 | uint32_t *tx_val) | |
7f342629 | 86 | { |
d62a17ae | 87 | *dm_val = strtoul(dm_str, NULL, 10); |
88 | *rx_val = strtoul(rx_str, NULL, 10); | |
89 | *tx_val = strtoul(tx_str, NULL, 10); | |
90 | return CMD_SUCCESS; | |
7f342629 DS |
91 | } |
92 | ||
93 | /* | |
94 | * bfd_set_param - Set the configured BFD paramter values | |
95 | */ | |
d7c0a89a QY |
96 | void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, |
97 | uint8_t detect_mult, int defaults, int *command) | |
7f342629 | 98 | { |
d62a17ae | 99 | if (!*bfd_info) { |
100 | *bfd_info = bfd_info_create(); | |
101 | *command = ZEBRA_BFD_DEST_REGISTER; | |
102 | } else { | |
103 | if (((*bfd_info)->required_min_rx != min_rx) | |
104 | || ((*bfd_info)->desired_min_tx != min_tx) | |
105 | || ((*bfd_info)->detect_mult != detect_mult)) | |
106 | *command = ZEBRA_BFD_DEST_UPDATE; | |
107 | } | |
108 | ||
109 | if (*command) { | |
110 | (*bfd_info)->required_min_rx = min_rx; | |
111 | (*bfd_info)->desired_min_tx = min_tx; | |
112 | (*bfd_info)->detect_mult = detect_mult; | |
113 | } | |
114 | ||
115 | if (!defaults) | |
116 | SET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG); | |
117 | else | |
118 | UNSET_FLAG((*bfd_info)->flags, BFD_FLAG_PARAM_CFG); | |
7f342629 DS |
119 | } |
120 | ||
121 | /* | |
122 | * bfd_peer_sendmsg - Format and send a peer register/Unregister | |
123 | * command to Zebra to be forwarded to BFD | |
124 | */ | |
d62a17ae | 125 | void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, |
126 | int family, void *dst_ip, void *src_ip, char *if_name, | |
9beff0bd PG |
127 | int ttl, int multihop, int cbit, int command, |
128 | int set_flag, vrf_id_t vrf_id) | |
7f342629 | 129 | { |
d62a17ae | 130 | struct stream *s; |
131 | int ret; | |
132 | int len; | |
133 | ||
0437e105 | 134 | /* Individual reg/dereg messages are suppressed during shutdown. */ |
d62a17ae | 135 | if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) { |
136 | if (bfd_debug) | |
137 | zlog_debug( | |
138 | "%s: Suppressing BFD peer reg/dereg messages", | |
15569c58 | 139 | __func__); |
d62a17ae | 140 | return; |
141 | } | |
142 | ||
143 | /* Check socket. */ | |
144 | if (!zclient || zclient->sock < 0) { | |
145 | if (bfd_debug) | |
146 | zlog_debug( | |
147 | "%s: Can't send BFD peer register, Zebra client not " | |
148 | "established", | |
15569c58 | 149 | __func__); |
d62a17ae | 150 | return; |
151 | } | |
152 | ||
153 | s = zclient->obuf; | |
154 | stream_reset(s); | |
155 | zclient_create_header(s, command, vrf_id); | |
156 | ||
157 | stream_putl(s, getpid()); | |
158 | ||
159 | stream_putw(s, family); | |
160 | switch (family) { | |
161 | case AF_INET: | |
162 | stream_put_in_addr(s, (struct in_addr *)dst_ip); | |
163 | break; | |
164 | case AF_INET6: | |
165 | stream_put(s, dst_ip, 16); | |
166 | break; | |
167 | default: | |
168 | break; | |
169 | } | |
170 | ||
171 | if (command != ZEBRA_BFD_DEST_DEREGISTER) { | |
172 | stream_putl(s, bfd_info->required_min_rx); | |
173 | stream_putl(s, bfd_info->desired_min_tx); | |
174 | stream_putc(s, bfd_info->detect_mult); | |
175 | } | |
176 | ||
177 | if (multihop) { | |
178 | stream_putc(s, 1); | |
179 | /* Multi-hop destination send the source IP address to BFD */ | |
180 | if (src_ip) { | |
181 | stream_putw(s, family); | |
182 | switch (family) { | |
183 | case AF_INET: | |
184 | stream_put_in_addr(s, (struct in_addr *)src_ip); | |
185 | break; | |
186 | case AF_INET6: | |
187 | stream_put(s, src_ip, 16); | |
188 | break; | |
189 | default: | |
190 | break; | |
191 | } | |
192 | } | |
193 | stream_putc(s, ttl); | |
194 | } else { | |
195 | stream_putc(s, 0); | |
196 | if ((family == AF_INET6) && (src_ip)) { | |
197 | stream_putw(s, family); | |
198 | stream_put(s, src_ip, 16); | |
199 | } | |
200 | if (if_name) { | |
201 | len = strlen(if_name); | |
202 | stream_putc(s, len); | |
203 | stream_put(s, if_name, len); | |
204 | } else { | |
205 | stream_putc(s, 0); | |
206 | } | |
207 | } | |
9beff0bd PG |
208 | /* cbit */ |
209 | if (cbit) | |
210 | stream_putc(s, 1); | |
211 | else | |
212 | stream_putc(s, 0); | |
d62a17ae | 213 | |
214 | stream_putw_at(s, 0, stream_get_endp(s)); | |
215 | ||
216 | ret = zclient_send_message(zclient); | |
217 | ||
218 | if (ret < 0) { | |
219 | if (bfd_debug) | |
220 | zlog_debug( | |
221 | "bfd_peer_sendmsg: zclient_send_message() failed"); | |
222 | return; | |
223 | } | |
224 | ||
225 | if (set_flag) { | |
226 | if (command == ZEBRA_BFD_DEST_REGISTER) | |
227 | SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
228 | else if (command == ZEBRA_BFD_DEST_DEREGISTER) | |
229 | UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); | |
230 | } | |
231 | ||
232 | return; | |
7f342629 DS |
233 | } |
234 | ||
235 | /* | |
236 | * bfd_get_command_dbg_str - Convert command to a debug string. | |
237 | */ | |
d62a17ae | 238 | const char *bfd_get_command_dbg_str(int command) |
7f342629 | 239 | { |
d62a17ae | 240 | switch (command) { |
241 | case ZEBRA_BFD_DEST_REGISTER: | |
242 | return "Register"; | |
243 | case ZEBRA_BFD_DEST_DEREGISTER: | |
244 | return "Deregister"; | |
245 | case ZEBRA_BFD_DEST_UPDATE: | |
246 | return "Update"; | |
247 | default: | |
248 | return "Unknown"; | |
249 | } | |
7f342629 DS |
250 | } |
251 | ||
252 | /* | |
253 | * bfd_get_peer_info - Extract the Peer information for which the BFD session | |
254 | * went down from the message sent from Zebra to clients. | |
255 | */ | |
d62a17ae | 256 | struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, |
257 | struct prefix *sp, int *status, | |
9beff0bd | 258 | int *remote_cbit, |
d62a17ae | 259 | vrf_id_t vrf_id) |
7f342629 | 260 | { |
d62a17ae | 261 | unsigned int ifindex; |
262 | struct interface *ifp = NULL; | |
263 | int plen; | |
9beff0bd | 264 | int local_remote_cbit; |
d62a17ae | 265 | |
266 | /* Get interface index. */ | |
267 | ifindex = stream_getl(s); | |
268 | ||
269 | /* Lookup index. */ | |
270 | if (ifindex != 0) { | |
271 | ifp = if_lookup_by_index(ifindex, vrf_id); | |
272 | if (ifp == NULL) { | |
273 | if (bfd_debug) | |
274 | zlog_debug( | |
275 | "zebra_interface_bfd_read: " | |
276 | "Can't find interface by ifindex: %d ", | |
277 | ifindex); | |
278 | return NULL; | |
279 | } | |
280 | } | |
281 | ||
282 | /* Fetch destination address. */ | |
283 | dp->family = stream_getc(s); | |
284 | ||
285 | plen = prefix_blen(dp); | |
286 | stream_get(&dp->u.prefix, s, plen); | |
287 | dp->prefixlen = stream_getc(s); | |
288 | ||
289 | /* Get BFD status. */ | |
290 | *status = stream_getl(s); | |
291 | ||
292 | if (sp) { | |
293 | sp->family = stream_getc(s); | |
294 | ||
295 | plen = prefix_blen(sp); | |
296 | stream_get(&sp->u.prefix, s, plen); | |
297 | sp->prefixlen = stream_getc(s); | |
298 | } | |
9beff0bd PG |
299 | local_remote_cbit = stream_getc(s); |
300 | if (remote_cbit) | |
301 | *remote_cbit = local_remote_cbit; | |
d62a17ae | 302 | return ifp; |
7f342629 | 303 | } |
68fe91d6 | 304 | |
305 | /* | |
306 | * bfd_get_status_str - Convert BFD status to a display string. | |
307 | */ | |
d62a17ae | 308 | const char *bfd_get_status_str(int status) |
68fe91d6 | 309 | { |
d62a17ae | 310 | switch (status) { |
311 | case BFD_STATUS_DOWN: | |
312 | return "Down"; | |
313 | case BFD_STATUS_UP: | |
314 | return "Up"; | |
7555dc61 S |
315 | case BFD_STATUS_ADMIN_DOWN: |
316 | return "Admin Down"; | |
d62a17ae | 317 | case BFD_STATUS_UNKNOWN: |
318 | default: | |
319 | return "Unknown"; | |
320 | } | |
68fe91d6 | 321 | } |
322 | ||
323 | /* | |
324 | * bfd_last_update - Calculate the last BFD update time and convert it | |
325 | * into a dd:hh:mm:ss display format. | |
326 | */ | |
d62a17ae | 327 | static void bfd_last_update(time_t last_update, char *buf, size_t len) |
68fe91d6 | 328 | { |
d62a17ae | 329 | time_t curr; |
330 | time_t diff; | |
a2700b50 | 331 | struct tm tm; |
d62a17ae | 332 | struct timeval tv; |
333 | ||
334 | /* If no BFD satatus update has ever been received, print `never'. */ | |
335 | if (last_update == 0) { | |
336 | snprintf(buf, len, "never"); | |
337 | return; | |
338 | } | |
339 | ||
340 | /* Get current time. */ | |
341 | monotime(&tv); | |
342 | curr = tv.tv_sec; | |
343 | diff = curr - last_update; | |
a2700b50 | 344 | gmtime_r(&diff, &tm); |
d62a17ae | 345 | |
a2700b50 MS |
346 | snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour, |
347 | tm.tm_min, tm.tm_sec); | |
68fe91d6 | 348 | } |
349 | ||
350 | /* | |
351 | * bfd_show_param - Show the BFD parameter information. | |
352 | */ | |
d62a17ae | 353 | void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag, |
9f049418 | 354 | int extra_space, bool use_json, json_object *json_obj) |
68fe91d6 | 355 | { |
d62a17ae | 356 | json_object *json_bfd = NULL; |
357 | ||
358 | if (!bfd_info) | |
359 | return; | |
360 | ||
361 | if (use_json) { | |
362 | if (bfd_tag) | |
363 | json_bfd = json_object_new_object(); | |
364 | else | |
365 | json_bfd = json_obj; | |
366 | ||
367 | json_object_int_add(json_bfd, "detectMultiplier", | |
368 | bfd_info->detect_mult); | |
369 | json_object_int_add(json_bfd, "rxMinInterval", | |
370 | bfd_info->required_min_rx); | |
371 | json_object_int_add(json_bfd, "txMinInterval", | |
372 | bfd_info->desired_min_tx); | |
373 | if (bfd_tag) | |
374 | json_object_object_add(json_obj, "peerBfdInfo", | |
375 | json_bfd); | |
376 | } else { | |
377 | vty_out(vty, | |
b077b2e6 | 378 | " %s%sDetect Multiplier: %d, Min Rx interval: %d," |
d62a17ae | 379 | " Min Tx interval: %d\n", |
380 | (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ", | |
381 | bfd_info->detect_mult, bfd_info->required_min_rx, | |
382 | bfd_info->desired_min_tx); | |
383 | } | |
68fe91d6 | 384 | } |
385 | ||
386 | /* | |
387 | * bfd_show_status - Show the BFD status information. | |
388 | */ | |
d62a17ae | 389 | static void bfd_show_status(struct vty *vty, struct bfd_info *bfd_info, |
9f049418 | 390 | int bfd_tag, int extra_space, bool use_json, |
d62a17ae | 391 | json_object *json_bfd) |
68fe91d6 | 392 | { |
d62a17ae | 393 | char time_buf[32]; |
394 | ||
395 | if (!bfd_info) | |
396 | return; | |
397 | ||
398 | bfd_last_update(bfd_info->last_update, time_buf, 32); | |
399 | if (use_json) { | |
400 | json_object_string_add(json_bfd, "status", | |
401 | bfd_get_status_str(bfd_info->status)); | |
402 | json_object_string_add(json_bfd, "lastUpdate", time_buf); | |
403 | } else { | |
404 | vty_out(vty, " %s%sStatus: %s, Last update: %s\n", | |
405 | (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ", | |
406 | bfd_get_status_str(bfd_info->status), time_buf); | |
407 | } | |
68fe91d6 | 408 | } |
409 | ||
410 | /* | |
411 | * bfd_show_info - Show the BFD information. | |
412 | */ | |
d62a17ae | 413 | void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, |
9f049418 | 414 | int extra_space, bool use_json, json_object *json_obj) |
68fe91d6 | 415 | { |
d62a17ae | 416 | json_object *json_bfd = NULL; |
417 | ||
418 | if (!bfd_info) | |
419 | return; | |
420 | ||
421 | if (use_json) { | |
422 | json_bfd = json_object_new_object(); | |
423 | if (multihop) | |
424 | json_object_string_add(json_bfd, "type", "multi hop"); | |
425 | else | |
426 | json_object_string_add(json_bfd, "type", "single hop"); | |
427 | } else { | |
428 | vty_out(vty, " %sBFD: Type: %s\n", (extra_space) ? " " : "", | |
429 | (multihop) ? "multi hop" : "single hop"); | |
430 | } | |
431 | ||
432 | bfd_show_param(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
433 | bfd_show_status(vty, bfd_info, 0, extra_space, use_json, json_bfd); | |
434 | ||
435 | if (use_json) | |
436 | json_object_object_add(json_obj, "peerBfdInfo", json_bfd); | |
437 | else | |
438 | vty_out(vty, "\n"); | |
68fe91d6 | 439 | } |
055c4dfc | 440 | |
441 | /* | |
442 | * bfd_client_sendmsg - Format and send a client register | |
443 | * command to Zebra to be forwarded to BFD | |
444 | */ | |
0945d5ed PG |
445 | void bfd_client_sendmsg(struct zclient *zclient, int command, |
446 | vrf_id_t vrf_id) | |
055c4dfc | 447 | { |
d62a17ae | 448 | struct stream *s; |
449 | int ret; | |
450 | ||
451 | /* Check socket. */ | |
452 | if (!zclient || zclient->sock < 0) { | |
453 | if (bfd_debug) | |
454 | zlog_debug( | |
455 | "%s: Can't send BFD client register, Zebra client not " | |
456 | "established", | |
15569c58 | 457 | __func__); |
d62a17ae | 458 | return; |
459 | } | |
460 | ||
461 | s = zclient->obuf; | |
462 | stream_reset(s); | |
0945d5ed | 463 | zclient_create_header(s, command, vrf_id); |
d62a17ae | 464 | |
465 | stream_putl(s, getpid()); | |
466 | ||
467 | stream_putw_at(s, 0, stream_get_endp(s)); | |
468 | ||
469 | ret = zclient_send_message(zclient); | |
470 | ||
471 | if (ret < 0) { | |
472 | if (bfd_debug) | |
473 | zlog_debug( | |
474 | "bfd_client_sendmsg %ld: zclient_send_message() failed", | |
475 | (long)getpid()); | |
476 | return; | |
477 | } | |
478 | ||
479 | return; | |
055c4dfc | 480 | } |