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