summaryrefslogtreecommitdiff
path: root/nhrp/opennhrp.c
diff options
context:
space:
mode:
Diffstat (limited to 'nhrp/opennhrp.c')
-rw-r--r--nhrp/opennhrp.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/nhrp/opennhrp.c b/nhrp/opennhrp.c
new file mode 100644
index 0000000..8ba870d
--- /dev/null
+++ b/nhrp/opennhrp.c
@@ -0,0 +1,524 @@
+/* opennhrp.c - OpenNHRP main routines
+ *
+ * Copyright (C) 2007-2009 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or later as
+ * published by the Free Software Foundation.
+ *
+ * See http://www.gnu.org/ for details.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "nhrp_common.h"
+#include "nhrp_peer.h"
+#include "nhrp_interface.h"
+
+const char *nhrp_version_string =
+ "OpenNHRP " OPENNHRP_VERSION
+#ifdef NHRP_NO_NBMA_GRE
+ " (no NBMA GRE support)"
+#endif
+ ;
+
+const char *nhrp_admin_socket = OPENNHRP_ADMIN_SOCKET;
+const char *nhrp_pid_file = "/var/run/opennhrp.pid";
+const char *nhrp_config_file = "/etc/opennhrp/opennhrp.conf";
+const char *nhrp_script_file = "/etc/opennhrp/opennhrp-script";
+int nhrp_verbose = 0;
+int nhrp_running = FALSE;
+
+static int pid_file_fd;
+
+void nhrp_hex_dump(const char *name, const uint8_t *buf, int bytes)
+{
+ int i, j;
+ int left;
+
+ fprintf(stderr, "%s:\n", name);
+ for (i = 0; i < bytes; i++) {
+ fprintf(stderr, "%02X ", buf[i]);
+ if (i % 0x10 == 0x0f) {
+ fprintf(stderr, " ");
+ for (j = 0; j < 0x10; j++)
+ fprintf(stderr, "%c", isgraph(buf[i+j-0xf]) ?
+ buf[i+j-0xf]: '.');
+ fprintf(stderr, "\n");
+ }
+ }
+
+ left = i % 0x10;
+ if (left != 0) {
+ fprintf(stderr, "%*s ", 3 * (0x10 - left), "");
+
+ for (j = 0; j < left; j++)
+ fprintf(stderr, "%c", isgraph(buf[i+j-left]) ?
+ buf[i+j-left]: '.');
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+}
+
+static void handle_signal_cb(struct ev_signal *w, int revents)
+{
+ struct nhrp_peer_selector sel;
+
+ switch (w->signum) {
+ case SIGUSR1:
+ nhrp_peer_dump_cache();
+ break;
+ case SIGINT:
+ case SIGTERM:
+ ev_unloop(EVUNLOOP_ALL);
+ break;
+ case SIGHUP:
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = NHRP_PEER_TYPEMASK_REMOVABLE;
+ nhrp_peer_foreach(nhrp_peer_remove_matching, NULL, &sel);
+ break;
+ }
+}
+
+static int hook_signal[] = { SIGUSR1, SIGHUP, SIGINT, SIGTERM };
+static ev_signal signal_event[ARRAY_SIZE(hook_signal)];
+
+static void signal_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hook_signal); i++) {
+ ev_signal_init(&signal_event[i], handle_signal_cb,
+ hook_signal[i]);
+ ev_signal_start(&signal_event[i]);
+ }
+}
+
+static int read_word(FILE *in, int *lineno, size_t len, char *word)
+{
+ int ch, i, comment = 0;
+
+ ch = fgetc(in);
+ while (1) {
+ if (ch == EOF)
+ return FALSE;
+ if (ch == '#')
+ comment = 1;
+ if (!comment && !isspace(ch))
+ break;
+ if (ch == '\n') {
+ (*lineno)++;
+ comment = 0;
+ }
+ ch = fgetc(in);
+ }
+
+ for (i = 0; i < len-1 && !isspace(ch); i++) {
+ word[i] = ch;
+ ch = fgetc(in);
+ if (ch == EOF)
+ break;
+ if (ch == '\n')
+ (*lineno)++;
+ }
+ word[i] = 0;
+
+ return TRUE;
+}
+
+static int load_config(const char *config_file)
+{
+#define NEED_INTERFACE() if (iface == NULL) { rc = 2; break; } peer = NULL;
+#define NEED_PEER() if (peer == NULL || peer->type == NHRP_PEER_TYPE_LOCAL_ADDR) { rc = 3; break; }
+
+ static const char *errors[] = {
+ "syntax error",
+ "missing keyword",
+ "keyword valid only for 'interface' definition",
+ "keyword valid only for 'map' definition",
+ "invalid address",
+ "dynamic-map requires a network address",
+ "bad multicast destination",
+ "keyword valid only for 'interace' and 'shortcut-target' definition",
+ };
+ struct nhrp_interface *iface = NULL;
+ struct nhrp_peer *peer = NULL;
+ struct nhrp_address paddr;
+ char word[32], nbma[32], addr[32];
+ FILE *in;
+ int lineno = 1, rc = -1;
+
+ in = fopen(config_file, "r");
+ if (in == NULL) {
+ nhrp_error("Unable to open configuration file '%s'.",
+ config_file);
+ return FALSE;
+ }
+
+ while (read_word(in, &lineno, sizeof(word), word)) {
+ if (strcmp(word, "interface") == 0) {
+ if (!read_word(in, &lineno, sizeof(word), word)) {
+ rc = 1;
+ break;
+ }
+ iface = nhrp_interface_get_by_name(word, TRUE);
+ if (iface != NULL)
+ iface->flags |= NHRP_INTERFACE_FLAG_CONFIGURED;
+ peer = NULL;
+ } else if (strcmp(word, "shortcut-target") == 0) {
+ NEED_INTERFACE();
+ if (!read_word(in, &lineno, sizeof(addr), addr)) {
+ rc = 1;
+ break;
+ }
+ peer = nhrp_peer_alloc(iface);
+ peer->type = NHRP_PEER_TYPE_LOCAL_ADDR;
+ peer->afnum = AFNUM_RESERVED;
+ if (!nhrp_address_parse(addr, &peer->protocol_address,
+ &peer->prefix_length)) {
+ rc = 4;
+ break;
+ }
+ peer->protocol_type = nhrp_protocol_from_pf(peer->protocol_address.type);
+ nhrp_peer_insert(peer);
+ nhrp_peer_put(peer);
+ } else if (strcmp(word, "dynamic-map") == 0) {
+ NEED_INTERFACE();
+ read_word(in, &lineno, sizeof(addr), addr);
+ read_word(in, &lineno, sizeof(nbma), nbma);
+
+ peer = nhrp_peer_alloc(iface);
+ peer->type = NHRP_PEER_TYPE_STATIC_DNS;
+ if (!nhrp_address_parse(addr, &peer->protocol_address,
+ &peer->prefix_length)) {
+ rc = 4;
+ break;
+ }
+ if (!nhrp_address_is_network(&peer->protocol_address,
+ peer->prefix_length)) {
+ rc = 5;
+ break;
+ }
+ peer->protocol_type = nhrp_protocol_from_pf(
+ peer->protocol_address.type);
+ peer->nbma_hostname = strdup(nbma);
+ peer->afnum = nhrp_afnum_from_pf(
+ peer->next_hop_address.type);
+ nhrp_peer_insert(peer);
+ nhrp_peer_put(peer);
+ } else if (strcmp(word, "map") == 0) {
+ NEED_INTERFACE();
+ read_word(in, &lineno, sizeof(addr), addr);
+ read_word(in, &lineno, sizeof(nbma), nbma);
+
+ peer = nhrp_peer_alloc(iface);
+ peer->type = NHRP_PEER_TYPE_STATIC;
+ if (!nhrp_address_parse(addr, &peer->protocol_address,
+ &peer->prefix_length)) {
+ rc = 4;
+ break;
+ }
+ peer->protocol_type = nhrp_protocol_from_pf(
+ peer->protocol_address.type);
+ if (!nhrp_address_parse(nbma, &peer->next_hop_address,
+ NULL))
+ peer->nbma_hostname = strdup(nbma);
+ peer->afnum = nhrp_afnum_from_pf(peer->next_hop_address.type);
+ nhrp_peer_insert(peer);
+ nhrp_peer_put(peer);
+ } else if (strcmp(word, "register") == 0) {
+ NEED_PEER();
+ peer->flags |= NHRP_PEER_FLAG_REGISTER;
+ } else if (strcmp(word, "cisco") == 0) {
+ NEED_PEER();
+ peer->flags |= NHRP_PEER_FLAG_CISCO;
+ } else if (strcmp(word, "holding-time") == 0) {
+ read_word(in, &lineno, sizeof(word), word);
+ if (peer != NULL &&
+ peer->type == NHRP_PEER_TYPE_LOCAL_ADDR) {
+ peer->holding_time = atoi(word);
+ } else if (iface != NULL) {
+ iface->holding_time = atoi(word);
+ peer = NULL;
+ } else {
+ rc = 7;
+ }
+ } else if (strcmp(word, "cisco-authentication") == 0) {
+ struct nhrp_buffer *buf;
+ struct nhrp_cisco_authentication_extension *auth;
+
+ NEED_INTERFACE();
+ read_word(in, &lineno, sizeof(word), word);
+
+ buf = nhrp_buffer_alloc(strlen(word) + sizeof(uint32_t));
+ auth = (struct nhrp_cisco_authentication_extension *) buf->data;
+ auth->type = NHRP_AUTHENTICATION_PLAINTEXT;
+ memcpy(auth->secret, word, strlen(word));
+
+ iface->auth_token = buf;
+ } else if (strcmp(word, "route-table") == 0) {
+ NEED_INTERFACE();
+ read_word(in, &lineno, sizeof(word), word);
+ iface->route_table = atoi(word);
+ } else if (strcmp(word, "shortcut") == 0) {
+ NEED_INTERFACE();
+ iface->flags |= NHRP_INTERFACE_FLAG_SHORTCUT;
+ } else if (strcmp(word, "redirect") == 0) {
+ NEED_INTERFACE();
+ iface->flags |= NHRP_INTERFACE_FLAG_REDIRECT;
+ } else if (strcmp(word, "non-caching") == 0) {
+ NEED_INTERFACE();
+ iface->flags |= NHRP_INTERFACE_FLAG_NON_CACHING;
+ } else if (strcmp(word, "shortcut-destination") == 0) {
+ NEED_INTERFACE();
+ iface->flags |= NHRP_INTERFACE_FLAG_SHORTCUT_DEST;
+ } else if (strcmp(word, "multicast") == 0) {
+ NEED_INTERFACE();
+ read_word(in, &lineno, sizeof(word), word);
+ if (strcmp(word, "dynamic") == 0) {
+ iface->mcast_mask = \
+ BIT(NHRP_PEER_TYPE_STATIC) |
+ BIT(NHRP_PEER_TYPE_DYNAMIC_NHS) |
+ BIT(NHRP_PEER_TYPE_DYNAMIC);
+ } else if (strcmp(word, "nhs") == 0) {
+ iface->mcast_mask = \
+ BIT(NHRP_PEER_TYPE_STATIC) |
+ BIT(NHRP_PEER_TYPE_DYNAMIC_NHS);
+ } else if (nhrp_address_parse(word, &paddr, NULL)) {
+ iface->mcast_numaddr++;
+ iface->mcast_addr = realloc(iface->mcast_addr,
+ iface->mcast_numaddr *
+ sizeof(struct nhrp_address));
+ iface->mcast_addr[iface->mcast_numaddr-1] =
+ paddr;
+ } else {
+ rc = 6;
+ break;
+ }
+ } else {
+ rc = 0;
+ break;
+ }
+ }
+ fclose(in);
+
+ if (rc >= 0) {
+ nhrp_error("Configuration file %s in %s:%d, near word '%s'",
+ errors[rc], config_file, lineno, word);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void remove_pid_file(void)
+{
+ if (pid_file_fd != 0) {
+ close(pid_file_fd);
+ pid_file_fd = 0;
+ remove(nhrp_pid_file);
+ }
+}
+
+static int open_pid_file(void)
+{
+ if (strlen(nhrp_pid_file) == 0)
+ return TRUE;
+
+ pid_file_fd = open(nhrp_pid_file, O_CREAT | O_WRONLY,
+ S_IRUSR | S_IWUSR);
+ if (pid_file_fd < 0)
+ goto err;
+
+ fcntl(pid_file_fd, F_SETFD, FD_CLOEXEC);
+ if (flock(pid_file_fd, LOCK_EX | LOCK_NB) < 0)
+ goto err_close;
+
+ return TRUE;
+
+err_close:
+ close(pid_file_fd);
+err:
+ nhrp_error("Unable to open/lock pid file: %s.", strerror(errno));
+ return FALSE;
+}
+
+static int write_pid(void)
+{
+ char tmp[16];
+ int n;
+
+ if (pid_file_fd >= 0) {
+ if (ftruncate(pid_file_fd, 0) < 0)
+ return FALSE;
+
+ n = sprintf(tmp, "%d\n", getpid());
+ if (write(pid_file_fd, tmp, n) != n)
+ return FALSE;
+
+ atexit(remove_pid_file);
+ }
+
+ return TRUE;
+}
+
+static int daemonize(void)
+{
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0)
+ return FALSE;
+ if (pid > 0)
+ exit(0);
+
+ if (setsid() < 0)
+ return FALSE;
+
+ pid = fork();
+ if (pid < 0)
+ return FALSE;
+ if (pid > 0)
+ exit(0);
+
+ if (chdir("/") < 0)
+ return FALSE;
+
+ umask(0);
+
+ if (freopen("/dev/null", "r", stdin) == NULL ||
+ freopen("/dev/null", "w", stdout) == NULL ||
+ freopen("/dev/null", "w", stderr) == NULL) {
+ nhrp_error("Unable reopen standard file descriptors");
+ goto err;
+ }
+
+ ev_default_fork();
+
+ return TRUE;
+
+err:
+ close(pid_file_fd);
+ pid_file_fd = 0;
+ return FALSE;
+}
+
+int usage(const char *prog)
+{
+ fprintf(stderr,
+ "usage: opennhrp [-a admin-socket] [-c config-file] [-s script-file]\n"
+ " [-p pid-file] [-d] [-v]\n"
+ " opennhrp -V\n"
+ "\n"
+ "\t-a admin-socket\tspecify management interface socket\n"
+ "\t-c config-file\tread configuration from config-file\n"
+ "\t-s script-file\tuse specified script-file for event handling\n"
+ "\t-p pid-file\tspecify pid-file\n"
+ "\t-d\t\tfork to background after startup\n"
+ "\t-v\t\tverbose logging\n"
+ "\t-V\t\tshow version number and exit\n"
+ "\n");
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct nhrp_address any;
+ int i, daemonmode = 0;
+
+ nhrp_address_set_type(&any, AF_UNSPEC);
+
+ for (i = 1; i < argc; i++) {
+ if (strlen(argv[i]) != 2 || argv[i][0] != '-')
+ return usage(argv[0]);
+
+ switch (argv[i][1]) {
+ case 'c':
+ if (++i >= argc)
+ return usage(argv[0]);
+ nhrp_config_file = argv[i];
+ break;
+ case 's':
+ if (++i >= argc)
+ return usage(argv[0]);
+ nhrp_script_file = argv[i];
+ break;
+ case 'a':
+ if (++i >= argc)
+ return usage(argv[0]);
+ nhrp_admin_socket = argv[i];
+ break;
+ case 'p':
+ if (++i >= argc)
+ return usage(argv[0]);
+ nhrp_pid_file = argv[i];
+ break;
+ case 'd':
+ daemonmode = 1;
+ break;
+ case 'v':
+ nhrp_verbose = 1;
+ break;
+ case 'V':
+ puts(nhrp_version_string);
+ return 0;
+ default:
+ return usage(argv[0]);
+ }
+ }
+
+ srandom(time(NULL));
+ if (!log_init())
+ return 1;
+ if (!open_pid_file())
+ return 1;
+
+ nhrp_info("%s starting", nhrp_version_string);
+
+ ev_default_loop(0);
+ signal_init();
+ server_init();
+ if (!nhrp_address_init())
+ return 3;
+ if (!load_config(nhrp_config_file))
+ return 4;
+ if (!kernel_init())
+ return 5;
+ if (!admin_init(nhrp_admin_socket))
+ return 6;
+ if (!forward_init())
+ return 7;
+
+ if (daemonmode && !daemonize()) {
+ nhrp_error("Failed to daemonize. Exit.");
+ return 8;
+ }
+
+ write_pid();
+
+ nhrp_running = TRUE;
+ ev_loop(0);
+ nhrp_running = FALSE;
+
+ forward_cleanup();
+ kernel_stop_listening();
+ nhrp_peer_cleanup();
+ kernel_cleanup();
+ nhrp_interface_cleanup();
+ nhrp_rate_limit_clear(&any, 0);
+ nhrp_address_cleanup();
+
+ ev_default_destroy();
+
+ return 0;
+}
+