summaryrefslogtreecommitdiff
path: root/babeld/babel_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'babeld/babel_main.c')
-rw-r--r--babeld/babel_main.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c
new file mode 100644
index 00000000..6065b7bc
--- /dev/null
+++ b/babeld/babel_main.c
@@ -0,0 +1,522 @@
+/*
+ * This file is free software: you may copy, redistribute and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+
+Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* include zebra library */
+#include <zebra.h>
+#include "getopt.h"
+#include "if.h"
+#include "log.h"
+#include "thread.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "version.h"
+#include "command.h"
+#include "vty.h"
+#include "memory.h"
+
+#include "babel_main.h"
+#include "babeld.h"
+#include "util.h"
+#include "kernel.h"
+#include "babel_interface.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "message.h"
+#include "resend.h"
+#include "babel_zebra.h"
+
+
+static void babel_init (int argc, char **argv);
+static void babel_usage (char *progname);
+static char *babel_get_progname(char *argv_0);
+static void babel_fail(void);
+static void babel_init_random(void);
+static void babel_replace_by_null(int fd);
+static void babel_load_state_file(void);
+static void babel_init_signals(void);
+static void babel_exit_properly(void);
+static void babel_save_state_file(void);
+
+
+struct thread_master *master; /* quagga's threads handler */
+struct timeval babel_now; /* current time */
+
+unsigned char myid[8]; /* unique id (mac address of an interface) */
+int debug = BABEL_DEBUG_COMMON;
+
+time_t reboot_time;
+int idle_time = 320;
+int link_detect = 0;
+int wireless_hello_interval = -1;
+int wired_hello_interval = -1;
+int idle_hello_interval = -1;
+char *pidfile = "/var/run/babeld.pid";
+
+const unsigned char zeroes[16] = {0};
+const unsigned char ones[16] =
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+static char *state_file = "/var/lib/babeld/babel-state";
+
+unsigned char protocol_group[16]; /* babel's link-local multicast address */
+int protocol_port; /* babel's port */
+int protocol_socket = -1; /* socket: communicate with others babeld */
+
+static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG;
+static char *babel_config_file = NULL;
+static char *babel_vty_addr = NULL;
+static int babel_vty_port = BABEL_VTY_PORT;
+
+/* Babeld options. */
+struct option longopts[] =
+{
+ { "daemon", no_argument, NULL, 'd'},
+ { "config_file", required_argument, NULL, 'f'},
+ { "pid_file", required_argument, NULL, 'i'},
+ { "help", no_argument, NULL, 'h'},
+ { "vty_addr", required_argument, NULL, 'A'},
+ { "vty_port", required_argument, NULL, 'P'},
+ { "user", required_argument, NULL, 'u'},
+ { "group", required_argument, NULL, 'g'},
+ { "version", no_argument, NULL, 'v'},
+ { 0 }
+};
+
+/* babeld privileges */
+static zebra_capabilities_t _caps_p [] =
+{
+ ZCAP_NET_RAW,
+ ZCAP_BIND
+};
+static struct zebra_privs_t babeld_privs =
+{
+#if defined(QUAGGA_USER)
+ .user = QUAGGA_USER,
+#endif
+#if defined QUAGGA_GROUP
+ .group = QUAGGA_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = 2,
+ .cap_num_i = 0
+};
+
+
+int
+main(int argc, char **argv)
+{
+ struct thread thread;
+ /* and print banner too */
+ babel_init(argc, argv);
+ while (thread_fetch (master, &thread)) {
+ thread_call (&thread);
+ }
+ return 0;
+}
+
+/* make initialisations witch don't need infos about kernel(interfaces, etc.) */
+static void
+babel_init(int argc, char **argv)
+{
+ int rc, opt;
+ int do_daemonise = 0;
+ char *progname = NULL;
+ char *pidfile = PATH_BABELD_PID;
+
+ /* Set umask before anything for security */
+ umask (0027);
+ progname = babel_get_progname(argv[0]);
+
+ /* set default log (lib/log.h) */
+ zlog_default = openzlog(progname, ZLOG_BABEL,
+ LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ /* set log destination as stdout until the config file is read */
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING);
+
+ babel_init_random();
+
+ /* set the Babel's default link-local multicast address and Babel's port */
+ parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL);
+ protocol_port = 6696;
+
+ /* get options */
+ while(1) {
+ opt = getopt_long(argc, argv, "df:i:hA:P:u:g:v", longopts, 0);
+ if(opt < 0)
+ break;
+
+ switch(opt) {
+ case 0:
+ break;
+ case 'd':
+ do_daemonise = -1;
+ break;
+ case 'f':
+ babel_config_file = optarg;
+ break;
+ case 'i':
+ pidfile = optarg;
+ break;
+ case 'A':
+ babel_vty_addr = optarg;
+ break;
+ case 'P':
+ babel_vty_port = atoi (optarg);
+ if (babel_vty_port < 0 || babel_vty_port > 0xffff
+ || (babel_vty_port == 0 && optarg[0] != '0'/*atoi error*/))
+ babel_vty_port = BABEL_VTY_PORT;
+ break;
+ case 'u':
+ babeld_privs.user = optarg;
+ break;
+ case 'g':
+ babeld_privs.group = optarg;
+ break;
+ case 'v':
+ print_version (progname);
+ exit (0);
+ break;
+ case 'h':
+ babel_usage (progname);
+ break;
+ default:
+ babel_usage(progname);
+ break;
+ }
+ }
+
+ /* create the threads handler */
+ master = thread_master_create ();
+
+ /* Library inits. */
+ zprivs_init (&babeld_privs);
+ babel_init_signals();
+ cmd_init (1);
+ vty_init (master);
+ memory_init ();
+
+ /* babeld inits (default options) */
+ /* set default interval's values */
+ if(wireless_hello_interval <= 0)
+ wireless_hello_interval = 4000;
+ wireless_hello_interval = MAX(wireless_hello_interval, 5);
+
+ if(wired_hello_interval <= 0)
+ wired_hello_interval = 4000;
+ wired_hello_interval = MAX(wired_hello_interval, 5);
+
+ /* an assertion */
+ if(parasitic && allow_duplicates >= 0) {
+ /* Too difficult to get right. */
+ zlog_err("Sorry, -P and -A are incompatible.");
+ exit(1);
+ }
+
+ babel_replace_by_null(STDIN_FILENO);
+
+ if (do_daemonise && daemonise() < 0) {
+ zlog_err("daemonise: %s", safe_strerror(errno));
+ exit (1);
+ }
+
+ /* write pid file */
+ if (pid_output(pidfile) < 0) {
+ zlog_err("error while writing pidfile");
+ exit (1);
+ };
+
+ /* init some quagga's dependencies, and babeld's commands */
+ babeld_quagga_init();
+ /* init zebra client's structure and it's commands */
+ /* this replace kernel_setup && kernel_setup_socket */
+ babelz_zebra_init ();
+
+ /* Sort all installed commands. */
+ sort_node ();
+
+ /* Get zebra configuration file. */
+ zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+ vty_read_config (babel_config_file, babel_config_default);
+
+ myseqno = (random() & 0xFFFF);
+ babel_load_state_file();
+
+ /* Create VTY socket */
+ vty_serv_sock (babel_vty_addr, babel_vty_port, BABEL_VTYSH_PATH);
+
+ /* init buffer */
+ rc = resize_receive_buffer(1500);
+ if(rc < 0)
+ babel_fail();
+
+ schedule_neighbours_check(5000, 1);
+
+ zlog_notice ("BABELd %s starting: vty@%d", BABEL_VERSION, babel_vty_port);
+}
+
+/* return the progname (without path, example: "./x/progname" --> "progname") */
+static char *
+babel_get_progname(char *argv_0) {
+ char *p = strrchr (argv_0, '/');
+ return (p ? ++p : argv_0);
+}
+
+static void
+babel_usage(char *progname)
+{
+ fprintf(stderr,
+ "Syntax: %s "
+ "[-m multicast_address] [-p port] [-S state-file]\n"
+ " "
+ "[-h hello] [-H wired_hello] [-i idle_hello]\n"
+ " "
+ "[-k metric] [-A metric] [-s] [-P] [-l] [-w] [-d level] [-g port]\n"
+ " "
+ "[-t table] [-T table] [-c file] [-C statement]\n"
+ " "
+ "[-D] [-L logfile] [-I pidfile]\n"
+ " "
+ "[id] interface...\n",
+ progname);
+ exit(1);
+}
+
+static void
+babel_fail(void)
+{
+ exit(1);
+}
+
+/* initialize random value, and set 'babel_now' by the way. */
+static void
+babel_init_random(void)
+{
+ gettime(&babel_now);
+ int rc;
+ unsigned int seed;
+
+ rc = read_random_bytes(&seed, sizeof(seed));
+ if(rc < 0) {
+ zlog_err("read(random): %s", safe_strerror(errno));
+ seed = 42;
+ }
+
+ seed ^= (babel_now.tv_sec ^ babel_now.tv_usec);
+ srandom(seed);
+}
+
+/*
+ close fd, and replace it by "/dev/null"
+ exit if error
+ */
+static void
+babel_replace_by_null(int fd)
+{
+ int fd_null;
+ int rc;
+
+ fd_null = open("/dev/null", O_RDONLY);
+ if(fd_null < 0) {
+ zlog_err("open(null): %s", safe_strerror(errno));
+ exit(1);
+ }
+
+ rc = dup2(fd_null, fd);
+ if(rc < 0) {
+ zlog_err("dup2(null, 0): %s", safe_strerror(errno));
+ exit(1);
+ }
+
+ close(fd_null);
+}
+
+/*
+ Load the state file: check last babeld's running state, usefull in case of
+ "/etc/init.d/babeld restart"
+ */
+static void
+babel_load_state_file(void)
+{
+ reboot_time = babel_now.tv_sec;
+ int fd;
+ int rc;
+
+ fd = open(state_file, O_RDONLY);
+ if(fd < 0 && errno != ENOENT)
+ zlog_err("open(babel-state: %s)", safe_strerror(errno));
+ rc = unlink(state_file);
+ if(fd >= 0 && rc < 0) {
+ zlog_err("unlink(babel-state): %s", safe_strerror(errno));
+ /* If we couldn't unlink it, it's probably stale. */
+ close(fd);
+ fd = -1;
+ }
+ if(fd >= 0) {
+ char buf[100];
+ char buf2[100];
+ int s;
+ long t;
+ rc = read(fd, buf, 99);
+ if(rc < 0) {
+ zlog_err("read(babel-state): %s", safe_strerror(errno));
+ } else {
+ buf[rc] = '\0';
+ rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t);
+ if(rc == 3 && s >= 0 && s <= 0xFFFF) {
+ unsigned char sid[8];
+ rc = parse_eui64(buf2, sid);
+ if(rc < 0) {
+ zlog_err("Couldn't parse babel-state.");
+ } else {
+ struct timeval realnow;
+ debugf(BABEL_DEBUG_COMMON,
+ "Got %s %d %ld from babel-state.",
+ format_eui64(sid), s, t);
+ gettimeofday(&realnow, NULL);
+ if(memcmp(sid, myid, 8) == 0)
+ myseqno = seqno_plus(s, 1);
+ else
+ zlog_err("ID mismatch in babel-state.");
+ /* Convert realtime into monotonic time. */
+ if(t >= 1176800000L && t <= realnow.tv_sec)
+ reboot_time = babel_now.tv_sec - (realnow.tv_sec - t);
+ }
+ } else {
+ zlog_err("Couldn't parse babel-state.");
+ }
+ }
+ close(fd);
+ fd = -1;
+ }
+}
+
+static void
+babel_sigexit(void)
+{
+ zlog_notice("Terminating on signal");
+
+ babel_exit_properly();
+}
+
+static void
+babel_sigusr1 (void)
+{
+ zlog_rotate (NULL);
+}
+
+static void
+babel_init_signals(void)
+{
+ static struct quagga_signal_t babel_signals[] =
+ {
+ {
+ .signal = SIGUSR1,
+ .handler = &babel_sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &babel_sigexit,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &babel_sigexit,
+ },
+ };
+
+ signal_init (master, Q_SIGC(babel_signals), babel_signals);
+}
+
+static void
+babel_exit_properly(void)
+{
+ debugf(BABEL_DEBUG_COMMON, "Exiting...");
+ usleep(roughly(10000));
+ gettime(&babel_now);
+
+ /* Uninstall and flush all routes. */
+ debugf(BABEL_DEBUG_COMMON, "Uninstall routes.");
+ babel_uninstall_all_routes();
+ babel_interface_close_all();
+ babel_zebra_close_connexion();
+ babel_save_state_file();
+ debugf(BABEL_DEBUG_COMMON, "Remove pid file.");
+ if(pidfile)
+ unlink(pidfile);
+ debugf(BABEL_DEBUG_COMMON, "Done.");
+
+ exit(0);
+}
+
+static void
+babel_save_state_file(void)
+{
+ int fd;
+ int rc;
+
+ debugf(BABEL_DEBUG_COMMON, "Save state file.");
+ fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ if(fd < 0) {
+ zlog_err("creat(babel-state): %s", safe_strerror(errno));
+ unlink(state_file);
+ } else {
+ struct timeval realnow;
+ char buf[100];
+ gettimeofday(&realnow, NULL);
+ rc = snprintf(buf, 100, "%s %d %ld\n",
+ format_eui64(myid), (int)myseqno,
+ (long)realnow.tv_sec);
+ if(rc < 0 || rc >= 100) {
+ zlog_err("write(babel-state): overflow.");
+ unlink(state_file);
+ } else {
+ rc = write(fd, buf, rc);
+ if(rc < 0) {
+ zlog_err("write(babel-state): %s", safe_strerror(errno));
+ unlink(state_file);
+ }
+ fsync(fd);
+ }
+ close(fd);
+ }
+}