diff options
author | Esben Norby <norby@cvs.openbsd.org> | 2006-06-01 14:12:21 +0000 |
---|---|---|
committer | Esben Norby <norby@cvs.openbsd.org> | 2006-06-01 14:12:21 +0000 |
commit | 50b1cb4247102023b3d4274ecdef33ecc5ecf92d (patch) | |
tree | 3f03e69e83c764e467726916d53f06f34a5d7a0f /usr.sbin/dvmrpd | |
parent | 7acba1e80465cf8a211886130942b49523343441 (diff) |
Welcome dvmrpd
started by Esben Norby some time ago by using the imsg/three process framework
of ospfd.
Right now it is not particularly useful, major parts are still missing but is
imported to allow more people to work on it.
Status:
The IGMP part is mostly complete, allowing clients to join and leave groups.
Election the IGMP querier of a network is also functional, only thing missing is
some corner cases when going from non-querier to querier.
About half of the DVMRP is functional, probes and route reports are functional.
Multicast streams can be detected and the MFC can be manipulated.
The RIB is not complete but operational.
Not yet connected to the builds.
OK claudio@
Diffstat (limited to 'usr.sbin/dvmrpd')
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); + } +} |