diff options
Diffstat (limited to 'usr.sbin/ripd/ripd.c')
-rw-r--r-- | usr.sbin/ripd/ripd.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/usr.sbin/ripd/ripd.c b/usr.sbin/ripd/ripd.c new file mode 100644 index 00000000000..f0fc22e2678 --- /dev/null +++ b/usr.sbin/ripd/ripd.c @@ -0,0 +1,507 @@ +/* $OpenBSD: ripd.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <event.h> +#include <err.h> +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include "rip.h" +#include "ripd.h" +#include "ripe.h" +#include "log.h" +#include "control.h" +#include "rde.h" + +__dead void usage(void); +int check_child(pid_t, const char *); +void main_sig_handler(int, short, void *); +void ripd_shutdown(void); +void main_dispatch_ripe(int, short, void *); +void main_dispatch_rde(int, short, void *); +int check_file_secrecy(int, const char *); + +int pipe_parent2ripe[2]; +int pipe_parent2rde[2]; +int pipe_ripe2rde[2]; + +struct ripd_conf *conf = NULL; +struct imsgbuf *ibuf_ripe; +struct imsgbuf *ibuf_rde; + +pid_t ripe_pid = 0; +pid_t rde_pid = 0; + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dnv] [-f file]\n", __progname); + exit(1); +} + +/* ARGSUSED */ +void +main_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + int die = 0; + + switch (sig) { + case SIGTERM: + case SIGINT: + die = 1; + /* FALLTHROUGH */ + case SIGCHLD: + if (check_child(ripe_pid, "rip engine")) { + ripe_pid = 0; + die = 1; + } + if (check_child(rde_pid, "route decision engine")) { + rde_pid = 0; + die = 1; + } + if (die) + ripd_shutdown(); + break; + case SIGHUP: + /* reconfigure */ + /* ... */ + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + int mib[4]; + int debug = 0; + int ipforwarding; + int ch; + int opts = 0; + char *conffile; + size_t len; + + conffile = CONF_FILE; + ripd_process = PROC_MAIN; + + while ((ch = getopt(argc, argv, "df:nv")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'f': + conffile = optarg; + break; + case 'n': + opts |= RIPD_OPT_NOACTION; + break; + case 'v': + if (opts & RIPD_OPT_VERBOSE) + opts |= RIPD_OPT_VERBOSE2; + opts |= RIPD_OPT_VERBOSE; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + /* start logging */ + log_init(debug); + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1) + err(1, "sysctl"); + + if (!ipforwarding) + log_warnx("WARNING: IP forwarding NOT enabled"); + + /* fetch interfaces early */ + kif_init(); + + /* parse config file */ + if ((conf = parse_config(conffile, opts)) == NULL ) + exit(1); + + if (conf->opts & RIPD_OPT_NOACTION) { + if (conf->opts & RIPD_OPT_VERBOSE) + print_config(conf); + else + fprintf(stderr, "configuration OK\n"); + exit(0); + } + + /* check for root privileges */ + if (geteuid()) + errx(1, "need root privileges"); + + /* check for ripd user */ + if (getpwnam(RIPD_USER) == NULL) + errx(1, "unknown user %s", RIPD_USER); + + if (!debug) + daemon(1, 0); + + log_info("startup"); + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, + pipe_parent2ripe) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2rde) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ripe2rde) == -1) + fatal("socketpair"); + session_socket_blockmode(pipe_parent2ripe[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2ripe[1], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK); + session_socket_blockmode(pipe_ripe2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_ripe2rde[1], BM_NONBLOCK); + + /* start children */ + rde_pid = rde(conf, pipe_parent2rde, pipe_ripe2rde, pipe_parent2ripe); + ripe_pid = ripe(conf, pipe_parent2ripe, pipe_ripe2rde, pipe_parent2rde); + + /* show who we are */ + setproctitle("parent"); + + event_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sigchld, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sigchld, NULL); + signal_add(&ev_sighup, NULL); + + /* setup pipes to children */ + close(pipe_parent2ripe[1]); + close(pipe_parent2rde[1]); + close(pipe_ripe2rde[0]); + close(pipe_ripe2rde[1]); + + if ((ibuf_ripe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ripe, pipe_parent2ripe[0], main_dispatch_ripe); + imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde); + + /* setup event handler */ + ibuf_ripe->events = EV_READ; + event_set(&ibuf_ripe->ev, ibuf_ripe->fd, ibuf_ripe->events, + ibuf_ripe->handler, ibuf_ripe); + event_add(&ibuf_ripe->ev, NULL); + + ibuf_rde->events = EV_READ; + event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events, + ibuf_rde->handler, ibuf_rde); + event_add(&ibuf_rde->ev, NULL); + + if (kr_init(!(conf->flags & RIPD_FLAG_NO_FIB_UPDATE)) == -1) + fatalx("kr_init failed"); + + event_dispatch(); + + ripd_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ripd_shutdown(void) +{ + struct iface *i; + pid_t pid; + + if (ripe_pid) + kill(ripe_pid, SIGTERM); + + if (rde_pid) + kill(rde_pid, SIGTERM); + + while ((i = LIST_FIRST(&conf->iface_list)) != NULL) { + LIST_REMOVE(i, entry); + if_del(i); + } + + control_cleanup(); + kr_shutdown(); + + do { + if ((pid = wait(NULL)) == -1 && + errno != EINTR && errno != ECHILD) + fatal("wait"); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + msgbuf_clear(&ibuf_ripe->w); + free(ibuf_ripe); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + free(conf); + + log_info("terminating"); + exit(0); +} + +int +check_child(pid_t pid, const char *pname) +{ + int status; + + if (waitpid(pid, &status, WNOHANG) > 0) { + if (WIFEXITED(status)) { + log_warnx("lost child: %s exited", pname); + return (1); + } + if (WIFSIGNALED(status)) { + log_warnx("lost child: %s terminated; signal %d", + pname, WTERMSIG(status)); + return (1); + } + } + + return (0); +} + +/* imsg handling */ +/* ARGSUSED */ +void +main_dispatch_ripe(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + ssize_t n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_RELOAD: + /* XXX reconfig */ + break; + case IMSG_CTL_FIB_COUPLE: + kr_fib_couple(); + break; + case IMSG_CTL_FIB_DECOUPLE: + kr_fib_decouple(); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + kr_show_route(&imsg); + break; + case IMSG_CTL_IFINFO: + if (imsg.hdr.len == IMSG_HEADER_SIZE) + kr_ifinfo(NULL, imsg.hdr.pid); + else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ) + kr_ifinfo(imsg.data, imsg.hdr.pid); + else + log_warnx("IFINFO request with wrong len"); + break; + default: + log_debug("main_dispatch_ripe: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +/* ARGSUSED */ +void +main_dispatch_rde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + ssize_t n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_KROUTE_CHANGE: + if (kr_change(imsg.data)) + log_warn("main_dispatch_rde: error changing " + "route"); + break; + case IMSG_KROUTE_DELETE: + if (kr_delete(imsg.data)) + log_warn("main_dispatch_rde: error deleting " + "route"); + break; + default: + log_debug("main_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +main_imsg_compose_ripe(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_ripe, type, 0, pid, data, datalen); +} + +void +main_imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_rde, type, 0, pid, data, datalen); +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + log_warn("cannot stat %s", fname); + return (-1); + } + + if (st.st_uid != 0 && st.st_uid != getuid()) { + log_warnx("%s: owner not root or current user", fname); + return (-1); + } + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + log_warnx("%s: group/world readable/writeable", fname); + return (-1); + } + + return (0); +} + +int +rip_redistribute(struct kroute *kr) +{ + if (kr->flags & F_RIPD_INSERTED) + return (1); + + /* XXX this is funky, it is not possible to distribute static and + * connected. OSPFD has a much better way to do this including rtlabel + * support + */ + switch (conf->redistribute_flags) { + case REDISTRIBUTE_NONE: + return (0); + case REDISTRIBUTE_STATIC: + return (kr->flags & F_KERNEL ? 1 : 0); + case REDISTRIBUTE_CONNECTED: + return (kr->flags & F_CONNECTED ? 1 : 0); + case REDISTRIBUTE_DEFAULT: + if (kr->prefix.s_addr == INADDR_ANY && + kr->netmask.s_addr == INADDR_ANY) + return (1); + else + return (0); + default: + fatalx("unknown redistribute type"); + } +} + +/* this needs to be added here so that ripctl can be used without libevent */ +void +imsg_event_add(struct imsgbuf *ibuf) +{ + ibuf->events = EV_READ; + if (ibuf->w.queued) + ibuf->events |= EV_WRITE; + + event_del(&ibuf->ev); + event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf); + event_add(&ibuf->ev, NULL); +} |