diff options
Diffstat (limited to 'usr.sbin')
36 files changed, 8493 insertions, 0 deletions
diff --git a/usr.sbin/dvmrpd/Makefile b/usr.sbin/dvmrpd/Makefile new file mode 100644 index 00000000000..13dcb621791 --- /dev/null +++ b/usr.sbin/dvmrpd/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1 2006/06/01 14:12:20 norby Exp $ + +PROG= dvmrpd +SRCS= ask_nbrs2.c buffer.c control.c dvmrpd.c dvmrpe.c graft.c graft_ack.c \ + group.c igmp.c imsg.c interface.c in_cksum.c kmroute.c kroute.c log.c \ + nbrs2.c neighbor.c packet.c parse.y printconf.c probe.c prune.c rde.c \ + rde_mfc.c rde_srt.c report.c + +MAN= dvmrpd.8 dvmrpd.conf.5 + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare -g +YFLAGS= +LDADD+= -levent + +.include <bsd.prog.mk> diff --git a/usr.sbin/dvmrpd/ask_nbrs2.c b/usr.sbin/dvmrpd/ask_nbrs2.c new file mode 100644 index 00000000000..2080a0a253b --- /dev/null +++ b/usr.sbin/dvmrpd/ask_nbrs2.c @@ -0,0 +1,80 @@ +/* $OpenBSD: ask_nbrs2.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +/* DVMRP ask neighbors2 packet handling */ +int +send_ask_nbrs2(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_ask_nbrs2: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_ask_nbrs2"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK)) + goto fail; + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_ask_nbrs2"); + buf_free(buf); + return (-1); +} + +void +recv_ask_nbrs2(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_ask_nbrs2: neighbor ID %s", inet_ntoa(nbr->id)); + + return; +} diff --git a/usr.sbin/dvmrpd/buffer.c b/usr.sbin/dvmrpd/buffer.c new file mode 100644 index 00000000000..751e0ac45eb --- /dev/null +++ b/usr.sbin/dvmrpd/buffer.c @@ -0,0 +1,218 @@ +/* $OpenBSD: buffer.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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/uio.h> + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dvmrpd.h" + +int buf_realloc(struct buf *, size_t); +void buf_enqueue(struct msgbuf *, struct buf *); +void buf_dequeue(struct msgbuf *, struct buf *); + +struct buf * +buf_open(size_t len) +{ + struct buf *buf; + + if ((buf = calloc(1, sizeof(struct buf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = buf->max = len; + + return (buf); +} + +struct buf * +buf_dynamic(size_t len, size_t max) +{ + struct buf *buf; + + if (max < len) + return (NULL); + + if ((buf = buf_open(len)) == NULL) + return (NULL); + + if (max > 0) + buf->max = max; + + return (buf); +} + +int +buf_realloc(struct buf *buf, size_t len) +{ + u_char *b; + + /* on static buffers max is eq size and so the following fails */ + if (buf->wpos + len > buf->max) { + errno = ENOMEM; + return (-1); + } + + b = realloc(buf->buf, buf->wpos + len); + if (b == NULL) + return (-1); + buf->buf = b; + buf->size = buf->wpos + len; + + return (0); +} + +int +buf_add(struct buf *buf, void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + if (buf_realloc(buf, len) == -1) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +buf_reserve(struct buf *buf, size_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + if (buf_realloc(buf, len) == -1) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +void * +buf_seek(struct buf *buf, size_t pos, size_t len) +{ + /* only allowed to seek in already written parts */ + if (pos + len > buf->wpos) + return (NULL); + + return (buf->buf + pos); +} + +int +buf_close(struct msgbuf *msgbuf, struct buf *buf) +{ + buf_enqueue(msgbuf, buf); + return (1); +} + +void +buf_free(struct buf *buf) +{ + free(buf->buf); + free(buf); +} + +void +msgbuf_init(struct msgbuf *msgbuf) +{ + msgbuf->queued = 0; + msgbuf->fd = -1; + TAILQ_INIT(&msgbuf->bufs); +} + +void +msgbuf_clear(struct msgbuf *msgbuf) +{ + struct buf *buf; + + while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + buf_dequeue(msgbuf, buf); +} + +int +msgbuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct buf *buf, *next; + int i = 0; + ssize_t n; + struct msghdr msg; + + bzero(&iov, sizeof(iov)); + bzero(&msg, sizeof(msg)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->size - buf->rpos; + i++; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS || + errno == EINTR) /* try later */ + return (0); + else + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (-2); + } + + for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; + buf = next) { + next = TAILQ_NEXT(buf, entry); + if (buf->rpos + n >= buf->size) { + n -= buf->size - buf->rpos; + buf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } + + return (0); +} + +void +buf_enqueue(struct msgbuf *msgbuf, struct buf *buf) +{ + TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); + msgbuf->queued++; +} + +void +buf_dequeue(struct msgbuf *msgbuf, struct buf *buf) +{ + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); + msgbuf->queued--; + buf_free(buf); +} diff --git a/usr.sbin/dvmrpd/control.c b/usr.sbin/dvmrpd/control.c new file mode 100644 index 00000000000..04664401437 --- /dev/null +++ b/usr.sbin/dvmrpd/control.c @@ -0,0 +1,297 @@ +/* $OpenBSD: control.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" +#include "control.h" + +#define CONTROL_BACKLOG 5 + +int control_imsg_relay(struct imsg *imsg); + +struct ctl_conn *control_connbyfd(int); +struct ctl_conn *control_connbypid(pid_t); +void control_close(int); + +int +control_init(void) +{ + struct sockaddr_un sun; + int fd; + mode_t old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + log_warn("control_init: socket"); + return (-1); + } + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, DVMRPD_SOCKET, sizeof(sun.sun_path)); + + if (unlink(DVMRPD_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("control_init: unlink %s", DVMRPD_SOCKET); + close(fd); + return (-1); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("control_init: bind: %s", DVMRPD_SOCKET); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(DVMRPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("control_init: chmod"); + close(fd); + (void)unlink(DVMRPD_SOCKET); + return (-1); + } + + session_socket_blockmode(fd, BM_NONBLOCK); + control_state.fd = fd; + + return (0); +} + +int +control_listen(void) +{ + + if (listen(control_state.fd, CONTROL_BACKLOG) == -1) { + log_warn("control_listen: listen"); + return (-1); + } + + event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST, + control_accept, NULL); + event_add(&control_state.ev, NULL); + + return (0); +} + +void +control_cleanup(void) +{ + unlink(DVMRPD_SOCKET); +} + +/* ARGSUSED */ +void +control_accept(int listenfd, short event, void *bula) +{ + int connfd; + socklen_t len; + struct sockaddr_un sun; + struct ctl_conn *c; + + len = sizeof(sun); + if ((connfd = accept(listenfd, + (struct sockaddr *)&sun, &len)) == -1) { + if (errno != EWOULDBLOCK && errno != EINTR) + log_warn("control_accept"); + return; + } + + session_socket_blockmode(connfd, BM_NONBLOCK); + + if ((c = malloc(sizeof(struct ctl_conn))) == NULL) { + log_warn("control_accept"); + return; + } + + imsg_init(&c->ibuf, connfd, control_dispatch_imsg); + c->ibuf.events = EV_READ; + event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events, + c->ibuf.handler, &c->ibuf); + event_add(&c->ibuf.ev, NULL); + + TAILQ_INSERT_TAIL(&ctl_conns, c, entry); +} + +struct ctl_conn * +control_connbyfd(int fd) +{ + struct ctl_conn *c; + + for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd; + c = TAILQ_NEXT(c, entry)) + ; /* nothing */ + + return (c); +} + +struct ctl_conn * +control_connbypid(pid_t pid) +{ + struct ctl_conn *c; + + for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.pid != pid; + c = TAILQ_NEXT(c, entry)) + ; /* nothing */ + + return (c); +} + +void +control_close(int fd) +{ + struct ctl_conn *c; + + if ((c = control_connbyfd(fd)) == NULL) + log_warn("control_close: fd %d: not found", fd); + + msgbuf_clear(&c->ibuf.w); + TAILQ_REMOVE(&ctl_conns, c, entry); + + event_del(&c->ibuf.ev); + close(c->ibuf.fd); + free(c); +} + +/* ARGSUSED */ +void +control_dispatch_imsg(int fd, short event, void *bula) +{ + struct ctl_conn *c; + struct imsg imsg; + int n; + unsigned int ifidx; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warn("control_dispatch_imsg: fd %d: not found", fd); + return; + } + + switch (event) { + case EV_READ: + if ((n = imsg_read(&c->ibuf)) <= 0) { + control_close(fd); + return; + } + break; + case EV_WRITE: + if (msgbuf_write(&c->ibuf.w) < 0) { + control_close(fd); + return; + } + imsg_event_add(&c->ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { + control_close(fd); + return; + } + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_MFC_COUPLE: + case IMSG_CTL_MFC_DECOUPLE: + case IMSG_CTL_RELOAD: + dvmrpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + break; + case IMSG_CTL_SHOW_IFACE: + if (imsg.hdr.len == IMSG_HEADER_SIZE + + sizeof(ifidx)) { + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + dvmrpe_iface_ctl(c, ifidx); + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, + 0, NULL, 0); + } + break; + case IMSG_CTL_SHOW_IGMP: + if (imsg.hdr.len == IMSG_HEADER_SIZE + + sizeof(ifidx)) { + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + dvmrpe_iface_igmp_ctl(c, ifidx); + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, + 0, NULL, 0); + } + break; + case IMSG_CTL_SHOW_NBR: + dvmrpe_nbr_ctl(c); + break; + case IMSG_CTL_SHOW_RIB: + case IMSG_CTL_SHOW_MFC: + case IMSG_CTL_SHOW_SUM: + c->ibuf.pid = imsg.hdr.pid; + dvmrpe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + break; + default: + log_debug("control_dispatch_imsg: " + "error handling imsg %d", imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + + imsg_event_add(&c->ibuf); +} + +int +control_imsg_relay(struct imsg *imsg) +{ + struct ctl_conn *c; + + if ((c = control_connbypid(imsg->hdr.pid)) == NULL) + return (0); + + return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, + imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); +} + +void +session_socket_blockmode(int fd, enum blockmodes bm) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + fatal("fnctl F_GETFL"); + + if (bm == BM_NONBLOCK) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + fatal("fnctl F_SETFL"); +} diff --git a/usr.sbin/dvmrpd/control.h b/usr.sbin/dvmrpd/control.h new file mode 100644 index 00000000000..d7518e5275a --- /dev/null +++ b/usr.sbin/dvmrpd/control.h @@ -0,0 +1,50 @@ +/* $OpenBSD: control.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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. + */ + +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include <sys/queue.h> +#include <sys/time.h> +#include <event.h> + +struct { + struct event ev; + int fd; +} control_state; + +enum blockmodes { + BM_NORMAL, + BM_NONBLOCK +}; + +struct ctl_conn { + TAILQ_ENTRY(ctl_conn) entry; + struct imsgbuf ibuf; +}; + +int control_init(void); +int control_listen(void); +void control_accept(int, short, void *); +void control_dispatch_imsg(int, short, void *); +int control_imsg_relay(struct imsg *); +void control_cleanup(void); + +void session_socket_blockmode(int, enum blockmodes); + +#endif /* _CONTROL_H_ */ diff --git a/usr.sbin/dvmrpd/dvmrp.h b/usr.sbin/dvmrpd/dvmrp.h new file mode 100644 index 00000000000..cc8e264df2c --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrp.h @@ -0,0 +1,129 @@ +/* $OpenBSD: dvmrp.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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. + */ + +/* DVMRP protocol definitions */ + +#ifndef _DVMRP_H_ +#define _DVMRP_H_ + +/* misc */ +#define IPPROTO_DVMRP 2 +#define AllDVMRPRouters "224.0.0.4" + +#define DEFAULT_PROBE_INTERVAL 10 +#define NBR_TMOUT 35 +#define MIN_FLASH_UPDATE_INTERVAL 5 +#define ROUTE_REPORT_INTERVAL 60 +#define ROUTE_EXPIRATION_TIME 140 +#define ROUTE_HOLD_DOWN 2 * ROUTE_REPORT_INTERVAL +#define MAX_PRUNE_LIFETIME 2 * 3600 /* 2 hours */ +#define PRUNE_RETRANS_TIME 3 /* with exp. back off */ +#define GRAFT_RETRANS_TIME 5 /* with exp. back off */ + +#define DEFAULT_NBR_TMOUT 86400 /* 24 hours */ + /* remove neighbor completely */ + +#define DEFAULT_CACHE_LIFETIME 300 + +#define DEFAULT_METRIC 1 +#define MIN_METRIC 1 +#define MAX_METRIC 31 +#define INFINITY_METRIC 31 + +#define LAST_MASK 0x80 /* route reports */ +#define METRIC_MASK ~LAST_MASK /* route reports */ + +#define DVMRP_MAJOR_VERSION 3 +#define DVMRP_MINOR_VERSION 255 + +/* DVMRP packet types */ +#define PKT_TYPE_DVMRP 0x13 + +#define DVMRP_CODE_PROBE 0x01 +#define DVMRP_CODE_REPORT 0x02 +#define DVMRP_CODE_ASK_NBRS 0x03 /* obsolete */ +#define DVMRP_CODE_NBRS 0x04 /* obsolete */ +#define DVMRP_CODE_ASK_NBRS2 0x05 +#define DVMRP_CODE_NBRS2 0x06 +#define DVMRP_CODE_PRUNE 0x07 +#define DVMRP_CODE_GRAFT 0x08 +#define DVMRP_CODE_GRAFT_ACK 0x09 + +/* DVMRP command types */ +#define DVMRP_CMD_NULL 0 +#define DVMRP_CMD_AF_INDICATOR 2 +#define DVMRP_CMD_SUBNETMASK 3 +#define DVMRP_CMD_METRIC 4 +#define DVMRP_CMD_FLAGS0 5 +#define DVMRP_CMD_INFINITY 6 +#define DVMRP_CMD_DEST_ADDR 7 +#define DVMRP_CMD_REQ_DEST_ADDR 8 +#define DVMRP_CMD_NON_MEM_REPORT 9 +#define DVMRP_CMD_NON_MEM_CANCEL 10 + +/* DVMRP capabilities */ +#define DVMRP_CAP_LEAF 0x01 +#define DVMRP_CAP_PRUNE 0x02 +#define DVMRP_CAP_GENID 0x04 +#define DVMRP_CAP_MTRACE 0x08 +#define DVMRP_CAP_SNMP 0x10 +#define DVMRP_CAP_NETMASK 0x20 +#define DVMRP_CAP_DEFAULT (DVMRP_CAP_PRUNE | DVMRP_CAP_GENID | \ + DVMRP_CAP_MTRACE) + +/* DVMRP header */ +struct dvmrp_hdr { + u_int8_t type; + u_int8_t code; + u_int16_t chksum; + u_int8_t dummy; + u_int8_t capabilities; + u_int8_t minor_version; + u_int8_t major_version; +}; + +/* Prune header */ +struct prune_hdr { + u_int32_t src_host_addr; + u_int32_t group_addr; + u_int32_t lifetime; + u_int32_t src_netmask; +}; + +/* Graft and Graft Ack header */ +struct graft_hdr { + u_int32_t src_host_addr; + u_int32_t group_addr; + u_int32_t src_netmask; +}; + +struct igmpmsg { + u_int32_t unused1; + u_int32_t unused2; + u_int8_t im_msgtype; /* what type of message */ +#define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */ +#define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */ +#define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */ +#define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */ + u_int8_t im_mbz; /* must be zero */ + u_int8_t im_vif; /* vif rec'd on */ + u_int8_t unused3; + struct in_addr im_src, im_dst; +}; + +#endif /* !_DVMRP_H_ */ diff --git a/usr.sbin/dvmrpd/dvmrpd.8 b/usr.sbin/dvmrpd/dvmrpd.8 new file mode 100644 index 00000000000..0a5e629b505 --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpd.8 @@ -0,0 +1,78 @@ +.\" $OpenBSD: dvmrpd.8,v 1.1 2006/06/01 14:12:20 norby Exp $ +.\" +.\" Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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. +.\" +.Dd February 27, 2006 +.Dt DVMRPD 8 +.Os +.Sh NAME +.Nm dvmrpd +.Nd "Distance Vector Multicast Routing Protocol daemon" +.Sh SYNOPSIS +.Nm +.Op Fl dnv +.Op Fl f Ar file +.Sh DESCRIPTION +.Nm +is an Distance Vector Multicast Routing Protocol +.Pq DVMRP +daemon which manages multicast routing tables. +Its main purpose is to keep multicast routing tables in sync. +.Pp +A running +.Nm +can be controlled with the +.Xr dvmrpctl 8 +utility. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground and log to +.Em stderr . +.It Fl f Ar file +Specify an alternative configuration file. +.It Fl n +Configtest mode. +Only check the configuration file for validity. +.It Fl v +Produce more verbose output. +.El +.Sh FILES +.Bl -tag -width "/var/run/dvmrpd.sockXX" -compact +.It /etc/dvmrpd.conf +Default +.Nm +configuration file. +.It /var/run/dvmrpd.sock +Unix-domain socket used for communication with +.Xr dvmrpctl 8 . +.El +.Sh SEE ALSO +.Xr dvmrpd.conf 5 , +.Xr dvmrpctl 8 +.Rs +.%R "draft-ietf-idmr-dvmrp-v3-11" +.%T "DVMRP Version 3" +.%D August 2000 +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 4.0 . diff --git a/usr.sbin/dvmrpd/dvmrpd.c b/usr.sbin/dvmrpd/dvmrpd.c new file mode 100644 index 00000000000..62a1fe8ddca --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpd.c @@ -0,0 +1,497 @@ +/* $OpenBSD: dvmrpd.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2005, 2006 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/param.h> +#include <sys/sysctl.h> +#include <sys/wait.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 <util.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "control.h" +#include "log.h" +#include "rde.h" + +void main_sig_handler(int, short, void *); +__dead void usage(void); +void dvmrpd_shutdown(void); +int check_child(pid_t, const char *); + +void main_dispatch_dvmrpe(int, short, void *); +void main_dispatch_rde(int, short, void *); +void main_imsg_compose_dvmrpe(int, pid_t, void *, u_int16_t); +void main_imsg_compose_rde(int, pid_t, void *, u_int16_t); + +int check_file_secrecy(int, const char *); + +int pipe_parent2dvmrpe[2]; +int pipe_parent2rde[2]; +int pipe_dvmrpe2rde[2]; + +struct dvmrpd_conf *conf = NULL; +struct imsgbuf *ibuf_dvmrpe; +struct imsgbuf *ibuf_rde; + +pid_t dvmrpe_pid; +pid_t rde_pid; + +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(dvmrpe_pid, "dvmrp engine")) { + dvmrpe_pid = 0; + die = 1; + } + if (check_child(rde_pid, "route decision engine")) { + rde_pid = 0; + die = 1; + } + if (die) + dvmrpd_shutdown(); + break; + case SIGHUP: + /* reconfigure */ + /* ... */ + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dnv] [-f file]\n", __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + char *conffile; + int ch, opts = 0; + int debug = 0; + int ipmforwarding; + int mib[4]; + size_t len; + + conffile = CONF_FILE; + dvmrpd_process = PROC_MAIN; + + /* start logging */ + log_init(1); + + while ((ch = getopt(argc, argv, "df:nv")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'f': + conffile = optarg; + break; + case 'n': + opts |= DVMRPD_OPT_NOACTION; + break; + case 'v': + if (opts & DVMRPD_OPT_VERBOSE) + opts |= DVMRPD_OPT_VERBOSE2; + opts |= DVMRPD_OPT_VERBOSE; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + log_init(debug); + + /* multicast IP forwarding must be enabled */ + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_MFORWARDING; + len = sizeof(ipmforwarding); + if (sysctl(mib, 4, &ipmforwarding, &len, NULL, 0) == -1) + err(1, "sysctl"); + + if (!ipmforwarding) + errx(1, "multicast IP forwarding not enabled"); + + /* fetch interfaces early */ + kif_init(); + + /* parse config file */ + if ((conf = parse_config(conffile, opts)) == NULL ) + exit(1); + + if (conf->opts & DVMRPD_OPT_NOACTION) { + if (conf->opts & DVMRPD_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 dvmrpd user */ + if (getpwnam(DVMRPD_USER) == NULL) + errx(1, "unknown user %s", DVMRPD_USER); + + endpwent(); + + if (!debug) + daemon(1, 0); + + log_info("startup"); + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, + pipe_parent2dvmrpe) == -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_dvmrpe2rde) == -1) + fatal("socketpair"); + session_socket_blockmode(pipe_parent2dvmrpe[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2dvmrpe[1], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK); + session_socket_blockmode(pipe_dvmrpe2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_dvmrpe2rde[1], BM_NONBLOCK); + + /* start children */ + rde_pid = rde(conf, pipe_parent2rde, pipe_dvmrpe2rde, + pipe_parent2dvmrpe); + dvmrpe_pid = dvmrpe(conf, pipe_parent2dvmrpe, pipe_dvmrpe2rde, + pipe_parent2rde); + + /* create the raw ip socket */ + if ((conf->mroute_socket = socket(AF_INET, SOCK_RAW, + IPPROTO_IGMP)) == -1) + fatal("error creating raw socket"); + + if_set_recvbuf(conf->mroute_socket); + + if (mrt_init(conf->mroute_socket)) + fatal("multicast routing not enabled in kernel"); + + /* 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, SIGTERM, 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_parent2dvmrpe[1]); + close(pipe_parent2rde[1]); + close(pipe_dvmrpe2rde[0]); + close(pipe_dvmrpe2rde[1]); + + if ((ibuf_dvmrpe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_dvmrpe, pipe_parent2dvmrpe[0], main_dispatch_dvmrpe); + imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde); + + /* 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_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 (kmr_init(!(conf->flags & DVMRPD_FLAG_NO_FIB_UPDATE)) == -1) + dvmrpd_shutdown(); + + event_set(&conf->ev, conf->mroute_socket, EV_READ|EV_PERSIST, + kmr_recv_msg, conf); + event_add(&conf->ev, NULL); + + event_dispatch(); + + dvmrpd_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +dvmrpd_shutdown(void) +{ + struct iface *iface; + pid_t pid; + + if (dvmrpe_pid) + kill(dvmrpe_pid, SIGTERM); + + if (rde_pid) + kill(rde_pid, SIGTERM); + + control_cleanup(); + kmr_shutdown(); + kr_shutdown(); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + if_del(iface); + } + + mrt_done(conf->mroute_socket); + + do { + if ((pid = wait(NULL)) == -1 && + errno != EINTR && errno != ECHILD) + fatal("wait"); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + msgbuf_clear(&ibuf_dvmrpe->w); + free(ibuf_dvmrpe); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + + 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 */ +void +main_dispatch_dvmrpe(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + int 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: + log_debug("main_dispatch_dvmrpe: IMSG_CTL_RELOAD"); + /* reconfig */ + break; + case IMSG_CTL_MFC_COUPLE: + kmr_mfc_couple(); + break; + case IMSG_CTL_MFC_DECOUPLE: + kmr_mfc_decouple(); + break; + default: + log_debug("main_dispatch_dvmrpe: error handling " + "imsg %d", imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +main_dispatch_rde(int fd, short event, void *bula) +{ + struct mfc mfc; + struct imsgbuf *ibuf = bula; + struct imsg imsg; + int 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_MFC_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) + fatalx("invalid size of RDE request"); + memcpy(&mfc, imsg.data, sizeof(mfc)); + + /* add to MFC */ + mrt_add_mfc(conf->mroute_socket, &mfc); + break; + case IMSG_MFC_DEL: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(mfc)) + fatalx("invalid size of RDE request"); + memcpy(&mfc, imsg.data, sizeof(mfc)); + + /* remove from MFC */ + mrt_del_mfc(conf->mroute_socket, &mfc); + 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_dvmrpe(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_dvmrpe, 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); +} + +/* this needs to be added here so that dvmrpctl 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); +} diff --git a/usr.sbin/dvmrpd/dvmrpd.conf.5 b/usr.sbin/dvmrpd/dvmrpd.conf.5 new file mode 100644 index 00000000000..8843d29c7fb --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpd.conf.5 @@ -0,0 +1,167 @@ +.\" $OpenBSD: dvmrpd.conf.5,v 1.1 2006/06/01 14:12:20 norby Exp $ +.\" +.\" Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> +.\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> +.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> +.\" Copyright (c) 2002 Daniel Hartmeier <dhartmei@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. +.\" +.Dd February 27, 2006 +.Dt DVMRPD.CONF 5 +.Os +.Sh NAME +.Nm dvmrpd.conf +.Nd Distance Vector Multicast Routing Protocol daemon configuration file +.Sh DESCRIPTION +The +.Xr dvmrpd 8 +daemon implements the Distance Vector Multicast Routing Protocol as described +in draft-ietf-idmr-dvmrp-v3-11 DVMRP Version 3. +.Sh SECTIONS +The +.Nm +config file is divided into three main sections. +.Bl -tag -width xxxx +.It Sy Macros +User-defined variables may be defined and used later, simplifying the +configuration file. +.It Sy Global Configuration +Global settings for +.Xr dvmrpd 8 . +A number of global settings can be overruled in specific areas or interfaces. +.It Sy Groups +Groups are used to group similar interfaces, simplifying configuration. +.El +.Sh MACROS +Much like +.Xr cpp 1 +or +.Xr m4 1 , +macros can be defined that will later be expanded in context. +Macro names must start with a letter, and may contain letters, digits, +and underscores. +Macro names may not be reserved words (for example, +.Ic area , +.Ic interface , +or +.Ic hello-interval ) . +Macros are not expanded inside quotes. +.Pp +For example: +.Bd -literal -offset indent +hi="60" +group { + interface em0 { + query-interval $hi + } +} +.Ed +.Pp +The same can be accomplished by specifying the query-interval +globally or within the group declaration. +.Sh GLOBAL CONFIGURATION +All interface related settings can be configured globally, per group and per +interface. +The only settings that can be set globally and not overruled are listed below. +.Bl -tag -width Ds +.It Xo +.Ic mfc-update +.Pq Ic yes Ns \&| Ns Ic no +.Xc +If set to +.Ic yes , +the kernel Multicast Forwarding Cache is updated with information from the +daemon. +The default is +.Ic yes . +.El +.Sh GROUPS +Groups can be used to easily configure similar interfaces. +All interface specific parameters can +be configured per group, overruling the global settings. +.Bl -tag -width Ds +.It Ic group +Specify a group section, grouping one or more interfaces. +.Bd -literal -offset indent +group { + robustness 4 + + interface em0 + interface em1 +} +.Ed +.El +.Sh INTERFACES +Each interface can have several parameters configured individually, otherwise +they are inherited. +.Bd -literal -offset indent +interface em0 { +} +.Ed +.Pp +Interface specific parameters are listed below. +.Bl -tag -width Ds +.It Ic igmp-version Ar number +Do not use a a newer version than specified. Valid range 1-2. +The default version used is 2. +.It Ic last-member-query-count Ar count +The default value is equal to the default robustness variable; +valid range is 1\-255. +.It Ic last-member-query-interval Ar seconds +The default value is 10; valid range is 1\-65535. +.It Ic metric Ar cost +Set the interface metric a.k.a. cost. The default value is 1; valid range is +1-31. +.It Ic passive +Prevent transmission and reception of IGMP and DVMRP packets on this interface. +.It Ic query-interval Ar seconds +Set the query interval. +The default value is 125 seconds; valid range is 1\-65535 seconds. +.It Ic query-response-interval Ar seconds +Set the query-response-interval, note that this must be less +than the query-interval. +The default value is 100 seconds; valid range is 1\-65535 seconds. +.It Ic robustness Ar factor +Set the robustness factor, note that this value might impact other variables. +The default value is 2; valid range is 1\-4. +.It Ic startup-query-count Ar count +Set the startup-query-interval, used during startup to speed up the process of +building the IGMP table for an interface. +The default value is equal to the default robustness variable; valid range is +the same as the robustness variable. +.It Ic startup-query-interval Ar seconds +Used during startup, see the startup-query-interval. +The default value is query-interval / 4; valid range is the same as the +query-interval. +.El +.Sh FILES +.Bl -tag -width "/etc/dvmrpd.conf" -compact +.It Pa /etc/dvmrpd.conf +.Xr dvmrpd 8 +configuration file +.El +.Sh SEE ALSO +.Xr dvmrpctl 8 , +.Xr dvmrpd 8 , +.Xr rc.conf.local 8 +.Rs +.%R "draft-ietf-idmr-dvmrp-v3-11" +.%T "DVMRP Version 3" +.%D August 2000 +.Re +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 4.0 . diff --git a/usr.sbin/dvmrpd/dvmrpd.h b/usr.sbin/dvmrpd/dvmrpd.h new file mode 100644 index 00000000000..bb23678559d --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpd.h @@ -0,0 +1,447 @@ +/* $OpenBSD: dvmrpd.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2006 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. + */ + +#ifndef _DVMRPD_H_ +#define _DVMRPD_H_ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <net/if.h> +#include <netinet/in.h> +#include <event.h> + +#define CONF_FILE "/etc/dvmrpd.conf" +#define DVMRPD_SOCKET "/var/run/dvmrpd.sock" +#define DVMRPD_USER "_dvmrpd" + +#define NBR_HASHSIZE 128 + +#define NBR_IDSELF 1 +#define NBR_CNTSTART (NBR_IDSELF + 1) + +#define READ_BUF_SIZE 65535 +#define RT_BUF_SIZE 16384 + +#define DVMRPD_FLAG_NO_FIB_UPDATE 0x0001 + +#define F_DVMRPD_INSERTED 0x0001 +#define F_KERNEL 0x0002 +#define F_CONNECTED 0x0004 +#define F_DOWN 0x0010 +#define F_STATIC 0x0020 +#define F_LONGER 0x0040 + +#define MAXVIFS 32 /* XXX */ + +/* buffer */ +struct buf { + TAILQ_ENTRY(buf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; + int fd; +}; + +struct msgbuf { + u_int32_t queued; + int fd; + TAILQ_HEAD(, buf) bufs; +}; + +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 8192 + +struct buf_read { + u_char buf[READ_BUF_SIZE]; + u_char *rptr; + size_t wpos; +}; + +struct imsgbuf { + TAILQ_HEAD(, imsg_fd) fds; + struct buf_read r; + struct msgbuf w; + struct event ev; + void (*handler)(int, short, void *); + int fd; + pid_t pid; + short events; +}; + +enum imsg_type { + IMSG_NONE, + IMSG_CTL_RELOAD, + IMSG_CTL_SHOW_IFACE, + IMSG_CTL_SHOW_IGMP, + IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_RIB, + IMSG_CTL_SHOW_MFC, + IMSG_CTL_MFC_COUPLE, + IMSG_CTL_MFC_DECOUPLE, + IMSG_CTL_KROUTE, + IMSG_CTL_KROUTE_ADDR, + IMSG_CTL_IFINFO, + IMSG_CTL_SHOW_SUM, + IMSG_CTL_END, + IMSG_IFINFO, + IMSG_ROUTE_REPORT, + IMSG_FULL_ROUTE_REPORT, + IMSG_FULL_ROUTE_REPORT_END, + IMSG_MFC_ADD, + IMSG_MFC_DEL +}; + +struct imsg_hdr { + enum imsg_type type; + u_int16_t len; + u_int32_t peerid; + pid_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + void *data; +}; + +struct imsg_fd { + TAILQ_ENTRY(imsg_fd) entry; + int fd; +}; + +/* interface states */ +#define IF_STA_DOWN 0x01 +#define IF_STA_QUERIER 0x02 +#define IF_STA_NONQUERIER 0x04 +#define IF_STA_ANY 0x07 +#define IF_STA_ACTIVE (~IF_STA_DOWN) + +/* interface events */ +enum iface_event { + IF_EVT_NOTHING, + IF_EVT_UP, + IF_EVT_QTMOUT, /* query timer expired */ + IF_EVT_QRECVD, /* query received, check for lower IP */ + IF_EVT_QPRSNTTMOUT, /* other querier present timeout */ + IF_EVT_DOWN +}; + +/* interface actions */ +enum iface_action { + IF_ACT_NOTHING, + IF_ACT_STRT, + IF_ACT_QPRSNT, + IF_ACT_RST +}; + +/* interface types */ +enum iface_type { + IF_TYPE_POINTOPOINT, + IF_TYPE_BROADCAST +}; + +/* neighbor states */ +#define NBR_STA_DOWN 0x01 +#define NBR_STA_1_WAY 0x02 +#define NBR_STA_2_WAY 0x04 +#define NBR_STA_ACTIVE (~NBR_STA_DOWN) +#define NBR_STA_ANY 0xff + +struct group { + TAILQ_ENTRY(group) entry; + struct event dead_timer; + struct event v1_host_timer; + struct event retrans_timer; + + struct in_addr addr; + + struct iface *iface; + + time_t uptime; + int state; +}; + +struct mfc { + struct in_addr origin; + struct in_addr group; + u_short ifindex; + u_int8_t ttls[MAXVIFS]; +}; + +TAILQ_HEAD(rr_head, rr_entry); + +struct iface { + LIST_ENTRY(iface) entry; + struct event probe_timer; + struct event query_timer; + struct event querier_present_timer; + time_t uptime; + LIST_HEAD(, nbr) nbr_list; + TAILQ_HEAD(, group) group_list; + struct rr_head rr_list; + + char name[IF_NAMESIZE]; + struct in_addr addr; + struct in_addr dst; + struct in_addr mask; + struct in_addr querier; /* designated querier */ + + u_int32_t gen_id; + u_int32_t group_cnt; + + u_int32_t baudrate; + u_int32_t probe_interval; + + u_int32_t query_interval; + u_int32_t query_resp_interval; + u_int32_t recv_query_resp_interval; + u_int32_t group_member_interval; + u_int32_t querier_present_interval; + u_int32_t startup_query_interval; + u_int32_t startup_query_cnt; + u_int32_t last_member_query_interval; + u_int32_t last_member_query_cnt; + u_int32_t last_member_query_time; + u_int32_t v1_querier_present_tmout; + u_int32_t v1_host_present_interval; + u_int32_t startup_query_counter; /* actual counter */ + u_int32_t dead_interval; + + unsigned int ifindex; /* ifindex and vif */ + int fd; + int state; + int mtu; + u_int16_t flags; + u_int16_t metric; + enum iface_type type; + + u_int8_t robustness; + u_int8_t linkstate; + u_int8_t media_type; + u_int8_t passive; + u_int8_t igmp_version; +}; + +/* dvmrp_conf */ +enum { + PROC_MAIN, + PROC_DVMRP_ENGINE, + PROC_RDE_ENGINE +} dvmrpd_process; + +struct dvmrpd_conf { + struct event ev; + struct event report_timer; + u_int32_t gen_id; + u_int32_t opts; +#define DVMRPD_OPT_VERBOSE 0x00000001 +#define DVMRPD_OPT_VERBOSE2 0x00000002 +#define DVMRPD_OPT_NOACTION 0x00000004 + int maxdepth; + LIST_HEAD(, iface) iface_list; + int dvmrp_socket; + int mroute_socket; + int flags; +}; + +/* kroute */ +struct kroute { + struct in_addr prefix; + struct in_addr nexthop; + u_int16_t flags; + u_short ifindex; + u_int8_t prefixlen; +}; + +struct kif { + char ifname[IF_NAMESIZE]; + u_long baudrate; + int flags; + int mtu; + u_short ifindex; + u_int8_t media_type; + u_int8_t link_state; + u_int8_t nh_reachable; /* for nexthop verification */ +}; + +/* control data structures */ +struct ctl_iface { + char name[IF_NAMESIZE]; + struct in_addr addr; + struct in_addr mask; + struct in_addr querier; + + time_t probe_timer; + time_t query_timer; + time_t querier_present_timer; + time_t uptime; + + u_int32_t gen_id; + u_int32_t group_cnt; + u_int32_t baudrate; + u_int32_t probe_interval; + u_int32_t query_interval; + u_int32_t query_resp_interval; + u_int32_t recv_query_resp_interval; + u_int32_t group_member_interval; + u_int32_t querier_present_interval; + u_int32_t startup_query_interval; + u_int32_t startup_query_cnt; + u_int32_t last_member_query_interval; + u_int32_t last_member_query_cnt; + u_int32_t last_member_query_time; + u_int32_t v1_querier_present_tmout; + u_int32_t v1_host_present_interval; + u_int32_t dead_interval; + + unsigned int ifindex; + int state; + int mtu; + int nbr_cnt; + int adj_cnt; + + u_int16_t flags; + u_int16_t metric; + enum iface_type type; + u_int8_t robustness; + u_int8_t linkstate; + u_int8_t mediatype; + u_int8_t passive; + u_int8_t igmp_version; +}; + +struct ctl_group { + time_t dead_timer; + time_t v1_host_timer; + time_t retrans_timer; + time_t uptime; + struct in_addr addr; + int state; +}; + +struct ctl_nbr { + char name[IF_NAMESIZE]; + struct in_addr id; + struct in_addr addr; + time_t dead_timer; + time_t uptime; + int state; +}; + +struct ctl_rt { + struct in_addr prefix; + struct in_addr nexthop; + struct in_addr area; + struct in_addr adv_rtr; + time_t uptime; + time_t expire; + u_int32_t cost; + u_int8_t flags; + u_int8_t prefixlen; +}; + +struct ctl_mfc { + u_int8_t ttls[MAXVIFS]; /* outgoing vif(s) */ + struct in_addr origin; + struct in_addr group; + time_t uptime; + time_t expire; + u_short ifindex; /* incoming vif */ +}; + +struct ctl_sum { + struct in_addr rtr_id; + u_int32_t hold_time; +}; + +/* buffer.c */ +struct buf *buf_open(size_t); +struct buf *buf_dynamic(size_t, size_t); +int buf_add(struct buf *, void *, size_t); +void *buf_reserve(struct buf *, size_t); +void *buf_seek(struct buf *, size_t, size_t); +int buf_close(struct msgbuf *, struct buf *); +void buf_free(struct buf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); + +/* dvmrpd.c */ +void main_imsg_compose_dvmrpe(int, pid_t, void *, u_int16_t); + +/* parse.y */ +struct dvmrpd_conf *parse_config(char *, int); +int cmdline_symset(char *); + +/* imsg.c */ +void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *)); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + void *, u_int16_t); +struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + u_int16_t); +int imsg_add(struct buf *, void *, u_int16_t); +int imsg_close(struct imsgbuf *, struct buf *); +void imsg_free(struct imsg *); +void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */ + +/* in_cksum.c */ +u_int16_t in_cksum(void *, size_t); + +/* kroute.c */ +int kif_init(void); +void kif_clear(void); +int kr_init(int); +void kr_shutdown(void); +void kr_dispatch_msg(int, short, void *); +void kr_ifinfo(char *); +struct kif *kif_findname(char *); + +u_int8_t prefixlen_classful(in_addr_t); +u_int8_t mask2prefixlen(in_addr_t); +in_addr_t prefixlen2mask(u_int8_t); + +/* kmroute.c */ +int kmr_init(int); +void kmr_shutdown(void); +void kmr_recv_msg(int, short, void *); +void kmr_mfc_couple(void); +void kmr_mfc_decouple(void); +void kmroute_clear(void); +int mrt_init(int); +int mrt_done(int); +int mrt_add_vif(int, struct iface *); +void mrt_del_vif(int, struct iface *); +int mrt_add_mfc(int, struct mfc *); +int mrt_del_mfc(int, struct mfc *); + +/* log.h */ +const char *nbr_state_name(int); +const char *if_state_name(int); +const char *if_type_name(enum iface_type); +const char *group_state_name(int); + +/* printconf.c */ +void print_config(struct dvmrpd_conf *); + +#endif /* _DVMRPD_H_ */ diff --git a/usr.sbin/dvmrpd/dvmrpe.c b/usr.sbin/dvmrpd/dvmrpe.c new file mode 100644 index 00000000000..025d9d11d65 --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpe.c @@ -0,0 +1,411 @@ +/* $OpenBSD: dvmrpe.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2006 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 <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <pwd.h> +#include <unistd.h> +#include <event.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "dvmrpe.h" +#include "control.h" +#include "log.h" + +void dvmrpe_sig_handler(int, short, void *); +void dvmrpe_shutdown(void); + +volatile sig_atomic_t dvmrpe_quit = 0; +struct dvmrpd_conf *deconf = NULL; +struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_rde; +struct ctl_conn *ctl_conn; + +void +dvmrpe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + dvmrpe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +/* dvmrp engine */ +pid_t +dvmrpe(struct dvmrpd_conf *xconf, int pipe_parent2dvmrpe[2], + int pipe_dvmrpe2rde[2], int pipe_parent2rde[2]) +{ + struct iface *iface = NULL; + struct passwd *pw; + struct event ev_sigint, ev_sigterm; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + /* NOTREACHED */ + case 0: + break; + default: + + return (pid); + } + + /* create the raw ip socket */ + if ((xconf->dvmrp_socket = socket(AF_INET, SOCK_RAW, + IPPROTO_IGMP)) == -1) + fatal("error creating raw socket"); + + /* create dvmrpd control socket outside chroot */ + if (control_init() == -1) + fatalx("control socket setup failed"); + + /* set some defaults */ + if (if_set_mcast_ttl(xconf->dvmrp_socket, + IP_DEFAULT_MULTICAST_TTL) == -1) + fatal("if_set_mcast_ttl"); + + if (if_set_mcast_loop(xconf->dvmrp_socket) == -1) + fatal("if_set_mcast_loop"); + + if (if_set_tos(xconf->dvmrp_socket, IPTOS_PREC_INTERNETCONTROL) == -1) + fatal("if_set_tos"); + + if_set_recvbuf(xconf->dvmrp_socket); + + deconf = xconf; + + if ((pw = getpwnam(DVMRPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("dvmrp engine"); + dvmrpd_process = PROC_DVMRP_ENGINE; + + if (setgroups(1, &pw->pw_gid) || + setegid(pw->pw_gid) || setgid(pw->pw_gid) || + seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { + fatal("can't drop privileges"); + } + + event_init(); + nbr_init(NBR_HASHSIZE); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, dvmrpe_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, dvmrpe_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + + /* setup pipes */ + close(pipe_parent2dvmrpe[0]); + close(pipe_dvmrpe2rde[1]); + close(pipe_parent2rde[0]); + close(pipe_parent2rde[1]); + + if ((ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_rde, pipe_dvmrpe2rde[0], dvmrpe_dispatch_rde); + imsg_init(ibuf_main, pipe_parent2dvmrpe[1], dvmrpe_dispatch_main); + + /* setup event handler */ + 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); + + 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); + + event_set(&deconf->ev, deconf->dvmrp_socket, EV_READ|EV_PERSIST, + recv_packet, deconf); + event_add(&deconf->ev, NULL); + + /* listen on dvmrpd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("dvmrpe"); + + /* start interfaces */ + LIST_FOREACH(iface, &deconf->iface_list, entry) { + if_init(xconf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting interface %s", iface->name); + } + } + + evtimer_set(&deconf->report_timer, report_timer, deconf); + start_report_timer(); + + event_dispatch(); + + dvmrpe_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +dvmrpe_shutdown(void) +{ + struct iface *iface; + + /* stop all interfaces and delete them */ + LIST_FOREACH(iface, &deconf->iface_list, entry) { + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + if_del(iface); + } + + /* clean up */ + msgbuf_write(&ibuf_rde->w); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + msgbuf_write(&ibuf_main->w); + msgbuf_clear(&ibuf_main->w); + free(ibuf_main); + free(pkt_ptr); + + log_info("dvmrp engine exiting"); + _exit(0); +} + +/* imesg */ +int +dvmrpe_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 +dvmrpe_imsg_compose_rde(int type, u_int32_t peerid, pid_t pid, + void *data, u_int16_t datalen) +{ + return (imsg_compose(ibuf_rde, type, peerid, pid, data, datalen)); +} + +void +dvmrpe_dispatch_main(int fd, short event, void *bula) +{ + struct imsg imsg; + struct imsgbuf *ibuf = bula; + int 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("dvmrpe_dispatch_main: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + default: + log_debug("dvmrpe_dispatch_main: error handling " + "imsg %d", imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +dvmrpe_dispatch_rde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + struct nbr *nbr; + struct iface *iface; + struct route_report *rr; + int 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("dvmrpe_dispatch_rde: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_SHOW_RIB: + case IMSG_CTL_SHOW_SUM: + case IMSG_CTL_SHOW_MFC: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + case IMSG_FULL_ROUTE_REPORT: + /* add route reports to list */ + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr)) + fatalx("invalid size of RDE request"); + + if ((rr = calloc(1, sizeof(*rr))) == NULL) + fatal("dvmrpe_dispatch_rde"); + + memcpy(rr, imsg.data, sizeof(*rr)); + + /* general update, per interface */ + if (imsg.hdr.peerid == 0) { + /* add to interface list */ + LIST_FOREACH(iface, &deconf->iface_list, + entry) { + if (!if_nbr_list_empty(iface)) + rr_list_add(&iface->rr_list, + rr); + } + break; + } + + /* add to neighbor list */ + nbr = nbr_find_peerid(imsg.hdr.peerid); + rr_list_add(&nbr->rr_list, rr); + break; + case IMSG_FULL_ROUTE_REPORT_END: + /* transmit route report */ + if (imsg.hdr.peerid == 0) { + /* + * send general route report on all + * interfaces with neighbors. + */ + LIST_FOREACH(iface, &deconf->iface_list, + entry) { + rr_list_send(&iface->rr_list, + iface, NULL); + } + break; + } + + nbr = nbr_find_peerid(imsg.hdr.peerid); + rr_list_send(&nbr->rr_list, NULL, nbr); + break; + default: + log_debug("dvmrpe_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +dvmrpe_iface_ctl(struct ctl_conn *c, unsigned int idx) +{ + struct iface *iface; + struct ctl_iface *ictl; + + LIST_FOREACH(iface, &deconf->iface_list, entry) + if (idx == 0 || idx == iface->ifindex) { + ictl = if_to_ctl(iface); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IFACE, + 0, 0, ictl, sizeof(struct ctl_iface)); + } +} + +void +dvmrpe_iface_igmp_ctl(struct ctl_conn *c, unsigned int idx) +{ + struct iface *iface; + struct ctl_iface *ictl; + + LIST_FOREACH(iface, &deconf->iface_list, entry) + if (idx == 0 || idx == iface->ifindex) { + ictl = if_to_ctl(iface); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IFACE, + 0, 0, ictl, sizeof(struct ctl_iface)); + group_list_dump(iface, c); + + } +} + +void +dvmrpe_nbr_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct nbr *nbr; + struct ctl_nbr *nctl; + + LIST_FOREACH(iface, &deconf->iface_list, entry) + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + nctl = nbr_to_ctl(nbr); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NBR, 0, 0, nctl, + sizeof(struct ctl_nbr)); + } + + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, NULL, 0); +} diff --git a/usr.sbin/dvmrpd/dvmrpe.h b/usr.sbin/dvmrpd/dvmrpe.h new file mode 100644 index 00000000000..f4fead67baa --- /dev/null +++ b/usr.sbin/dvmrpd/dvmrpe.h @@ -0,0 +1,212 @@ +/* $OpenBSD: dvmrpe.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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. + */ + +#ifndef _DVMRPE_H_ +#define _DVMRPE_H_ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns; + +/* neighbor events */ +enum nbr_event { + NBR_EVT_NOTHING, + NBR_EVT_PROBE_RCVD, + NBR_EVT_1_WAY_RCVD, + NBR_EVT_2_WAY_RCVD, + NBR_EVT_KILL_NBR, + NBR_EVT_ITIMER, + NBR_EVT_LL_DOWN +}; + +/* neighbor actions */ +enum nbr_action { + NBR_ACT_NOTHING, + NBR_ACT_RST_ITIMER, + NBR_ACT_STRT_ITIMER, + NBR_ACT_RESET, + NBR_ACT_DEL, + NBR_ACT_CLR_LST +}; + +struct nbr { + LIST_ENTRY(nbr) entry, hash; + struct event inactivity_timer; + + struct rr_head rr_list; + + struct in_addr addr; + struct in_addr id; + + struct iface *iface; + + u_int32_t peerid; /* unique ID in DB */ + u_int32_t gen_id; + + time_t uptime; + + int state; + u_int8_t link_state; + u_int8_t capabilities; + u_int8_t compat; /* mrouted compat */ +}; + +struct route_report { + struct in_addr net; + struct in_addr mask; + struct in_addr nexthop; + struct in_addr adv_rtr; + u_short ifindex; + u_int8_t metric; + +}; + +struct rr_entry { + TAILQ_ENTRY(rr_entry) entry; + struct route_report *re; +}; + +/* ask_nbrs2.c */ +int send_ask_nbrs2(struct iface *, struct in_addr, void *, int); +void recv_ask_nbrs2(struct nbr *, char *, u_int16_t); + +/* dvmrpe.c */ +pid_t dvmrpe(struct dvmrpd_conf *, int[2], int[2], int[2]); +void dvmrpe_dispatch_main(int, short, void *); +void dvmrpe_dispatch_rde(int, short, void *); +int dvmrpe_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int dvmrpe_imsg_compose_rde(int, u_int32_t, pid_t, void *, u_int16_t); + +void dvmrpe_iface_ctl(struct ctl_conn *, unsigned int); +void dvmrpe_iface_igmp_ctl(struct ctl_conn *, unsigned int); +void dvmrpe_nbr_ctl(struct ctl_conn *); + +/* graft.c */ +int send_graft(struct iface *, struct in_addr, void *, int); +void recv_graft(struct nbr *, char *, u_int16_t); + +/* graft_ack.c */ +int send_graft_ack(struct iface *, struct in_addr, void *, int); +void recv_graft_ack(struct nbr *, char *, u_int16_t); + +/* group.c */ +struct ctl_group *group_to_ctl(struct group *); + +/* igmp.c */ +int group_fsm(struct group *, enum group_event); +int send_igmp_query(struct iface *, struct group *group); +void recv_igmp_query(struct iface *, struct in_addr, char *, u_int16_t); +void recv_igmp_report(struct iface *, struct in_addr, char *, u_int16_t, + u_int8_t); +void recv_igmp_leave(struct iface *, struct in_addr, char *, u_int16_t); + +struct group *group_list_add(struct iface *, u_int32_t); +void group_list_remove(struct iface *, struct group *); +struct group *group_list_find(struct iface *, u_int32_t); +void group_list_clr(struct iface *); +int group_list_empty(struct iface *); +void group_list_dump(struct iface *, struct ctl_conn *); + +const char *group_event_name(int); +const char *group_action_name(int); + +/* interface.c */ +int if_fsm(struct iface *, enum iface_event); +struct iface *if_new(struct kif *); +int if_del(struct iface *); +int if_nbr_list_empty(struct iface *); +void if_init(struct dvmrpd_conf *, struct iface *); + +const char *if_event_name(int); +const char *if_action_name(int); + +int if_set_mcast_ttl(int, u_int8_t); +int if_set_tos(int, int); +int if_set_mcast_loop(int); +void if_set_recvbuf(int); + +int if_join_group(struct iface *, struct in_addr *); +int if_leave_group(struct iface *, struct in_addr *); +int if_set_mcast(struct iface *); + +struct ctl_iface *if_to_ctl(struct iface *); + +/* nbrs2.c */ +int send_nbrs2(struct iface *, struct in_addr, void *, int); +void recv_nbrs2(struct nbr *, char *, u_int16_t); + +/* neighbor.c */ +void nbr_init(u_int32_t); +struct nbr *nbr_new(u_int32_t, struct iface *, int); +int nbr_del(struct nbr *); + +struct nbr *nbr_find_ip(struct iface *, u_int32_t); +struct nbr *nbr_find_peerid(u_int32_t); + +int nbr_fsm(struct nbr *, enum nbr_event); + +int nbr_start_itimer(struct nbr *); +int nbr_stop_itimer(struct nbr *); +int nbr_reset_itimer(struct nbr *); + +void nbr_itimer(int, short, void *); + +int nbr_act_start(struct nbr *); +int nbr_act_reset_itimer(struct nbr *); +int nbr_act_start_itimer(struct nbr *); +int nbr_act_delete(struct nbr *); +int nbr_act_clear_lists(struct nbr *); + +const char *nbr_event_name(int); +const char *nbr_action_name(int); + +struct ctl_nbr *nbr_to_ctl(struct nbr *); + +/* packet.c */ +int gen_dvmrp_hdr(struct buf *, struct iface *, u_int8_t); +int send_packet(struct iface *, void *, size_t, struct sockaddr_in *); +void recv_packet(int, short, void *); + +char *pkt_ptr; /* packet buffer */ + +/* probe.c */ +int send_probe(struct iface *); +void recv_probe(struct iface *, struct in_addr, u_int32_t, u_int8_t, + char *, u_int16_t); + +/* prune.c */ +int send_prune(struct nbr *, void *, int); +void recv_prune(struct nbr *, char *, u_int16_t); + +/* report.c */ +int send_report(struct iface *, struct in_addr, void *, int); +void recv_report(struct nbr *, char *, u_int16_t); + +void report_timer(int, short, void *); +int start_report_timer(void); +int stop_report_timer(void); + +void rr_list_add(struct rr_head *, struct route_report *); +void rr_list_clr(struct rr_head *); +void rr_list_send(struct rr_head *, struct iface *, struct nbr *); + +#endif /* _DVMRPE_H_ */ diff --git a/usr.sbin/dvmrpd/graft.c b/usr.sbin/dvmrpd/graft.c new file mode 100644 index 00000000000..1931de3e3dc --- /dev/null +++ b/usr.sbin/dvmrpd/graft.c @@ -0,0 +1,80 @@ +/* $OpenBSD: graft.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +/* DVMRP graft packet handling */ +int +send_graft(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_graft: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_graft"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT)) + goto fail; + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_graft"); + buf_free(buf); + return (-1); +} + +void +recv_graft(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_graft: neighbor ID %s", inet_ntoa(nbr->id)); + + return; +} diff --git a/usr.sbin/dvmrpd/graft_ack.c b/usr.sbin/dvmrpd/graft_ack.c new file mode 100644 index 00000000000..682293273da --- /dev/null +++ b/usr.sbin/dvmrpd/graft_ack.c @@ -0,0 +1,80 @@ +/* $OpenBSD: graft_ack.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +/* DVMRP graft ack packet handling */ +int +send_graft_ack(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_graft_ack: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_graft_ack"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK)) + goto fail; + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_graft_ack"); + buf_free(buf); + return (-1); +} + +void +recv_graft_ack(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_graft_ack: neighbor ID %s", inet_ntoa(nbr->id)); + + return; +} diff --git a/usr.sbin/dvmrpd/group.c b/usr.sbin/dvmrpd/group.c new file mode 100644 index 00000000000..99bb35b7fbd --- /dev/null +++ b/usr.sbin/dvmrpd/group.c @@ -0,0 +1,497 @@ +/* $OpenBSD: group.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 Esben Norby <norby@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 <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" +#include "control.h" + +void dead_timer(int, short, void *); +int start_dead_timer(struct group *); +int start_dead_timer_all(struct group *); +int stop_dead_timer(struct group *); + +void v1_host_timer(int, short, void *); +int start_v1_host_timer(struct group *); +int stop_v1_host_timer(struct group *); + +void retrans_timer(int, short, void *); +int start_retrans_timer(struct group *); +int stop_retrans_timer(struct group *); + +extern struct dvmrpd_conf *deconf; + +#define MAX_ACTIONS 4 + +struct { + int state; + enum group_event event; + enum group_action action[MAX_ACTIONS]; + int new_state; +} grp_fsm[] = { + /* current state event that happened action(s) to take resulting state */ + /* querier fsm */ + {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_ADD_ROUTE, + GRP_ACT_START_TMR, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_ADD_ROUTE, + GRP_ACT_START_TMR, + GRP_ACT_START_V1_HOST_TMR, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + + {GRP_STA_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE, + GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT}, + {GRP_STA_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_END}, 0}, + {GRP_STA_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_START_V1_HOST_TMR, + GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT}, + {GRP_STA_MEMB_PRSNT, GRP_EVT_LEAVE_RCVD, {GRP_ACT_START_TMR_ALL, + GRP_ACT_START_RETRANS_TMR, + GRP_ACT_SEND_GRP_QUERY, + GRP_ACT_END}, GRP_STA_CHECK_MEMB}, + + {GRP_STA_CHECK_MEMB, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + {GRP_STA_CHECK_MEMB, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE, + GRP_ACT_CLR_RETRANS_TMR, + GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT}, + {GRP_STA_CHECK_MEMB, GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY, + GRP_ACT_START_RETRANS_TMR, + GRP_ACT_END}, 0}, + {GRP_STA_CHECK_MEMB, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_START_V1_HOST_TMR, + GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT}, + + {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_START_V1_HOST_TMR, + GRP_ACT_END}, 0}, + {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_END}, 0}, + {GRP_STA_V1_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_ROUTE, + GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT}, + + /* non querier fsm */ + {GRP_STA_NO_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_ADD_ROUTE, + GRP_ACT_START_TMR, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + {GRP_STA_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_END}, 0}, + {GRP_STA_MEMB_PRSNT, GRP_EVT_QUERY_RCVD, {GRP_ACT_START_TMR_ALL, + GRP_ACT_END}, GRP_STA_CHECK_MEMB}, + + {GRP_STA_CHECK_MEMB, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR, + GRP_ACT_END}, GRP_STA_MEMB_PRSNT}, + {-1, GRP_EVT_NOTHING, {GRP_ACT_NOTHING, + GRP_ACT_END}, 0}, +}; + +const char * const group_action_names[] = { + "END MARKER", + "START TIMER", + "START ALL TIMER", + "START RETRANSMISSION TIMER", + "START V1 HOST TIMER", + "SEND GROUP QUERY", + "ADD ROUTE", + "DEL ROUTE", + "CLEAR RETRANSMISSION TIMER", + "NOTHING" +}; + +static const char * const group_event_names[] = { + "V2 REPORT RCVD", + "V1 REPORT RCVD", + "LEAVE RCVD", + "TIMER EXPIRED", + "RETRANS TIMER EXPIRED", + "V1 HOST TIMER EXPIRED", + "REPORT RCVD", + "QUERY RCVD", + "NOTHING" +}; + +int +group_fsm(struct group *group, enum group_event event) +{ + struct mfc mfc; + int old_state; + int new_state = 0; + int i, j, ret = 0; + + old_state = group->state; + + for (i = 0; grp_fsm[i].state != -1; i++) + if ((grp_fsm[i].state & old_state) && + (grp_fsm[i].event == event)) { + new_state = grp_fsm[i].new_state; + break; + } + + if (grp_fsm[i].state == -1) { + /* XXX event outside of the defined fsm, ignore it. */ + log_debug("group_fsm: group %s, event '%s' not expected in " + "state '%s'", inet_ntoa(group->addr), + group_event_name(event), group_state_name(old_state)); + return (0); + } + + for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) { + switch (grp_fsm[i].action[j]) { + case GRP_ACT_START_TMR: + ret = start_dead_timer(group); + break; + case GRP_ACT_START_TMR_ALL: + ret = start_dead_timer_all(group); + break; + case GRP_ACT_START_RETRANS_TMR: + ret = start_retrans_timer(group); + break; + case GRP_ACT_START_V1_HOST_TMR: + ret = start_v1_host_timer(group); + break; + case GRP_ACT_SEND_GRP_QUERY: + ret = send_igmp_query(group->iface, group); + break; + case GRP_ACT_ADD_ROUTE: + mfc.origin.s_addr = 0; + mfc.group = group->addr; + mfc.ifindex = group->iface->ifindex; /* XXX fixme */ + dvmrpe_imsg_compose_rde(IMSG_MFC_ADD, 0, 0, &mfc, + sizeof(mfc)); + break; + case GRP_ACT_DEL_ROUTE: + mfc.origin.s_addr = 0; + mfc.group = group->addr; + mfc.ifindex = group->iface->ifindex; /* XXX fixme */ + dvmrpe_imsg_compose_rde(IMSG_MFC_DEL, 0, 0, &mfc, + sizeof(mfc)); + break; + case GRP_ACT_CLR_RETRANS_TMR: + ret = stop_retrans_timer(group); + break; + case GRP_ACT_NOTHING: + case GRP_ACT_END: + /* do nothing */ + break; + } + + if (ret) { + log_debug("group_fsm: error changing state for " + "group %s, event '%s', state '%s'", + inet_ntoa(group->addr), group_event_name(event), + group_state_name(old_state)); + return (-1); + } + } + + if (new_state != 0) + group->state = new_state; + + for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) + log_debug("group_fsm: event '%s' resulted in action '%s' and " + "changing state for group %s from '%s' to '%s'", + group_event_name(event), + group_action_name(grp_fsm[i].action[j]), + inet_ntoa(group->addr), group_state_name(old_state), + group_state_name(group->state)); + + return (ret); +} + +/* timers */ +void +dead_timer(int fd, short event, void *arg) +{ + struct group *group = arg; + + log_debug("dead_timer: %s", inet_ntoa(group->addr)); + + group_fsm(group, GRP_EVT_TMR_EXPIRED); +} + +int +start_dead_timer(struct group *group) +{ + struct timeval tv; + + log_debug("start_dead_timer: %s", inet_ntoa(group->addr)); + + /* Group Membership Interval */ + timerclear(&tv); + tv.tv_sec = group->iface->robustness * group->iface->query_interval + + (group->iface->query_resp_interval / 2); + + return (evtimer_add(&group->dead_timer, &tv)); +} + +int +start_dead_timer_all(struct group *group) +{ + struct timeval tv; + + log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr)); + + timerclear(&tv); + if (group->iface->state == IF_STA_QUERIER) { + /* querier */ + tv.tv_sec = group->iface->last_member_query_interval * + group->iface->last_member_query_cnt; + } else { + /* non querier */ + /* XXX max response time received in packet */ + tv.tv_sec = group->iface->recv_query_resp_interval * + group->iface->last_member_query_cnt; + } + + return (evtimer_add(&group->dead_timer, &tv)); +} + +int +stop_dead_timer(struct group *group) +{ + log_debug("stop_dead_timer: %s", inet_ntoa(group->addr)); + + return (evtimer_del(&group->dead_timer)); +} + +void +v1_host_timer(int fd, short event, void *arg) +{ + struct group *group = arg; + + log_debug("v1_host_timer: %s", inet_ntoa(group->addr)); + + group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP); +} + +int +start_v1_host_timer(struct group *group) +{ + struct timeval tv; + + log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr)); + + /* Group Membership Interval */ + timerclear(&tv); + tv.tv_sec = group->iface->robustness * group->iface->query_interval + + (group->iface->query_resp_interval / 2); + + return (evtimer_add(&group->v1_host_timer, &tv)); +} + +int +stop_v1_host_timer(struct group *group) +{ + log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr)); + + return (evtimer_del(&group->v1_host_timer)); +} + +void +retrans_timer(int fd, short event, void *arg) +{ + struct group *group = arg; + struct timeval tv; + + log_debug("retrans_timer: %s", inet_ntoa(group->addr)); + + send_igmp_query(group->iface, group); + + /* reschedule retrans_timer */ + if (group->state == GRP_STA_CHECK_MEMB) { + timerclear(&tv); + tv.tv_sec = group->iface->last_member_query_interval; + evtimer_add(&group->retrans_timer, &tv); + } +} + +int +start_retrans_timer(struct group *group) +{ + struct timeval tv; + + log_debug("start_retrans_timer: %s", inet_ntoa(group->addr)); + + timerclear(&tv); + tv.tv_sec = group->iface->last_member_query_interval; + + return (evtimer_add(&group->retrans_timer, &tv)); +} + +int +stop_retrans_timer(struct group *group) +{ + log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr)); + + return (evtimer_del(&group->retrans_timer)); +} + +/* group list */ +struct group * +group_list_add(struct iface *iface, u_int32_t group) +{ + struct group *ge; + struct timeval now; + + /* validate group id */ + if (!IN_MULTICAST(htonl(group))) + fatalx("group_list_add: invalid group"); + + if ((ge = group_list_find(iface, group)) != NULL) { + return (ge); + } + + if ((ge = calloc(1, sizeof(*ge))) == NULL) + fatal("group_list_add"); + + ge->addr.s_addr = group; + ge->state = GRP_STA_NO_MEMB_PRSNT; + evtimer_set(&ge->dead_timer, dead_timer, ge); + evtimer_set(&ge->v1_host_timer, v1_host_timer, ge); + evtimer_set(&ge->retrans_timer, retrans_timer, ge); + + gettimeofday(&now, NULL); + ge->uptime = now.tv_sec; + + TAILQ_INSERT_TAIL(&iface->group_list, ge, entry); + iface->group_cnt++; + + ge->iface = iface; + + log_debug("group_list_add: interface %s, group %s", iface->name, + inet_ntoa(ge->addr)); + + return (ge); +} + +void +group_list_remove(struct iface *iface, struct group *group) +{ + log_debug("group_list_remove: interface %s, group %s", iface->name, + inet_ntoa(group->addr)); + + /* stop timers */ + stop_dead_timer(group); + start_v1_host_timer(group); + stop_retrans_timer(group); + + TAILQ_REMOVE(&iface->group_list, group, entry); + free(group); + iface->group_cnt--; +} + +struct group * +group_list_find(struct iface *iface, u_int32_t group) +{ + struct group *ge = NULL; + + /* validate group id */ + if (!IN_MULTICAST(htonl(group))) + fatalx("group_list_find: invalid group"); + + TAILQ_FOREACH(ge, &iface->group_list, entry) { + if (ge->addr.s_addr == group) + return (ge); + } + + return (ge); +} + +void +group_list_clr(struct iface *iface) +{ + struct group *ge; + + while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) { + TAILQ_REMOVE(&iface->group_list, ge, entry); + free(ge); + } + iface->group_cnt = 0; +} + +int +group_list_empty(struct iface *iface) +{ + return (TAILQ_EMPTY(&iface->group_list)); +} + +void +group_list_dump(struct iface *iface, struct ctl_conn *c) +{ + struct group *ge; + struct ctl_group *gctl; + + TAILQ_FOREACH(ge, &iface->group_list, entry) { + gctl = group_to_ctl(ge); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_IGMP, 0, 0, gctl, + sizeof(struct ctl_group)); + } +} + +/* names */ +const char * +group_event_name(int event) +{ + return (group_event_names[event]); +} + +const char * +group_action_name(int action) +{ + return (group_action_names[action]); +} + +struct ctl_group * +group_to_ctl(struct group *group) +{ + static struct ctl_group gctl; + struct timeval tv, now, res; + + memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr)); + + gctl.state = group->state; + + gettimeofday(&now, NULL); + if (evtimer_pending(&group->dead_timer, &tv)) { + timersub(&tv, &now, &res); + gctl.dead_timer = res.tv_sec; + } else + gctl.dead_timer = 0; + + if (group->state != GRP_STA_NO_MEMB_PRSNT) { + gctl.uptime = now.tv_sec - group->uptime; + } else + gctl.uptime = 0; + + return (&gctl); +} diff --git a/usr.sbin/dvmrpd/igmp.c b/usr.sbin/dvmrpd/igmp.c new file mode 100644 index 00000000000..da0f3e3d0b7 --- /dev/null +++ b/usr.sbin/dvmrpd/igmp.c @@ -0,0 +1,242 @@ +/* $OpenBSD: igmp.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +int igmp_chksum(struct igmp_hdr *); + +/* IGMP packet handling */ +int +send_igmp_query(struct iface *iface, struct group *group) +{ + struct igmp_hdr igmp_hdr; + struct sockaddr_in dst; + struct buf *buf; + int ret = 0; + + log_debug("send_igmp_query: interface %s", iface->name); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_igmp_query"); + + /* IGMP header */ + bzero(&igmp_hdr, sizeof(igmp_hdr)); + igmp_hdr.type = PKT_TYPE_MEMBER_QUERY; + + if (group == NULL) { + /* general query - version is configured */ + igmp_hdr.grp_addr = 0; + + switch (iface->igmp_version) { + case 1: + break; + case 2: + igmp_hdr.max_resp_time = iface->query_resp_interval; + break; + default: + fatal("send_igmp_query: invalid igmp version"); + } + } else { + /* group specific query - only version 2 */ + igmp_hdr.grp_addr = group->addr.s_addr; + igmp_hdr.max_resp_time = iface->last_member_query_interval; + } + + /* update chksum */ + igmp_hdr.chksum = in_cksum(&igmp_hdr, sizeof(igmp_hdr)); + + buf_add(buf, &igmp_hdr, sizeof(igmp_hdr)); + + /* set destination address */ + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + inet_aton(AllSystems, &dst.sin_addr); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +} + +void +recv_igmp_query(struct iface *iface, struct in_addr src, char *buf, + u_int16_t len) +{ + struct igmp_hdr igmp_hdr; + struct group *group; + + log_debug("recv_igmp_query: interface %s", iface->name); + + if (len < sizeof(igmp_hdr)) { + log_debug("recv_igmp_query: invalid IGMP report, interface %s", + iface->name); + return; + } + + memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); + iface->recv_query_resp_interval = igmp_hdr.max_resp_time; + + /* verify chksum */ + if (igmp_chksum(&igmp_hdr) == -1) { + log_debug("recv_igmp_query: invalid chksum, interface %s", + iface->name); + return; + } + + if (src.s_addr < iface->addr.s_addr && igmp_hdr.grp_addr == 0) { + /* we received a general query and we lost the election */ + if_fsm(iface, IF_EVT_QRECVD); + /* remember who is querier */ + iface->querier = src; + return; + } + + if (iface->state == IF_STA_NONQUERIER && igmp_hdr.grp_addr != 0) { + /* validate group id */ + if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { + log_debug("recv_igmp_query: invalid group, " + "interface %s", iface->name); + return; + } + + if ((group = group_list_add(iface, igmp_hdr.grp_addr)) + != NULL) + group_fsm(group, GRP_EVT_QUERY_RCVD); + } +} + +void +recv_igmp_report(struct iface *iface, struct in_addr src, char *buf, + u_int16_t len, u_int8_t type) +{ + struct igmp_hdr igmp_hdr; + struct group *group; + + log_debug("recv_igmp_report: interface %s", iface->name); + + if (len < sizeof(igmp_hdr)) { + log_debug("recv_igmp_report: invalid IGMP report, interface %s", + iface->name); + return; + } + + memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); + + /* verify chksum */ + if (igmp_chksum(&igmp_hdr) == -1) { + log_debug("recv_igmp_report: invalid chksum, interface %s", + iface->name); + return; + } + + /* validate group id */ + if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { + log_debug("recv_igmp_report: invalid group, interface %s", + iface->name); + return; + } + + if ((group = group_list_add(iface, igmp_hdr.grp_addr)) == NULL) + return; + + if (iface->state == IF_STA_QUERIER) { + /* querier */ + switch (type) { + case PKT_TYPE_MEMBER_REPORTv1: + group_fsm(group, GRP_EVT_V1_REPORT_RCVD); + break; + case PKT_TYPE_MEMBER_REPORTv2: + group_fsm(group, GRP_EVT_V2_REPORT_RCVD); + break; + default: + fatalx("recv_igmp_report: unknown IGMP report type"); + } + } else { + /* non querier */ + group_fsm(group, GRP_EVT_REPORT_RCVD); + } +} + +void +recv_igmp_leave(struct iface *iface, struct in_addr src, char *buf, + u_int16_t len) +{ + struct igmp_hdr igmp_hdr; + struct group *group; + + log_debug("recv_igmp_leave: interface %s", iface->name); + + if (iface->state != IF_STA_QUERIER) + return; + + if (len < sizeof(igmp_hdr)) { + log_debug("recv_igmp_leave: invalid IGMP leave, interface %s", + iface->name); + return; + } + + memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); + + /* verify chksum */ + if (igmp_chksum(&igmp_hdr) == -1) { + log_debug("recv_igmp_leave: invalid chksum, interface %s", + iface->name); + return; + } + + /* validate group id */ + if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { + log_debug("recv_igmp_leave: invalid group, interface %s", + iface->name); + return; + } + + if ((group = group_list_find(iface, igmp_hdr.grp_addr)) != NULL) { + group_fsm(group, GRP_EVT_LEAVE_RCVD); + } +} + +int +igmp_chksum(struct igmp_hdr *igmp_hdr) +{ + u_int16_t chksum; + + chksum = igmp_hdr->chksum; + igmp_hdr->chksum = 0; + + if (chksum != in_cksum(igmp_hdr, sizeof(*igmp_hdr))) + return (-1); + + return (0); +} diff --git a/usr.sbin/dvmrpd/igmp.h b/usr.sbin/dvmrpd/igmp.h new file mode 100644 index 00000000000..81165ee67c0 --- /dev/null +++ b/usr.sbin/dvmrpd/igmp.h @@ -0,0 +1,111 @@ +/* $OpenBSD: igmp.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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. + */ + +/* IGMP protocol definitions */ + +#ifndef _IGMP_H_ +#define _IGMP_H_ + +/* misc */ +#define AllSystems "224.0.0.1" +#define AllRouters "224.0.0.2" +#define AllIGMPv3Routers "224.0.0.22" + +#define DEFAULT_ROBUSTNESS 2 +#define MIN_ROBUSTNESS 1 +#define MAX_ROBUSTNESS 4 + +#define DEFAULT_QUERY_INTERVAL 125 +#define MIN_QUERY_INTERVAL 1 +#define MAX_QUERY_INTERVAL 65535 + +/* must be less than QUERY_INTERVAL */ +#define DEFAULT_QUERY_RESP_INTERVAL 100 /* 10 seconds */ +#define MIN_QUERY_RESP_INTERVAL 1 +#define MAX_QUERY_RESP_INTERVAL 65535 + +#define DEFAULT_STARTUP_QUERY_INTERVAL DEFAULT_QUERY_INTERVAL / 4 +#define MIN_STARTUP_QUERY_INTERVAL MIN_QUERY_INTERVAL +#define MAX_STARTUP_QUERY_INTERVAL MAX_QUERY_INTERVAL + +#define DEFAULT_STARTUP_QUERY_CNT DEFAULT_ROBUSTNESS +#define MIN_STARTUP_QUERY_CNT MIN_ROBUSTNESS /* XXX */ +#define MAX_STARTUP_QUERY_CNT MAX_ROBUSTNESS /* XXX */ + +#define DEFAULT_LAST_MEMBER_QUERY_INTERVAL 10 /* 1 second */ +#define MIN_LAST_MEMBER_QUERY_INTERVAL 1 +#define MAX_LAST_MEMBER_QUERY_INTERVAL 65535 + +#define DEFAULT_LAST_MEMBER_QUERY_CNT DEFAULT_ROBUSTNESS +#define MIN_LAST_MEMBER_QUERY_CNT 1 +#define MAX_LAST_MEMBER_QUERY_CNT 255 + +/* IGMP packet types */ +#define PKT_TYPE_MEMBER_QUERY 0x11 +#define PKT_TYPE_MEMBER_REPORTv1 0x12 +#define PKT_TYPE_MEMBER_REPORTv2 0x16 +#define PKT_TYPE_LEAVE_GROUPv2 0x17 +#define PKT_TYPE_MEMBER_REPORTv3 0x22 + +#define DEFAULT_IGMP_VERSION 2 +#define MIN_IGMP_VERSION 1 +#define MAX_IGMP_VERSION 2 + +/* IGMP header */ +struct igmp_hdr { + u_int8_t type; + u_int8_t max_resp_time; + u_int16_t chksum; + u_int32_t grp_addr; +}; + +/* group states */ +#define GRP_STA_NO_MEMB_PRSNT 0x01 +#define GRP_STA_MEMB_PRSNT 0x02 +#define GRP_STA_V1_MEMB_PRSNT 0x04 +#define GRP_STA_CHECK_MEMB 0x08 +#define GRP_STA_ANY 0x0e + +/* group events */ +enum group_event { + GRP_EVT_V2_REPORT_RCVD, + GRP_EVT_V1_REPORT_RCVD, + GRP_EVT_LEAVE_RCVD, + GRP_EVT_TMR_EXPIRED, + GRP_EVT_RETRANS_TMR_EXP, + GRP_EVT_V1_HOST_TMR_EXP, + GRP_EVT_REPORT_RCVD, + GRP_EVT_QUERY_RCVD, + GRP_EVT_NOTHING +}; + +/* group actions */ +enum group_action { + GRP_ACT_END, + GRP_ACT_START_TMR, + GRP_ACT_START_TMR_ALL, + GRP_ACT_START_RETRANS_TMR, + GRP_ACT_START_V1_HOST_TMR, + GRP_ACT_SEND_GRP_QUERY, + GRP_ACT_ADD_ROUTE, + GRP_ACT_DEL_ROUTE, + GRP_ACT_CLR_RETRANS_TMR, + GRP_ACT_NOTHING +}; + +#endif /* _IGMP_H_ */ diff --git a/usr.sbin/dvmrpd/imsg.c b/usr.sbin/dvmrpd/imsg.c new file mode 100644 index 00000000000..da001cec783 --- /dev/null +++ b/usr.sbin/dvmrpd/imsg.c @@ -0,0 +1,178 @@ +/* $OpenBSD: imsg.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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/uio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dvmrpd.h" +#include "log.h" + +void +imsg_init(struct imsgbuf *ibuf, int fd, void (*handler)(int, short, void *)) +{ + msgbuf_init(&ibuf->w); + bzero(&ibuf->r, sizeof(ibuf->r)); + ibuf->fd = fd; + ibuf->w.fd = fd; + ibuf->pid = getpid(); + ibuf->handler = handler; + TAILQ_INIT(&ibuf->fds); +} + +ssize_t +imsg_read(struct imsgbuf *ibuf) +{ + ssize_t n; + + if ((n = recv(ibuf->fd, ibuf->r.buf + ibuf->r.wpos, + sizeof(ibuf->r.buf) - ibuf->r.wpos, 0)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + log_warn("imsg_read: pipe read error"); + return (-1); + } + return (0); + } + + ibuf->r.wpos += n; + + return (n); +} + +ssize_t +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + size_t av, left, datalen; + + av = ibuf->r.wpos; + + if (IMSG_HEADER_SIZE > av) + return (0); + + memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); + if (imsg->hdr.len < IMSG_HEADER_SIZE || + imsg->hdr.len > MAX_IMSGSIZE) { + log_warnx("imsg_get: imsg hdr len %u out of bounds, type=%u", + imsg->hdr.len, imsg->hdr.type); + return (-1); + } + if (imsg->hdr.len > av) + return (0); + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; + if ((imsg->data = malloc(datalen)) == NULL) { + log_warn("imsg_get"); + return (-1); + } + memcpy(imsg->data, ibuf->r.rptr, datalen); + + if (imsg->hdr.len < av) { + left = av - imsg->hdr.len; + memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); + ibuf->r.wpos = left; + } else + ibuf->r.wpos = 0; + + return (datalen + IMSG_HEADER_SIZE); +} + +int +imsg_compose(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid, + pid_t pid, void *data, u_int16_t datalen) +{ + struct buf *wbuf; + int n; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + if (imsg_add(wbuf, data, datalen) == -1) + return (-1); + + if ((n = imsg_close(ibuf, wbuf)) < 0) + return (-1); + + return (n); +} + +/* ARGSUSED */ +struct buf * +imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid, + pid_t pid, u_int16_t datalen) +{ + struct buf *wbuf; + struct imsg_hdr hdr; + + if (datalen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { + log_warnx("imsg_create: len %u > MAX_IMSGSIZE; " + "type %u peerid %lu", datalen + IMSG_HEADER_SIZE, + type, peerid); + return (NULL); + } + + hdr.len = (u_int16_t)(datalen + IMSG_HEADER_SIZE); + hdr.type = type; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = buf_open(hdr.len)) == NULL) { + log_warn("imsg_create: buf_open"); + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct buf *msg, void *data, u_int16_t datalen) +{ + if (datalen) + if (buf_add(msg, data, datalen) == -1) { + log_warnx("imsg_add: buf_add error"); + buf_free(msg); + return (-1); + } + return (datalen); +} + +int +imsg_close(struct imsgbuf *ibuf, struct buf *msg) +{ + int n; + + if ((n = buf_close(&ibuf->w, msg)) < 0) { + log_warnx("imsg_close: buf_close error"); + buf_free(msg); + return (-1); + } + imsg_event_add(ibuf); + + return (n); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} diff --git a/usr.sbin/dvmrpd/in_cksum.c b/usr.sbin/dvmrpd/in_cksum.c new file mode 100644 index 00000000000..67a2145a73f --- /dev/null +++ b/usr.sbin/dvmrpd/in_cksum.c @@ -0,0 +1,90 @@ +/* $OpenBSD: in_cksum.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ +/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */ + +/* + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL) + */ + +#include <sys/types.h> + +#include "dvmrpd.h" +#include "log.h" + +/* + * Checksum routine for Internet Protocol family headers. + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + * In particular, it should not be this one. + */ +u_int16_t +in_cksum(void *p, size_t l) +{ + unsigned int sum = 0; + int len, oddbyte = 0, v = 0; + u_char *cp = p; + + /* ensure that < 2^16 bytes being summed */ + if (l >= (1 << 16)) + fatalx("in_cksum: packet to big"); + len = (int)l; + + while (len > 0) { + if (oddbyte) { + sum += v + *cp++; + len--; + } + if (((long)cp & 1) == 0) { + while ((len -= 2) >= 0) { + sum += *(u_short *)cp; + cp += 2; + } + } else { + while ((len -= 2) >= 0) { + sum += *cp++ << 8; + sum += *cp++; + } + } + if ((oddbyte = len & 1) != 0) + v = *cp << 8; + } + if (oddbyte) + sum += v; + sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */ + sum += sum >> 16; /* add potential last carry */ + return (0xffff & ~sum); +} diff --git a/usr.sbin/dvmrpd/interface.c b/usr.sbin/dvmrpd/interface.c new file mode 100644 index 00000000000..1f37aedb139 --- /dev/null +++ b/usr.sbin/dvmrpd/interface.c @@ -0,0 +1,688 @@ +/* $OpenBSD: interface.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/ip_mroute.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +void if_probe_timer(int, short, void *); +int if_start_probe_timer(struct iface *); +int if_stop_probe_timer(struct iface *); +void if_query_timer(int, short, void *); +int if_start_query_timer(struct iface *); +int if_stop_query_timer(struct iface *); +void if_querier_present_timer(int, short, void *); +int if_start_querier_present_timer(struct iface *); +int if_stop_querier_present_timer(struct iface *); +int if_reset_querier_present_timer(struct iface *); +int if_act_start(struct iface *); +int if_act_query_seen(struct iface *); +int if_act_reset(struct iface *); + +struct { + int state; + enum iface_event event; + enum iface_action action; + int new_state; +} iface_fsm[] = { + /* current state event that happened action to take resulting state */ + {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, 0}, + {IF_STA_ACTIVE, IF_EVT_QRECVD, IF_ACT_QPRSNT, 0}, + {IF_STA_NONQUERIER, IF_EVT_QPRSNTTMOUT, IF_ACT_STRT, 0}, + {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, + {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, +}; + +const char * const if_action_names[] = { + "NOTHING", + "START", + "QPRSNT", + "RESET" +}; + +static const char * const if_event_names[] = { + "NOTHING", + "UP", + "QTMOUT", + "QRECVD", + "QPRSNTTMOUT", + "DOWN" +}; + +int +if_fsm(struct iface *iface, enum iface_event event) +{ + int old_state; + int new_state = 0; + int i, ret = 0; + + old_state = iface->state; + + for (i = 0; iface_fsm[i].state != -1; i++) + if ((iface_fsm[i].state & old_state) && + (iface_fsm[i].event == event)) { + new_state = iface_fsm[i].new_state; + break; + } + + if (iface_fsm[i].state == -1) { + /* XXX event outside of the defined fsm, ignore it. */ + log_debug("fsm_if: interface %s, " + "event '%s' not expected in state '%s'", iface->name, + if_event_name(event), if_state_name(old_state)); + return (0); + } + + switch (iface_fsm[i].action) { + case IF_ACT_STRT: + ret = if_act_start(iface); + break; + case IF_ACT_QPRSNT: + ret = if_act_query_seen(iface); + break; + case IF_ACT_RST: + ret = if_act_reset(iface); + break; + case IF_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_debug("fsm_if: error changing state for interface %s, " + "event '%s', state '%s'", iface->name, if_event_name(event), + if_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + iface->state = new_state; + + log_debug("fsm_if: event '%s' resulted in action '%s' and changing " + "state for interface %s from '%s' to '%s'", + if_event_name(event), if_action_name(iface_fsm[i].action), + iface->name, if_state_name(old_state), if_state_name(iface->state)); + + return (ret); +} + +struct iface * +if_new(struct kif *kif) +{ + struct sockaddr_in *sain; + struct iface *iface; + struct ifreq *ifr; + int s; + + if ((iface = calloc(1, sizeof(*iface))) == NULL) + err(1, "if_new: calloc"); + + iface->state = IF_STA_DOWN; + iface->passive = 1; + + LIST_INIT(&iface->nbr_list); + TAILQ_INIT(&iface->group_list); + strlcpy(iface->name, kif->ifname, sizeof(iface->name)); + + if ((ifr = calloc(1, sizeof(*ifr))) == NULL) + err(1, "if_new: calloc"); + + /* set up ifreq */ + strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name)); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + err(1, "if_new: socket"); + + /* get type */ + if ((kif->flags & IFF_POINTOPOINT)) + iface->type = IF_TYPE_POINTOPOINT; + if ((kif->flags & IFF_BROADCAST) && + (kif->flags & IFF_MULTICAST)) + iface->type = IF_TYPE_BROADCAST; + + /* get mtu, index and flags */ + iface->mtu = kif->mtu; + iface->ifindex = kif->ifindex; + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + iface->media_type = kif->media_type; + + /* get address */ + if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) < 0) + err(1, "if_new: cannot get address"); + sain = (struct sockaddr_in *) &ifr->ifr_addr; + iface->addr = sain->sin_addr; + + /* get mask */ + if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) < 0) + err(1, "if_new: cannot get mask"); + sain = (struct sockaddr_in *) &ifr->ifr_addr; + iface->mask = sain->sin_addr; + + /* get p2p dst address */ + if (iface->type == IF_TYPE_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)ifr) < 0) + err(1, "if_new: cannot get dst addr"); + sain = (struct sockaddr_in *) &ifr->ifr_addr; + iface->dst = sain->sin_addr; + } + + free(ifr); + close(s); + + return (iface); +} + +void +if_init(struct dvmrpd_conf *xconf, struct iface *iface) +{ + /* set event handlers for interface */ + evtimer_set(&iface->probe_timer, if_probe_timer, iface); + evtimer_set(&iface->query_timer, if_query_timer, iface); + evtimer_set(&iface->querier_present_timer, if_querier_present_timer, + iface); + + TAILQ_INIT(&iface->rr_list); + + iface->fd = xconf->dvmrp_socket; + iface->gen_id = xconf->gen_id; +} + +int +if_del(struct iface *iface) +{ + struct nbr *nbr = NULL; + + /* clear lists etc */ + while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) { + LIST_REMOVE(nbr, entry); + nbr_del(nbr); + } + group_list_clr(iface); + + return (-1); +} + +int +if_nbr_list_empty(struct iface *iface) +{ + return (LIST_EMPTY(&iface->nbr_list)); +} + +/* timers */ +void +if_probe_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + struct timeval tv; + + send_probe(iface); + + /* reschedule probe_timer */ + if (!iface->passive) { + timerclear(&tv); + tv.tv_sec = iface->probe_interval; + evtimer_add(&iface->probe_timer, &tv); + } +} + +int +if_start_probe_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + return (evtimer_add(&iface->probe_timer, &tv)); +} + +int +if_stop_probe_timer(struct iface *iface) +{ + return (evtimer_del(&iface->probe_timer)); +} + +void +if_query_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + struct timeval tv; + + /* send a general query */ + send_igmp_query(iface, NULL); + + /* reschedule query_timer */ + if (!iface->passive) { + timerclear(&tv); + if (iface->startup_query_counter != 0) { + tv.tv_sec = iface->startup_query_interval; + iface->startup_query_counter--; + } else + tv.tv_sec = iface->query_interval; + + evtimer_add(&iface->query_timer, &tv); + } +} + +int +if_start_query_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + return (evtimer_add(&iface->query_timer, &tv)); +} + +int +if_stop_query_timer(struct iface *iface) +{ + return (evtimer_del(&iface->query_timer)); +} + +void +if_querier_present_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + + if_fsm(iface, IF_EVT_QPRSNTTMOUT); +} + +int +if_start_querier_present_timer(struct iface *iface) +{ + struct timeval tv; + + /* Other Querier Present Interval */ + timerclear(&tv); + tv.tv_sec = iface->robustness * iface->query_interval + + (iface->query_resp_interval / 2); + + return (evtimer_add(&iface->querier_present_timer, &tv)); +} + +int +if_stop_querier_present_timer(struct iface *iface) +{ + return (evtimer_del(&iface->querier_present_timer)); +} + +int +if_reset_querier_present_timer(struct iface *iface) +{ + struct timeval tv; + + /* Other Querier Present Interval */ + timerclear(&tv); + tv.tv_sec = iface->robustness * iface->query_interval + + (iface->query_resp_interval / 2); + + return (evtimer_add(&iface->querier_present_timer, &tv)); +} + +/* actions */ +int +if_act_start(struct iface *iface) +{ + struct in_addr addr; + struct timeval now; + + if (iface->passive) { + log_debug("if_act_start: cannot start passive interface %s", + iface->name); + return (-1); + } + + if (!((iface->flags & IFF_UP) && + (iface->linkstate != LINK_STATE_DOWN))) { + log_debug("if_act_start: interface %s link down", + iface->name); + return (0); + } + + gettimeofday(&now, NULL); + iface->uptime = now.tv_sec; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_aton(AllSystems, &addr); + if (if_join_group(iface, &addr)) { + log_warnx("if_act_start: error joining group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + inet_aton(AllRouters, &addr); + if (if_join_group(iface, &addr)) { + log_warnx("if_act_start: error joining group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + inet_aton(AllDVMRPRouters, &addr); + if (if_join_group(iface, &addr)) { + log_warnx("if_act_start: error joining group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + + iface->state = IF_STA_QUERIER; + if_start_query_timer(iface); + if_start_probe_timer(iface); + iface->startup_query_counter = iface->startup_query_cnt; + break; + default: + fatalx("if_act_start: unknown type"); + } + + return (0); +} + +int +if_act_query_seen(struct iface *iface) +{ + log_debug("if_act_query_seen: interface %s", iface->name); + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + /* XXX iface p2p */ + break; + case IF_TYPE_BROADCAST: + iface->state = IF_STA_NONQUERIER; + if_stop_query_timer(iface); + if_reset_querier_present_timer(iface); + break; + default: + fatalx("if_act_querier_seen: unknown type"); + } + + return (0); +} + +int +if_act_reset(struct iface *iface) +{ + struct in_addr addr; + struct nbr *nbr; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_aton(AllSystems, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + inet_aton(AllRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + inet_aton(AllDVMRPRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + + iface->state = IF_STA_DOWN; + iface->gen_id++; + if_stop_query_timer(iface); + if_stop_querier_present_timer(iface); + /* XXX clear nbr list? */ + break; + default: + fatalx("if_act_reset: unknown type"); + } + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) { + log_debug("if_act_reset: error killing neighbor %s", + inet_ntoa(nbr->id)); + } + } + + group_list_clr(iface); /* XXX clear group list? */ + + return (0); +} + +const char * +if_event_name(int event) +{ + return (if_event_names[event]); +} + +const char * +if_action_name(int action) +{ + return (if_action_names[action]); +} + +/* misc */ +int +if_set_mcast_ttl(int fd, u_int8_t ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + log_warn("if_set_mcast_ttl: error setting " + "IP_MULTICAST_TTL to %d", ttl); + return (-1); + } + + return (0); +} + +int +if_set_tos(int fd, int tos) +{ + if (setsockopt(fd, IPPROTO_IP, IP_TOS, + (int *)&tos, sizeof(tos)) < 0) { + log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); + return (-1); + } + + return (0); +} + +void +if_set_recvbuf(int fd) +{ + int bsize; + + bsize = 65535; + while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, + sizeof(bsize)) == -1) + bsize /= 2; +} + +int +if_join_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)) < 0) { + log_debug("if_join_group: error IP_ADD_MEMBERSHIP, " + "interface %s", iface->name); + return (-1); + } + break; + default: + fatalx("if_join_group: unknown interface type"); + } + + return (0); +} + +int +if_leave_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)) < 0) { + log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, " + "interface %s", iface->name); + return (-1); + } + break; + default: + fatalx("if_leave_group: unknown interface type"); + } + + return (0); +} + +int +if_set_mcast(struct iface *iface) +{ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&iface->addr.s_addr, + sizeof(iface->addr.s_addr)) < 0) { + log_debug("if_set_mcast: error setting " + "IP_MULTICAST_IF, interface %s", iface->name); + return (-1); + } + break; + default: + fatalx("if_set_mcast: unknown interface type"); + } + + return (0); +} + +int +if_set_mcast_loop(int fd) +{ + u_int8_t loop = 0; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) { + log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); + return (-1); + } + + return (0); +} + +struct ctl_iface * +if_to_ctl(struct iface *iface) +{ + static struct ctl_iface ictl; + struct timeval tv, now, res; + + memcpy(ictl.name, iface->name, sizeof(ictl.name)); + memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); + memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); + memcpy(&ictl.querier, &iface->querier, sizeof(ictl.querier)); + + ictl.ifindex = iface->ifindex; + ictl.state = iface->state; + ictl.mtu = iface->mtu; + ictl.nbr_cnt = 0; /* XXX */ + ictl.adj_cnt = 0; /* XXX */ + + ictl.gen_id = iface->gen_id; + ictl.group_cnt = iface->group_cnt; + ictl.baudrate = iface->baudrate; + ictl.probe_interval = iface->probe_interval; + ictl.query_interval = iface->query_interval; + ictl.query_resp_interval = iface->query_resp_interval; + ictl.recv_query_resp_interval = iface->recv_query_resp_interval; + ictl.group_member_interval = iface->group_member_interval; + ictl.querier_present_interval = iface->querier_present_interval; + ictl.startup_query_interval = iface->startup_query_interval; + ictl.startup_query_cnt = iface->startup_query_cnt; + ictl.last_member_query_interval = iface->last_member_query_interval; + ictl.last_member_query_cnt = iface->last_member_query_cnt; + ictl.last_member_query_time = iface->last_member_query_time; + ictl.v1_querier_present_tmout = iface->v1_querier_present_tmout; + ictl.v1_host_present_interval = iface->v1_host_present_interval; + ictl.dead_interval = iface->dead_interval; + + ictl.baudrate = iface->baudrate; + ictl.flags = iface->flags; + ictl.metric = iface->metric; + ictl.type = iface->type; + ictl.robustness = iface->robustness; + ictl.linkstate = iface->linkstate; + ictl.passive = iface->passive; + ictl.igmp_version = iface->igmp_version; + ictl.mediatype = iface->media_type; + + gettimeofday(&now, NULL); + if (evtimer_pending(&iface->probe_timer, &tv)) { + timersub(&tv, &now, &res); + ictl.probe_timer = res.tv_sec; + } else + ictl.probe_timer = -1; + + if (evtimer_pending(&iface->query_timer, &tv)) { + timersub(&tv, &now, &res); + ictl.query_timer = res.tv_sec; + } else + ictl.query_timer = -1; + + if (evtimer_pending(&iface->querier_present_timer, &tv)) { + timersub(&tv, &now, &res); + ictl.querier_present_timer = res.tv_sec; + } else + ictl.querier_present_timer = -1; + + if (iface->state != IF_STA_DOWN) { + ictl.uptime = now.tv_sec - iface->uptime; + } else + ictl.uptime = 0; + + return (&ictl); +} diff --git a/usr.sbin/dvmrpd/kmroute.c b/usr.sbin/dvmrpd/kmroute.c new file mode 100644 index 00000000000..911319bc995 --- /dev/null +++ b/usr.sbin/dvmrpd/kmroute.c @@ -0,0 +1,256 @@ +/* $OpenBSD: kmroute.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <arpa/inet.h> +#include <netinet/ip_mroute.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" + +extern struct dvmrpd_conf *conf; +char *mroute_ptr; /* packet buffer */ + +void main_imsg_compose_rde(int, pid_t, void *, u_int16_t); + +int +kmr_init(int fd) +{ + struct iface *iface; + struct route_report rr; + + LIST_FOREACH(iface, &conf->iface_list, entry) { + log_debug("kmr_init: interface %s", iface->name); + + rr.net.s_addr = iface->addr.s_addr & iface->mask.s_addr; + rr.mask = iface->mask; + rr.nexthop.s_addr = 0; + rr.metric = iface->metric; + rr.ifindex = iface->ifindex; + main_imsg_compose_rde(IMSG_ROUTE_REPORT, -1, &rr, sizeof(rr)); + + mrt_add_vif(conf->mroute_socket, iface); + } + + if ((mroute_ptr = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("kmr_init"); + + return (0); +} + +void +kmr_shutdown(void) +{ + struct iface *iface; + + kmr_mfc_decouple(); + kmroute_clear(); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + log_debug("kmr_shutdown: interface %s", iface->name); + + mrt_del_vif(conf->mroute_socket, iface); + } + + free(mroute_ptr); +} + +void +kmr_recv_msg(int fd, short event, void *bula) +{ + struct mfc mfc; + struct igmpmsg kernel_msg; + char *buf; + ssize_t r; + + if (event != EV_READ) + return; + + /* setup buffer */ + buf = mroute_ptr; + + if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) { + if (errno != EAGAIN && errno != EINTR) + log_debug("kmr_recv_msg: error receiving packet"); + return; + } + + memcpy(&kernel_msg, buf, sizeof(kernel_msg)); + + /* we are only interested in kernel messages */ + if (kernel_msg.im_mbz != 0) + return; + + switch (kernel_msg.im_msgtype) { + case IGMPMSG_NOCACHE: + /* verify that dst is a multicast group */ + if (!IN_MULTICAST(ntohl(kernel_msg.im_dst.s_addr))) { + log_debug("kmr_recv_msg: kernel providing garbage!"); + return; + } + + /* send MFC entry to RDE */ + mfc.origin = kernel_msg.im_src; + mfc.group = kernel_msg.im_dst; + mfc.ifindex = kernel_msg.im_vif; + main_imsg_compose_rde(IMSG_MFC_ADD, 0, &mfc, sizeof(mfc)); + break; + case IGMPMSG_WRONGVIF: + case IGMPMSG_WHOLEPKT: + case IGMPMSG_BW_UPCALL: + default: + log_debug("kmr_recv_msg: unhandled msg type %d!", + kernel_msg.im_msgtype); + } +} + +void +kmr_mfc_couple(void) +{ + log_info("kernel multicast forwarding cache coupled"); +} + +void +kmr_mfc_decouple(void) +{ + log_info("kernel multicast forwarding cache decoupled"); +} + +void +kmroute_clear(void) +{ + +} + +int +mrt_init(int fd) +{ + int flag = 1; + + if (setsockopt(fd, IPPROTO_IP, MRT_INIT, &flag, + sizeof(flag)) < 0) { + log_warn("mrt_init: error setting MRT_INIT"); + return (-1); + } + + return (0); +} + +int +mrt_done(int fd) +{ + int flag = 0; + + if (setsockopt(fd, IPPROTO_IP, MRT_DONE, &flag, + sizeof(flag)) < 0) { + log_warn("mrt_done: error setting MRT_DONE"); + return (-1); + } + + return (0); +} + +int +mrt_add_vif(int fd, struct iface *iface) +{ + struct vifctl vc; + + vc.vifc_vifi = iface->ifindex; + vc.vifc_flags = 0; + vc.vifc_threshold = 1; + vc.vifc_rate_limit = 0; + vc.vifc_lcl_addr.s_addr = iface->addr.s_addr; + vc.vifc_rmt_addr.s_addr = 0; + + if (setsockopt(fd, IPPROTO_IP, MRT_ADD_VIF, &vc, + sizeof(vc)) < 0) { + log_warn("mrt_add_vif: error adding VIF"); + return (-1); + } + + return (0); +} + +void +mrt_del_vif(int fd, struct iface *iface) +{ + vifi_t vifi; + + vifi = iface->ifindex; + + if (setsockopt(fd, IPPROTO_IP, MRT_DEL_VIF, &vifi, + sizeof(vifi)) < 0) + log_warn("mrt_del_vif: error deleting VIF"); +} + +int +mrt_add_mfc(int fd, struct mfc *mfc) +{ + struct mfcctl mc; + int i; + + log_debug("mrt_add_mfc: interface %d, group %s", mfc->ifindex, + inet_ntoa(mfc->group)); + + mc.mfcc_origin = mfc->origin; + mc.mfcc_mcastgrp = mfc->group; + mc.mfcc_parent = mfc->ifindex; + + for (i = 0; i < MAXVIFS; i++) { + mc.mfcc_ttls[i] = mfc->ttls[i]; + } + + if (setsockopt(fd, IPPROTO_IP, MRT_ADD_MFC, &mc, sizeof(mc)) + < 0) { + log_warnx("mrt_add_mfc: error adding group %s to interface %d", + inet_ntoa(mfc->group), mfc->ifindex); + return (-1); + } + + return (0); +} + +int +mrt_del_mfc(int fd, struct mfc *mfc) +{ + struct mfcctl mc; + + log_debug("mrt_del_mfc: group %s", inet_ntoa(mfc->group)); + + mc.mfcc_origin = mfc->origin; + mc.mfcc_mcastgrp = mfc->group; + + if (setsockopt(fd, IPPROTO_IP, MRT_DEL_MFC, &mc, sizeof(mc)) + < 0) { + log_warnx("mrt_del_mfc: error deleting group %s ", + inet_ntoa(mfc->group)); + return (-1); + } + + return (0); +} diff --git a/usr.sbin/dvmrpd/kroute.c b/usr.sbin/dvmrpd/kroute.c new file mode 100644 index 00000000000..51e9f7571ee --- /dev/null +++ b/usr.sbin/dvmrpd/kroute.c @@ -0,0 +1,398 @@ +/* $OpenBSD: kroute.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/tree.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dvmrp.h" +#include "dvmrpd.h" +#include "log.h" + +struct { + u_int32_t rtseq; + pid_t pid; + struct event ev; +} kr_state; + +struct kif_node { + RB_ENTRY(kif_node) entry; + struct kif k; +}; + +int kif_compare(struct kif_node *, struct kif_node *); + +struct kif_node *kif_find(int); +int kif_insert(struct kif_node *); +int kif_remove(struct kif_node *); +void kif_clear(void); + +in_addr_t prefixlen2mask(u_int8_t); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +void if_change(u_short, int, struct if_data *); +void if_announce(void *); + +int fetchifs(int); + +RB_HEAD(kif_tree, kif_node) kit; +RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) +RB_GENERATE(kif_tree, kif_node, entry, kif_compare) + +int +kif_init(void) +{ + RB_INIT(&kit); + + if (fetchifs(0) == -1) + return (-1); + + return (0); +} + +int +kr_init(int fs) +{ + int opt, fd; + + + if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + log_warn("kr_init: socket"); + return (-1); + } + + /* not interested in my own messages */ + if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, sizeof(opt)) == -1) + log_warn("kr_init: setsockopt"); /* not fatal */ + + kr_state.pid = getpid(); + kr_state.rtseq = 1; + + event_set(&kr_state.ev, fd, EV_READ | EV_PERSIST, + kr_dispatch_msg, NULL); + event_add(&kr_state.ev, NULL); + + return (0); +} + +void +kr_shutdown(void) +{ + kif_clear(); +} + +void +kr_ifinfo(char *ifname) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + if (!strcmp(ifname, kif->k.ifname)) { + main_imsg_compose_dvmrpe(IMSG_CTL_IFINFO, 0, + &kif->k, sizeof(kif->k)); + return; + } +} + +/* rb-tree compare */ +int +kif_compare(struct kif_node *a, struct kif_node *b) +{ + return (b->k.ifindex - a->k.ifindex); +} + +struct kif_node * +kif_find(int ifindex) +{ + struct kif_node s; + + bzero(&s, sizeof(s)); + s.k.ifindex = ifindex; + + return (RB_FIND(kif_tree, &kit, &s)); +} + +struct kif * +kif_findname(char *ifname) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + if (!strcmp(ifname, kif->k.ifname)) + return (&kif->k); + + return (NULL); +} + +int +kif_insert(struct kif_node *kif) +{ + if (RB_INSERT(kif_tree, &kit, kif) != NULL) { + log_warnx("RB_INSERT(kif_tree, &kit, kif)"); + free(kif); + return (-1); + } + + return (0); +} + +int +kif_remove(struct kif_node *kif) +{ + if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { + log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); + return (-1); + } + + free(kif); + return (0); +} + +void +kif_clear(void) +{ + struct kif_node *kif; + + while ((kif = RB_MIN(kif_tree, &kit)) != NULL) + kif_remove(kif); +} + +/* misc */ +u_int8_t +prefixlen_classful(in_addr_t ina) +{ + /* it hurt to write this. */ + + if (ina >= 0xf0000000U) /* class E */ + return (32); + else if (ina >= 0xe0000000U) /* class D */ + return (4); + else if (ina >= 0xc0000000U) /* class C */ + return (24); + else if (ina >= 0x80000000U) /* class B */ + return (16); + else /* class A */ + return (8); +} + +u_int8_t +mask2prefixlen(in_addr_t ina) +{ + if (ina == 0) + return (0); + else + return (33 - ffs(ntohl(ina))); +} + +in_addr_t +prefixlen2mask(u_int8_t prefixlen) +{ + if (prefixlen == 0) + return (0); + + return (0xffffffff << (32 - prefixlen)); +} + +void +if_change(u_short ifindex, int flags, struct if_data *ifd) +{ + struct kif_node *kif; + u_int8_t reachable; + + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("interface with index %u not found", + ifindex); + return; + } + + kif->k.flags = flags; + kif->k.link_state = ifd->ifi_link_state; + kif->k.media_type = ifd->ifi_type; + kif->k.baudrate = ifd->ifi_baudrate; + + if ((reachable = (flags & IFF_UP) && + (ifd->ifi_link_state != LINK_STATE_DOWN)) == kif->k.nh_reachable) + return; /* nothing changed wrt nexthop validity */ + + kif->k.nh_reachable = reachable; + main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); +} + +void +if_announce(void *msg) +{ + struct if_announcemsghdr *ifan; + struct kif_node *kif; + + ifan = msg; + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { + log_warn("if_announce"); + return; + } + + kif->k.ifindex = ifan->ifan_index; + strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); + kif_insert(kif); + break; + case IFAN_DEPARTURE: + kif = kif_find(ifan->ifan_index); + kif_remove(kif); + break; + } +} + +/* rtsock */ +#define ROUNDUP(a, size) \ + (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) + +void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + ROUNDUP(sa->sa_len, sizeof(long))); + } else + rti_info[i] = NULL; + } +} + +int +fetchifs(int ifindex) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct if_msghdr ifm; + struct kif_node *kif; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = ifindex; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchif"); + return (-1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm.ifm_msglen) { + memcpy(&ifm, next, sizeof(ifm)); + sa = (struct sockaddr *)(next + sizeof(ifm)); + get_rtaddrs(ifm.ifm_addrs, sa, rti_info); + + if (ifm.ifm_type != RTM_IFINFO) + continue; + + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { + log_warn("fetchifs"); + free(buf); + return (-1); + } + + kif->k.ifindex = ifm.ifm_index; + kif->k.flags = ifm.ifm_flags; + kif->k.link_state = ifm.ifm_data.ifi_link_state; + kif->k.media_type = ifm.ifm_data.ifi_type; + kif->k.baudrate = ifm.ifm_data.ifi_baudrate; + kif->k.mtu = ifm.ifm_data.ifi_mtu; + kif->k.nh_reachable = (kif->k.flags & IFF_UP) && + (ifm.ifm_data.ifi_link_state != LINK_STATE_DOWN); + if ((sa = rti_info[RTAX_IFP]) != NULL) + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (sdl->sdl_nlen > 0) + strlcpy(kif->k.ifname, sdl->sdl_data, + sizeof(kif->k.ifname)); + } + + kif_insert(kif); + } + free(buf); + return (0); +} + +void +kr_dispatch_msg(int fd, short event, void *bula) +{ + char buf[RT_BUF_SIZE]; + ssize_t n; + char *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + + if ((n = read(fd, &buf, sizeof(buf))) == -1) + fatal("dispatch_rtmsg: read error"); + + if (n == 0) + fatalx("routing socket closed"); + + lim = buf + n; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + + switch (rtm->rtm_type) { + case RTM_IFINFO: + memcpy(&ifm, next, sizeof(ifm)); + if_change(ifm.ifm_index, ifm.ifm_flags, + &ifm.ifm_data); + break; + case RTM_IFANNOUNCE: + if_announce(next); + break; + default: + /* ignore for now */ + break; + } + } +} + diff --git a/usr.sbin/dvmrpd/log.c b/usr.sbin/dvmrpd/log.c new file mode 100644 index 00000000000..f10d9e044c9 --- /dev/null +++ b/usr.sbin/dvmrpd/log.c @@ -0,0 +1,225 @@ +/* $OpenBSD: log.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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 <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "log.h" + +static const char * const procnames[] = { + "parent", + "dvmrpe", + "rde" +}; + +int debug; + +void logit(int, const char *, ...); + +void +log_init(int n_debug) +{ + extern char *__progname; + + debug = n_debug; + + if (!debug) + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + + tzset(); +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_CRIT, "%s", strerror(errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { + /* we tried it... */ + vlog(LOG_CRIT, emsg, ap); + logit(LOG_CRIT, "%s", strerror(errno)); + } else { + vlog(LOG_CRIT, nfmt, ap); + free(nfmt); + } + va_end(ap); + } +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_CRIT, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (debug) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +void +fatal(const char *emsg) +{ + if (emsg == NULL) + logit(LOG_CRIT, "fatal in %s: %s", procnames[dvmrpd_process], + strerror(errno)); + else + if (errno) + logit(LOG_CRIT, "fatal in %s: %s: %s", + procnames[dvmrpd_process], emsg, strerror(errno)); + else + logit(LOG_CRIT, "fatal in %s: %s", + procnames[dvmrpd_process], emsg); + + if (dvmrpd_process == PROC_MAIN) + exit(1); + else /* parent copes via SIGCHLD */ + _exit(1); +} + +void +fatalx(const char *emsg) +{ + errno = 0; + fatal(emsg); +} + +/* names */ +const char * +nbr_state_name(int state) +{ + switch (state) { + case NBR_STA_DOWN: + return ("DOWN"); + case NBR_STA_1_WAY: + return ("1-WAY"); + case NBR_STA_2_WAY: + return ("2-WAY"); + default: + return ("UNKNOWN"); + } +} + +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_QUERIER: + return ("QUERIER"); + case IF_STA_NONQUERIER: + return ("NONQUERIER"); + default: + return ("UNKNOWN"); + } +} + +const char * +group_state_name(int state) +{ + switch (state) { + case GRP_STA_NO_MEMB_PRSNT: + return ("NO MEMBER"); + case GRP_STA_MEMB_PRSNT: + return ("MEMBER"); + case GRP_STA_V1_MEMB_PRSNT: + return ("V1 MEMBER"); + case GRP_STA_CHECK_MEMB: + return ("CHECKING"); + default: + return ("UNKNOWN"); + } +} + +const char * +if_type_name(enum iface_type type) +{ + switch (type) { + case IF_TYPE_POINTOPOINT: + return ("POINTOPOINT"); + case IF_TYPE_BROADCAST: + return ("BROADCAST"); + } + /* NOTREACHED */ + return ("UNKNOWN"); +} diff --git a/usr.sbin/dvmrpd/log.h b/usr.sbin/dvmrpd/log.h new file mode 100644 index 00000000000..9fbc18e29b6 --- /dev/null +++ b/usr.sbin/dvmrpd/log.h @@ -0,0 +1,33 @@ +/* $OpenBSD: log.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * 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. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include <stdarg.h> + +void log_init(int); +void vlog(int, const char *, va_list); +void log_warn(const char *, ...); +void log_warnx(const char *, ...); +void log_info(const char *, ...); +void log_debug(const char *, ...); +void fatal(const char *) __dead; +void fatalx(const char *) __dead; + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/dvmrpd/nbrs2.c b/usr.sbin/dvmrpd/nbrs2.c new file mode 100644 index 00000000000..1ea88464dc6 --- /dev/null +++ b/usr.sbin/dvmrpd/nbrs2.c @@ -0,0 +1,80 @@ +/* $OpenBSD: nbrs2.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +/* DVMRP neighbors2 packet handling */ +int +send_nbrs2(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_nbrs2: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_nbrs2"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK)) + goto fail; + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_nbrs2"); + buf_free(buf); + return (-1); +} + +void +recv_nbrs2(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_nbrs2: neighbor ID %s", inet_ntoa(nbr->id)); + + return; +} diff --git a/usr.sbin/dvmrpd/neighbor.c b/usr.sbin/dvmrpd/neighbor.c new file mode 100644 index 00000000000..c8282c5eaa2 --- /dev/null +++ b/usr.sbin/dvmrpd/neighbor.c @@ -0,0 +1,427 @@ +/* $OpenBSD: neighbor.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" +#include "rde.h" + +LIST_HEAD(nbr_head, nbr); + +struct nbr_table { + struct nbr_head *hashtbl; + u_int32_t hashmask; +} nbrtable; + +#define NBR_HASH(x) \ + &nbrtable.hashtbl[(x) & nbrtable.hashmask] + +u_int32_t peercnt = NBR_CNTSTART; + +struct { + int state; + enum nbr_event event; + enum nbr_action action; + int new_state; +} nbr_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ + {NBR_STA_DOWN, NBR_EVT_PROBE_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_1_WAY}, + {NBR_STA_ACTIVE, NBR_EVT_PROBE_RCVD, NBR_ACT_RST_ITIMER, 0}, + {NBR_STA_1_WAY, NBR_EVT_2_WAY_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_2_WAY}, + {NBR_STA_ACTIVE, NBR_EVT_1_WAY_RCVD, NBR_ACT_RESET, NBR_STA_1_WAY}, + {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "PROBE RCVD", + "1-WAY RCVD", + "2-WAY RCVD", + "KILL NBR", + "ITIMER", + "LL DOWN" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "RESET ITIMER", + "START ITIMER", + "RESET", + "DELETE", + "CLEAR LISTS" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i, ret = 0; + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if ((nbr_fsm_tbl[i].state & old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* XXX event outside of the defined fsm, ignore it. */ + log_warnx("nbr_fsm: neighbor ID %s, " + "event '%s' not expected in state '%s'", + inet_ntoa(nbr->id), nbr_event_name(event), + nbr_state_name(old_state)); + return (0); + } + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_ITIMER: + ret = nbr_act_reset_itimer(nbr); + break; + case NBR_ACT_STRT_ITIMER: + ret = nbr_act_start_itimer(nbr); + break; + case NBR_ACT_RESET: + /* XXX nbr action reset */ + break; + case NBR_ACT_DEL: + ret = nbr_act_delete(nbr); + break; + case NBR_ACT_CLR_LST: + ret = nbr_act_clear_lists(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_warnx("nbr_fsm: error changing state for neighbor ID %s, " + "event '%s', state '%s'", inet_ntoa(nbr->id), + nbr_event_name(event), nbr_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + if (old_state & NBR_STA_2_WAY || nbr->state & NBR_STA_2_WAY) { + /* neighbor changed from/to 2_WAY */ + + gettimeofday(&now, NULL); + nbr->uptime = now.tv_sec; + } + + log_debug("nbr_fsm: event '%s' resulted in action '%s' and " + "changing state for neighbor ID %s from '%s' to '%s'", + nbr_event_name(event), + nbr_action_name(nbr_fsm_tbl[i].action), + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + } + + return (ret); +} + +void +nbr_init(u_int32_t hashsize) +{ + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head)); + if (nbrtable.hashtbl == NULL) + fatal("nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&nbrtable.hashtbl[i]); + + nbrtable.hashmask = hs - 1; +} + +struct nbr * +nbr_new(u_int32_t nbr_id, struct iface *iface, int self) +{ + struct nbr_head *head; + struct nbr *nbr = NULL; + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_new"); + + nbr->state = NBR_STA_DOWN; + nbr->id.s_addr = nbr_id; + + /* get next unused peerid */ + while (nbr_find_peerid(++peercnt)) + ; + nbr->peerid = peercnt; + head = NBR_HASH(nbr->peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + /* add to peer list */ + nbr->iface = iface; + LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry); + + TAILQ_INIT(&nbr->rr_list); + + /* set event structures */ + evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr); + + log_debug("nbr_new: neighbor ID %s, peerid %lu", + inet_ntoa(nbr->id), nbr->peerid); + + return (nbr); +} + +int +nbr_del(struct nbr *nbr) +{ + log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + /* clear lists */ + rr_list_clr(&nbr->rr_list); + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); + + return (0); +} + +struct nbr * +nbr_find_peerid(u_int32_t peerid) +{ + struct nbr_head *head; + struct nbr *nbr; + + head = NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct nbr * +nbr_find_ip(struct iface *iface, u_int32_t src_ip) +{ + struct nbr *nbr = NULL; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->id.s_addr == src_ip) { + return (nbr); + } + } + + return (NULL); +} + +/* timers */ +void +nbr_itimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_itimer: %s", inet_ntoa(nbr->id)); + + if (nbr->state == NBR_STA_DOWN) { + nbr_del(nbr); + } else + nbr_fsm(nbr, NBR_EVT_ITIMER); +} + +int +nbr_start_itimer(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id)); + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +int +nbr_stop_itimer(struct nbr *nbr) +{ + return (evtimer_del(&nbr->inactivity_timer)); +} + +int +nbr_reset_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +/* actions */ +int +nbr_act_start(struct nbr *nbr) +{ + log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id)); + + return (-1); +} + +int +nbr_act_reset_itimer(struct nbr *nbr) +{ + if (nbr_reset_itimer(nbr)) { + log_warnx("nbr_act_reset_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + return (0); +} + +int +nbr_act_start_itimer(struct nbr *nbr) +{ + if (nbr_start_itimer(nbr)) { + log_warnx("nbr_act_start_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", + inet_ntoa(nbr->id)); + return (-1); + } + + if (nbr->state == NBR_STA_1_WAY) { + /* new nbr, send entire route table, unicast */ + log_debug("nbr_act_start_itimer: nbr %s, send route table", + inet_ntoa(nbr->id)); + + dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, nbr->peerid, 0, + NULL, 0); + } + + return (0); +} + +int +nbr_act_delete(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id)); + + /* stop timers */ + if (nbr_stop_itimer(nbr)) { + log_warnx("nbr_act_delete: error removing inactivity timer, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + /* schedule kill timer */ + timerclear(&tv); + tv.tv_sec = DEFAULT_NBR_TMOUT; + + if (evtimer_add(&nbr->inactivity_timer, &tv)) { + log_warnx("nbr_act_delete: error scheduling neighbor ID %s " + "for removal", inet_ntoa(nbr->id)); + } + + return (nbr_act_clear_lists(nbr)); +} + +int +nbr_act_clear_lists(struct nbr *nbr) +{ + log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id)); + + /* XXX clear lists */ + + return (0); +} + +/* names */ +const char * +nbr_event_name(int event) +{ + return (nbr_event_names[event]); +} + +const char * +nbr_action_name(int action) +{ + return (nbr_action_names[action]); +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval tv, now, res; + + memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name)); + memcpy(&nctl.id, &nbr->id, sizeof(nctl.id)); + memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr)); + + nctl.state = nbr->state; + + gettimeofday(&now, NULL); + if (evtimer_pending(&nbr->inactivity_timer, &tv)) { + timersub(&tv, &now, &res); + if (nbr->state & NBR_STA_DOWN) + nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec; + else + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; + + if (nbr->state == NBR_STA_2_WAY) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} diff --git a/usr.sbin/dvmrpd/packet.c b/usr.sbin/dvmrpd/packet.c new file mode 100644 index 00000000000..f81a1614308 --- /dev/null +++ b/usr.sbin/dvmrpd/packet.c @@ -0,0 +1,288 @@ +/* $OpenBSD: packet.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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/time.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_mroute.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +int ip_hdr_sanity_check(const struct ip *, u_int16_t); +int dvmrp_hdr_sanity_check(const struct ip *, struct dvmrp_hdr *, + u_int16_t, const struct iface *); +struct iface *find_iface(struct dvmrpd_conf *, struct in_addr); + +extern struct dvmrpd_conf *deconf; + +int +gen_dvmrp_hdr(struct buf *buf, struct iface *iface, u_int8_t code) +{ + struct dvmrp_hdr dvmrp_hdr; + + bzero(&dvmrp_hdr, sizeof(dvmrp_hdr)); + dvmrp_hdr.type = PKT_TYPE_DVMRP; + dvmrp_hdr.code = code; + dvmrp_hdr.chksum = 0; /* updated later */ + dvmrp_hdr.capabilities = DVMRP_CAP_DEFAULT; /* XXX update */ + dvmrp_hdr.minor_version = DVMRP_MINOR_VERSION; + dvmrp_hdr.major_version = DVMRP_MAJOR_VERSION; + + return (buf_add(buf, &dvmrp_hdr, sizeof(dvmrp_hdr))); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst) +{ + if (iface->passive) { + log_warnx("send_packet: cannot send packet on passive " + "interface %s", iface->name); + return (-1); + } + + /* set outgoing interface for multicast traffic */ + if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) + if (if_set_mcast(iface) == -1) { + log_warn("send_packet: error setting multicast " + "interface, %s", iface->name); + return (-1); + } + + if (sendto(iface->fd, pkt, len, 0, + (struct sockaddr *)dst, sizeof(*dst)) == -1 ) { + log_warn("send_packet: error sending packet on interface %s", + iface->name); + return (-1); + } + + return (0); +} + +void +recv_packet(int fd, short event, void *bula) +{ + struct dvmrpd_conf *xconf = bula; + struct ip ip_hdr; + struct dvmrp_hdr *dvmrp_hdr; + struct iface *iface; + struct nbr *nbr = NULL; + struct in_addr addr; + char *buf; + ssize_t r; + u_int16_t len; + int l; + + if (event != EV_READ) + return; + + /* setup buffer */ + buf = pkt_ptr; + + if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) { + if (errno != EAGAIN && errno != EINTR) + log_debug("recv_packet: error receiving packet"); + return; + } + + len = (u_int16_t)r; + + /* IP header sanity checks */ + if (len < sizeof(ip_hdr)) { + log_warnx("recv_packet: bad packet size"); + return; + } + + memcpy(&ip_hdr, buf, sizeof(ip_hdr)); + if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) + return; + buf += l; + len -= l; + + /* find a matching interface */ + if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) { + log_debug("recv_packet: cannot find valid interface, ip src %s", + inet_ntoa(ip_hdr.ip_src)); + return; + } + + /* header sanity checks */ + if (len < sizeof(*dvmrp_hdr)) { + log_warnx("recv_packet: bad packet size"); + return; + } + dvmrp_hdr = (struct dvmrp_hdr *)buf; + + switch (dvmrp_hdr->type) { + /* DVMRP */ + case PKT_TYPE_DVMRP: + if ((l = dvmrp_hdr_sanity_check(&ip_hdr, dvmrp_hdr, len, + iface)) == -1) + return; + + /* + * mrouted compat + * + * Old mrouted versions, send route reports before establishing + * 2-WAY neighbor relationships. + */ + if ((nbr_find_ip(iface, ip_hdr.ip_src.s_addr) == NULL) && + (dvmrp_hdr->code == DVMRP_CODE_REPORT)) { + log_debug("recv_packet: route report from neighbor" + " ID %s, compat", inet_ntoa(ip_hdr.ip_src)); + nbr = nbr_new(ip_hdr.ip_src.s_addr, iface, 0); + nbr_fsm(nbr, NBR_EVT_PROBE_RCVD); + nbr->compat = 1; + nbr->addr = ip_hdr.ip_src; + } + + if ((dvmrp_hdr->type == PKT_TYPE_DVMRP) && + (dvmrp_hdr->code != DVMRP_CODE_PROBE)) + /* find neighbor */ + if ((nbr = nbr_find_ip(iface, ip_hdr.ip_src.s_addr)) + == NULL) { + log_debug("recv_packet: unknown neighbor ID"); + return; + } + + buf += sizeof(*dvmrp_hdr); + len = l - sizeof(*dvmrp_hdr); + + inet_aton(AllDVMRPRouters, &addr); + if ((ip_hdr.ip_dst.s_addr != addr.s_addr) && + (ip_hdr.ip_dst.s_addr != iface->addr.s_addr)) { + log_debug("recv_packet: interface %s, invalid" + " destination IP address %s", iface->name, + inet_ntoa(ip_hdr.ip_dst)); + break; + } + + switch (dvmrp_hdr->code) { + case DVMRP_CODE_PROBE: + recv_probe(iface, ip_hdr.ip_src, ip_hdr.ip_src.s_addr, + dvmrp_hdr->capabilities, buf, len); + break; + case DVMRP_CODE_REPORT: + recv_report(nbr, buf, len); + break; + case DVMRP_CODE_ASK_NBRS2: + recv_ask_nbrs2(nbr, buf,len); + break; + case DVMRP_CODE_NBRS2: + recv_nbrs2(nbr, buf,len); + break; + case DVMRP_CODE_PRUNE: + recv_prune(nbr, buf, len); + break; + case DVMRP_CODE_GRAFT: + recv_graft(nbr, buf,len); + break; + case DVMRP_CODE_GRAFT_ACK: + recv_graft_ack(nbr, buf,len); + break; + default: + log_debug("recv_packet: unknown DVMRP packet type, " + "interface %s", iface->name); + } + break; + /* IGMP */ + case PKT_TYPE_MEMBER_QUERY: + recv_igmp_query(iface, ip_hdr.ip_src, buf, len); + break; + case PKT_TYPE_MEMBER_REPORTv1: + case PKT_TYPE_MEMBER_REPORTv2: + recv_igmp_report(iface, ip_hdr.ip_src, buf, len, + dvmrp_hdr->type); + break; + case PKT_TYPE_LEAVE_GROUPv2: + recv_igmp_leave(iface, ip_hdr.ip_src, buf, len); + break; + default: + log_debug("recv_packet: unknown IGMP packet type, interface %s", + iface->name); + } +} + +int +ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len) +{ + if (ntohs(ip_hdr->ip_len) != len) { + log_debug("recv_packet: invalid IP packet length %u", + ntohs(ip_hdr->ip_len)); + return (-1); + } + + if (ip_hdr->ip_p != IPPROTO_IGMP) + /* this is enforced by the socket itself */ + fatalx("recv_packet: invalid IP proto"); + + return (ip_hdr->ip_hl << 2); +} + +int +dvmrp_hdr_sanity_check(const struct ip *ip_hdr, struct dvmrp_hdr *dvmrp_hdr, + u_int16_t len, const struct iface *iface) +{ + /* we only support DVMRPv3 */ + if (dvmrp_hdr->major_version != DVMRP_MAJOR_VERSION) { + log_debug("recv_packet: invalid DVMRP version"); + return (-1); + } + + /* XXX enforce minor version as well, but not yet */ + + /* XXX chksum */ + + return (len); +} + +struct iface * +find_iface(struct dvmrpd_conf *xconf, struct in_addr src) +{ + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + if (iface->fd > 0 && + (iface->type == IF_TYPE_POINTOPOINT) && + (iface->dst.s_addr == src.s_addr) && + !iface->passive) + return (iface); + + if (iface->fd > 0 && (iface->addr.s_addr & + iface->mask.s_addr) == (src.s_addr & + iface->mask.s_addr) && !iface->passive) + return (iface); + } + + return (NULL); +} diff --git a/usr.sbin/dvmrpd/parse.y b/usr.sbin/dvmrpd/parse.y new file mode 100644 index 00000000000..faa778d80c6 --- /dev/null +++ b/usr.sbin/dvmrpd/parse.y @@ -0,0 +1,786 @@ +/* $OpenBSD: parse.y,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org> + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * 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/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "dvmrpe.h" +#include "log.h" + +static struct dvmrpd_conf *conf; +static FILE *fin = NULL; +static int lineno = 1; +static int errors = 0; +static int pdebug = 1; +char *infile; +char *start_state; + +struct iface *iface = NULL; + +int yyerror(const char *, ...); +int yyparse(void); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(FILE *); +int lungetc(int); +int findeol(void); +int yylex(void); +void clear_config(struct dvmrpd_conf *xconf); +int check_file_secrecy(int fd, const char *fname); + +static struct { + u_int32_t probe_interval; + u_int32_t query_interval; + u_int32_t query_resp_interval; + u_int32_t startup_query_interval; + u_int32_t startup_query_cnt; + u_int32_t last_member_query_interval; + u_int32_t last_member_query_cnt; + u_int32_t dead_interval; + u_int16_t metric; + u_int8_t robustness; + u_int8_t igmp_version; +} *defs, *grdefs, globaldefs, groupdefs, ifacedefs; + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entries; + int used; + int persist; + char *nam; + char *val; +}; + +int symset(const char *, const char *, int); +char *symget(const char *); +int atoul(char *, u_long *); +struct iface *conf_get_if(struct kif *); +struct iface *new_group(void); + +typedef struct { + union { + u_int32_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token INTERFACE FIBUPDATE +%token GROUP +%token METRIC PASSIVE +%token ROBUSTNESS QUERYINTERVAL QUERYRESPINTERVAL +%token STARTUPQUERYINTERVAL STARTUPQUERYCNT +%token LASTMEMBERQUERYINTERVAL LASTMEMBERQUERYCNT +%token IGMPVERSION +%token ERROR +%token <v.string> STRING +%type <v.number> number yesno +%type <v.string> string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar conf_main '\n' + | grammar varset '\n' + | grammar interface '\n' + | grammar group '\n' + | grammar error '\n' { errors++; } + ; + +number : STRING { + u_long ulval; + + if (atoul($1, &ulval) == -1) { + yyerror("%s is not a number", $1); + free($1); + YYERROR; + } else + $$ = ulval; + free($1); + } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : STRING { + if (!strcmp($1, "yes")) + $$ = 1; + else if (!strcmp($1, "no")) + $$ = 0; + else { + free($1); + YYERROR; + } + free($1); + } + ; + +varset : STRING '=' string { + if (conf->opts & DVMRPD_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +conf_main : FIBUPDATE yesno { + if ($2 == 0) + conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE; + else + conf->flags &= ~DVMRPD_FLAG_NO_FIB_UPDATE; + } + | defaults + ; + +defaults : LASTMEMBERQUERYCNT number { + if ($2 < MIN_LAST_MEMBER_QUERY_CNT || + $2 > MAX_LAST_MEMBER_QUERY_CNT) { + yyerror("last-member-query-count out of " + "range (%d-%d)", + MIN_LAST_MEMBER_QUERY_CNT, + MAX_LAST_MEMBER_QUERY_CNT); + YYERROR; + } + defs->last_member_query_cnt = $2; + } + | LASTMEMBERQUERYINTERVAL number { + if ($2 < MIN_LAST_MEMBER_QUERY_INTERVAL || + $2 > MAX_LAST_MEMBER_QUERY_INTERVAL) { + yyerror("last-member-query-interval out of " + "range (%d-%d)", + MIN_LAST_MEMBER_QUERY_INTERVAL, + MAX_LAST_MEMBER_QUERY_INTERVAL); + YYERROR; + } + defs->last_member_query_interval = $2; + } + | METRIC number { + if ($2 < MIN_METRIC || $2 > MAX_METRIC) { + yyerror("metric out of range (%d-%d)", + MIN_METRIC, MAX_METRIC); + YYERROR; + } + defs->metric = $2; + } + | QUERYINTERVAL number { + if ($2 < MIN_QUERY_INTERVAL || + $2 > MAX_QUERY_INTERVAL) { + yyerror("query-interval out of range (%d-%d)", + MIN_QUERY_INTERVAL, MAX_QUERY_INTERVAL); + YYERROR; + } + defs->query_interval = $2; + } + | QUERYRESPINTERVAL number { + if ($2 < MIN_QUERY_RESP_INTERVAL || + $2 > MAX_QUERY_RESP_INTERVAL) { + yyerror("query-response-interval out of " + "range (%d-%d)", + MIN_QUERY_RESP_INTERVAL, + MAX_QUERY_RESP_INTERVAL); + YYERROR; + } + defs->query_resp_interval = $2; + } + | ROBUSTNESS number { + if ($2 < MIN_ROBUSTNESS || $2 > MAX_ROBUSTNESS) { + yyerror("robustness out of range (%d-%d)", + MIN_ROBUSTNESS, MAX_ROBUSTNESS); + YYERROR; + } + defs->robustness = $2; + } + | STARTUPQUERYCNT number { + if ($2 < MIN_STARTUP_QUERY_CNT || + $2 > MAX_STARTUP_QUERY_CNT) { + yyerror("startup-query-count out of " + "range (%d-%d)", + MIN_STARTUP_QUERY_CNT, + MAX_STARTUP_QUERY_CNT); + YYERROR; + } + defs->startup_query_cnt = $2; + } + | STARTUPQUERYINTERVAL number { + if ($2 < MIN_STARTUP_QUERY_INTERVAL || + $2 > MAX_STARTUP_QUERY_INTERVAL) { + yyerror("startup-query-interval out of " + "range (%d-%d)", + MIN_STARTUP_QUERY_INTERVAL, + MAX_STARTUP_QUERY_INTERVAL); + YYERROR; + } + defs->startup_query_interval = $2; + } + | IGMPVERSION number { + if ($2 < MIN_IGMP_VERSION || + $2 > MAX_IGMP_VERSION) { + yyerror("igmp-version out of range (%d-%d)", + MIN_IGMP_VERSION, MAX_IGMP_VERSION); + YYERROR; + } + defs->igmp_version = $2; + } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl /* one newline or more */ + ; + +interface : INTERFACE STRING { + struct kif *kif; + + if ((kif = kif_findname($2)) == NULL) { + yyerror("unknown interface %s", $2); + free($2); + YYERROR; + } + free($2); + iface = conf_get_if(kif); + if (iface == NULL) + YYERROR; + LIST_INSERT_HEAD(&conf->iface_list, iface, entry); + + memcpy(&ifacedefs, defs, sizeof(ifacedefs)); + defs = &ifacedefs; + } interface_block { + iface->probe_interval = defs->probe_interval; + iface->query_interval = defs->query_interval; + iface->query_resp_interval = defs->query_resp_interval; + iface->startup_query_interval = + defs->startup_query_interval; + iface->startup_query_cnt = defs->startup_query_cnt; + iface->last_member_query_interval = + defs->last_member_query_interval; + iface->last_member_query_cnt = + defs->last_member_query_cnt; + iface->dead_interval = defs->dead_interval; + iface->metric = defs->metric; + iface->robustness = defs->robustness; + iface->igmp_version = defs->igmp_version; + if (grdefs) + defs = grdefs; + else + defs = &globaldefs; + iface = NULL; + } + ; + +interface_block : '{' optnl interfaceopts_l '}' + | '{' optnl '}' + | + ; + +interfaceopts_l : interfaceopts_l interfaceoptsl + | interfaceoptsl + ; + +interfaceoptsl : PASSIVE nl { iface->passive = 1; } + | defaults nl + ; + +group : GROUP optnl '{' optnl { + memcpy(&groupdefs, defs, sizeof(groupdefs)); + grdefs = defs = &groupdefs; + } + groupopts_l '}' { + grdefs = NULL; + defs = &globaldefs; + } + ; + +groupopts_l : groupopts_l groupoptsl + | groupoptsl + ; + +groupoptsl : interface nl + | defaults nl + | error nl + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + + errors = 1; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", infile, yylval.lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + {"fib-update", FIBUPDATE}, + {"group", GROUP}, + {"igmp-version", IGMPVERSION}, + {"interface", INTERFACE}, + {"last-member-query-count", LASTMEMBERQUERYCNT}, + {"last-member-query-interval", LASTMEMBERQUERYINTERVAL}, + {"metric", METRIC}, + {"passive", PASSIVE}, + {"query-interval", QUERYINTERVAL}, + {"query-response-interval", QUERYRESPINTERVAL}, + {"robustness", ROBUSTNESS}, + {"startup-query-count", STARTUPQUERYCNT}, + {"startup-query-interval", STARTUPQUERYINTERVAL} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) { + if (pdebug > 1) + fprintf(stderr, "%s: %d\n", s, p->k_val); + return (p->k_val); + } else { + if (pdebug > 1) + fprintf(stderr, "string: %s\n", s); + return (STRING); + } +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(FILE *f) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + while ((c = getc(f)) == '\\') { + next = getc(f); + if (next != '\n') { + if (isspace(next)) + yyerror("whitespace after \\"); + ungetc(next, f); + break; + } + yylval.lineno = lineno; + lineno++; + } + if (c == '\t' || c == ' ') { + /* Compress blanks to a single space. */ + do { + c = getc(f); + } while (c == '\t' || c == ' '); + ungetc(c, f); + c = ' '; + } + + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + pushback_index = 0; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(fin); + if (c == '\n') { + lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int endc, c; + int token; + +top: + p = buf; + while ((c = lgetc(fin)) == ' ') + ; /* nothing */ + + yylval.lineno = lineno; + if (c == '#') + while ((c = lgetc(fin)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + endc = c; + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + if (c == endc) { + *p = '\0'; + break; + } + if (c == '\n') { + lineno++; + continue; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + errx(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = lineno; + lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct dvmrpd_conf * +parse_config(char *filename, int opts) +{ + struct sym *sym, *next; + struct timeval now; + + if ((conf = calloc(1, sizeof(struct dvmrpd_conf))) == NULL) { + errx(1, "parse_config calloc"); + return (NULL); + } + + defs = &globaldefs; + defs->probe_interval = DEFAULT_PROBE_INTERVAL; + defs->last_member_query_cnt = DEFAULT_LAST_MEMBER_QUERY_CNT; + defs->last_member_query_interval = DEFAULT_LAST_MEMBER_QUERY_INTERVAL; + defs->metric = DEFAULT_METRIC; + defs->query_interval = DEFAULT_QUERY_INTERVAL; + defs->query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL; + defs->robustness = DEFAULT_ROBUSTNESS; + defs->startup_query_cnt = DEFAULT_STARTUP_QUERY_CNT; + defs->startup_query_interval = DEFAULT_STARTUP_QUERY_INTERVAL; + defs->igmp_version = DEFAULT_IGMP_VERSION; + defs->dead_interval = NBR_TMOUT; + + if ((fin = fopen(filename, "r")) == NULL) { + warn("%s", filename); + free(conf); + return (NULL); + } + infile = filename; + + /* Generation ID must be non decreasing */ + gettimeofday(&now, NULL); + conf->gen_id = now.tv_sec; + conf->opts = opts; + + if (check_file_secrecy(fileno(fin), filename)) { + fclose(fin); + free(conf); + return (NULL); + } + + yyparse(); + + fclose(fin); + + /* Free macros and check which have not been used. */ + for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { + next = TAILQ_NEXT(sym, entries); + if ((conf->opts & DVMRPD_OPT_VERBOSE2) && !sym->used) + fprintf(stderr, "warning: macro '%s' not " + "used\n", sym->nam); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entries); + free(sym); + } + } + + if (errors) { + clear_config(conf); + return (NULL); + } + + return (conf); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entries)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entries); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entries); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entries) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +int +atoul(char *s, u_long *ulvalp) +{ + u_long ulval; + char *ep; + + errno = 0; + ulval = strtoul(s, &ep, 0); + if (s[0] == '\0' || *ep != '\0') + return (-1); + if (errno == ERANGE && ulval == ULONG_MAX) + return (-1); + *ulvalp = ulval; + return (0); +} + +struct iface * +conf_get_if(struct kif *kif) +{ + struct iface *i; + + LIST_FOREACH(i, &conf->iface_list, entry) + if (i->ifindex == kif->ifindex) { + yyerror("interface %s already configured", + kif->ifname); + return (NULL); + } + + i = if_new(kif); + i->passive = 0; + i->recv_query_resp_interval = DEFAULT_QUERY_RESP_INTERVAL; + + return (i); +} + +void +clear_config(struct dvmrpd_conf *xconf) +{ + /* XXX clear conf */ + /* ... */ +} diff --git a/usr.sbin/dvmrpd/printconf.c b/usr.sbin/dvmrpd/printconf.c new file mode 100644 index 00000000000..bfc4bd56c07 --- /dev/null +++ b/usr.sbin/dvmrpd/printconf.c @@ -0,0 +1,79 @@ +/* $OpenBSD: printconf.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@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/queue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <stdio.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "dvmrpe.h" + +void print_mainconf(struct dvmrpd_conf *); +void print_iface(struct iface *); + +void +print_mainconf(struct dvmrpd_conf *conf) +{ + if (conf->flags |= DVMRPD_FLAG_NO_FIB_UPDATE) + printf("fib-update yes\n"); + else + printf("fib-update no\n"); +} + +void +print_iface(struct iface *iface) +{ + printf("interface %s {\n", iface->name); + + if (iface->passive) + printf("\tpassive\n"); + + printf("\tmetric %d\n", iface->metric); + printf("\tquery-interval %d\n", iface->query_interval); + printf("\tquery-response-interval %d\n", iface->query_resp_interval); + printf("\trobustness %d\n", iface->robustness); + printf("\tstartup-query-count %d\n", iface->startup_query_cnt); + printf("\tlast-member-query-count %d\n", iface->last_member_query_cnt); + printf("\tlast-member-query-interval %d\n", + iface->last_member_query_interval); + printf("\tigmp-version %d\n", iface->igmp_version); + + printf("}\n"); +} + +void +print_config(struct dvmrpd_conf *conf) +{ + struct iface *iface; + + printf("\n"); + print_mainconf(conf); + printf("\n"); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + print_iface(iface); + } + printf("\n"); + +} diff --git a/usr.sbin/dvmrpd/probe.c b/usr.sbin/dvmrpd/probe.c new file mode 100644 index 00000000000..93e3dccb607 --- /dev/null +++ b/usr.sbin/dvmrpd/probe.c @@ -0,0 +1,139 @@ +/* $OpenBSD: probe.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +extern struct dvmrpd_conf *deconf; + +/* DVMRP probe packet handling */ +int +send_probe(struct iface *iface) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + struct nbr *nbr; + int ret = 0; + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_probe"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_PROBE)) + goto fail; + + /* generation ID */ + buf_add(buf, &iface->gen_id, sizeof(iface->gen_id)); + + /* generate neighbor list */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->state > NBR_STA_DOWN) + buf_add(buf, &nbr->id, sizeof(nbr->id)); + } + + /* set destination address */ + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + inet_aton(AllDVMRPRouters, &dst.sin_addr); + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_probe"); + buf_free(buf); + return (-1); +} + +void +recv_probe(struct iface *iface, struct in_addr src, u_int32_t src_ip, + u_int8_t capabilities, char *buf, u_int16_t len) +{ + struct nbr *nbr = NULL; + u_int32_t gen_id; + u_int32_t nbr_id; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->id.s_addr == src_ip) + break; + } + + memcpy(&gen_id, buf, sizeof(gen_id)); + len -= sizeof(gen_id); + buf += sizeof(gen_id); + + if (!nbr) { + nbr = nbr_new(src_ip, iface, 0); + nbr->gen_id = gen_id; + nbr->capabilities = capabilities; + nbr->addr = src; + } + + nbr_fsm(nbr, NBR_EVT_PROBE_RCVD); + + if ((nbr->gen_id != gen_id) || (nbr->capabilities != capabilities)) { + if (!nbr->compat) + nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); + nbr->gen_id = gen_id; + nbr->capabilities = capabilities; + + /* XXX handle nbr change! */ + } + + while (len >= sizeof(nbr_id)) { + memcpy(&nbr_id, buf, sizeof(nbr_id)); + if (nbr_id == iface->addr.s_addr) { + /* seen myself */ + if (nbr->state < NBR_STA_2_WAY) + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + break; + } + buf += sizeof(nbr_id); + len -= sizeof(nbr_id); + } + + if (len == 0) { + nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); + return; + } + + /* XXX len correct?? */ + + return; +} diff --git a/usr.sbin/dvmrpd/prune.c b/usr.sbin/dvmrpd/prune.c new file mode 100644 index 00000000000..697603432f5 --- /dev/null +++ b/usr.sbin/dvmrpd/prune.c @@ -0,0 +1,80 @@ +/* $OpenBSD: prune.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "log.h" +#include "dvmrpe.h" + +/* DVMRP prune packet handling */ +int +send_prune(struct nbr *nbr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_prune: interface %s nbr %s", nbr->iface->name, + inet_ntoa(nbr->addr)); + + if (nbr->iface->passive) + return (0); + + if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_prune"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, nbr->iface, DVMRP_CODE_PRUNE)) + goto fail; + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr = nbr->addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_prune"); + buf_free(buf); + return (-1); +} + +void +recv_prune(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_prune: neighbor ID %s", inet_ntoa(nbr->id)); + + return; +} diff --git a/usr.sbin/dvmrpd/rde.c b/usr.sbin/dvmrpd/rde.c new file mode 100644 index 00000000000..ad359830b34 --- /dev/null +++ b/usr.sbin/dvmrpd/rde.c @@ -0,0 +1,281 @@ +/* $OpenBSD: rde.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2006 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 <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <pwd.h> +#include <unistd.h> +#include <event.h> + +#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 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"); + /* NOTREACHED */ + } +} + +/* 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"); + /* NOTREACHED */ + 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) || + setegid(pw->pw_gid) || setgid(pw->pw_gid) || + seteuid(pw->pw_uid) || setuid(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); + + /* 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 imsgbuf *ibuf = bula; + struct imsg imsg; + struct route_report rr; + int n, connected = 0; + int i; + 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; + } + rt_update(rr.net, mask2prefixlen(rr.mask.s_addr), + rr.nexthop, rr.metric, rr.adv_rtr, rr.ifindex, 0, + connected); + 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; + default: + log_debug("rde_dispatch_msg: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} diff --git a/usr.sbin/dvmrpd/rde.h b/usr.sbin/dvmrpd/rde.h new file mode 100644 index 00000000000..8483a05d0cd --- /dev/null +++ b/usr.sbin/dvmrpd/rde.h @@ -0,0 +1,82 @@ +/* $OpenBSD: rde.h,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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. + */ + +#ifndef _RDE_H_ +#define _RDE_H_ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <sys/queue.h> +#include <event.h> +#include <limits.h> + +struct rt_node { + RB_ENTRY(rt_node) entry; + struct event expiration_timer; + struct in_addr prefix; + struct in_addr nexthop; + struct in_addr adv_rtr; + u_int32_t cost; + u_short ifindex; /* learned from this iface */ + time_t uptime; + u_int8_t flags; + u_int8_t prefixlen; + u_int8_t invalid; + u_int8_t connected; +}; + +struct mfc_node { + RB_ENTRY(mfc_node) entry; + struct event expiration_timer; + u_int8_t ttls[MAXVIFS]; /* outgoing vif(s) */ + struct in_addr origin; + struct in_addr group; + u_short ifindex; /* incoming vif */ + time_t uptime; +}; + +/* rde.c */ +pid_t rde(struct dvmrpd_conf *, int [2], int [2], int [2]); +int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int rde_imsg_compose_dvmrpe(int, u_int32_t, pid_t, void *, u_int16_t); + +/* rde_mfc.c */ +void mfc_init(void); +int mfc_compare(struct mfc_node *, struct mfc_node *); +struct mfc_node *mfc_find(in_addr_t, in_addr_t); +int mfc_insert(struct mfc_node *); +int mfc_remove(struct mfc_node *); +void mfc_clear(void); +void mfc_dump(pid_t); +void mfc_update(struct mfc *); +void mfc_delete(struct mfc *); + +/* rde_srt.c */ +void rt_init(void); +int rt_compare(struct rt_node *, struct rt_node *); +struct rt_node *rt_find(in_addr_t, u_int8_t); +int rt_insert(struct rt_node *); +int rt_remove(struct rt_node *); +void rt_clear(void); +void rt_snap(u_int32_t); +void rt_dump(pid_t); +void rt_update(struct in_addr, u_int8_t, struct in_addr, + u_int32_t, struct in_addr, u_short, u_int8_t, u_int8_t); + +#endif /* _RDE_H_ */ diff --git a/usr.sbin/dvmrpd/rde_mfc.c b/usr.sbin/dvmrpd/rde_mfc.c new file mode 100644 index 00000000000..b985360fa56 --- /dev/null +++ b/usr.sbin/dvmrpd/rde_mfc.c @@ -0,0 +1,218 @@ +/* $OpenBSD: rde_mfc.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 Esben Norby <norby@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/tree.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "rde.h" +#include "log.h" +#include "dvmrpe.h" + +/* multicast forwarding cache */ + +void mfc_invalidate(void); +void mfc_expire_timer(int, short, void *); +int mfc_start_expire_timer(struct mfc_node *); +int mfc_compare(struct mfc_node *, struct mfc_node *); + +RB_HEAD(mfc_tree, mfc_node) mfc; +RB_PROTOTYPE(mfc_tree, mfc_node, entry, mfc_compare) +RB_GENERATE(mfc_tree, mfc_node, entry, mfc_compare) + +extern struct dvmrpd_conf *rdeconf; + +/* timers */ +void +mfc_expire_timer(int fd, short event, void *arg) +{ + struct mfc_node *mn = arg; + struct mfc nmfc; + + log_debug("mfc_expire_timer: group %s", inet_ntoa(mn->group)); + + /* remove route entry */ + nmfc.origin = mn->origin; + nmfc.group = mn->group; + rde_imsg_compose_parent(IMSG_MFC_DEL, 0, &nmfc, sizeof(nmfc)); + + event_del(&mn->expiration_timer); + mfc_remove(mn); +} + +int +mfc_start_expire_timer(struct mfc_node *mn) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ROUTE_EXPIRATION_TIME; + return (evtimer_add(&mn->expiration_timer, &tv)); +} + +/* route table */ +void +mfc_init(void) +{ + RB_INIT(&mfc); +} + +int +mfc_compare(struct mfc_node *a, struct mfc_node *b) +{ + if (ntohl(a->origin.s_addr) < ntohl(b->origin.s_addr)) + return (-1); + if (ntohl(a->origin.s_addr) > ntohl(b->origin.s_addr)) + return (1); + if (ntohl(a->group.s_addr) < ntohl(b->group.s_addr)) + return (-1); + if (ntohl(a->group.s_addr) > ntohl(b->group.s_addr)) + return (1); + return (0); +} + +struct mfc_node * +mfc_find(in_addr_t origin, in_addr_t group) +{ + struct mfc_node s; + + s.origin.s_addr = origin; + s.group.s_addr = group; + + return (RB_FIND(mfc_tree, &mfc, &s)); +} + +int +mfc_insert(struct mfc_node *m) +{ + if (RB_INSERT(mfc_tree, &mfc, m) != NULL) { + log_warnx("mfc_insert failed for group %s", + inet_ntoa(m->group)); + free(m); + return (-1); + } + + return (0); +} + +int +mfc_remove(struct mfc_node *m) +{ + if (RB_REMOVE(mfc_tree, &mfc, m) == NULL) { + log_warnx("mfc_remove failed for group %s", + inet_ntoa(m->group)); + return (-1); + } + + free(m); + return (0); +} + +void +mfc_clear(void) +{ + struct mfc_node *m; + + while ((m = RB_MIN(mfc_tree, &mfc)) != NULL) + mfc_remove(m); +} + +void +mfc_dump(pid_t pid) +{ + static struct ctl_mfc mfcctl; + struct timespec now; + struct timeval tv, now2, res; + struct mfc_node *mn; + int i; + + clock_gettime(CLOCK_MONOTONIC, &now); + + RB_FOREACH(mn, mfc_tree, &mfc) { + mfcctl.origin.s_addr = mn->origin.s_addr; + mfcctl.group.s_addr = mn->group.s_addr; + mfcctl.uptime = now.tv_sec - mn->uptime; + mfcctl.ifindex = mn->ifindex; + + for (i = 0; i < MAXVIFS; i ++) { + mfcctl.ttls[i] = mn->ttls[i]; + } + + gettimeofday(&now2, NULL); + if (evtimer_pending(&mn->expiration_timer, &tv)) { + timersub(&tv, &now2, &res); + mfcctl.expire = res.tv_sec; + } else + mfcctl.expire = -1; + + rde_imsg_compose_dvmrpe(IMSG_CTL_SHOW_MFC, 0, pid, &mfcctl, + sizeof(mfcctl)); + } +} + +void +mfc_update(struct mfc *nmfc) +{ + struct timespec now; + struct mfc_node *mn; + int i; + + clock_gettime(CLOCK_MONOTONIC, &now); + + if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL) { + if ((mn = calloc(1, sizeof(struct mfc_node))) == NULL) + fatalx("mfc_update"); + + mn->origin.s_addr = nmfc->origin.s_addr; + mn->group.s_addr = nmfc->group.s_addr; + mn->ifindex = nmfc->ifindex; + mn->uptime = now.tv_sec; + for (i = 0; i < MAXVIFS; i++) + mn->ttls[i] = nmfc->ttls[i]; + + if (mfc_insert(mn) == 0) { + if (nmfc->origin.s_addr != 0) + rde_imsg_compose_parent(IMSG_MFC_ADD, 0, nmfc, + sizeof(*nmfc)); + } + + evtimer_set(&mn->expiration_timer, mfc_expire_timer, mn); + mfc_start_expire_timer(mn); + } +} + +void +mfc_delete(struct mfc *nmfc) +{ + struct mfc_node *mn; + + if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL) + return; + + /* XXX decide if it should really be removed */ + mfc_remove(mn); + + /* XXX notify parent */ +} diff --git a/usr.sbin/dvmrpd/rde_srt.c b/usr.sbin/dvmrpd/rde_srt.c new file mode 100644 index 00000000000..bc288db30b5 --- /dev/null +++ b/usr.sbin/dvmrpd/rde_srt.c @@ -0,0 +1,246 @@ +/* $OpenBSD: rde_srt.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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/tree.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "rde.h" +#include "log.h" +#include "dvmrpe.h" + +/* source route tree */ + +void rt_invalidate(void); +void rt_expire_timer(int, short, void *); +int rt_start_expire_timer(struct rt_node *); + +RB_HEAD(rt_tree, rt_node) rt; +RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare) +RB_GENERATE(rt_tree, rt_node, entry, rt_compare) + +extern struct dvmrpd_conf *rdeconf; + +/* timers */ +void +rt_expire_timer(int fd, short event, void *arg) +{ + struct rt_node *rn = arg; + + log_debug("rt_expire_timer: route %s/%d", inet_ntoa(rn->prefix), + rn->prefixlen); + + /* XXX tell neighbors */ + + /* remove route entry */ + event_del(&rn->expiration_timer); + rt_remove(rn); +} + +int +rt_start_expire_timer(struct rt_node *rn) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ROUTE_EXPIRATION_TIME; + return (evtimer_add(&rn->expiration_timer, &tv)); + +} + +/* route table */ +void +rt_init(void) +{ + RB_INIT(&rt); +} + +int +rt_compare(struct rt_node *a, struct rt_node *b) +{ + /* + * sort route entries based on prefixlen since generating route + * reports rely on that. + */ + if (a->prefixlen < b->prefixlen) + return (-1); + if (a->prefixlen > b->prefixlen) + return (1); + if (ntohl(a->prefix.s_addr) < ntohl(b->prefix.s_addr)) + return (-1); + if (ntohl(a->prefix.s_addr) > ntohl(b->prefix.s_addr)) + return (1); + return (0); +} + +struct rt_node * +rt_find(in_addr_t prefix, u_int8_t prefixlen) +{ + struct rt_node s; + + s.prefix.s_addr = prefix; + s.prefixlen = prefixlen; + + return (RB_FIND(rt_tree, &rt, &s)); +} + +int +rt_insert(struct rt_node *r) +{ + if (RB_INSERT(rt_tree, &rt, r) != NULL) { + log_warnx("rt_insert failed for %s/%u", inet_ntoa(r->prefix), + r->prefixlen); + free(r); + return (-1); + } + + return (0); +} + +int +rt_remove(struct rt_node *r) +{ + if (RB_REMOVE(rt_tree, &rt, r) == NULL) { + log_warnx("rt_remove failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + return (-1); + } + + free(r); + return (0); +} + +void +rt_invalidate(void) +{ + struct rt_node *r, *nr; + + for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) { + nr = RB_NEXT(rt_tree, &rt, r); + if (r->invalid) + rt_remove(r); + else + r->invalid = 1; + } +} + +void +rt_clear(void) +{ + struct rt_node *r; + + while ((r = RB_MIN(rt_tree, &rt)) != NULL) + rt_remove(r); +} + +void +rt_snap(u_int32_t peerid) +{ + struct rt_node *r; + struct route_report rr; + + RB_FOREACH(r, rt_tree, &rt) { + if (r->invalid) + continue; + + rr.net = r->prefix; + rr.mask.s_addr = ntohl(prefixlen2mask(r->prefixlen)); + rr.metric = r->cost; + rr.ifindex = r->ifindex; + rde_imsg_compose_dvmrpe(IMSG_FULL_ROUTE_REPORT, peerid, 0, &rr, + sizeof(rr)); + } +} + +void +rt_dump(pid_t pid) +{ + static struct ctl_rt rtctl; + struct timespec now; + struct timeval tv, now2, res; + struct rt_node *r; + + clock_gettime(CLOCK_MONOTONIC, &now); + + RB_FOREACH(r, rt_tree, &rt) { + if (r->invalid) + continue; + + rtctl.prefix.s_addr = r->prefix.s_addr; + rtctl.nexthop.s_addr = r->nexthop.s_addr; + rtctl.adv_rtr.s_addr = r->adv_rtr.s_addr; + rtctl.cost = r->cost; + rtctl.flags = r->flags; + rtctl.prefixlen = r->prefixlen; + rtctl.uptime = now.tv_sec - r->uptime; + + gettimeofday(&now2, NULL); + if (evtimer_pending(&r->expiration_timer, &tv)) { + timersub(&tv, &now2, &res); + rtctl.expire = res.tv_sec; + } else + rtctl.expire = -1; + + rde_imsg_compose_dvmrpe(IMSG_CTL_SHOW_RIB, 0, pid, &rtctl, + sizeof(rtctl)); + } +} + +void +rt_update(struct in_addr prefix, u_int8_t prefixlen, struct in_addr nexthop, + u_int32_t cost, struct in_addr adv_rtr, u_short ifindex, u_int8_t flags, + u_int8_t connected) +{ + struct timespec now; + struct rt_node *rte; + + clock_gettime(CLOCK_MONOTONIC, &now); + + if ((rte = rt_find(prefix.s_addr, prefixlen)) == NULL) { + if ((rte = calloc(1, sizeof(struct rt_node))) == NULL) + fatalx("rt_update"); + rte->prefix.s_addr = prefix.s_addr; + rte->prefixlen = prefixlen; + rte->nexthop.s_addr = nexthop.s_addr; + rte->adv_rtr.s_addr = adv_rtr.s_addr; + rte->cost = cost; + rte->ifindex = ifindex; + rte->flags = flags; + rte->invalid = 0; + rte->connected = connected; + rte->uptime = now.tv_sec; + + rt_insert(rte); + + evtimer_set(&rte->expiration_timer, rt_expire_timer, rte); + + if (!rte->connected) + rt_start_expire_timer(rte); + + } else { + if (!rte->connected) + rt_start_expire_timer(rte); + } +} diff --git a/usr.sbin/dvmrpd/report.c b/usr.sbin/dvmrpd/report.c new file mode 100644 index 00000000000..6fae0421507 --- /dev/null +++ b/usr.sbin/dvmrpd/report.c @@ -0,0 +1,304 @@ +/* $OpenBSD: report.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005, 2006 Esben Norby <norby@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 <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" + +extern struct dvmrpd_conf *deconf; + +void rr_list_remove(struct rr_head *, struct route_report *); + +/* DVMRP report packet handling */ +int +send_report(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + struct buf *buf; + struct dvmrp_hdr *dvmrp_hdr; + int ret = 0; + + log_debug("send_report: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_report"); + + /* DVMRP header */ + if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT)) + goto fail; + + buf_add(buf, data, len); + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* update chksum */ + dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); + dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + buf_free(buf); + return (ret); +fail: + log_warn("send_report"); + buf_free(buf); + return (-1); +} + +void +recv_report(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct route_report rr; + u_int32_t netid, netmask; + u_int8_t metric, netid_len, prefixlen; + + log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id)); + + if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) { + log_warnx("recv_report: neighbor %s not in state %s", + inet_ntoa(nbr->id), "2-WAY"); + return; + } + + /* parse route report */ + do { + /* + * get netmask + * + * The netmask in a DVMRP report is only represented by 3 bytes, + * to cope with that we read 4 bytes and shift 8 bits. + * The most significant part of the mask is always 255. + */ + + /* XXX handle special case 0.0.0.0/0 */ + + /* read four bytes */ + memcpy(&netmask, buf, sizeof(netmask)); + /* ditch one byte, since we only need three */ + netmask = netmask << 8; + /* set the highest byte to 255 */ + netmask = netmask | htonl(0xff000000); + buf += 3; + len -= 3; + + netid_len = 0; + prefixlen = mask2prefixlen(netmask); + if (prefixlen > 0) + netid_len = 1; + if (prefixlen > 8) + netid_len = 2; + if (prefixlen > 16) + netid_len = 3; + if (prefixlen > 24) + netid_len = 4; + + do { + /* + * get netid + * + * The length of the netid is depending on the above + * netmask. + * Read 4 bytes and use the netmask from above to + * determine the netid. + */ + memcpy(&netid, buf, sizeof(netid)); + netid = netid & netmask; + buf += netid_len; + len -= netid_len; + + /* get metric */ + memcpy(&metric, buf, sizeof(metric)); + buf += sizeof(metric); + len -= sizeof(metric); + + rr.net.s_addr = netid; + rr.mask.s_addr = netmask; + rr.nexthop = nbr->id; + /* adjusted metric */ + rr.metric = (metric & METRIC_MASK) + nbr->iface->metric; + + /* ifindex */ + rr.ifindex = nbr->iface->ifindex; + + /* send route report to RDE */ + dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid, + 0, &rr, sizeof(rr)); + + } while (!(metric & LAST_MASK) && (len > 0)); + } while (len > 0); + + return; +} + +/* timers */ +void +report_timer(int fd, short event, void *arg) +{ + struct timeval tv; + + /* request full route report */ + dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0); + + /* restart report timer */ + timerclear(&tv); + tv.tv_sec = ROUTE_REPORT_INTERVAL; + evtimer_add(&deconf->report_timer, &tv); +} + +int +start_report_timer(void) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL; /* XXX safe?? */ + return (evtimer_add(&deconf->report_timer, &tv)); +} + +int +stop_report_timer(void) +{ + return (evtimer_del(&deconf->report_timer)); +} + +/* route report list */ +void +rr_list_add(struct rr_head *rr_list, struct route_report *rr) +{ + struct rr_entry *le; + + if (rr == NULL) + fatalx("rr_list_add: no route report"); + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("rr_list_add"); + + TAILQ_INSERT_TAIL(rr_list, le, entry); + le->re = rr; +} + +void +rr_list_clr(struct rr_head *rr_list) +{ + struct rr_entry *le; + + while ((le = TAILQ_FIRST(rr_list)) != NULL) { + TAILQ_REMOVE(rr_list, le, entry); + free(le->re); + free(le); + } +} + +void +rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr) +{ + struct rr_entry *le, *le2; + struct buf *buf; + struct iface *iface; + struct in_addr addr; + u_int32_t netid, netmask; + u_int8_t metric, netid_len, prefixlen; + + /* set destination */ + if (xiface == NULL) { + /* directly to a nbr */ + iface = nbr->iface; + addr = nbr->addr; + } else { + /* multicast on interface */ + iface = xiface; + inet_aton(AllDVMRPRouters, &addr); + } + + while (!TAILQ_EMPTY(rr_list)) { + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("rr_list_send"); + + prefixlen = 0; + while (((le = TAILQ_FIRST(rr_list)) != NULL) && + (buf->wpos < 1000)) { + /* netmask */ + netmask = le->re->mask.s_addr; + if (prefixlen != mask2prefixlen(netmask)) { + prefixlen = mask2prefixlen(netmask); + netmask = netmask >> 8; + buf_add(buf, &netmask, 3); + } + + netid_len = 0; + if (prefixlen > 0) + netid_len = 1; + if (prefixlen > 8) + netid_len = 2; + if (prefixlen > 16) + netid_len = 3; + if (prefixlen > 24) + netid_len = 4; + + /* netid */ + netid = le->re->net.s_addr; + buf_add(buf, &netid, netid_len); + + /* metric */ + if (iface->ifindex == le->re->ifindex) + /* poison reverse */ + metric = le->re->metric + INFINITY_METRIC; + else + metric = le->re->metric; + + /* + * determine if we need to flag last entry with current + * netmask. + */ + le2 = TAILQ_NEXT(le, entry); + if (le2 != NULL) { + if (mask2prefixlen(le2->re->mask.s_addr) != + prefixlen) + metric = metric | LAST_MASK; + } else { + metric = metric | LAST_MASK; + } + + buf_add(buf, &metric, sizeof(metric)); + + /* XXX rr_list_remove */ + TAILQ_REMOVE(rr_list, le, entry); + /* XXX free(le->re); */ + free(le); + } + send_report(iface, addr, buf->buf, buf->wpos); + buf_free(buf); + } +} |