Line data Source code
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 :
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 :
30 : #include "filter.h"
31 : #include "if.h"
32 : #include "vrf.h"
33 :
34 : #include "bfd.h"
35 : #include "bfdd_nb.h"
36 : #include "bfddp_packet.h"
37 : #include "lib/version.h"
38 : #include "lib/command.h"
39 :
40 :
41 : /*
42 : * FRR related code.
43 : */
44 24 : DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon");
45 24 : DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory");
46 24 : DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data");
47 :
48 : /* Master of threads. */
49 : struct thread_master *master;
50 :
51 : /* BFDd privileges */
52 : static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW};
53 :
54 : /* BFD daemon information. */
55 : static struct frr_daemon_info bfdd_di;
56 :
57 64 : void socket_close(int *s)
58 : {
59 64 : if (*s <= 0)
60 : return;
61 :
62 56 : if (close(*s) != 0)
63 0 : zlog_err("%s: close(%d): (%d) %s", __func__, *s, errno,
64 : strerror(errno));
65 :
66 56 : *s = -1;
67 : }
68 :
69 0 : static void sigusr1_handler(void)
70 : {
71 0 : zlog_rotate();
72 0 : }
73 :
74 8 : static void sigterm_handler(void)
75 : {
76 8 : bglobal.bg_shutdown = true;
77 :
78 : /* Signalize shutdown. */
79 8 : frr_early_fini();
80 :
81 : /* Stop receiving message from zebra. */
82 8 : bfdd_zclient_stop();
83 :
84 : /* Shutdown controller to avoid receiving anymore commands. */
85 8 : control_shutdown();
86 :
87 : /* Shutdown and free all protocol related memory. */
88 8 : bfd_shutdown();
89 :
90 8 : bfd_vrf_terminate();
91 :
92 : /* Terminate and free() FRR related memory. */
93 8 : frr_fini();
94 :
95 8 : exit(0);
96 : }
97 :
98 0 : static void sighup_handler(void)
99 : {
100 0 : zlog_info("SIGHUP received");
101 :
102 : /* Reload config file. */
103 0 : vty_read_config(NULL, bfdd_di.config_file, config_default);
104 0 : }
105 :
106 : static struct frr_signal_t bfd_signals[] = {
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 : },
119 : {
120 : .signal = SIGHUP,
121 : .handler = &sighup_handler,
122 : },
123 : };
124 :
125 : static const struct frr_yang_module_info *const bfdd_yang_modules[] = {
126 : &frr_filter_info,
127 : &frr_interface_info,
128 : &frr_bfdd_info,
129 : &frr_vrf_info,
130 : };
131 :
132 8 : 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),
135 : .privs = &bglobal.bfdd_privs,
136 : .yang_modules = bfdd_yang_modules,
137 : .n_yang_modules = array_size(bfdd_yang_modules),
138 : );
139 :
140 : #define OPTION_CTLSOCK 1001
141 : #define OPTION_DPLANEADDR 2000
142 : static const struct option longopts[] = {
143 : {"bfdctl", required_argument, NULL, OPTION_CTLSOCK},
144 : {"dplaneaddr", required_argument, NULL, OPTION_DPLANEADDR},
145 : {0}
146 : };
147 :
148 :
149 : /*
150 : * BFD daemon related code.
151 : */
152 : struct bfd_global bglobal;
153 :
154 : const struct bfd_diag_str_list diag_list[] = {
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},
163 : {.str = NULL},
164 : };
165 :
166 : const struct bfd_state_str_list state_list[] = {
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},
171 : {.str = NULL},
172 : };
173 :
174 : static uint16_t
175 0 : parse_port(const char *str)
176 : {
177 0 : char *nulbyte;
178 0 : long rv;
179 :
180 0 : errno = 0;
181 0 : rv = strtol(str, &nulbyte, 10);
182 : /* No conversion performed. */
183 0 : if (rv == 0 && errno == EINVAL) {
184 0 : fprintf(stderr, "invalid BFD data plane address port: %s\n",
185 : str);
186 0 : exit(0);
187 : }
188 : /* Invalid number range. */
189 0 : if ((rv <= 0 || rv >= 65535) || errno == ERANGE) {
190 0 : fprintf(stderr, "invalid BFD data plane port range: %s\n",
191 : str);
192 0 : exit(0);
193 : }
194 : /* There was garbage at the end of the string. */
195 0 : if (*nulbyte != 0) {
196 0 : fprintf(stderr, "invalid BFD data plane port: %s\n",
197 : str);
198 0 : exit(0);
199 : }
200 :
201 0 : return (uint16_t)rv;
202 : }
203 :
204 : static void
205 0 : distributed_bfd_init(const char *arg)
206 : {
207 0 : char *sptr, *saux;
208 0 : bool is_client = false;
209 0 : size_t slen;
210 0 : socklen_t salen;
211 0 : char addr[64];
212 0 : char type[64];
213 0 : 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 0 : sptr = strchr(arg, ':');
221 0 : if (sptr == NULL) {
222 0 : fprintf(stderr, "invalid BFD data plane socket: %s\n", arg);
223 0 : exit(1);
224 : }
225 :
226 : /* Calculate type string length. */
227 0 : slen = (size_t)(sptr - arg);
228 :
229 : /* Copy the address part. */
230 0 : sptr++;
231 0 : strlcpy(addr, sptr, sizeof(addr));
232 :
233 : /* Copy type part. */
234 0 : strlcpy(type, arg, slen + 1);
235 :
236 : /* Reset address data. */
237 0 : memset(&sa, 0, sizeof(sa));
238 :
239 : /* Fill the address information. */
240 0 : if (strcmp(type, "unix") == 0 || strcmp(type, "unixc") == 0) {
241 0 : if (strcmp(type, "unixc") == 0)
242 0 : is_client = true;
243 :
244 0 : salen = sizeof(sa.sun);
245 0 : sa.sun.sun_family = AF_UNIX;
246 0 : strlcpy(sa.sun.sun_path, addr, sizeof(sa.sun.sun_path));
247 0 : } else if (strcmp(type, "ipv4") == 0 || strcmp(type, "ipv4c") == 0) {
248 0 : if (strcmp(type, "ipv4c") == 0)
249 0 : is_client = true;
250 :
251 0 : salen = sizeof(sa.sin);
252 0 : sa.sin.sin_family = AF_INET;
253 :
254 : /* Parse port if any. */
255 0 : sptr = strchr(addr, ':');
256 0 : if (sptr == NULL) {
257 0 : sa.sin.sin_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
258 : } else {
259 0 : *sptr = 0;
260 0 : sa.sin.sin_port = htons(parse_port(sptr + 1));
261 : }
262 :
263 0 : if (inet_pton(AF_INET, addr, &sa.sin.sin_addr) != 1)
264 0 : errx(1, "%s: inet_pton: invalid address %s", __func__,
265 : addr);
266 0 : } else if (strcmp(type, "ipv6") == 0 || strcmp(type, "ipv6c") == 0) {
267 0 : if (strcmp(type, "ipv6c") == 0)
268 0 : is_client = true;
269 :
270 0 : salen = sizeof(sa.sin6);
271 0 : sa.sin6.sin6_family = AF_INET6;
272 :
273 : /* Check for IPv6 enclosures '[]' */
274 0 : sptr = &addr[0];
275 0 : if (*sptr != '[')
276 0 : errx(1, "%s: invalid IPv6 address format: %s", __func__,
277 : addr);
278 :
279 0 : saux = strrchr(addr, ']');
280 0 : if (saux == NULL)
281 0 : errx(1, "%s: invalid IPv6 address format: %s", __func__,
282 : addr);
283 :
284 : /* Consume the '[]:' part. */
285 0 : slen = saux - sptr;
286 0 : memmove(addr, addr + 1, slen);
287 0 : addr[slen - 1] = 0;
288 :
289 : /* Parse port if any. */
290 0 : saux++;
291 0 : sptr = strrchr(saux, ':');
292 0 : if (sptr == NULL) {
293 0 : sa.sin6.sin6_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
294 : } else {
295 0 : *sptr = 0;
296 0 : sa.sin6.sin6_port = htons(parse_port(sptr + 1));
297 : }
298 :
299 0 : if (inet_pton(AF_INET6, addr, &sa.sin6.sin6_addr) != 1)
300 0 : errx(1, "%s: inet_pton: invalid address %s", __func__,
301 : addr);
302 : } else {
303 0 : fprintf(stderr, "invalid BFD data plane socket type: %s\n",
304 : type);
305 0 : exit(1);
306 : }
307 :
308 : /* Initialize BFD data plane listening socket. */
309 0 : bfd_dplane_init((struct sockaddr *)&sa, salen, is_client);
310 0 : }
311 :
312 8 : static void bg_init(void)
313 : {
314 8 : 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 :
327 8 : TAILQ_INIT(&bglobal.bg_bcslist);
328 8 : TAILQ_INIT(&bglobal.bg_obslist);
329 :
330 8 : memcpy(&bglobal.bfdd_privs, &bfdd_privs,
331 : sizeof(bfdd_privs));
332 8 : }
333 :
334 8 : int main(int argc, char *argv[])
335 : {
336 8 : char ctl_path[512], dplane_addr[512];
337 8 : bool ctlsockused = false;
338 8 : int opt;
339 :
340 8 : bglobal.bg_use_dplane = false;
341 :
342 : /* Initialize system sockets. */
343 8 : bg_init();
344 :
345 8 : frr_preinit(&bfdd_di, argc, argv);
346 8 : frr_opt_add("", longopts,
347 : " --bfdctl Specify bfdd control socket\n"
348 : " --dplaneaddr Specify BFD data plane address\n");
349 :
350 8 : snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET,
351 : "", "");
352 8 : while (true) {
353 8 : opt = frr_getopt(argc, argv, NULL);
354 8 : if (opt == EOF)
355 : break;
356 :
357 0 : switch (opt) {
358 0 : case OPTION_CTLSOCK:
359 0 : strlcpy(ctl_path, optarg, sizeof(ctl_path));
360 0 : ctlsockused = true;
361 0 : break;
362 0 : case OPTION_DPLANEADDR:
363 0 : strlcpy(dplane_addr, optarg, sizeof(dplane_addr));
364 0 : bglobal.bg_use_dplane = true;
365 0 : break;
366 :
367 0 : default:
368 0 : frr_help_exit(1);
369 : }
370 : }
371 :
372 8 : if (bfdd_di.pathspace && !ctlsockused)
373 0 : snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET,
374 : "/", bfdd_di.pathspace);
375 :
376 : /* Initialize FRR infrastructure. */
377 8 : master = frr_init();
378 :
379 : /* Initialize control socket. */
380 8 : control_init(ctl_path);
381 :
382 : /* Initialize BFD data structures. */
383 8 : bfd_initialize();
384 :
385 8 : bfd_vrf_init();
386 :
387 8 : access_list_init();
388 :
389 : /* Initialize zebra connection. */
390 8 : bfdd_zclient_init(&bglobal.bfdd_privs);
391 :
392 8 : thread_add_read(master, control_accept, NULL, bglobal.bg_csock,
393 : &bglobal.bg_csockev);
394 :
395 : /* Install commands. */
396 8 : bfdd_vty_init();
397 :
398 : /* read configuration file and daemonize */
399 8 : frr_config_fork();
400 :
401 : /* Initialize BFD data plane listening socket. */
402 8 : if (bglobal.bg_use_dplane)
403 0 : distributed_bfd_init(dplane_addr);
404 :
405 8 : frr_run(master);
406 : /* NOTREACHED */
407 :
408 0 : return 0;
409 : }
|