/* $OpenBSD: rde.c,v 1.13 2009/03/14 15:32:55 michele Exp $ */ /* * Copyright (c) 2004, 2005 Claudio Jeker * Copyright (c) 2004, 2005, 2006 Esben Norby * Copyright (c) 2003, 2004 Henning Brauer * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "igmp.h" #include "dvmrp.h" #include "dvmrpd.h" #include "dvmrpe.h" #include "log.h" #include "rde.h" void rde_sig_handler(int sig, short, void *); void rde_shutdown(void); void rde_dispatch_imsg(int, short, void *); volatile sig_atomic_t rde_quit = 0; struct dvmrpd_conf *rdeconf = NULL; struct rde_nbr *nbrself; struct imsgbuf *ibuf_dvmrpe; struct imsgbuf *ibuf_main; void rde_sig_handler(int sig, short event, void *arg) { /* * signal handler rules don't apply, libevent decouples for us */ switch (sig) { case SIGINT: case SIGTERM: rde_shutdown(); /* NOTREACHED */ default: fatalx("unexpected signal"); } } /* route decision engine */ pid_t rde(struct dvmrpd_conf *xconf, int pipe_parent2rde[2], int pipe_dvmrpe2rde[2], int pipe_parent2dvmrpe[2]) { struct passwd *pw; struct event ev_sigint, ev_sigterm; pid_t pid; switch (pid = fork()) { case -1: fatal("cannot fork"); case 0: break; default: return (pid); } rdeconf = xconf; if ((pw = getpwnam(DVMRPD_USER)) == NULL) fatal("getpwnam"); if (chroot(pw->pw_dir) == -1) fatal("chroot"); if (chdir("/") == -1) fatal("chdir(\"/\")"); setproctitle("route decision engine"); dvmrpd_process = PROC_RDE_ENGINE; if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("can't drop privileges"); endpwent(); event_init(); /* setup signal handler */ signal_set(&ev_sigint, SIGINT, rde_sig_handler, NULL); signal_set(&ev_sigterm, SIGTERM, rde_sig_handler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal(SIGPIPE, SIG_IGN); /* setup pipes */ close(pipe_dvmrpe2rde[0]); close(pipe_parent2rde[0]); close(pipe_parent2dvmrpe[0]); close(pipe_parent2dvmrpe[1]); if ((ibuf_dvmrpe = malloc(sizeof(struct imsgbuf))) == NULL || (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) fatal(NULL); imsg_init(ibuf_dvmrpe, pipe_dvmrpe2rde[1], rde_dispatch_imsg); imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_imsg); /* setup event handler */ ibuf_dvmrpe->events = EV_READ; event_set(&ibuf_dvmrpe->ev, ibuf_dvmrpe->fd, ibuf_dvmrpe->events, ibuf_dvmrpe->handler, ibuf_dvmrpe); event_add(&ibuf_dvmrpe->ev, NULL); ibuf_main->events = EV_READ; event_set(&ibuf_main->ev, ibuf_main->fd, ibuf_main->events, ibuf_main->handler, ibuf_main); event_add(&ibuf_main->ev, NULL); rt_init(); mfc_init(); event_dispatch(); rde_shutdown(); /* NOTREACHED */ return (0); } void rde_shutdown(void) { struct iface *iface; rt_clear(); mfc_clear(); LIST_FOREACH(iface, &rdeconf->iface_list, entry) { if_del(iface); } msgbuf_clear(&ibuf_dvmrpe->w); free(ibuf_dvmrpe); msgbuf_clear(&ibuf_main->w); free(ibuf_main); free(rdeconf); log_info("route decision engine exiting"); _exit(0); } /* imesg */ int rde_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen) { return (imsg_compose(ibuf_main, type, 0, pid, data, datalen)); } int rde_imsg_compose_dvmrpe(int type, u_int32_t peerid, pid_t pid, void *data, u_int16_t datalen) { return (imsg_compose(ibuf_dvmrpe, type, peerid, pid, data, datalen)); } void rde_dispatch_imsg(int fd, short event, void *bula) { struct mfc mfc; struct prune p; struct imsgbuf *ibuf = bula; struct imsg imsg; struct route_report rr; int i, n, connected = 0; struct iface *iface; 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("rde_dispatch_imsg: imsg_read error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_CTL_SHOW_RIB: rt_dump(imsg.hdr.pid); imsg_compose(ibuf_dvmrpe, IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; case IMSG_CTL_SHOW_MFC: mfc_dump(imsg.hdr.pid); imsg_compose(ibuf_dvmrpe, IMSG_CTL_END, 0, imsg.hdr.pid, NULL, 0); break; case IMSG_ROUTE_REPORT: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) fatalx("invalid size of OE request"); memcpy(&rr, imsg.data, sizeof(rr)); /* directly connected networks from parent */ if (imsg.hdr.peerid == 0) connected = 1; if (srt_check_route(&rr, connected) == -1) log_debug("rde_dispatch_imsg: " "packet malformed"); break; case IMSG_FULL_ROUTE_REPORT: rt_snap(imsg.hdr.peerid); rde_imsg_compose_dvmrpe(IMSG_FULL_ROUTE_REPORT_END, imsg.hdr.peerid, 0, NULL, 0); break; case IMSG_MFC_ADD: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) fatalx("invalid size of OE request"); memcpy(&mfc, imsg.data, sizeof(mfc)); #if 1 for (i = 0; i < MAXVIFS; i++) mfc.ttls[i] = 0; LIST_FOREACH(iface, &rdeconf->iface_list, entry) { if (mfc.ifindex != iface->ifindex) mfc.ttls[iface->ifindex] = 1; } mfc_update(&mfc); #endif break; case IMSG_MFC_DEL: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) fatalx("invalid size of OE request"); memcpy(&mfc, imsg.data, sizeof(mfc)); #if 1 mfc_delete(&mfc); #endif break; case IMSG_GROUP_ADD: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) fatalx("invalid size of OE request"); memcpy(&mfc, imsg.data, sizeof(mfc)); iface = if_find_index(mfc.ifindex); if (iface == NULL) { fatalx("rde_dispatch_imsg: " "cannot find matching interface"); } rde_group_list_add(iface, mfc.group); break; case IMSG_GROUP_DEL: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) fatalx("invalid size of OE request"); memcpy(&mfc, imsg.data, sizeof(mfc)); iface = if_find_index(mfc.ifindex); if (iface == NULL) { fatalx("rde_dispatch_imsg: " "cannot find matching interface"); } rde_group_list_remove(iface, mfc.group); break; case IMSG_RECV_PRUNE: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(p)) fatalx("invalid size of OE request"); memcpy(&p, imsg.data, sizeof(p)); break; default: log_debug("rde_dispatch_msg: unexpected imsg %d", imsg.hdr.type); break; } imsg_free(&imsg); } imsg_event_add(ibuf); } /* rde group functions */ void rde_group_list_add(struct iface *iface, struct in_addr group) { struct rde_group *rdegrp; /* validate group id */ if (!IN_MULTICAST(htonl(group.s_addr))) { log_debug("rde_group_list_add: interface %s, %s is not a " "multicast address", iface->name, inet_ntoa(group)); return; } if (rde_group_list_find(iface, group)) return; rdegrp = calloc(1, sizeof(*rdegrp)); if (rdegrp == NULL) fatal("rde_group_list_add"); rdegrp->rde_group.s_addr = group.s_addr; TAILQ_INSERT_TAIL(&iface->rde_group_list, rdegrp, entry); log_debug("rde_group_list_add: interface %s, group %s", iface->name, inet_ntoa(rdegrp->rde_group)); return; } int rde_group_list_find(struct iface *iface, struct in_addr group) { struct rde_group *rdegrp = NULL; /* validate group id */ if (!IN_MULTICAST(htonl(group.s_addr))) { log_debug("rde_group_list_find: interface %s, %s is not a " "multicast address", iface->name, inet_ntoa(group)); return (0); } TAILQ_FOREACH(rdegrp, &iface->rde_group_list, entry) { if (rdegrp->rde_group.s_addr == group.s_addr) return (1); } return (0); } void rde_group_list_remove(struct iface *iface, struct in_addr group) { struct rde_group *rg; struct rt_node *rn; if (TAILQ_EMPTY(&iface->rde_group_list)) fatalx("rde_group_list_remove: group does not exist"); for(rg = TAILQ_FIRST(&iface->rde_group_list); rg != NULL; rg = TAILQ_NEXT(rg, entry)) { if (rg->rde_group.s_addr == group.s_addr) { log_debug("group_list_remove: interface %s, group %s", iface->name, inet_ntoa(rg->rde_group)); TAILQ_REMOVE(&iface->rde_group_list, rg, entry); free(rg); } } rn = mfc_find_origin(group); if (rn == NULL) return; srt_check_downstream_ifaces(rn, iface); }