diff options
author | Paul Jakma <paul@quagga.net> | 2011-12-25 17:52:09 +0100 |
---|---|---|
committer | Paul Jakma <paul@quagga.net> | 2012-03-25 17:06:51 +0100 |
commit | 5734509c0545ebd95a5b8e3f22a911c1a39ffa1b (patch) | |
tree | 2f35ad50d793080db1f858c2de2228cfd2946dfd /babeld/babel_main.c | |
parent | fe67e668881b8b8d71b1570575c913cd287fafcf (diff) |
babeld: Initial import, for Babel routing protocol.
* Initial import of the Babel routing protocol, ported to Quagga.
* LICENCE: Update the original LICENCE file to include all known potentially
applicable copyright claims. Ask that any future contributors to babeld/
grant MIT/X11 licence to their work.
* *.{c,h}: Add GPL headers, in according with the SFLC guidance on
dealing with potentially mixed GPL/other licensed work, at:
https://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
Diffstat (limited to 'babeld/babel_main.c')
-rw-r--r-- | babeld/babel_main.c | 522 |
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); + } +} |