]>
Commit | Line | Data |
---|---|---|
e9e2c950 RZ |
1 | /* |
2 | * BFD daemon code | |
3 | * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF") | |
4 | * | |
5 | * FRR is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2, or (at your option) any | |
8 | * later version. | |
9 | * | |
10 | * FRR is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with FRR; see the file COPYING. If not, write to the Free | |
17 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
18 | * 02111-1307, USA. | |
19 | */ | |
20 | ||
21 | #include <zebra.h> | |
22 | ||
230aefe2 RZ |
23 | #include <arpa/inet.h> |
24 | #include <netinet/in.h> | |
25 | #include <sys/socket.h> | |
26 | #include <sys/un.h> | |
27 | ||
28 | #include <err.h> | |
29 | ||
9f95a33a | 30 | #include "filter.h" |
463d46a3 IR |
31 | #include "if.h" |
32 | #include "vrf.h" | |
9f95a33a | 33 | |
e9e2c950 | 34 | #include "bfd.h" |
6c574029 | 35 | #include "bfdd_nb.h" |
230aefe2 | 36 | #include "bfddp_packet.h" |
e9e2c950 | 37 | #include "lib/version.h" |
0bdeb5e5 | 38 | #include "lib/command.h" |
6ed84949 | 39 | |
e9e2c950 RZ |
40 | |
41 | /* | |
42 | * FRR related code. | |
43 | */ | |
bf8d3d6a DL |
44 | DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon"); |
45 | DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory"); | |
46 | DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data"); | |
e9e2c950 RZ |
47 | |
48 | /* Master of threads. */ | |
49 | struct thread_master *master; | |
50 | ||
51 | /* BFDd privileges */ | |
4e6b48d3 | 52 | static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW}; |
e9e2c950 | 53 | |
49cc9e7b RZ |
54 | /* BFD daemon information. */ |
55 | static struct frr_daemon_info bfdd_di; | |
56 | ||
e9e2c950 RZ |
57 | void socket_close(int *s) |
58 | { | |
59 | if (*s <= 0) | |
60 | return; | |
61 | ||
62 | if (close(*s) != 0) | |
259b64eb RZ |
63 | zlog_err("%s: close(%d): (%d) %s", __func__, *s, errno, |
64 | strerror(errno)); | |
e9e2c950 RZ |
65 | |
66 | *s = -1; | |
67 | } | |
68 | ||
69 | static void sigusr1_handler(void) | |
70 | { | |
71 | zlog_rotate(); | |
72 | } | |
73 | ||
74 | static void sigterm_handler(void) | |
75 | { | |
f3e1d224 RZ |
76 | bglobal.bg_shutdown = true; |
77 | ||
e9e2c950 RZ |
78 | /* Signalize shutdown. */ |
79 | frr_early_fini(); | |
80 | ||
d3af6147 RZ |
81 | /* Stop receiving message from zebra. */ |
82 | bfdd_zclient_stop(); | |
83 | ||
e9e2c950 RZ |
84 | /* Shutdown controller to avoid receiving anymore commands. */ |
85 | control_shutdown(); | |
86 | ||
87 | /* Shutdown and free all protocol related memory. */ | |
88 | bfd_shutdown(); | |
89 | ||
7bcadbae | 90 | bfd_vrf_terminate(); |
e9e2c950 RZ |
91 | |
92 | /* Terminate and free() FRR related memory. */ | |
93 | frr_fini(); | |
94 | ||
95 | exit(0); | |
96 | } | |
97 | ||
49cc9e7b RZ |
98 | static void sighup_handler(void) |
99 | { | |
100 | zlog_info("SIGHUP received"); | |
101 | ||
102 | /* Reload config file. */ | |
103 | vty_read_config(NULL, bfdd_di.config_file, config_default); | |
104 | } | |
105 | ||
7cc91e67 | 106 | static struct frr_signal_t bfd_signals[] = { |
e9e2c950 RZ |
107 | { |
108 | .signal = SIGUSR1, | |
109 | .handler = &sigusr1_handler, | |
110 | }, | |
111 | { | |
112 | .signal = SIGTERM, | |
113 | .handler = &sigterm_handler, | |
114 | }, | |
115 | { | |
116 | .signal = SIGINT, | |
117 | .handler = &sigterm_handler, | |
118 | }, | |
49cc9e7b RZ |
119 | { |
120 | .signal = SIGHUP, | |
121 | .handler = &sighup_handler, | |
122 | }, | |
e9e2c950 RZ |
123 | }; |
124 | ||
0d8c7a26 | 125 | static const struct frr_yang_module_info *const bfdd_yang_modules[] = { |
c2aab693 | 126 | &frr_filter_info, |
adc26455 RZ |
127 | &frr_interface_info, |
128 | &frr_bfdd_info, | |
6fd8972a | 129 | &frr_vrf_info, |
adc26455 RZ |
130 | }; |
131 | ||
e9e2c950 RZ |
132 | FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617, |
133 | .proghelp = "Implementation of the BFD protocol.", | |
134 | .signals = bfd_signals, .n_signals = array_size(bfd_signals), | |
adc26455 RZ |
135 | .privs = &bglobal.bfdd_privs, |
136 | .yang_modules = bfdd_yang_modules, | |
80413c20 DL |
137 | .n_yang_modules = array_size(bfdd_yang_modules), |
138 | ); | |
e9e2c950 RZ |
139 | |
140 | #define OPTION_CTLSOCK 1001 | |
230aefe2 | 141 | #define OPTION_DPLANEADDR 2000 |
2b64873d | 142 | static const struct option longopts[] = { |
e9e2c950 | 143 | {"bfdctl", required_argument, NULL, OPTION_CTLSOCK}, |
230aefe2 | 144 | {"dplaneaddr", required_argument, NULL, OPTION_DPLANEADDR}, |
e9e2c950 RZ |
145 | {0} |
146 | }; | |
147 | ||
148 | ||
149 | /* | |
150 | * BFD daemon related code. | |
151 | */ | |
152 | struct bfd_global bglobal; | |
153 | ||
2b64873d | 154 | const struct bfd_diag_str_list diag_list[] = { |
40675ea9 RZ |
155 | {.str = "control-expired", .type = BD_CONTROL_EXPIRED}, |
156 | {.str = "echo-failed", .type = BD_ECHO_FAILED}, | |
157 | {.str = "neighbor-down", .type = BD_NEIGHBOR_DOWN}, | |
158 | {.str = "forwarding-reset", .type = BD_FORWARDING_RESET}, | |
159 | {.str = "path-down", .type = BD_PATH_DOWN}, | |
160 | {.str = "concatenated-path-down", .type = BD_CONCATPATH_DOWN}, | |
161 | {.str = "administratively-down", .type = BD_ADMIN_DOWN}, | |
162 | {.str = "reverse-concat-path-down", .type = BD_REVCONCATPATH_DOWN}, | |
e9e2c950 RZ |
163 | {.str = NULL}, |
164 | }; | |
165 | ||
2b64873d | 166 | const struct bfd_state_str_list state_list[] = { |
40675ea9 RZ |
167 | {.str = "admin-down", .type = PTM_BFD_ADM_DOWN}, |
168 | {.str = "down", .type = PTM_BFD_DOWN}, | |
169 | {.str = "init", .type = PTM_BFD_INIT}, | |
170 | {.str = "up", .type = PTM_BFD_UP}, | |
e9e2c950 RZ |
171 | {.str = NULL}, |
172 | }; | |
173 | ||
230aefe2 RZ |
174 | static uint16_t |
175 | parse_port(const char *str) | |
176 | { | |
177 | char *nulbyte; | |
178 | long rv; | |
179 | ||
180 | errno = 0; | |
181 | rv = strtol(str, &nulbyte, 10); | |
182 | /* No conversion performed. */ | |
183 | if (rv == 0 && errno == EINVAL) { | |
184 | fprintf(stderr, "invalid BFD data plane address port: %s\n", | |
185 | str); | |
186 | exit(0); | |
187 | } | |
188 | /* Invalid number range. */ | |
189 | if ((rv <= 0 || rv >= 65535) || errno == ERANGE) { | |
190 | fprintf(stderr, "invalid BFD data plane port range: %s\n", | |
191 | str); | |
192 | exit(0); | |
193 | } | |
194 | /* There was garbage at the end of the string. */ | |
195 | if (*nulbyte != 0) { | |
196 | fprintf(stderr, "invalid BFD data plane port: %s\n", | |
197 | str); | |
198 | exit(0); | |
199 | } | |
200 | ||
201 | return (uint16_t)rv; | |
202 | } | |
203 | ||
204 | static void | |
205 | distributed_bfd_init(const char *arg) | |
206 | { | |
207 | char *sptr, *saux; | |
6655b43d | 208 | bool is_client = false; |
230aefe2 RZ |
209 | size_t slen; |
210 | socklen_t salen; | |
211 | char addr[64]; | |
212 | char type[64]; | |
213 | union { | |
214 | struct sockaddr_in sin; | |
215 | struct sockaddr_in6 sin6; | |
216 | struct sockaddr_un sun; | |
217 | } sa; | |
218 | ||
219 | /* Basic parsing: find ':' to figure out type part and address part. */ | |
220 | sptr = strchr(arg, ':'); | |
221 | if (sptr == NULL) { | |
222 | fprintf(stderr, "invalid BFD data plane socket: %s\n", arg); | |
223 | exit(1); | |
224 | } | |
225 | ||
226 | /* Calculate type string length. */ | |
227 | slen = (size_t)(sptr - arg); | |
228 | ||
229 | /* Copy the address part. */ | |
230 | sptr++; | |
231 | strlcpy(addr, sptr, sizeof(addr)); | |
232 | ||
233 | /* Copy type part. */ | |
234 | strlcpy(type, arg, slen + 1); | |
235 | ||
236 | /* Reset address data. */ | |
237 | memset(&sa, 0, sizeof(sa)); | |
238 | ||
239 | /* Fill the address information. */ | |
6655b43d RZ |
240 | if (strcmp(type, "unix") == 0 || strcmp(type, "unixc") == 0) { |
241 | if (strcmp(type, "unixc") == 0) | |
242 | is_client = true; | |
243 | ||
230aefe2 RZ |
244 | salen = sizeof(sa.sun); |
245 | sa.sun.sun_family = AF_UNIX; | |
246 | strlcpy(sa.sun.sun_path, addr, sizeof(sa.sun.sun_path)); | |
6655b43d RZ |
247 | } else if (strcmp(type, "ipv4") == 0 || strcmp(type, "ipv4c") == 0) { |
248 | if (strcmp(type, "ipv4c") == 0) | |
249 | is_client = true; | |
250 | ||
230aefe2 RZ |
251 | salen = sizeof(sa.sin); |
252 | sa.sin.sin_family = AF_INET; | |
253 | ||
254 | /* Parse port if any. */ | |
255 | sptr = strchr(addr, ':'); | |
256 | if (sptr == NULL) { | |
257 | sa.sin.sin_port = htons(BFD_DATA_PLANE_DEFAULT_PORT); | |
258 | } else { | |
259 | *sptr = 0; | |
260 | sa.sin.sin_port = htons(parse_port(sptr + 1)); | |
261 | } | |
262 | ||
263 | if (inet_pton(AF_INET, addr, &sa.sin.sin_addr) != 1) | |
264 | errx(1, "%s: inet_pton: invalid address %s", __func__, | |
265 | addr); | |
6655b43d RZ |
266 | } else if (strcmp(type, "ipv6") == 0 || strcmp(type, "ipv6c") == 0) { |
267 | if (strcmp(type, "ipv6c") == 0) | |
268 | is_client = true; | |
269 | ||
230aefe2 RZ |
270 | salen = sizeof(sa.sin6); |
271 | sa.sin6.sin6_family = AF_INET6; | |
272 | ||
273 | /* Check for IPv6 enclosures '[]' */ | |
274 | sptr = &addr[0]; | |
275 | if (*sptr != '[') | |
276 | errx(1, "%s: invalid IPv6 address format: %s", __func__, | |
277 | addr); | |
278 | ||
279 | saux = strrchr(addr, ']'); | |
280 | if (saux == NULL) | |
281 | errx(1, "%s: invalid IPv6 address format: %s", __func__, | |
282 | addr); | |
283 | ||
284 | /* Consume the '[]:' part. */ | |
285 | slen = saux - sptr; | |
286 | memmove(addr, addr + 1, slen); | |
287 | addr[slen - 1] = 0; | |
288 | ||
289 | /* Parse port if any. */ | |
290 | saux++; | |
291 | sptr = strrchr(saux, ':'); | |
292 | if (sptr == NULL) { | |
293 | sa.sin6.sin6_port = htons(BFD_DATA_PLANE_DEFAULT_PORT); | |
294 | } else { | |
295 | *sptr = 0; | |
296 | sa.sin6.sin6_port = htons(parse_port(sptr + 1)); | |
297 | } | |
298 | ||
299 | if (inet_pton(AF_INET6, addr, &sa.sin6.sin6_addr) != 1) | |
300 | errx(1, "%s: inet_pton: invalid address %s", __func__, | |
301 | addr); | |
302 | } else { | |
303 | fprintf(stderr, "invalid BFD data plane socket type: %s\n", | |
304 | type); | |
305 | exit(1); | |
306 | } | |
307 | ||
308 | /* Initialize BFD data plane listening socket. */ | |
6655b43d | 309 | bfd_dplane_init((struct sockaddr *)&sa, salen, is_client); |
230aefe2 | 310 | } |
e9e2c950 RZ |
311 | |
312 | static void bg_init(void) | |
313 | { | |
f21536d2 PG |
314 | struct zebra_privs_t bfdd_privs = { |
315 | #if defined(FRR_USER) && defined(FRR_GROUP) | |
316 | .user = FRR_USER, | |
317 | .group = FRR_GROUP, | |
318 | #endif | |
319 | #if defined(VTY_GROUP) | |
320 | .vty_group = VTY_GROUP, | |
321 | #endif | |
322 | .caps_p = _caps_p, | |
323 | .cap_num_p = array_size(_caps_p), | |
324 | .cap_num_i = 0, | |
325 | }; | |
326 | ||
e9e2c950 | 327 | TAILQ_INIT(&bglobal.bg_bcslist); |
d245e522 | 328 | TAILQ_INIT(&bglobal.bg_obslist); |
f21536d2 PG |
329 | |
330 | memcpy(&bglobal.bfdd_privs, &bfdd_privs, | |
331 | sizeof(bfdd_privs)); | |
e9e2c950 RZ |
332 | } |
333 | ||
334 | int main(int argc, char *argv[]) | |
335 | { | |
230aefe2 | 336 | char ctl_path[512], dplane_addr[512]; |
89277ebf | 337 | bool ctlsockused = false; |
e9e2c950 RZ |
338 | int opt; |
339 | ||
f21536d2 PG |
340 | /* Initialize system sockets. */ |
341 | bg_init(); | |
342 | ||
e9e2c950 RZ |
343 | frr_preinit(&bfdd_di, argc, argv); |
344 | frr_opt_add("", longopts, | |
230aefe2 RZ |
345 | " --bfdctl Specify bfdd control socket\n" |
346 | " --dplaneaddr Specify BFD data plane address\n"); | |
e9e2c950 | 347 | |
89277ebf DS |
348 | snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, |
349 | "", ""); | |
e9e2c950 RZ |
350 | while (true) { |
351 | opt = frr_getopt(argc, argv, NULL); | |
352 | if (opt == EOF) | |
353 | break; | |
354 | ||
355 | switch (opt) { | |
356 | case OPTION_CTLSOCK: | |
89277ebf DS |
357 | strlcpy(ctl_path, optarg, sizeof(ctl_path)); |
358 | ctlsockused = true; | |
e9e2c950 | 359 | break; |
230aefe2 RZ |
360 | case OPTION_DPLANEADDR: |
361 | strlcpy(dplane_addr, optarg, sizeof(dplane_addr)); | |
362 | bglobal.bg_use_dplane = true; | |
363 | break; | |
e9e2c950 RZ |
364 | |
365 | default: | |
366 | frr_help_exit(1); | |
e9e2c950 RZ |
367 | } |
368 | } | |
369 | ||
89277ebf DS |
370 | if (bfdd_di.pathspace && !ctlsockused) |
371 | snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, | |
372 | "/", bfdd_di.pathspace); | |
373 | ||
d85b048d DL |
374 | /* Initialize FRR infrastructure. */ |
375 | master = frr_init(); | |
e9e2c950 | 376 | |
e9e2c950 RZ |
377 | /* Initialize control socket. */ |
378 | control_init(ctl_path); | |
379 | ||
e9e2c950 RZ |
380 | /* Initialize BFD data structures. */ |
381 | bfd_initialize(); | |
382 | ||
9fc0bc5c PG |
383 | bfd_vrf_init(); |
384 | ||
9f95a33a DS |
385 | access_list_init(); |
386 | ||
d3af6147 | 387 | /* Initialize zebra connection. */ |
f21536d2 | 388 | bfdd_zclient_init(&bglobal.bfdd_privs); |
d3af6147 | 389 | |
e9e2c950 RZ |
390 | thread_add_read(master, control_accept, NULL, bglobal.bg_csock, |
391 | &bglobal.bg_csockev); | |
392 | ||
c2f29cf3 RZ |
393 | /* Install commands. */ |
394 | bfdd_vty_init(); | |
395 | ||
e9e2c950 RZ |
396 | /* read configuration file and daemonize */ |
397 | frr_config_fork(); | |
398 | ||
230aefe2 RZ |
399 | /* Initialize BFD data plane listening socket. */ |
400 | if (bglobal.bg_use_dplane) | |
401 | distributed_bfd_init(dplane_addr); | |
402 | ||
e9e2c950 RZ |
403 | frr_run(master); |
404 | /* NOTREACHED */ | |
405 | ||
406 | return 0; | |
407 | } |