summaryrefslogtreecommitdiffstats
path: root/bfdd/bfdd.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfdd/bfdd.c')
-rw-r--r--bfdd/bfdd.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
new file mode 100644
index 0000000..95066b9
--- /dev/null
+++ b/bfdd/bfdd.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BFD daemon code
+ * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#include <zebra.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <err.h>
+
+#include "filter.h"
+#include "if.h"
+#include "vrf.h"
+
+#include "bfd.h"
+#include "bfdd_nb.h"
+#include "bfddp_packet.h"
+#include "lib/version.h"
+#include "lib/command.h"
+
+
+/*
+ * FRR related code.
+ */
+DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon");
+DEFINE_MTYPE(BFDD, BFDD_CONTROL, "control socket memory");
+DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "control notification data");
+
+/* Master of threads. */
+struct event_loop *master;
+
+/* BFDd privileges */
+static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW};
+
+/* BFD daemon information. */
+static struct frr_daemon_info bfdd_di;
+
+void socket_close(int *s)
+{
+ if (*s <= 0)
+ return;
+
+ if (close(*s) != 0)
+ zlog_err("%s: close(%d): (%d) %s", __func__, *s, errno,
+ strerror(errno));
+
+ *s = -1;
+}
+
+static void sigusr1_handler(void)
+{
+ zlog_rotate();
+}
+
+static void sigterm_handler(void)
+{
+ bglobal.bg_shutdown = true;
+
+ /* Signalize shutdown. */
+ frr_early_fini();
+
+ /* Stop receiving message from zebra. */
+ bfdd_zclient_stop();
+
+ /* Shutdown controller to avoid receiving anymore commands. */
+ control_shutdown();
+
+ /* Shutdown and free all protocol related memory. */
+ bfd_shutdown();
+
+ bfd_vrf_terminate();
+
+ /* Terminate and free() FRR related memory. */
+ frr_fini();
+
+ exit(0);
+}
+
+static void sighup_handler(void)
+{
+ zlog_info("SIGHUP received");
+
+ /* Reload config file. */
+ vty_read_config(NULL, bfdd_di.config_file, config_default);
+}
+
+static struct frr_signal_t bfd_signals[] = {
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1_handler,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigterm_handler,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigterm_handler,
+ },
+ {
+ .signal = SIGHUP,
+ .handler = &sighup_handler,
+ },
+};
+
+static const struct frr_yang_module_info *const bfdd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_bfdd_info,
+ &frr_vrf_info,
+};
+
+FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617,
+ .proghelp = "Implementation of the BFD protocol.",
+ .signals = bfd_signals, .n_signals = array_size(bfd_signals),
+ .privs = &bglobal.bfdd_privs,
+ .yang_modules = bfdd_yang_modules,
+ .n_yang_modules = array_size(bfdd_yang_modules),
+);
+
+#define OPTION_CTLSOCK 1001
+#define OPTION_DPLANEADDR 2000
+static const struct option longopts[] = {
+ {"bfdctl", required_argument, NULL, OPTION_CTLSOCK},
+ {"dplaneaddr", required_argument, NULL, OPTION_DPLANEADDR},
+ {0}
+};
+
+
+/*
+ * BFD daemon related code.
+ */
+struct bfd_global bglobal;
+
+const struct bfd_diag_str_list diag_list[] = {
+ {.str = "control-expired", .type = BD_CONTROL_EXPIRED},
+ {.str = "echo-failed", .type = BD_ECHO_FAILED},
+ {.str = "neighbor-down", .type = BD_NEIGHBOR_DOWN},
+ {.str = "forwarding-reset", .type = BD_FORWARDING_RESET},
+ {.str = "path-down", .type = BD_PATH_DOWN},
+ {.str = "concatenated-path-down", .type = BD_CONCATPATH_DOWN},
+ {.str = "administratively-down", .type = BD_ADMIN_DOWN},
+ {.str = "reverse-concat-path-down", .type = BD_REVCONCATPATH_DOWN},
+ {.str = NULL},
+};
+
+const struct bfd_state_str_list state_list[] = {
+ {.str = "admin-down", .type = PTM_BFD_ADM_DOWN},
+ {.str = "down", .type = PTM_BFD_DOWN},
+ {.str = "init", .type = PTM_BFD_INIT},
+ {.str = "up", .type = PTM_BFD_UP},
+ {.str = NULL},
+};
+
+static uint16_t
+parse_port(const char *str)
+{
+ char *nulbyte;
+ long rv;
+
+ errno = 0;
+ rv = strtol(str, &nulbyte, 10);
+ /* No conversion performed. */
+ if (rv == 0 && errno == EINVAL) {
+ fprintf(stderr, "invalid BFD data plane address port: %s\n",
+ str);
+ exit(0);
+ }
+ /* Invalid number range. */
+ if ((rv <= 0 || rv >= 65535) || errno == ERANGE) {
+ fprintf(stderr, "invalid BFD data plane port range: %s\n",
+ str);
+ exit(0);
+ }
+ /* There was garbage at the end of the string. */
+ if (*nulbyte != 0) {
+ fprintf(stderr, "invalid BFD data plane port: %s\n",
+ str);
+ exit(0);
+ }
+
+ return (uint16_t)rv;
+}
+
+static void
+distributed_bfd_init(const char *arg)
+{
+ char *sptr, *saux;
+ bool is_client = false;
+ size_t slen;
+ socklen_t salen;
+ char addr[64];
+ char type[64];
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_un sun;
+ } sa;
+
+ /* Basic parsing: find ':' to figure out type part and address part. */
+ sptr = strchr(arg, ':');
+ if (sptr == NULL) {
+ fprintf(stderr, "invalid BFD data plane socket: %s\n", arg);
+ exit(1);
+ }
+
+ /* Calculate type string length. */
+ slen = (size_t)(sptr - arg);
+
+ /* Copy the address part. */
+ sptr++;
+ strlcpy(addr, sptr, sizeof(addr));
+
+ /* Copy type part. */
+ strlcpy(type, arg, slen + 1);
+
+ /* Reset address data. */
+ memset(&sa, 0, sizeof(sa));
+
+ /* Fill the address information. */
+ if (strcmp(type, "unix") == 0 || strcmp(type, "unixc") == 0) {
+ if (strcmp(type, "unixc") == 0)
+ is_client = true;
+
+ salen = sizeof(sa.sun);
+ sa.sun.sun_family = AF_UNIX;
+ strlcpy(sa.sun.sun_path, addr, sizeof(sa.sun.sun_path));
+ } else if (strcmp(type, "ipv4") == 0 || strcmp(type, "ipv4c") == 0) {
+ if (strcmp(type, "ipv4c") == 0)
+ is_client = true;
+
+ salen = sizeof(sa.sin);
+ sa.sin.sin_family = AF_INET;
+
+ /* Parse port if any. */
+ sptr = strchr(addr, ':');
+ if (sptr == NULL) {
+ sa.sin.sin_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
+ } else {
+ *sptr = 0;
+ sa.sin.sin_port = htons(parse_port(sptr + 1));
+ }
+
+ if (inet_pton(AF_INET, addr, &sa.sin.sin_addr) != 1)
+ errx(1, "%s: inet_pton: invalid address %s", __func__,
+ addr);
+ } else if (strcmp(type, "ipv6") == 0 || strcmp(type, "ipv6c") == 0) {
+ if (strcmp(type, "ipv6c") == 0)
+ is_client = true;
+
+ salen = sizeof(sa.sin6);
+ sa.sin6.sin6_family = AF_INET6;
+
+ /* Check for IPv6 enclosures '[]' */
+ sptr = &addr[0];
+ if (*sptr != '[')
+ errx(1, "%s: invalid IPv6 address format: %s", __func__,
+ addr);
+
+ saux = strrchr(addr, ']');
+ if (saux == NULL)
+ errx(1, "%s: invalid IPv6 address format: %s", __func__,
+ addr);
+
+ /* Consume the '[]:' part. */
+ slen = saux - sptr;
+ memmove(addr, addr + 1, slen);
+ addr[slen - 1] = 0;
+
+ /* Parse port if any. */
+ saux++;
+ sptr = strrchr(saux, ':');
+ if (sptr == NULL) {
+ sa.sin6.sin6_port = htons(BFD_DATA_PLANE_DEFAULT_PORT);
+ } else {
+ *sptr = 0;
+ sa.sin6.sin6_port = htons(parse_port(sptr + 1));
+ }
+
+ if (inet_pton(AF_INET6, addr, &sa.sin6.sin6_addr) != 1)
+ errx(1, "%s: inet_pton: invalid address %s", __func__,
+ addr);
+ } else {
+ fprintf(stderr, "invalid BFD data plane socket type: %s\n",
+ type);
+ exit(1);
+ }
+
+ /* Initialize BFD data plane listening socket. */
+ bfd_dplane_init((struct sockaddr *)&sa, salen, is_client);
+}
+
+static void bg_init(void)
+{
+ struct zebra_privs_t bfdd_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0,
+ };
+
+ TAILQ_INIT(&bglobal.bg_bcslist);
+ TAILQ_INIT(&bglobal.bg_obslist);
+
+ memcpy(&bglobal.bfdd_privs, &bfdd_privs,
+ sizeof(bfdd_privs));
+}
+
+int main(int argc, char *argv[])
+{
+ char ctl_path[512], dplane_addr[512];
+ bool ctlsockused = false;
+ int opt;
+
+ bglobal.bg_use_dplane = false;
+
+ /* Initialize system sockets. */
+ bg_init();
+
+ frr_preinit(&bfdd_di, argc, argv);
+ frr_opt_add("", longopts,
+ " --bfdctl Specify bfdd control socket\n"
+ " --dplaneaddr Specify BFD data plane address\n");
+
+ snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET,
+ "", "");
+ while (true) {
+ opt = frr_getopt(argc, argv, NULL);
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case OPTION_CTLSOCK:
+ strlcpy(ctl_path, optarg, sizeof(ctl_path));
+ ctlsockused = true;
+ break;
+ case OPTION_DPLANEADDR:
+ strlcpy(dplane_addr, optarg, sizeof(dplane_addr));
+ bglobal.bg_use_dplane = true;
+ break;
+
+ default:
+ frr_help_exit(1);
+ }
+ }
+
+ if (bfdd_di.pathspace && !ctlsockused)
+ snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET,
+ "/", bfdd_di.pathspace);
+
+ /* Initialize FRR infrastructure. */
+ master = frr_init();
+
+ /* Initialize control socket. */
+ control_init(ctl_path);
+
+ /* Initialize BFD data structures. */
+ bfd_initialize();
+
+ bfd_vrf_init();
+
+ access_list_init();
+
+ /* Initialize zebra connection. */
+ bfdd_zclient_init(&bglobal.bfdd_privs);
+
+ event_add_read(master, control_accept, NULL, bglobal.bg_csock,
+ &bglobal.bg_csockev);
+
+ /* Install commands. */
+ bfdd_vty_init();
+
+ /* read configuration file and daemonize */
+ frr_config_fork();
+
+ /* Initialize BFD data plane listening socket. */
+ if (bglobal.bg_use_dplane)
+ distributed_bfd_init(dplane_addr);
+
+ frr_run(master);
+ /* NOTREACHED */
+
+ return 0;
+}