diff options
Diffstat (limited to 'usr.sbin/ripd')
-rw-r--r-- | usr.sbin/ripd/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/ripd/auth.c | 295 | ||||
-rw-r--r-- | usr.sbin/ripd/buffer.c | 218 | ||||
-rw-r--r-- | usr.sbin/ripd/control.c | 300 | ||||
-rw-r--r-- | usr.sbin/ripd/control.h | 50 | ||||
-rw-r--r-- | usr.sbin/ripd/imsg.c | 178 | ||||
-rw-r--r-- | usr.sbin/ripd/interface.c | 482 | ||||
-rw-r--r-- | usr.sbin/ripd/kroute.c | 1126 | ||||
-rw-r--r-- | usr.sbin/ripd/log.c | 222 | ||||
-rw-r--r-- | usr.sbin/ripd/log.h | 35 | ||||
-rw-r--r-- | usr.sbin/ripd/neighbor.c | 357 | ||||
-rw-r--r-- | usr.sbin/ripd/packet.c | 251 | ||||
-rw-r--r-- | usr.sbin/ripd/parse.y | 763 | ||||
-rw-r--r-- | usr.sbin/ripd/printconf.c | 106 | ||||
-rw-r--r-- | usr.sbin/ripd/rde.c | 455 | ||||
-rw-r--r-- | usr.sbin/ripd/rde.h | 64 | ||||
-rw-r--r-- | usr.sbin/ripd/rde_rib.c | 258 | ||||
-rw-r--r-- | usr.sbin/ripd/rip.h | 82 | ||||
-rw-r--r-- | usr.sbin/ripd/ripd.8 | 81 | ||||
-rw-r--r-- | usr.sbin/ripd/ripd.conf.5 | 195 | ||||
-rw-r--r-- | usr.sbin/ripd/ripd.h | 392 | ||||
-rw-r--r-- | usr.sbin/ripd/ripe.c | 504 | ||||
-rw-r--r-- | usr.sbin/ripd/ripe.h | 146 |
23 files changed, 6578 insertions, 0 deletions
diff --git a/usr.sbin/ripd/Makefile b/usr.sbin/ripd/Makefile new file mode 100644 index 00000000000..261abf70b56 --- /dev/null +++ b/usr.sbin/ripd/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.1 2006/10/18 16:11:58 norby Exp $ + +PROG= ripd +SRCS= auth.c buffer.c control.c imsg.c interface.c kroute.c \ + log.c message.c neighbor.c packet.c parse.y printconf.c \ + rde.c rde_rib.c ripe.c ripd.c + +MAN= ripd.8 ripd.conf.5 + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +YFLAGS= +LDADD+= -levent + +.include <bsd.prog.mk> diff --git a/usr.sbin/ripd/auth.c b/usr.sbin/ripd/auth.c new file mode 100644 index 00000000000..ae3a20b5891 --- /dev/null +++ b/usr.sbin/ripd/auth.c @@ -0,0 +1,295 @@ +/* $OpenBSD: auth.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 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 <limits.h> +#include <md5.h> +#include <stdlib.h> +#include <string.h> + +#include "ripd.h" +#include "rip.h" +#include "log.h" +#include "ripe.h" + +struct auth_md *md_list_find(struct auth_md_head *, u_int8_t); +void auth_trailer_header_gen(struct buf *); +u_int32_t auth_get_seq_num(void); + +u_int32_t +auth_get_seq_num() +{ + return time(NULL); +} + +void +auth_trailer_header_gen(struct buf *buf) +{ + u_int16_t field1 = 0xFFFF; + u_int16_t field2 = htons(0x01); + + buf_add(buf, &field1, sizeof(field1)); + buf_add(buf, &field2, sizeof(field2)); +} + +/* XXX add the support for key lifetime and rollover */ +int +auth_validate(char **buf, u_int16_t *len, struct iface *iface, struct nbr *nbr, + struct nbr_failed *nbr_failed, u_int32_t *crypt_seq_num) +{ + MD5_CTX hash; + u_int8_t digest[MD5_DIGEST_LENGTH]; + u_int8_t recv_digest[MD5_DIGEST_LENGTH]; + char pwd[MAX_SIMPLE_AUTH_LEN]; + struct rip_auth *auth_head; + struct md5_auth *a; + struct auth_md *md; + char *auth_data; + char *b = *buf; + + *buf += RIP_HDR_LEN; + *len -= RIP_HDR_LEN; + + auth_head = (struct rip_auth *)(*buf); + + if (auth_head->auth_fixed != AUTH) { + if (iface->auth_type != AUTH_NONE) { + log_debug("auth_validate: packet carrying no" + " authentication"); + return (-1); + } + return (0); + } else { + if (ntohs(auth_head->auth_type) != + (u_int16_t)iface->auth_type) { + log_debug("auth_validate: wrong auth type"); + return (-1); + } + } + + switch (iface->auth_type) { + case AUTH_SIMPLE: + bcopy(*buf+sizeof(*auth_head), pwd, MAX_SIMPLE_AUTH_LEN); + if (bcmp(pwd, iface->auth_key, MAX_SIMPLE_AUTH_LEN)) { + log_debug("auth_validate: wrong password, " + "interface: %s", iface->name); + return (-1); + } + break; + case AUTH_CRYPT: + a = (struct md5_auth *)(*buf + sizeof(*auth_head)); + + if ((md = md_list_find(&iface->auth_md_list, + a->auth_keyid)) == NULL) { + log_debug("auth_validate: keyid %d not configured, " + "interface %s", a->auth_keyid, + iface->name); + return (-1); + } + + if (nbr != NULL) { + if (ntohl(a->auth_seq) < nbr->auth_seq_num) { + log_debug("auth_validate: decreasing seq num, " + "interface %s", iface->name); + return (-1); + } + } else if (nbr_failed != NULL) { + if (ntohl(a->auth_seq) < nbr_failed->auth_seq_num && + ntohl(a->auth_seq)) { + log_debug("auth_validate: decreasing seq num, " + "interface %s", iface->name); + return (-1); + } + } + + /* XXX: maybe validate also the trailer header */ + if (a->auth_length != MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN) { + log_debug("auth_validate: invalid key length, " + "interface %s", iface->name); + return (-1); + } + + auth_data = *buf; + auth_data += ntohs(a->auth_offset); + + /* save the received digest and clear it in the packet */ + bcopy(auth_data, recv_digest, sizeof(recv_digest)); + bzero(auth_data, MD5_DIGEST_LENGTH); + + /* insert plaintext key */ + bzero(digest, MD5_DIGEST_LENGTH); + strncpy(digest, md->key, MD5_DIGEST_LENGTH); + + /* calculate MD5 digest */ + MD5Init(&hash); + MD5Update(&hash, b, ntohs(a->auth_offset) + RIP_HDR_LEN); + MD5Update(&hash, digest, MD5_DIGEST_LENGTH); + MD5Final(digest, &hash); + + if (bcmp(recv_digest, digest, sizeof(digest))) { + log_debug("auth_validate: invalid MD5 digest, " + "interface %s", iface->name); + return (-1); + } + + *crypt_seq_num = ntohl(a->auth_seq); + + *len -= AUTH_TRLR_HDR_LEN + MD5_DIGEST_LENGTH; + + break; + default: + log_debug("auth_validate: unknown auth type, interface %s", + iface->name); + return (-1); + } + + *buf += RIP_ENTRY_LEN; + *len -= RIP_ENTRY_LEN; + + return (0); +} + +int +auth_gen(struct buf *buf, struct iface *iface) +{ + struct rip_auth auth_head; + struct md5_auth a; + + auth_head.auth_fixed = AUTH; + auth_head.auth_type = htons(iface->auth_type); + + buf_add(buf, &auth_head, sizeof(auth_head)); + + switch (iface->auth_type) { + case AUTH_SIMPLE: + return (buf_add(buf, &iface->auth_key, MAX_SIMPLE_AUTH_LEN)); + break; + case AUTH_CRYPT: + bzero(&a, sizeof(a)); + a.auth_keyid = iface->auth_keyid; + a.auth_seq = htonl(auth_get_seq_num()); + a.auth_length = MD5_DIGEST_LENGTH + AUTH_TRLR_HDR_LEN; + + return (buf_add(buf, &a, sizeof(a))); + break; + default: + /* NOTREACHED */ + break; + } + + return (0); +} + +int +auth_add_trailer(struct buf *buf, struct iface *iface) +{ + MD5_CTX hash; + u_int8_t digest[MD5_DIGEST_LENGTH]; + struct auth_md *md; + struct md5_auth *a; + int pos; + + pos = sizeof(struct rip_hdr) + sizeof(struct rip_auth); + + /* add offset to header */ + a = buf_seek(buf, pos, sizeof(*a)); + a->auth_offset = htons(buf->wpos); + + /* insert plaintext key */ + if ((md = md_list_find(&iface->auth_md_list, + iface->auth_keyid)) == NULL) { + log_debug("auth_validate: keyid %d not configured, " + "interface %s", iface->auth_keyid, iface->name); + return (-1); + } + + bzero(digest, MD5_DIGEST_LENGTH); + strncpy(digest, md->key, MD5_DIGEST_LENGTH); + + auth_trailer_header_gen(buf); + + /* calculate MD5 digest */ + MD5Init(&hash); + MD5Update(&hash, buf->buf, buf->wpos); + MD5Update(&hash, digest, MD5_DIGEST_LENGTH); + MD5Final(digest, &hash); + + return (buf_add(buf, digest, MD5_DIGEST_LENGTH)); +} + +/* md list */ +void +md_list_add(struct auth_md_head *head, u_int8_t keyid, char *key) +{ + struct auth_md *md; + + if ((md = md_list_find(head, keyid)) != NULL) { + /* update key */ + strncpy(md->key, key, sizeof(md->key)); + return; + } + + if ((md = calloc(1, sizeof(struct auth_md))) == NULL) + fatalx("md_list_add"); + + md->keyid = keyid; + strncpy(md->key, key, sizeof(md->key)); + TAILQ_INSERT_TAIL(head, md, entry); +} + +void +md_list_copy(struct auth_md_head *to, struct auth_md_head *from) +{ + struct auth_md *m, *md; + + TAILQ_INIT(to); + + TAILQ_FOREACH(m, from, entry) { + if ((md = calloc(1, sizeof(struct auth_md))) == NULL) + fatalx("md_list_add"); + + md->keyid = m->keyid; + strncpy(md->key, m->key, sizeof(md->key)); + TAILQ_INSERT_TAIL(to, md, entry); + } +} + +void +md_list_clr(struct auth_md_head *head) +{ + struct auth_md *m; + + while ((m = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, m, entry); + free(m); + } +} + +struct auth_md * +md_list_find(struct auth_md_head *head, u_int8_t keyid) +{ + struct auth_md *m; + + TAILQ_FOREACH(m, head, entry) + if (m->keyid == keyid) + return (m); + + return (NULL); +} diff --git a/usr.sbin/ripd/buffer.c b/usr.sbin/ripd/buffer.c new file mode 100644 index 00000000000..645f8588ff3 --- /dev/null +++ b/usr.sbin/ripd/buffer.c @@ -0,0 +1,218 @@ +/* $OpenBSD: buffer.c,v 1.1 2006/10/18 16:11:58 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 "ripd.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/ripd/control.c b/usr.sbin/ripd/control.c new file mode 100644 index 00000000000..77b3101cf41 --- /dev/null +++ b/usr.sbin/ripd/control.c @@ -0,0 +1,300 @@ +/* $OpenBSD: control.c,v 1.1 2006/10/18 16:11:58 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 "ripd.h" +#include "rip.h" +#include "ripe.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, RIPD_SOCKET, sizeof(sun.sun_path)); + + if (unlink(RIPD_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("control_init: unlink %s", RIPD_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", RIPD_SOCKET); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(RIPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("control_init: chmod"); + close(fd); + (void)unlink(RIPD_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(RIPD_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_SHOW_INTERFACE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(ifidx)) + return; + + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + ripe_iface_ctl(c, ifidx); + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, NULL, 0); + + break; + case IMSG_CTL_SHOW_RIB: + c->ibuf.pid = imsg.hdr.pid; + ripe_imsg_compose_rde(imsg.hdr.type, 0, + imsg.hdr.pid, imsg.data, imsg.hdr.len - + IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_NBR: + ripe_nbr_ctl(c); + break; + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_KROUTE: + c->ibuf.pid = imsg.hdr.pid; + ripe_imsg_compose_parent(imsg.hdr.type, + imsg.hdr.pid, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_IFINFO: + c->ibuf.pid = imsg.hdr.pid; + ripe_imsg_compose_parent(imsg.hdr.type, + imsg.hdr.pid, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_FIB_COUPLE: + case IMSG_CTL_FIB_DECOUPLE: + case IMSG_CTL_RELOAD: + c->ibuf.pid = imsg.hdr.pid; + ripe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + 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/ripd/control.h b/usr.sbin/ripd/control.h new file mode 100644 index 00000000000..6d372673006 --- /dev/null +++ b/usr.sbin/ripd/control.h @@ -0,0 +1,50 @@ +/* $OpenBSD: control.h,v 1.1 2006/10/18 16:11:58 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/ripd/imsg.c b/usr.sbin/ripd/imsg.c new file mode 100644 index 00000000000..51157ee5ad4 --- /dev/null +++ b/usr.sbin/ripd/imsg.c @@ -0,0 +1,178 @@ +/* $OpenBSD: imsg.c,v 1.1 2006/10/18 16:11:58 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 "ripd.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/ripd/interface.c b/usr.sbin/ripd/interface.c new file mode 100644 index 00000000000..be9917361ad --- /dev/null +++ b/usr.sbin/ripd/interface.c @@ -0,0 +1,482 @@ +/* $OpenBSD: interface.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 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 <net/if_types.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <event.h> + +#include "ripd.h" +#include "log.h" +#include "rip.h" +#include "rde.h" +#include "ripe.h" + +extern struct ripd_conf *conf; + +int if_act_start(struct iface *); +int if_act_reset(struct iface *); +void if_keepalive_timer(int, short, void *); + +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_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", + "RESET" +}; + +static const char * const if_event_names[] = { + "NOTHING", + "UP", + "DOWN", +}; + +void +if_init(struct ripd_conf *xconf, struct iface *iface) +{ + /* XXX as in ospfd I would like to kill that. This is a design error */ + iface->fd = xconf->rip_socket; +} + +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) { + /* event outside of the defined fsm, ignore it. */ + log_debug("if_fsm: 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_RST: + ret = if_act_reset(iface); + break; + case IF_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_debug("if_fsm: error changing state for interface %s, " + "event '%s', state '%s'", iface->name, if_event_name(event), + if_state_name(old_state)); + return (0); + } + + if (new_state != 0) + iface->state = new_state; + + log_debug("if_fsm: 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_find_index(u_short ifindex) +{ + struct iface *iface; + + LIST_FOREACH(iface, &conf->iface_list, entry) { + if (iface->ifindex == ifindex) + return (iface); + } + + return (NULL); +} + + +/* 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 (0); + } + + if (!((iface->flags & IFF_UP) && + (iface->linkstate == LINK_STATE_UP || + (iface->linkstate == LINK_STATE_UNKNOWN && + iface->media_type != IFT_CARP)))) { + 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(ALL_RIP_ROUTERS, &addr); + if (if_join_group(iface, &addr)) { + log_warn("if_act_start: error joining group %s, " + "interface %s", inet_ntoa(addr), iface->name); + return (-1); + } + + iface->state = IF_STA_ACTIVE; + break; + default: + fatalx("if_act_start: unknown interface type"); + } + + return (0); +} + +int +if_act_reset(struct iface *iface) +{ + struct nbr *nbr = NULL; + struct in_addr addr; + + if (iface->passive) + return (0); + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_aton(ALL_RIP_ROUTERS, &addr); + if (if_leave_group(iface, &addr)) { + log_warn("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + } + break; + default: + fatalx("if_act_reset: unknown interface 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)); + } + } + + 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_opt(int fd) +{ + int yes = 1; + + if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes, + sizeof(int)) < 0) { + log_warn("if_set_opt: error setting IP_RECVIF"); + 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); +} + +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, + &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); +} + +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) + 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) + return (-1); + break; + default: + fatalx("if_leave_group: unknown interface type"); + } + + return (0); +} + +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; + + LIST_INIT(&iface->nbr_list); + TAILQ_INIT(&iface->rp_list); + TAILQ_INIT(&iface->rq_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; + if (kif->flags & IFF_LOOPBACK) { + iface->type = IF_TYPE_POINTOPOINT; + /* XXX protect loopback from sending packets over lo? */ + } + + /* 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, 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, 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 (kif->flags & IFF_POINTOPOINT) { + if (ioctl(s, SIOCGIFDSTADDR, 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_del(struct iface *iface) +{ + struct nbr *nbr; + + log_debug("if_del: interface %s", iface->name); + + /* clear lists etc */ + while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) + nbr_act_del(nbr); + + /* XXX rq_list, rp_list */ + + free(iface); +} + +struct ctl_iface * +if_to_ctl(struct iface *iface) +{ + static struct ctl_iface ictl; + struct timeval now; + + memcpy(ictl.name, iface->name, sizeof(ictl.name)); + memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr)); + memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask)); + + ictl.ifindex = iface->ifindex; + ictl.state = iface->state; + ictl.mtu = iface->mtu; + + ictl.baudrate = iface->baudrate; + ictl.flags = iface->flags; + ictl.metric = iface->cost; + ictl.type = iface->type; + ictl.linkstate = iface->linkstate; + ictl.passive = iface->passive; + ictl.mediatype = iface->media_type; + + gettimeofday(&now, NULL); + + if (iface->state != IF_STA_DOWN) { + ictl.uptime = now.tv_sec - iface->uptime; + } else + ictl.uptime = 0; + + return (&ictl); +} diff --git a/usr.sbin/ripd/kroute.c b/usr.sbin/ripd/kroute.c new file mode 100644 index 00000000000..39a18ab353b --- /dev/null +++ b/usr.sbin/ripd/kroute.c @@ -0,0 +1,1126 @@ +/* $OpenBSD: kroute.c,v 1.1 2006/10/18 16:11:58 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/if_types.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 "ripd.h" +#include "log.h" + +struct { + u_int32_t rtseq; + pid_t pid; + int fib_sync; + int fd; + struct event ev; +} kr_state; + +struct kroute_node { + RB_ENTRY(kroute_node) entry; + struct kroute r; +}; + +struct kif_node { + RB_ENTRY(kif_node) entry; + struct kif k; +}; + +void kr_redistribute(int, struct kroute *); +int kroute_compare(struct kroute_node *, struct kroute_node *); +int kif_compare(struct kif_node *, struct kif_node *); + +struct kroute_node *kroute_find(in_addr_t, in_addr_t); +int kroute_insert(struct kroute_node *); +int kroute_remove(struct kroute_node *); +void kroute_clear(void); + +struct kif_node *kif_find(int); +int kif_insert(struct kif_node *); +int kif_remove(struct kif_node *); +void kif_clear(void); +int kif_validate(int); + +struct kroute_node *kroute_match(in_addr_t); + +int protect_lo(void); +u_int8_t prefixlen_classful(in_addr_t); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +void if_change(u_short, int, struct if_data *); +void if_announce(void *); + +int send_rtmsg(int, int, struct kroute *); +int dispatch_rtmsg(void); +int fetchtable(void); +int fetchifs(int); + +RB_HEAD(kroute_tree, kroute_node) krt; +RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) +RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) + +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 = 0, rcvbuf, default_rcvbuf; + socklen_t optlen; + + kr_state.fib_sync = fs; + + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + log_warn("kr_init: socket"); + return (-1); + } + + /* not interested in my own messages */ + if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK, + &opt, sizeof(opt)) == -1) + log_warn("kr_init: setsockopt"); /* not fatal */ + + /* grow receive buffer, don't wanna miss messages */ + optlen = sizeof(default_rcvbuf); + if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &default_rcvbuf, &optlen) == -1) + log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); + else + for (rcvbuf = MAX_RTSOCK_BUF; + rcvbuf > default_rcvbuf && + setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; + rcvbuf /= 2) + ; /* nothing */ + + kr_state.pid = getpid(); + kr_state.rtseq = 1; + + RB_INIT(&krt); + + if (fetchtable() == -1) + return (-1); + + if (protect_lo() == -1) + return (-1); + + event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST, + kr_dispatch_msg, NULL); + event_add(&kr_state.ev, NULL); + + return (0); +} + +int +kr_change(struct kroute *kroute) +{ + struct kroute_node *kr; + int action = RTM_ADD; + + if ((kr = kroute_find(kroute->prefix.s_addr, kroute->netmask.s_addr)) != + NULL) { + if (!(kr->r.flags & F_KERNEL)) + action = RTM_CHANGE; + else { /* a non-rip route already exists. not a problem */ + if (!(kr->r.flags & (F_BGPD_INSERTED| + F_OSPFD_INSERTED))) { + kr->r.flags |= F_RIPD_INSERTED; + return (0); + } + /* + * rip route has higher pref + * - reset flags to the rip ones + * - use RTM_CHANGE + * - zero out ifindex (this is no longer relevant) + */ + action = RTM_CHANGE; + kr->r.flags = kroute->flags | F_RIPD_INSERTED; + kr->r.ifindex = 0; + } + } + + /* nexthop within 127/8 -> ignore silently */ + if ((kroute->nexthop.s_addr & htonl(IN_CLASSA_NET)) == + htonl(INADDR_LOOPBACK & IN_CLASSA_NET)) + return (0); + + if (send_rtmsg(kr_state.fd, action, kroute) == -1) + return (-1); + + if (action == RTM_ADD) { + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("kr_change"); + return (-1); + } + kr->r.prefix.s_addr = kroute->prefix.s_addr; + kr->r.netmask.s_addr = kroute->netmask.s_addr; + kr->r.nexthop.s_addr = kroute->nexthop.s_addr; + kr->r.flags = kroute->flags |= F_RIPD_INSERTED; + + if (kroute_insert(kr) == -1) + free(kr); + } else + kr->r.nexthop.s_addr = kroute->nexthop.s_addr; + + return (0); +} + +int +kr_delete(struct kroute *kroute) +{ + struct kroute_node *kr; + + if ((kr = kroute_find(kroute->prefix.s_addr, kroute->netmask.s_addr)) == + NULL) + return (0); + + if (!(kr->r.flags & F_RIPD_INSERTED)) + return (0); + + if (kr->r.flags & F_KERNEL) { + /* remove F_RIPD_INSERTED flag, route still exists in kernel */ + kr->r.flags &= ~F_RIPD_INSERTED; + return (0); + } + + if (send_rtmsg(kr_state.fd, RTM_DELETE, kroute) == -1) + return (-1); + + if (kroute_remove(kr) == -1) + return (-1); + + return (0); +} + +void +kr_shutdown(void) +{ + kr_fib_decouple(); + kroute_clear(); + kif_clear(); +} + +void +kr_fib_couple(void) +{ + struct kroute_node *kr; + + if (kr_state.fib_sync == 1) /* already coupled */ + return; + + kr_state.fib_sync = 1; + + RB_FOREACH(kr, kroute_tree, &krt) + if (!(kr->r.flags & F_KERNEL)) + send_rtmsg(kr_state.fd, RTM_ADD, &kr->r); + + log_info("kernel routing table coupled"); +} + +void +kr_fib_decouple(void) +{ + struct kroute_node *kr; + + if (kr_state.fib_sync == 0) /* already decoupled */ + return; + + RB_FOREACH(kr, kroute_tree, &krt) + if (!(kr->r.flags & F_KERNEL)) + send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + + kr_state.fib_sync = 0; + + log_info("kernel routing table decoupled"); +} + +/* ARGSUSED */ +void +kr_dispatch_msg(int fd, short event, void *bula) +{ + dispatch_rtmsg(); +} + +void +kr_show_route(struct imsg *imsg) +{ + struct kroute_node *kr; + int flags; + struct in_addr addr; + + switch (imsg->hdr.type) { + case IMSG_CTL_KROUTE: + if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { + log_warnx("kr_show_route: wrong imsg len"); + return; + } + memcpy(&flags, imsg->data, sizeof(flags)); + RB_FOREACH(kr, kroute_tree, &krt) + if (!flags || kr->r.flags & flags) { + main_imsg_compose_ripe(IMSG_CTL_KROUTE, + imsg->hdr.pid, &kr->r, sizeof(kr->r)); + } + break; + case IMSG_CTL_KROUTE_ADDR: + if (imsg->hdr.len != IMSG_HEADER_SIZE + + sizeof(struct in_addr)) { + log_warnx("kr_show_route: wrong imsg len"); + return; + } + memcpy(&addr, imsg->data, sizeof(addr)); + kr = NULL; + kr = kroute_match(addr.s_addr); + if (kr != NULL) + main_imsg_compose_ripe(IMSG_CTL_KROUTE, imsg->hdr.pid, + &kr->r, sizeof(kr->r)); + break; + default: + log_debug("kr_show_route: error handling imsg"); + break; + } + + main_imsg_compose_ripe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); +} + +void +kr_ifinfo(char *ifname, pid_t pid) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) { + main_imsg_compose_ripe(IMSG_CTL_IFINFO, + pid, &kif->k, sizeof(kif->k)); + } + + main_imsg_compose_ripe(IMSG_CTL_END, pid, NULL, 0); +} + +void +kr_redistribute(int type, struct kroute *kr) +{ + u_int32_t a; + + + if (type == IMSG_NETWORK_DEL) { +dont_redistribute: + /* was the route redistributed? */ + if (kr->flags & F_REDISTRIBUTED) { + /* remove redistributed flag */ + kr->flags &= ~F_REDISTRIBUTED; + main_imsg_compose_rde(type, 0, kr, + sizeof(struct kroute)); + } + return; + } + + /* interface is not up and running so don't announce */ + if (kr->flags & F_DOWN) + return; + + /* + * We consider the loopback net, multicast and experimental addresses + * as not redistributable. + */ + a = ntohl(kr->prefix.s_addr); + if (IN_MULTICAST(a) || IN_BADCLASS(a) || + (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) + return; + /* + * Consider networks with nexthop loopback as not redistributable. + */ + if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK)) + return; + + /* Should we redistrubute this route? */ + if (!rip_redistribute(kr)) + goto dont_redistribute; + + /* Does not matter if we resend the kr, the RDE will cope. */ + kr->flags |= F_REDISTRIBUTED; + main_imsg_compose_rde(type, 0, kr, sizeof(struct kroute)); +} + +/* rb-tree compare */ +int +kroute_compare(struct kroute_node *a, struct kroute_node *b) +{ + if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr)) + return (-1); + if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr)) + return (1); + if (ntohl(a->r.netmask.s_addr) < ntohl(b->r.netmask.s_addr)) + return (-1); + if (ntohl(a->r.netmask.s_addr) > ntohl(b->r.netmask.s_addr)) + return (1); + return (0); +} + +int +kif_compare(struct kif_node *a, struct kif_node *b) +{ + return (b->k.ifindex - a->k.ifindex); +} + +/* tree management */ +struct kroute_node * +kroute_find(in_addr_t prefix, in_addr_t netmask) +{ + struct kroute_node s; + + s.r.prefix.s_addr = prefix; + s.r.netmask.s_addr = netmask; + + return (RB_FIND(kroute_tree, &krt, &s)); +} + +int +kroute_insert(struct kroute_node *kr) +{ + if (RB_INSERT(kroute_tree, &krt, kr) != NULL) { + log_warnx("kroute_insert failed for %s/%u", + inet_ntoa(kr->r.prefix), + mask2prefixlen(kr->r.netmask.s_addr)); + free(kr); + return (-1); + } + + if (!(kr->r.flags & F_KERNEL)) { + /* don't validate or redistribute rip route */ + kr->r.flags &= ~F_DOWN; + return (0); + } + + if (kif_validate(kr->r.ifindex)) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + kr_redistribute(IMSG_NETWORK_ADD, &kr->r); + + return (0); +} + +int +kroute_remove(struct kroute_node *kr) +{ + if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + log_warnx("kroute_remove failed for %s/%u", + inet_ntoa(kr->r.prefix), + mask2prefixlen(kr->r.netmask.s_addr)); + return (-1); + } + + kr_redistribute(IMSG_NETWORK_DEL, &kr->r); + + free(kr); + return (0); +} + +void +kroute_clear(void) +{ + struct kroute_node *kr; + + while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) + kroute_remove(kr); +} + +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); +} + +int +kif_validate(int ifindex) +{ + struct kif_node *kif; + + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("interface with index %u not found", ifindex); + return (1); + } + + return (kif->k.nh_reachable); +} + +struct kroute_node * +kroute_match(in_addr_t key) +{ + int i; + struct kroute_node *kr; + + /* we will never match the default route */ + for (i = 32; i > 0; i--) + if ((kr = kroute_find(key & prefixlen2mask(i), + prefixlen2mask(i))) != NULL) + return (kr); + + /* if we don't have a match yet, try to find a default route */ + if ((kr = kroute_find(0, 0)) != NULL) + return (kr); + + return (NULL); +} + +/* misc */ +int +protect_lo(void) +{ + struct kroute_node *kr; + + /* special protection for 127/8 */ + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("protect_lo"); + return (-1); + } + kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK); + kr->r.netmask.s_addr = htonl(IN_CLASSA_NET); + kr->r.flags = F_KERNEL|F_CONNECTED; + + if (RB_INSERT(kroute_tree, &krt, kr) != NULL) + free(kr); /* kernel route already there, no problem */ + + return (0); +} + +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 (htonl(0xffffffff << (32 - prefixlen))); +} + +#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; + } +} + +void +if_change(u_short ifindex, int flags, struct if_data *ifd) +{ + struct kif_node *kif; + struct kroute_node *kr; + int type; + 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_UP || + (ifd->ifi_link_state == LINK_STATE_UNKNOWN && + ifd->ifi_type != IFT_CARP))) == kif->k.nh_reachable) + return; /* nothing changed wrt nexthop validity */ + + kif->k.nh_reachable = reachable; + type = reachable ? IMSG_NETWORK_ADD : IMSG_NETWORK_DEL; + + /* notify ripe about interface link state */ + main_imsg_compose_ripe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); + + /* update redistribute list */ + RB_FOREACH(kr, kroute_tree, &krt) + if (kr->r.ifindex == ifindex) { + if (reachable) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + kr_redistribute(type, &kr->r); + } +} + +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 */ +int +send_rtmsg(int fd, int action, struct kroute *kroute) +{ + struct { + struct rt_msghdr hdr; + struct sockaddr_in prefix; + struct sockaddr_in nexthop; + struct sockaddr_in mask; + } r; + + if (kr_state.fib_sync == 0) + return (0); + + bzero(&r, sizeof(r)); + r.hdr.rtm_msglen = sizeof(r); + r.hdr.rtm_version = RTM_VERSION; + r.hdr.rtm_type = action; + r.hdr.rtm_flags = RTF_PROTO3; + if (action == RTM_CHANGE) /* force PROTO3 reset the other flags */ + r.hdr.rtm_fmask = + RTF_PROTO3|RTF_PROTO2|RTF_PROTO1|RTF_REJECT|RTF_BLACKHOLE; + r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK; + r.prefix.sin_len = sizeof(r.prefix); + r.prefix.sin_family = AF_INET; + r.prefix.sin_addr.s_addr = kroute->prefix.s_addr; + + r.nexthop.sin_len = sizeof(r.nexthop); + r.nexthop.sin_family = AF_INET; + r.nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; + if (kroute->nexthop.s_addr != 0) + r.hdr.rtm_flags |= RTF_GATEWAY; + + r.mask.sin_len = sizeof(r.mask); + r.mask.sin_family = AF_INET; + r.mask.sin_addr.s_addr = kroute->netmask.s_addr; + +retry: + if (write(fd, &r, sizeof(r)) == -1) { + switch (errno) { + case ESRCH: + if (r.hdr.rtm_type == RTM_CHANGE) { + r.hdr.rtm_type = RTM_ADD; + goto retry; + } else if (r.hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + inet_ntoa(kroute->prefix), + mask2prefixlen(kroute->netmask.s_addr)); + return (0); + } else { + log_warnx("send_rtmsg: action %u, " + "prefix %s/%u: %s", r.hdr.rtm_type, + inet_ntoa(kroute->prefix), + mask2prefixlen(kroute->netmask.s_addr), + strerror(errno)); + return (0); + } + break; + default: + log_warnx("send_rtmsg: action %u, prefix %s/%u: %s", + r.hdr.rtm_type, inet_ntoa(kroute->prefix), + mask2prefixlen(kroute->netmask.s_addr), + strerror(errno)); + return (0); + } + } + + return (0); +} + +int +fetchtable(void) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct kroute_node *kr; + struct iface *iface = NULL; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchtable"); + 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 += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if ((sa = rti_info[RTAX_DST]) == NULL) + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) /* multipath */ + continue; +#endif + + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("fetchtable"); + free(buf); + return (-1); + } + + kr->r.flags = F_KERNEL; + + switch (sa->sa_family) { + case AF_INET: + kr->r.prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; + if (rtm->rtm_flags & RTF_STATIC) + kr->r.flags |= F_STATIC; + if (rtm->rtm_flags & RTF_DYNAMIC) + kr->r.flags |= F_DYNAMIC; + if (rtm->rtm_flags & RTF_PROTO1) + kr->r.flags |= F_BGPD_INSERTED; + if (rtm->rtm_flags & RTF_PROTO2) + kr->r.flags |= F_OSPFD_INSERTED; + if (sa_in != NULL) { + if (sa_in->sin_len == 0) + break; + kr->r.netmask.s_addr = + sa_in->sin_addr.s_addr; + } else if (rtm->rtm_flags & RTF_HOST) + kr->r.netmask.s_addr = prefixlen2mask(32); + else + kr->r.netmask.s_addr = + prefixlen2mask(prefixlen_classful + (kr->r.prefix.s_addr)); + break; + default: + free(kr); + continue; + } + + kr->r.ifindex = rtm->rtm_index; + + iface = if_find_index(rtm->rtm_index); + if (iface != NULL) + kr->r.metric = iface->cost; + else + kr->r.metric = 1; + + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) + switch (sa->sa_family) { + case AF_INET: + kr->r.nexthop.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + break; + case AF_LINK: + kr->r.flags |= F_CONNECTED; + break; + } + + if (rtm->rtm_flags & RTF_PROTO3) { + send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + free(kr); + } else + kroute_insert(kr); + + } + free(buf); + return (0); +} + +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)); + if (ifm.ifm_type != RTM_IFINFO) + continue; + + sa = (struct sockaddr *)(next + sizeof(ifm)); + get_rtaddrs(ifm.ifm_addrs, sa, rti_info); + + 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_UP || + (ifm.ifm_data.ifi_link_state == LINK_STATE_UNKNOWN && + ifm.ifm_data.ifi_type != IFT_CARP)); + if ((sa = rti_info[RTAX_IFP]) != NULL) + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (sdl->sdl_nlen >= sizeof(kif->k.ifname)) + memcpy(kif->k.ifname, sdl->sdl_data, + sizeof(kif->k.ifname) - 1); + else if (sdl->sdl_nlen > 0) + memcpy(kif->k.ifname, sdl->sdl_data, + sdl->sdl_nlen); + /* string already terminated via calloc() */ + } + + kif_insert(kif); + } + free(buf); + return (0); +} + +int +dispatch_rtmsg(void) +{ + char buf[RT_BUF_SIZE]; + ssize_t n; + char *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct kroute_node *kr; + struct in_addr prefix, nexthop, netmask; + int flags; + u_short ifindex = 0; + + if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { + log_warn("dispatch_rtmsg: read error"); + return (-1); + } + + if (n == 0) { + log_warnx("routing socket closed"); + return (-1); + } + + lim = buf + n; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + + prefix.s_addr = 0; + netmask.s_addr = 0; + flags = F_KERNEL; + nexthop.s_addr = 0; + + if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE || + rtm->rtm_type == RTM_DELETE) { + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if (rtm->rtm_pid == kr_state.pid) /* cause by us */ + continue; + + if (rtm->rtm_errno) /* failed attempts... */ + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + + switch (sa->sa_family) { + case AF_INET: + prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *) + rti_info[RTAX_NETMASK]; + if (sa_in != NULL) { + if (sa_in->sin_len != 0) + netmask.s_addr = + sa_in->sin_addr.s_addr; + } else if (rtm->rtm_flags & RTF_HOST) + netmask.s_addr = prefixlen2mask(32); + else + netmask.s_addr = + prefixlen2mask(prefixlen_classful( + prefix.s_addr)); + if (rtm->rtm_flags & RTF_STATIC) + flags |= F_STATIC; + if (rtm->rtm_flags & RTF_DYNAMIC) + flags |= F_DYNAMIC; + if (rtm->rtm_flags & RTF_PROTO1) + flags |= F_BGPD_INSERTED; + if (rtm->rtm_flags & RTF_PROTO2) + flags |= F_OSPFD_INSERTED; + break; + default: + continue; + } + + ifindex = rtm->rtm_index; + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) { + switch (sa->sa_family) { + case AF_INET: + nexthop.s_addr = ((struct + sockaddr_in *)sa)->sin_addr.s_addr; + break; + case AF_LINK: + flags |= F_CONNECTED; + break; + } + } + } + + switch (rtm->rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) { + log_warnx("dispatch_rtmsg no nexthop for %s/%u", + inet_ntoa(prefix), + mask2prefixlen(netmask.s_addr)); + continue; + } + + if ((kr = kroute_find(prefix.s_addr, netmask.s_addr)) != + NULL) { + /* rip route overridden by kernel */ + /* pref is not checked because this is forced */ + if (kr->r.flags & F_RIPD_INSERTED) + flags |= F_RIPD_INSERTED; + if (kr->r.flags & F_REDISTRIBUTED) + flags |= F_REDISTRIBUTED; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + if (kif_validate(kr->r.ifindex)) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + /* just readd, the RDE will care */ + kr_redistribute(IMSG_NETWORK_ADD, &kr->r); + } else { + if ((kr = calloc(1, + sizeof(struct kroute_node))) == NULL) { + log_warn("dispatch_rtmsg"); + return (-1); + } + kr->r.prefix.s_addr = prefix.s_addr; + kr->r.netmask.s_addr = netmask.s_addr; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + kroute_insert(kr); + } + break; + case RTM_DELETE: + if ((kr = kroute_find(prefix.s_addr, netmask.s_addr)) == + NULL) + continue; + if (!(kr->r.flags & F_KERNEL)) + continue; + if (kr->r.flags & F_RIPD_INSERTED) + main_imsg_compose_rde(IMSG_KROUTE_GET, 0, + &kr->r, sizeof(struct kroute)); + if (kroute_remove(kr) == -1) + return (-1); + break; + 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; + } + } + return (0); +} diff --git a/usr.sbin/ripd/log.c b/usr.sbin/ripd/log.c new file mode 100644 index 00000000000..e581250f667 --- /dev/null +++ b/usr.sbin/ripd/log.c @@ -0,0 +1,222 @@ +/* $OpenBSD: log.c,v 1.1 2006/10/18 16:11:58 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 "ripd.h" +#include "log.h" + +static const char * const procnames[] = { + "parent", + "ripe", + "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[ripd_process], + strerror(errno)); + else + if (errno) + logit(LOG_CRIT, "fatal in %s: %s: %s", + procnames[ripd_process], emsg, strerror(errno)); + else + logit(LOG_CRIT, "fatal in %s: %s", + procnames[ripd_process], emsg); + + if (ripd_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_ACTIVE: + return ("ACTIVE"); + 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"); + case IF_TYPE_NBMA: + return ("NBMA"); + case IF_TYPE_POINTOMULTIPOINT: + return ("POINTOMULTIPOINT"); + } + /* NOTREACHED */ + return ("UNKNOWN"); +} + +const char * +if_auth_name(enum auth_type type) +{ + switch (type) { + case AUTH_NONE: + return ("none"); + case AUTH_SIMPLE: + return ("simple"); + case AUTH_CRYPT: + return ("crypt"); + } + /* NOTREACHED */ + return ("unknown"); +} + +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_ACTIVE: + return ("ACTIVE"); + default: + return ("UNKNOWN"); + } +} diff --git a/usr.sbin/ripd/log.h b/usr.sbin/ripd/log.h new file mode 100644 index 00000000000..f1be0c23b55 --- /dev/null +++ b/usr.sbin/ripd/log.h @@ -0,0 +1,35 @@ +/* $OpenBSD: log.h,v 1.1 2006/10/18 16:11:58 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; +const char *if_type_name(enum iface_type); +const char *nbr_state_name(int); + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/ripd/neighbor.c b/usr.sbin/ripd/neighbor.c new file mode 100644 index 00000000000..edd33019bc2 --- /dev/null +++ b/usr.sbin/ripd/neighbor.c @@ -0,0 +1,357 @@ +/* $OpenBSD: neighbor.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 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 "ripd.h" +#include "rip.h" +#include "ripe.h" +#include "log.h" +#include "rde.h" + +void nbr_start_timer(struct nbr *); +void nbr_reset_timer(struct nbr *); + +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_REQUEST_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE}, + {NBR_STA_DOWN, NBR_EVT_RESPONSE_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE}, + {NBR_STA_ACTIVE, NBR_EVT_RESPONSE_RCVD, NBR_ACT_RST_TIMER, NBR_STA_ACTIVE}, + {NBR_STA_ACTIVE, NBR_EVT_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_ACTIVE}, + {NBR_STA_ACTIVE, NBR_EVT_TIMEOUT, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "RESPONSE RCVD", + "REQUEST RCVD", + "NBR TIMEOUT", + "NBR KILL", + "NOTHING" +}; + +const char * const nbr_action_names[] = { + "START TIMER", + "RESET TIMER", + "DELETE NBR", + "NOTHING" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i; + + 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) { + /* 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_TIMER: + nbr_reset_timer(nbr); + break; + case NBR_ACT_STRT_TIMER: + nbr_start_timer(nbr); + break; + case NBR_ACT_DEL: + nbr_act_del(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + /* neighbor changed from/to ACTIVE */ + 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 (0); +} + +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->rp_list); + TAILQ_INIT(&nbr->rq_list); + + /* set event structures */ + evtimer_set(&nbr->timeout_timer, nbr_timeout_timer, nbr); + + log_debug("nbr_new: neighbor ID %s, peerid %lu", + inet_ntoa(nbr->id), nbr->peerid); + + return (nbr); +} + +void +nbr_act_del(struct nbr *nbr) +{ + struct nbr_failed *nbr_failed; + struct iface *iface; + + if (nbr->iface->auth_type == AUTH_CRYPT) { + if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL) + fatal("nbr_act_del"); + + nbr_failed->addr = nbr->addr; + nbr_failed->auth_seq_num = nbr->auth_seq_num; + iface = nbr->iface; + LIST_INSERT_HEAD(&iface->failed_nbr_list, + nbr_failed, entry); + } + + log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + /* clear lists */ + //response_list_clr(&nbr->rr_list); + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); +} + +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); +} + +struct nbr_failed * +nbr_failed_find(struct iface *iface, u_int32_t src_ip) +{ + struct nbr_failed *nbr_failed = NULL; + + LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) { + if (nbr_failed->addr.s_addr == src_ip) { + return (nbr_failed); + } + } + + return (NULL); +} + +void +nbr_failed_delete(struct iface *iface, struct nbr_failed *nbr_failed) +{ + LIST_REMOVE(nbr_failed, entry); + + free(nbr_failed); +} + +/* timers */ +/* ARGSUSED */ +void +nbr_timeout_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + nbr_fsm(nbr, NBR_EVT_TIMEOUT); +} + +/* actions */ +void +nbr_reset_timer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = NBR_TIMEOUT; + + if (evtimer_add(&nbr->timeout_timer, &tv) == -1) + fatal("nbr_reset_timer"); +} + +void +nbr_start_timer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = NBR_TIMEOUT; + + if (evtimer_add(&nbr->timeout_timer, &tv) == -1) + fatal("nbr_start_timer"); +} + +/* 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.nbr_state = nbr->state; + nctl.iface_state = nbr->iface->state; + + gettimeofday(&now, NULL); + if (evtimer_pending(&nbr->timeout_timer, &tv)) { + timersub(&tv, &now, &res); + if (nbr->state & NBR_STA_DOWN) + nctl.dead_timer = NBR_TIMEOUT - res.tv_sec; + else + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; + + if (nbr->state == NBR_STA_ACTIVE) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} diff --git a/usr.sbin/ripd/packet.c b/usr.sbin/ripd/packet.c new file mode 100644 index 00000000000..6b812f34ccf --- /dev/null +++ b/usr.sbin/ripd/packet.c @@ -0,0 +1,251 @@ +/* $OpenBSD: packet.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 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/uio.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <arpa/inet.h> + +#include <net/if_dl.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ripd.h" +#include "rip.h" +#include "log.h" +#include "ripe.h" + +int rip_hdr_sanity_check(struct rip_hdr *); +struct iface *find_iface(struct ripd_conf *, unsigned int, struct in_addr); + +int +gen_rip_hdr(struct buf *buf, u_int8_t command) +{ + struct rip_hdr rip_hdr; + + bzero(&rip_hdr, sizeof(rip_hdr)); + rip_hdr.version = RIP_VERSION; + rip_hdr.command = command; + + return (buf_add(buf, &rip_hdr, sizeof(rip_hdr))); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, void *pkt, size_t len, struct sockaddr_in *dst) +{ + /* 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) +{ + char cbuf[CMSG_SPACE(sizeof(struct sockaddr_dl))]; + struct sockaddr_in src; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sockaddr_dl *dst = NULL; + struct nbr_failed *nbr_failed = NULL; + struct ripd_conf *xconf = bula; + struct iface *iface; + struct rip_hdr *rip_hdr; + struct nbr *nbr; + char *buf; + ssize_t r; + u_int16_t len, srcport; + u_int32_t auth_crypt_num = 0; + + if (event != EV_READ) + return; + + /* setup buffer */ + buf = pkt_ptr; + + bzero(&msg, sizeof(msg)); + + iov.iov_base = buf; + iov.iov_len = READ_BUF_SIZE; + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + if ((r = recvmsg(fd, &msg, 0)) == -1) { + if (errno != EINTR && errno != EAGAIN) + log_debug("recv_packet: read error: %s", + strerror(errno)); + return; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVIF) { + dst = (struct sockaddr_dl *)CMSG_DATA(cmsg); + break; + } + } + + if (dst == NULL) + return; + + len = (u_int16_t)r; + + /* Check the packet is not from one of the local interfaces */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + if (iface->addr.s_addr == src.sin_addr.s_addr) + return; + } + + /* find a matching interface */ + if ((iface = find_iface(xconf, dst->sdl_index, src.sin_addr)) == NULL) { + log_debug("recv_packet: cannot find a matching interface"); + return; + } + + srcport = ntohs(src.sin_port); + + /* RIP header sanity checks */ + if (len < RIP_HDR_LEN) { + log_debug("recv_packet: bad packet size"); + return; + } + rip_hdr = (struct rip_hdr *)buf; + + if (rip_hdr_sanity_check(rip_hdr) == -1) + return; + + nbr = nbr_find_ip(iface, src.sin_addr.s_addr); + + if (nbr == NULL && iface->auth_type == AUTH_CRYPT) + nbr_failed = nbr_failed_find(iface, src.sin_addr.s_addr); + + /* switch RIP command */ + switch (rip_hdr->command) { + case COMMAND_REQUEST: + if ((msg.msg_flags & MSG_MCAST) == 0 && srcport == RIP_PORT) + return; + + /* XXX: it would be better to not create a nbr on request + * becouse this could lead to DoS even on a authenticated + * environment. + */ + if (nbr == NULL) { + nbr = nbr_new(src.sin_addr.s_addr, iface, 0); + if (nbr_failed != NULL) { + nbr->auth_seq_num = nbr_failed->auth_seq_num; + nbr_failed_delete(iface, nbr_failed); + } + nbr->addr = src.sin_addr; + } + nbr->port = srcport; + nbr_fsm(nbr, NBR_EVT_REQUEST_RCVD); + + buf += RIP_HDR_LEN; + len -= RIP_HDR_LEN; + + recv_request(iface, nbr, buf, len); + break; + case COMMAND_RESPONSE: + if (srcport != RIP_PORT) + return; + + if (auth_validate(&buf, &len, iface, nbr, nbr_failed, + &auth_crypt_num)) { + log_warnx("recv_packet: authentication error, " + "interface %s", iface->name); + return; + } + + if (nbr == NULL) { + nbr = nbr_new(src.sin_addr.s_addr, iface, 0); + if (nbr_failed != NULL) + nbr_failed_delete(iface, nbr_failed); + nbr->addr = src.sin_addr; + } + nbr->auth_seq_num = auth_crypt_num; + nbr_fsm(nbr, NBR_EVT_RESPONSE_RCVD); + + recv_response(iface, nbr, buf, len); + break; + default: + log_debug("recv_packet: unknown RIP command, interface %s", + iface->name); + } +} + +int +rip_hdr_sanity_check(struct rip_hdr *rip_hdr) +{ + if (rip_hdr->version != RIP_VERSION) { + log_debug("recv_packet: invalid RIP version %d", + rip_hdr->version); + return (-1); + } + + return (0); +} + +struct iface * +find_iface(struct ripd_conf *xconf, unsigned int ifindex, struct in_addr src) +{ + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + if (ifindex != 0 && ifindex == iface->ifindex && + !iface->passive && (iface->addr.s_addr & + iface->mask.s_addr) == (src.s_addr & iface->mask.s_addr)) + /* + * XXX may fail on P2P links because src and dst don't + * have to share a common subnet on the otherhand + * checking somthing like this will help to support + * multiple networks configured on one interface. + */ + return (iface); + } + + return (NULL); +} diff --git a/usr.sbin/ripd/parse.y b/usr.sbin/ripd/parse.y new file mode 100644 index 00000000000..e5e98ca8b18 --- /dev/null +++ b/usr.sbin/ripd/parse.y @@ -0,0 +1,763 @@ +/* $OpenBSD: parse.y,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 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/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <ifaddrs.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "ripd.h" +#include "rip.h" +#include "ripe.h" +#include "log.h" + +static struct ripd_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 ripd_conf *); +int check_file_secrecy(int fd, const char *fname); +u_int32_t get_rtr_id(void); + +static struct { + char auth_key[MAX_SIMPLE_AUTH_LEN]; + struct auth_md_head md_list; + enum auth_type auth_type; + u_int8_t auth_keyid; + u_int8_t cost; +} *defs, globaldefs, 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 *); + +typedef struct { + union { + u_int32_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token SPLIT_HORIZON TRIGGERED_UPDATES FIBUPDATE REDISTRIBUTE +%token AUTHKEY AUTHTYPE AUTHMD AUTHMDKEYID +%token INTERFACE +%token COST PASSIVE +%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' + ; + +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("recognized values are yes or no"); + YYERROR; + } + free($1); + } + ; + +varset : STRING '=' string { + if (conf->opts & RIPD_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +conf_main : SPLIT_HORIZON STRING { + /* clean flags first */ + conf->options &= ~(OPT_SPLIT_HORIZON | + OPT_SPLIT_POISONED); + if (!strcmp($2, "none")) + /* nothing */ ; + else if (!strcmp($2, "default")) + conf->options |= OPT_SPLIT_HORIZON; + else if (!strcmp($2, "poisoned")) + conf->options |= OPT_SPLIT_POISONED; + else { + yyerror("unknon split horizon type"); + free($2); + YYERROR; + } + free($2); + } + | TRIGGERED_UPDATES yesno { + if ($2 == 1) + conf->options |= OPT_TRIGGERED_UPDATES; + else + conf->options &= ~OPT_TRIGGERED_UPDATES; + } + | FIBUPDATE yesno { + if ($2 == 0) + conf->flags |= RIPD_FLAG_NO_FIB_UPDATE; + else + conf->flags &= ~RIPD_FLAG_NO_FIB_UPDATE; + } + | REDISTRIBUTE STRING { + if (!strcmp($2, "static")) + conf->redistribute_flags |= + REDISTRIBUTE_STATIC; + else if (!strcmp($2, "connected")) + conf->redistribute_flags |= + REDISTRIBUTE_CONNECTED; + else if (!strcmp($2, "default")) + conf->redistribute_flags |= + REDISTRIBUTE_DEFAULT; + else if (!strcmp($2, "none")) + conf->redistribute_flags = 0; + else { + yyerror("unknown redistribute type"); + free($2); + YYERROR; + } + free($2); + } + ; + +authmd : AUTHMD number STRING { + if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) { + yyerror("auth-md key-id out of range " + "(%d-%d)", MIN_MD_ID, MAX_MD_ID); + free($3); + YYERROR; + } + if (strlen($3) > MD5_DIGEST_LENGTH) { + yyerror("auth-md key length out of range " + "(max length %d)", + MD5_DIGEST_LENGTH); + free($3); + YYERROR; + } + md_list_add(&defs->md_list, $2, $3); + free($3); + } + +authmdkeyid : AUTHMDKEYID number { + if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) { + yyerror("auth-md-keyid out of range " + "(%d-%d)", MIN_MD_ID, MAX_MD_ID); + YYERROR; + } + defs->auth_keyid = $2; + } + +authtype : AUTHTYPE STRING { + enum auth_type type; + + if (!strcmp($2, "none")) + type = AUTH_NONE; + else if (!strcmp($2, "simple")) + type = AUTH_SIMPLE; + else if (!strcmp($2, "crypt")) + type = AUTH_CRYPT; + else { + yyerror("unknown auth-type"); + free($2); + YYERROR; + } + free($2); + defs->auth_type = type; + } + ; + +authkey : AUTHKEY STRING { + if (strlen($2) > MAX_SIMPLE_AUTH_LEN) { + yyerror("auth-key too long (max lenght %d)", + MAX_SIMPLE_AUTH_LEN); + free($2); + YYERROR; + } + strncpy(defs->auth_key, $2, + sizeof(defs->auth_key)); + free($2); + } + ; + +defaults : COST number { + if ($2 < 1 || $2 > INFINITY) { + yyerror("cost out of range (%d-%d)", 1, + INFINITY); + YYERROR; + } + defs->cost = $2; + } + | authtype + | authkey + | authmdkeyid + | authmd + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl + ; + +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)); + md_list_copy(&ifacedefs.md_list, + &defs->md_list); + defs = &ifacedefs; + } interface_block { + iface->cost = defs->cost; + iface->auth_type = defs->auth_type; + iface->auth_keyid = defs->auth_keyid; + memcpy(iface->auth_key, defs->auth_key, + sizeof(iface->auth_key)); + md_list_copy(&iface->auth_md_list, &defs->md_list); + md_list_clr(&defs->md_list); + defs = &globaldefs; + } + ; + +interface_block : '{' optnl interfaceopts_l '}' + | '{' optnl '}' + ; + +interfaceopts_l : interfaceopts_l interfaceoptsl + | interfaceoptsl + ; + +interfaceoptsl : PASSIVE nl { iface->passive = 1; } + | defaults 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[] = { + {"auth-key", AUTHKEY}, + {"auth-md", AUTHMD}, + {"auth-md-keyid", AUTHMDKEYID}, + {"auth-type", AUTHTYPE}, + {"cost", COST}, + {"fib-update", FIBUPDATE}, + {"interface", INTERFACE}, + {"passive", PASSIVE}, + {"redistribute", REDISTRIBUTE}, + {"split-horizon", SPLIT_HORIZON}, + {"triggered-updates", TRIGGERED_UPDATES} + }; + 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 ripd_conf * +parse_config(char *filename, int opts) +{ + struct sym *sym, *next; + + if ((conf = calloc(1, sizeof(struct ripd_conf))) == NULL) + fatal("parse_config"); + + bzero(&globaldefs, sizeof(globaldefs)); + defs = &globaldefs; + TAILQ_INIT(&defs->md_list); + defs->cost = 1; + defs->auth_type = AUTH_NONE; + + if ((fin = fopen(filename, "r")) == NULL) { + warn("%s", filename); + free(conf); + return (NULL); + } + infile = filename; + + conf->opts = opts; + conf->redistribute_flags = 0; + + if (!(conf->opts & RIPD_OPT_NOACTION)) + 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 & RIPD_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); + } + } + + /* free global config defaults */ + md_list_clr(&globaldefs.md_list); + + 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->auth_keyid = 1; + i->passive = 0; + + return (i); +} + +void +clear_config(struct ripd_conf *xconf) +{ + struct iface *i; + + while ((i = LIST_FIRST(&conf->iface_list)) != NULL) { + LIST_REMOVE(i, entry); + if_del(i); + } + + free(xconf); +} diff --git a/usr.sbin/ripd/printconf.c b/usr.sbin/ripd/printconf.c new file mode 100644 index 00000000000..a87cde902f3 --- /dev/null +++ b/usr.sbin/ripd/printconf.c @@ -0,0 +1,106 @@ +/* $OpenBSD: printconf.c,v 1.1 2006/10/18 16:11:58 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 "rip.h" +#include "ripd.h" +#include "ripe.h" + +void print_mainconf(struct ripd_conf *); +void print_iface(struct iface *); + +void +print_mainconf(struct ripd_conf *conf) +{ + if (conf->flags |= RIPD_FLAG_NO_FIB_UPDATE) + printf("fib-update yes\n"); + else + printf("fib-update no\n"); + + if (conf->redistribute_flags & REDISTRIBUTE_STATIC) + printf("redistribute static\n"); + else if (conf->redistribute_flags & REDISTRIBUTE_CONNECTED) + printf("redistribute connected\n"); + else if (conf->redistribute_flags & REDISTRIBUTE_DEFAULT) + printf("redistribute default\n"); + else + printf("redistribute none\n"); + + if (conf->options & OPT_SPLIT_HORIZON) + printf("split-horizon default\n"); + else if (conf->options & OPT_SPLIT_POISONED) + printf("split-horizon poisoned\n"); + else + printf("split-horizon none\n"); + + if (conf->options & OPT_TRIGGERED_UPDATES) + printf("triggered-updates yes\n"); + else + printf("triggered-updates no\n"); +} + +void +print_iface(struct iface *iface) +{ + printf("interface %s {\n", iface->name); + + if (iface->passive) + printf("\tpassive\n"); + + printf("\tcost %d\n", iface->cost); + + printf("\tauth-type %s\n", if_auth_name(iface->auth_type)); + switch (iface->auth_type) { + case AUTH_NONE: + break; + case AUTH_SIMPLE: + printf("\tauth-key XXXXXX\n"); + break; + case AUTH_CRYPT: + break; + default: + printf("\tunknown auth type!\n"); + break; + } + + printf("}\n"); +} + +void +print_config(struct ripd_conf *conf) +{ + struct iface *iface; + + printf("\n"); + print_mainconf(conf); + printf("\n"); + + LIST_FOREACH(iface, &conf->iface_list, entry) { + printf("ooo\n"); + print_iface(iface); + } + printf("\n"); + +} diff --git a/usr.sbin/ripd/rde.c b/usr.sbin/ripd/rde.c new file mode 100644 index 00000000000..262eff65309 --- /dev/null +++ b/usr.sbin/ripd/rde.c @@ -0,0 +1,455 @@ +/* $OpenBSD: rde.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <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 "ripd.h" +#include "rip.h" +#include "ripe.h" +#include "log.h" +#include "rde.h" + +struct ripd_conf *rdeconf = NULL; +struct imsgbuf *ibuf_ripe; +struct imsgbuf *ibuf_main; + +void rde_sig_handler(int, short, void *); +void rde_shutdown(void); +void rde_dispatch_imsg(int, short, void *); +void rde_dispatch_parent(int, short, void *); +int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int rde_imsg_compose_ripe(int, u_int32_t, pid_t, void *, u_int16_t); +int rde_check_route(struct rip_route *); +void triggered_update(struct rt_node *); + +/* ARGSUSED */ +void +rde_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + rde_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* route decision engine */ +pid_t +rde(struct ripd_conf *xconf, int pipe_parent2rde[2], int pipe_ripe2rde[2], + int pipe_parent2ripe[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(RIPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("route decision engine"); + ripd_process = PROC_RDE_ENGINE; + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + 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_ripe2rde[0]); + close(pipe_parent2rde[0]); + close(pipe_parent2ripe[0]); + close(pipe_parent2ripe[1]); + + if ((ibuf_ripe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ripe, pipe_ripe2rde[1], rde_dispatch_imsg); + imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_parent); + + /* setup event handler */ + ibuf_ripe->events = EV_READ; + event_set(&ibuf_ripe->ev, ibuf_ripe->fd, ibuf_ripe->events, + ibuf_ripe->handler, ibuf_ripe); + event_add(&ibuf_ripe->ev, NULL); + + ibuf_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(); + event_dispatch(); + + rde_shutdown(); + /* NOTREACHED */ + + return (0); +} + +void +rde_shutdown(void) +{ + rt_clear(); + + msgbuf_clear(&ibuf_ripe->w); + free(ibuf_ripe); + 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_ripe(int type, u_int32_t peerid, pid_t pid, void *data, + u_int16_t datalen) +{ + return (imsg_compose(ibuf_ripe, type, peerid, pid, data, datalen)); +} + +/* ARGSUSED */ +void +rde_dispatch_imsg(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct rip_route rr; + 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("rde_dispatch_imsg: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_ROUTE_FEED: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) + fatalx("invalid size of RDE request"); + + memcpy(&rr, imsg.data, sizeof(rr)); + + if (rde_check_route(&rr) == -1) + log_debug("rde_dispatch_imsg: " + "packet malformed\n"); + break; + case IMSG_FULL_REQUEST: + bzero(&rr, sizeof(rr)); + /* AFI == 0 && metric == INFINITY request the + whole routing table + */ + rr.metric = INFINITY; + rde_imsg_compose_ripe(IMSG_REQUEST_ADD, 0, + 0, &rr, sizeof(rr)); + rde_imsg_compose_ripe(IMSG_SEND_REQUEST, 0, + 0, NULL, 0); + break; + case IMSG_FULL_RESPONSE: + rt_snap(imsg.hdr.peerid); + rde_imsg_compose_ripe(IMSG_SEND_RESPONSE, + imsg.hdr.peerid, 0, NULL, 0); + break; + case IMSG_ROUTE_REQUEST: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) + fatalx("invalid size of RDE request"); + + memcpy(&rr, imsg.data, sizeof(rr)); + + rt_complete(&rr); + rde_imsg_compose_ripe(IMSG_RESPONSE_ADD, + imsg.hdr.peerid, 0, &rr, sizeof(rr)); + + break; + case IMSG_ROUTE_REQUEST_END: + rde_imsg_compose_ripe(IMSG_SEND_RESPONSE, + imsg.hdr.peerid, 0, NULL, 0); + break; + case IMSG_CTL_SHOW_RIB: + rt_dump(imsg.hdr.pid); + + imsg_compose(ibuf_ripe, IMSG_CTL_END, 0, imsg.hdr.pid, + NULL, 0); + + break; + default: + log_debug("rde_dispatch_msg: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +/* ARGSUSED */ +void +rde_dispatch_parent(int fd, short event, void *bula) +{ + struct imsg imsg; + struct rt_node *rt; + struct kroute kr; + struct imsgbuf *ibuf = bula; + ssize_t n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("rde_dispatch_parent: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_NETWORK_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + + memcpy(&kr, imsg.data, sizeof(kr)); + + rt = rt_new_kr(&kr); + rt_insert(rt); + break; + case IMSG_NETWORK_DEL: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + memcpy(&kr, imsg.data, sizeof(kr)); + + if ((rt = rt_find(kr.prefix.s_addr, + kr.netmask.s_addr)) != NULL) + rt_remove(rt); + break; + case IMSG_KROUTE_GET: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(kr)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + memcpy(&kr, imsg.data, sizeof(kr)); + + if ((rt = rt_find(kr.prefix.s_addr, + kr.netmask.s_addr)) != NULL) + rde_send_change_kroute(rt); + else + /* should not happen */ + imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, + 0, &kr, sizeof(kr)); + + break; + default: + log_debug("rde_dispatch_parent: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +rde_send_change_kroute(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = r->nexthop.s_addr; + kr.netmask.s_addr = r->netmask.s_addr; + kr.metric = r->metric; + kr.flags = r->flags; + kr.ifindex = r->ifindex; + + imsg_compose(ibuf_main, IMSG_KROUTE_CHANGE, 0, 0, &kr, sizeof(kr)); +} + +void +rde_send_delete_kroute(struct rt_node *r) +{ + struct kroute kr; + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = r->nexthop.s_addr; + kr.netmask.s_addr = r->netmask.s_addr; + kr.metric = r->metric; + kr.flags = r->flags; + kr.ifindex = r->ifindex; + + imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, 0, &kr, sizeof(kr)); +} + +int +rde_check_route(struct rip_route *e) +{ + struct rt_node *rn; + struct iface *iface; + int metric; + + if ((e->nexthop.s_addr & htonl(IN_CLASSA_NET)) == + htonl(INADDR_LOOPBACK & IN_CLASSA_NET) || + (e->nexthop.s_addr == INADDR_ANY)) + return (-1); + + if ((e->address.s_addr & htonl(IN_CLASSA_NET)) == + htonl(INADDR_LOOPBACK & IN_CLASSA_NET)) + return (-1); + + if (e->metric > INFINITY) + return (-1); + + if ((iface = if_find_index(e->ifindex)) == NULL) + return (-1); + + metric = MIN(INFINITY, e->metric + iface->cost); + if (metric >= INFINITY) + return (0); + + if ((rn = rt_find(e->address.s_addr, e->mask.s_addr)) == NULL) { + rn = rt_new_rr(e, metric); + rt_insert(rn); + rde_send_change_kroute(rn); + route_start_timeout(rn); + triggered_update(rn); + } else { + /* + * XXX don't we have to track all incomming routes? + * what happens if the kernel route is removed later. + */ + if (rn->flags & F_KERNEL) + return (0); + + if (metric < rn->metric) { + rn->metric = metric; + rn->nexthop.s_addr = e->nexthop.s_addr; + rn->ifindex = e->ifindex; + rde_send_change_kroute(rn); + triggered_update(rn); + } else if (e->nexthop.s_addr == rn->nexthop.s_addr && + e->metric > metric) { + rn->metric = metric; + rde_send_change_kroute(rn); + triggered_update(rn); + } + + if (e->nexthop.s_addr == rn->nexthop.s_addr) + route_reset_timers(rn); + } + + return (0); +} + +void +triggered_update(struct rt_node *rn) +{ + struct rip_route rr; + + rr.address.s_addr = rn->prefix.s_addr; + rr.mask.s_addr = rn->netmask.s_addr; + rr.nexthop.s_addr = rn->nexthop.s_addr; + rr.metric = rn->metric; + rr.ifindex = rn->ifindex; + + rde_imsg_compose_ripe(IMSG_SEND_TRIGGERED_UPDATE, 0, 0, &rr, + sizeof(struct rip_route)); +} diff --git a/usr.sbin/ripd/rde.h b/usr.sbin/ripd/rde.h new file mode 100644 index 00000000000..f27d561a8ae --- /dev/null +++ b/usr.sbin/ripd/rde.h @@ -0,0 +1,64 @@ +/* $OpenBSD: rde.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 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 timeout_timer; + struct event garbage_timer; + struct in_addr prefix; + struct in_addr netmask; + struct in_addr nexthop; + u_short ifindex; + u_int16_t flags; + u_int8_t metric; +}; + +/* rde.c */ +pid_t rde(struct ripd_conf *, int [2], int [2], int [2]); +void rde_send_change_kroute(struct rt_node *); +void rde_send_delete_kroute(struct rt_node *); +int rde_imsg_compose_ripe(int, u_int32_t, pid_t, void *, + u_int16_t); + +/* rde_routes.c */ +void rt_init(void); +int rt_compare(struct rt_node *, struct rt_node *); +struct rt_node *rt_find(in_addr_t, in_addr_t); +struct rt_node *rt_new_kr(struct kroute *); +struct rt_node *rt_new_rr(struct rip_route *, int); +int rt_insert(struct rt_node *); +int rt_remove(struct rt_node *); +void rt_complete(struct rip_route *); +void rt_snap(u_int32_t); +void rt_clear(void); +void route_reset_timers(struct rt_node *); +int route_start_timeout(struct rt_node *); +void rt_dump(pid_t); + +#endif /* _RDE_H_ */ diff --git a/usr.sbin/ripd/rde_rib.c b/usr.sbin/ripd/rde_rib.c new file mode 100644 index 00000000000..e26292ee953 --- /dev/null +++ b/usr.sbin/ripd/rde_rib.c @@ -0,0 +1,258 @@ +/* $OpenBSD: rde_rib.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * + * 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 <net/if_types.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <event.h> + +#include "ripd.h" +#include "rip.h" +#include "log.h" +#include "rde.h" + +extern struct ripd_conf *rdeconf; +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) + +void route_action_timeout(int, short, void *); +void route_action_garbage(int, short, void *); +int route_start_timeout(struct rt_node *); + +/* timers */ +int +route_start_timeout(struct rt_node *rn) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ROUTE_TIMEOUT; + + return (evtimer_add(&rn->timeout_timer, &tv)); +} + +/* ARGSUSED */ +void +route_action_timeout(int fd, short event, void *arg) +{ + struct rt_node *r = arg; + struct timeval tv; + + timerclear(&tv); + r->metric = INFINITY; + tv.tv_sec = ROUTE_GARBAGE; + + if (evtimer_add(&r->garbage_timer, &tv) == -1) + fatal("route_action_timeout"); + + rde_send_change_kroute(r); +} + +/* ARGSUSED */ +void +route_action_garbage(int fd, short event, void *arg) +{ + struct rt_node *r = arg; + + rde_send_delete_kroute(r); + rt_remove(r); +} + +void +route_reset_timers(struct rt_node *r) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ROUTE_TIMEOUT; + evtimer_del(&r->timeout_timer); + evtimer_del(&r->garbage_timer); + + event_add(&r->timeout_timer, &tv); +} + +/* route table */ +void +rt_init(void) +{ + RB_INIT(&rt); +} + +int +rt_compare(struct rt_node *a, struct rt_node *b) +{ + 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); + if (ntohl(a->netmask.s_addr) < ntohl(b->netmask.s_addr)) + return (-1); + if (ntohl(a->netmask.s_addr) > ntohl(b->netmask.s_addr)) + return (1); + + return (0); +} + +struct rt_node * +rt_find(in_addr_t prefix, in_addr_t netmask) +{ + struct rt_node s; + + s.prefix.s_addr = prefix; + s.netmask.s_addr = netmask; + + return (RB_FIND(rt_tree, &rt, &s)); +} + +struct rt_node * +rt_new_kr(struct kroute *kr) +{ + struct rt_node *rn; + + if ((rn = calloc(1, sizeof(*rn))) == NULL) + fatal("rt_new_kr"); + + evtimer_set(&rn->timeout_timer, route_action_timeout, rn); + evtimer_set(&rn->garbage_timer, route_action_garbage, rn); + + rn->prefix.s_addr = kr->prefix.s_addr; + rn->netmask.s_addr = kr->netmask.s_addr; + rn->nexthop.s_addr = kr->nexthop.s_addr; + rn->metric = kr->metric; + rn->ifindex = kr->ifindex; + rn->flags = F_KERNEL; + + return (rn); +} + +struct rt_node * +rt_new_rr(struct rip_route *e, int metric) +{ + struct rt_node *rn; + + if ((rn = calloc(1, sizeof(*rn))) == NULL) + fatal("rt_new_rr"); + + evtimer_set(&rn->timeout_timer, route_action_timeout, rn); + evtimer_set(&rn->garbage_timer, route_action_garbage, rn); + + rn->prefix.s_addr = e->address.s_addr; + rn->netmask.s_addr = e->mask.s_addr; + rn->nexthop.s_addr = e->nexthop.s_addr; + rn->metric = metric; + rn->ifindex = e->ifindex; + rn->flags = F_RIPD_INSERTED; + + return (rn); +} + +int +rt_insert(struct rt_node *r) +{ + if (RB_INSERT(rt_tree, &rt, r) != NULL) { + log_warnx("rt_insert failed for %s/%s", + inet_ntoa(r->prefix), inet_ntoa(r->netmask)); + 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/%s", + inet_ntoa(r->prefix), inet_ntoa(r->netmask)); + return (-1); + } + + free(r); + return (0); +} + +void +rt_snap(u_int32_t peerid) +{ + struct rt_node *r; + struct rip_route rr; + + bzero(&rr, sizeof(rr)); + + RB_FOREACH(r, rt_tree, &rt) { + rr.address = r->prefix; + rr.mask = r->netmask; + rr.nexthop = r->nexthop; + rr.metric = r->metric; + rr.ifindex = r->ifindex; + + rde_imsg_compose_ripe(IMSG_RESPONSE_ADD, peerid, 0, &rr, + sizeof(rr)); + } +} + +void +rt_dump(pid_t pid) +{ + struct rt_node *r; + static struct ctl_rt rtctl; + + RB_FOREACH(r, rt_tree, &rt) { + rtctl.prefix.s_addr = r->prefix.s_addr; + rtctl.netmask.s_addr = r->netmask.s_addr; + rtctl.nexthop.s_addr = r->nexthop.s_addr; + rtctl.metric = r->metric; + rtctl.flags = r->flags; + + rde_imsg_compose_ripe(IMSG_CTL_SHOW_RIB, 0, pid, &rtctl, + sizeof(rtctl)); + } +} + +void +rt_complete(struct rip_route *rr) +{ + struct rt_node *rn; + + if ((rn = rt_find(rr->address.s_addr, rr->mask.s_addr)) == NULL) + rr->metric = INFINITY; + else + rr->metric = rn->metric; +} + +void +rt_clear(void) +{ + struct rt_node *r; + + while ((r = RB_MIN(rt_tree, &rt)) != NULL) + rt_remove(r); +} diff --git a/usr.sbin/ripd/rip.h b/usr.sbin/ripd/rip.h new file mode 100644 index 00000000000..a1b2a3c3d91 --- /dev/null +++ b/usr.sbin/ripd/rip.h @@ -0,0 +1,82 @@ +/* $OpenBSD: rip.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * + * 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. + */ + +/* RIP protocol definitions */ + +#ifndef _RIP_H_ +#define _RIP_H_ + +/* misc */ +#define RIP_VERSION 2 +#define ALL_RIP_ROUTERS "224.0.0.9" +#define RIP_PORT 520 + +#define MIN_MD_ID 0 +#define MAX_MD_ID 255 + +/* metric */ +#define INFINITY 16 + +/* timers */ +#define KEEPALIVE 30 +#define OFFSET 10 + +#define MAX_RIP_ENTRIES 25 + +/* RIP command */ +#define COMMAND_REQUEST 1 +#define COMMAND_RESPONSE 2 + +#define RIP_HDR_LEN sizeof(struct rip_hdr) +#define RIP_ENTRY_LEN sizeof(struct rip_entry) + +struct rip_hdr { + u_int8_t command; + u_int8_t version; + u_int16_t dummy; +}; + +struct rip_entry { + u_int16_t AFI; + u_int16_t route_tag; + u_int32_t address; + u_int32_t mask; + u_int32_t nexthop; + u_int32_t metric; +}; + +/* auth */ +#define AUTH 0xFFFF +#define AUTH_TRLR_HDR_LEN 4 + +/* auth general struct */ +struct rip_auth { + u_int16_t auth_fixed; + u_int16_t auth_type; +}; + +/* Keyed MD5 auth struct */ +struct md5_auth { + u_int16_t auth_offset; + u_int8_t auth_keyid; + u_int8_t auth_length; + u_int32_t auth_seq; + u_int64_t auth_reserved; +}; + +#endif /* _RIP_H_ */ diff --git a/usr.sbin/ripd/ripd.8 b/usr.sbin/ripd/ripd.8 new file mode 100644 index 00000000000..42092db8d0d --- /dev/null +++ b/usr.sbin/ripd/ripd.8 @@ -0,0 +1,81 @@ +.\" $OpenBSD: ripd.8,v 1.1 2006/10/18 16:11:58 norby Exp $ +.\" +.\" Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> +.\" 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 RIPD 8 +.Os +.Sh NAME +.Nm ripd +.Nd "Routing Information Protocol daemon" +.Sh SYNOPSIS +.Nm +.Op Fl dnv +.Op Fl f Ar file +.Sh DESCRIPTION +.Nm +is the Routing Information Protocol +.Pq RIP +daemon which manages routers' routing tables. +For RIPD to work, +multicast routing must be enabled by setting multicast_router=YES in +.Pa /etc/rc.conf.local . +.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/ripd.sockXX" -compact +.It /etc/ripd.conf +Default +.Nm +configuration file. +.It /var/run/ripd.sock +Unix-domain socket used for communication with +.Xr ripctl 8 . +.El +.Sh SEE ALSO +.Xr ripd.conf 5 , +.Xr rc.conf 8 , +.Xr ripctl 8 +.Rs +.%R RFC 2453 +.%T RIP Version 2 +.%D November 1998 +.Re +.Rs +.%R RFC 2082 +.%T "RIP-2 MD5 Authentication" +.%D January 1997 +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 4.0 . diff --git a/usr.sbin/ripd/ripd.conf.5 b/usr.sbin/ripd/ripd.conf.5 new file mode 100644 index 00000000000..7a93b144bf8 --- /dev/null +++ b/usr.sbin/ripd/ripd.conf.5 @@ -0,0 +1,195 @@ +.\" $OpenBSD: ripd.conf.5,v 1.1 2006/10/18 16:11:58 norby Exp $ +.\" +.\" Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> +.\" 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 RIPD.CONF 5 +.Os +.Sh NAME +.Nm ripd.conf +.Nd Routing Information Protocol daemon configuration file +.Sh DESCRIPTION +The +.Xr ripd 8 +daemon implements the Routing Information Protocol version 2 as described +in rfc 2453. +.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 ripd 8 . +.It Sy Interfaces Configuration +Interface specific parameters. +.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 cost ) . +Macros are not expanded inside quotes. +.Pp +For example: +.Bd -literal -offset indent +hi="2" +interface em0 { + cost $hi +} +.Ed +.Sh GLOBAL CONFIGURATION +Global settings concerns the main behaviour of the daemon. +.Bl -tag -width Ds +.It Xo +.Ic fib-update +.Pq Ic yes Ns \&| Ns Ic no +.Xc +If set to +.Ic no , +do not update the Forwarding Information Base, a.k.a. the kernel routing +table. +The default is +.Ic yes . +.Pp +.It Xo +.Ic redistribute +.Sm off +.Po Ic static Ns \&| Ns Ic connected Ns \&| Ns +.Ic default Ns \&| +.Ic none Pc +.Sm on +.Xc +If set to +.Ic connected , +routes to directly attached networks will be +announced over RIP. +If set to +.Ic static , +static routes will be announced over RIP. +If set to +.Ic default , +a default route pointing to this router will be announced over RIP. +By default no additional routes will be announced over RIP. +.Pp +.It Xo +.Ic split-horizon +.Sm off +.Po Ic default Ns \&| Ns Ic poisoned Ns \&| Ns +.Ic none Pc +.Sm on +.Xc +If set to +.Ic default , +do not redistribute routes to the interface from which they were learned. +If set to +.Ic poisoned , +redistribute routes to the interface from which they were learned, but +forcing the metric to infinity. +Both these behaviours can resolve routing loops when a router goes down. +The default is +.Ic none . +.Pp +.It Xo +.Ic triggered-updates +.Pq Ic yes Ns \&| Ns Ic no +.Xc +Immediately trigger responses when a route changes. +The default is +.Ic yes . +.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 auth-key Ar key +Set the authentication key for +.Ic simple +authentication. +Up to 16 characters can be specified. +.It Ic auth-md Ar key-id key +Set the authentication +.Ar key-id +and +.Ar key +for +.Ic crypt +authentication. +The valid range for +.Ar key-id +is 0\-255. +Up to 16 characters can be specified for +.Ar key . +Multiple keys may be specified. +.It Ic auth-md-keyid Ar key-id +Configure the +.Ar key-id +to use for +.Ic crypt +authentication. +The valid range for +.Ar key-id +is 0\-255. +The default key-id is 1. +.It Xo +.Ic auth-type +.Po Ic none Ns \&| Ns +.Ic simple Ns \&| Ns Ic crypt Pc +.Xc +Set the authentication type. +The default is +.Ic none . +Simple authentication uses a plaintext password, up to 16 characters. +Crypt authentication uses an MD5 hash. +.It Ic cost Ar number +Cost of the network connected through this interface. +Values must be between 1 and 16, inclusive. +.It Ic passive +Prevent transmission and reception of RIP packets on this interface. +.El +.Sh FILES +.Bl -tag -width "/etc/ripd.conf" -compact +.It Pa /etc/ripd.conf +.Xr ripd 8 +configuration file +.El +.Sh SEE ALSO +.Xr rc.conf.local 8 , +.Xr ripctl 8 , +.Xr ripd 8 +.Rs +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 4.0 . diff --git a/usr.sbin/ripd/ripd.h b/usr.sbin/ripd/ripd.h new file mode 100644 index 00000000000..12f143437e2 --- /dev/null +++ b/usr.sbin/ripd/ripd.h @@ -0,0 +1,392 @@ +/* $OpenBSD: ripd.h,v 1.1 2006/10/18 16:11:58 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. + */ + +#ifndef _RIPD_H_ +#define _RIPD_H_ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/tree.h> +#include <md5.h> +#include <net/if.h> +#include <netinet/in.h> +#include <event.h> + +#define CONF_FILE "/etc/ripd.conf" +#define RIPD_SOCKET "/var/run/ripd.sock" +#define RIPD_USER "_ripd" +#define READ_BUF_SIZE 65535 + +#define NBR_HASHSIZE 128 +#define NBR_IDSELF 1 +#define NBR_CNTSTART (NBR_IDSELF + 1) + +#define ROUTE_TIMEOUT 180 +#define ROUTE_GARBAGE 120 + +#define NBR_TIMEOUT 180 + +#define READ_BUF_SIZE 65535 +#define RT_BUF_SIZE 16384 +#define MAX_RTSOCK_BUF 128 * 1024 + +#define RIPD_FLAG_NO_FIB_UPDATE 0x0001 + +#define F_RIPD_INSERTED 0x0001 +#define F_KERNEL 0x0002 +#define F_BGPD_INSERTED 0x0004 +#define F_CONNECTED 0x0008 +#define F_DOWN 0x0010 +#define F_STATIC 0x0020 +#define F_DYNAMIC 0x0040 +#define F_OSPFD_INSERTED 0x0080 +#define F_REDISTRIBUTED 0x0100 + +#define REDISTRIBUTE_NONE 0x0 +#define REDISTRIBUTE_STATIC 0x01 +#define REDISTRIBUTE_CONNECTED 0x02 +#define REDISTRIBUTE_DEFAULT 0x04 + +#define OPT_SPLIT_HORIZON 0x01 +#define OPT_SPLIT_POISONED 0x02 +#define OPT_TRIGGERED_UPDATES 0x04 + +/* buffer */ +struct buf { + TAILQ_ENTRY(buf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; +}; + +struct msgbuf { + TAILQ_HEAD(, buf) bufs; + u_int32_t queued; + int fd; +}; + +#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_END, + IMSG_CTL_RELOAD, + IMSG_CTL_IFINFO, + IMSG_IFINFO, + IMSG_CTL_FIB_COUPLE, + IMSG_CTL_FIB_DECOUPLE, + IMSG_CTL_KROUTE, + IMSG_CTL_KROUTE_ADDR, + IMSG_CTL_SHOW_INTERFACE, + IMSG_CTL_SHOW_IFACE, + IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_RIB, + IMSG_KROUTE_CHANGE, + IMSG_KROUTE_DELETE, + IMSG_KROUTE_GET, + IMSG_NETWORK_ADD, + IMSG_NETWORK_DEL, + IMSG_ROUTE_FEED, + IMSG_RESPONSE_ADD, + IMSG_SEND_RESPONSE, + IMSG_FULL_RESPONSE, + IMSG_ROUTE_REQUEST, + IMSG_ROUTE_REQUEST_END, + IMSG_FULL_REQUEST, + IMSG_REQUEST_ADD, + IMSG_SEND_REQUEST, + IMSG_SEND_TRIGGERED_UPDATE +}; + +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_ACTIVE (~IF_STA_DOWN) +#define IF_STA_ANY 0x7f + +/* interface events */ +enum iface_event { + IF_EVT_NOTHING, + IF_EVT_UP, + IF_EVT_DOWN +}; + +/* interface actions */ +enum iface_action { + IF_ACT_NOTHING, + IF_ACT_STRT, + IF_ACT_RST +}; + +/* interface types */ +enum iface_type { + IF_TYPE_POINTOPOINT, + IF_TYPE_BROADCAST, + IF_TYPE_NBMA, + IF_TYPE_POINTOMULTIPOINT +}; + +/* neighbor states */ +#define NBR_STA_DOWN 0x01 +#define NBR_STA_ACTIVE (~NBR_STA_DOWN) +#define NBR_STA_ANY 0xff + +struct auth_md { + TAILQ_ENTRY(auth_md) entry; + char key[MD5_DIGEST_LENGTH]; + u_int8_t keyid; +}; + +#define MAX_SIMPLE_AUTH_LEN 16 + +/* auth types */ +enum auth_type { + AUTH_NONE = 1, + AUTH_SIMPLE, + AUTH_CRYPT +}; + +TAILQ_HEAD(auth_md_head, auth_md); +TAILQ_HEAD(packet_head, packet_entry); + +struct iface { + LIST_ENTRY(iface) entry; + LIST_HEAD(, nbr) nbr_list; + LIST_HEAD(, nbr_failed) failed_nbr_list; + char name[IF_NAMESIZE]; + char auth_key[MAX_SIMPLE_AUTH_LEN]; + struct in_addr addr; + struct in_addr dst; + struct in_addr mask; + struct packet_head rq_list; + struct packet_head rp_list; + struct auth_md_head auth_md_list; + + time_t uptime; + u_long mtu; + u_long baudrate; + int fd; /* XXX */ + int state; + u_short ifindex; + u_int16_t cost; + u_int16_t flags; + enum iface_type type; + enum auth_type auth_type; + u_int8_t linktype; + u_int8_t media_type; + u_int8_t passive; + u_int8_t linkstate; + u_int8_t auth_keyid; +}; + +struct rip_route { + struct in_addr address; + struct in_addr mask; + struct in_addr nexthop; + int refcount; + u_short ifindex; + u_int8_t metric; +}; + +struct packet_entry { + TAILQ_ENTRY(packet_entry) entry; + struct rip_route *rr; +}; + +enum { + PROC_MAIN, + PROC_RIP_ENGINE, + PROC_RDE_ENGINE +} ripd_process; + +struct ripd_conf { + struct event ev; + struct event report_timer; + LIST_HEAD(, iface) iface_list; + u_int32_t opts; +#define RIPD_OPT_VERBOSE 0x00000001 +#define RIPD_OPT_VERBOSE2 0x00000002 +#define RIPD_OPT_NOACTION 0x00000004 + int flags; + int redistribute_flags; + int options; + int rip_socket; +}; + +/* kroute */ +struct kroute { + struct in_addr prefix; + struct in_addr netmask; + struct in_addr nexthop; + u_int8_t metric; + u_int16_t flags; + u_short ifindex; +}; + +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; + + time_t uptime; + time_t report_timer; + + u_int32_t baudrate; + + unsigned int ifindex; + int state; + int mtu; + + u_int16_t flags; + u_int16_t metric; + enum iface_type type; + u_int8_t linkstate; + u_int8_t mediatype; + u_int8_t passive; +}; + +struct ctl_rt { + struct in_addr prefix; + struct in_addr netmask; + struct in_addr nexthop; + time_t uptime; + time_t expire; + u_int32_t metric; + u_int8_t flags; +}; + +struct ctl_nbr { + char name[IF_NAMESIZE]; + struct in_addr id; + struct in_addr addr; + time_t dead_timer; + time_t uptime; + int nbr_state; + int iface_state; +}; + +int kif_init(void); +int kr_init(int); +int kr_change(struct kroute *); +int kr_delete(struct kroute *); +void kr_shutdown(void); +void kr_fib_couple(void); +void kr_fib_decouple(void); +void kr_dispatch_msg(int, short, void *); +void kr_show_route(struct imsg *); +void kr_ifinfo(char *, pid_t); +struct kif *kif_findname(char *); + +in_addr_t prefixlen2mask(u_int8_t); +u_int8_t mask2prefixlen(in_addr_t); + +/* ripd.c */ +void main_imsg_compose_ripe(int, pid_t, void *, u_int16_t); +void main_imsg_compose_rde(int, pid_t, void *, u_int16_t); +int rip_redistribute(struct kroute *); + +/* parse.y */ +struct ripd_conf *parse_config(char *, int); +int cmdline_symset(char *); + +/* 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 *); + +/* 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 *); + +/* printconf.c */ +void print_config(struct ripd_conf *); + +/* log.c */ +const char *if_state_name(int); +const char *if_auth_name(enum auth_type); +const char *nbr_state_name(int); + +/* interface.c */ +struct iface *if_find_index(u_short); + +#endif /* _RIPD_H_ */ diff --git a/usr.sbin/ripd/ripe.c b/usr.sbin/ripd/ripe.c new file mode 100644 index 00000000000..3f651bb5411 --- /dev/null +++ b/usr.sbin/ripd/ripe.c @@ -0,0 +1,504 @@ +/* $OpenBSD: ripe.c,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_types.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 "ripd.h" +#include "rip.h" +#include "ripe.h" +#include "log.h" +#include "control.h" + +void ripe_sig_handler(int, short, void *); +void ripe_shutdown(void); + +struct ripd_conf *oeconf = NULL; +struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_rde; + +/* ARGSUSED */ +void +ripe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + ripe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* rip engine */ +pid_t +ripe(struct ripd_conf *xconf, int pipe_parent2ripe[2], int pipe_ripe2rde[2], + int pipe_parent2rde[2]) +{ + struct iface *iface = NULL; + struct sockaddr_in addr; + struct passwd *pw; + struct event ev_sigint, ev_sigterm; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + return (pid); + } + + /* create ripd control socket outside chroot */ + if (control_init() == -1) + fatalx("control socket setup failed"); + + addr.sin_family = AF_INET; + addr.sin_port = htons(RIP_PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + if ((xconf->rip_socket = socket(AF_INET, SOCK_DGRAM, + IPPROTO_UDP)) == -1) + fatalx("if_init: cannot create socket"); + + if (bind(xconf->rip_socket, (struct sockaddr *)&addr, + sizeof(addr)) == -1) + fatal("error creating socket"); + + /* set some defaults */ + if (if_set_opt(xconf->rip_socket) == -1) + fatal("if_set_opt"); + + if (if_set_mcast_ttl(xconf->rip_socket, IP_DEFAULT_MULTICAST_TTL) == -1) + fatal("if_set_mcast_ttl"); + + if (if_set_mcast_loop(xconf->rip_socket) == -1) + fatal("if_set_mcast_loop"); + + if (if_set_tos(xconf->rip_socket, IPTOS_PREC_INTERNETCONTROL) == -1) + fatal("if_set_tos"); + + if_set_recvbuf(xconf->rip_socket); + + oeconf = xconf; + + if ((pw = getpwnam(RIPD_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("rip engine"); + ripd_process = PROC_RIP_ENGINE; + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + event_init(); + nbr_init(NBR_HASHSIZE); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, ripe_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, ripe_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + + /* setup pipes */ + close(pipe_parent2ripe[0]); + close(pipe_ripe2rde[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_ripe2rde[0], ripe_dispatch_rde); + imsg_init(ibuf_main, pipe_parent2ripe[1], ripe_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(&oeconf->ev, oeconf->rip_socket, EV_READ|EV_PERSIST, + recv_packet, oeconf); + event_add(&oeconf->ev, NULL); + + /* listen on ripd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("ripe"); + + /* start interfaces */ + LIST_FOREACH(iface, &xconf->iface_list, entry) { + if_init(xconf, iface); + if (if_fsm(iface, IF_EVT_UP)) + log_debug("error starting interface: %s", iface->name); + } + + evtimer_set(&oeconf->report_timer, report_timer, oeconf); + start_report_timer(); + + ripe_imsg_compose_rde(IMSG_FULL_REQUEST, 0, 0, NULL, 0); + + event_dispatch(); + + ripe_shutdown(); + /* NOTREACHED */ + return (0); +} + +/* imesg */ +int +ripe_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 +ripe_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)); +} + +/* ARGSUSED */ +void +ripe_dispatch_main(int fd, short event, void *bula) +{ + struct imsg imsg; + struct imsgbuf *ibuf = bula; + int n, link_ok; + struct kif *kif; + 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("ripe_dispatch_main: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_IFINFO: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct kif)) + fatalx("IFINFO imsg with wrong len"); + kif = imsg.data; + link_ok = (kif->flags & IFF_UP) && + (kif->link_state == LINK_STATE_UP || + (kif->link_state == LINK_STATE_UNKNOWN && + kif->media_type != IFT_CARP)); + + LIST_FOREACH(iface, &oeconf->iface_list, entry) { + if (kif->ifindex == iface->ifindex) { + iface->flags = kif->flags; + iface->linkstate = kif->link_state; + + if (link_ok) { + if_fsm(iface, IF_EVT_UP); + log_warnx("interface %s up", + iface->name); + } else { + if_fsm(iface, IF_EVT_DOWN); + log_warnx("interface %s down", + iface->name); + } + } + } + break; + case IMSG_CTL_IFINFO: + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("ripe_dispatch_main: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +/* ARGSUSED */ +void +ripe_dispatch_rde(int fd, short event, void *bula) +{ + struct rip_route *rr; + struct imsg imsg; + struct imsgbuf *ibuf = bula; + struct iface *iface; + struct nbr *nbr; + 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("ripe_dispatch_rde: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_REQUEST_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr)) + fatalx("invalid size of RDE request"); + + if ((rr = malloc(sizeof(*rr))) == NULL) + fatal("ripe_dispatch_rde"); + + memcpy(rr, imsg.data, sizeof(*rr)); + + if (imsg.hdr.peerid != 0) { + if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == + NULL) { + log_debug("unknown neighbor id %u", + imsg.hdr.peerid); + break; + } + add_entry(&nbr->rq_list, rr); + break; + } + + LIST_FOREACH(iface, &oeconf->iface_list, entry) { + add_entry(&iface->rq_list, rr); + } + break; + case IMSG_SEND_REQUEST: + if (imsg.hdr.peerid != 0) { + if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == + NULL) { + log_debug("unknown neighbor id %u", + imsg.hdr.peerid); + break; + } + send_request(&nbr->rq_list, NULL, nbr); + break; + } + + LIST_FOREACH(iface, &oeconf->iface_list, entry) { + send_request(&iface->rq_list, iface, NULL); + } + break; + case IMSG_RESPONSE_ADD: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*rr)) + fatalx("invalid size of RDE request"); + + if ((rr = malloc(sizeof(*rr))) == NULL) + fatal("ripe_dispatch_rde"); + + memcpy(rr, imsg.data, sizeof(*rr)); + + if (imsg.hdr.peerid == 0) { + LIST_FOREACH(iface, &oeconf->iface_list, + entry) { + if ((iface->addr.s_addr & + iface->mask.s_addr) != + rr->address.s_addr || + iface->mask.s_addr != + rr->mask.s_addr) + add_entry(&iface->rp_list, + rr); + } + break; + } + + if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == NULL) { + log_debug("unknown neighbor id %u", + imsg.hdr.peerid); + break; + } + iface = nbr->iface; + if ((iface->addr.s_addr & iface->mask.s_addr) != + rr->address.s_addr || + iface->mask.s_addr != rr->mask.s_addr) + add_entry(&nbr->rp_list, rr); + break; + case IMSG_SEND_RESPONSE: + if (imsg.hdr.peerid == 0) { + LIST_FOREACH(iface, &oeconf->iface_list, + entry) { + send_response(&iface->rp_list, + iface, NULL); + } + break; + } + + if ((nbr = nbr_find_peerid(imsg.hdr.peerid)) == NULL) { + log_debug("unknown neighbor id %u", + imsg.hdr.peerid); + break; + } + send_response(&nbr->rp_list, NULL, nbr); + break; + case IMSG_SEND_TRIGGERED_UPDATE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct + rip_route)) + fatalx("invalid size of RDE request"); + + rr = imsg.data; + + LIST_FOREACH(iface, &oeconf->iface_list, + entry) { + if (rr->ifindex != iface->ifindex) + send_triggered_update(iface, rr); + } + break; + case IMSG_CTL_END: + case IMSG_CTL_SHOW_RIB: + control_imsg_relay(&imsg); + break; + default: + log_debug("ripe_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +ripe_shutdown(void) +{ + struct iface *iface; + + LIST_FOREACH(iface, &oeconf->iface_list, entry) { + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + } + while ((iface = LIST_FIRST(&oeconf->iface_list)) != NULL) { + LIST_REMOVE(iface, entry); + if_del(iface); + } + + close(oeconf->rip_socket); + + /* 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(oeconf); + free(pkt_ptr); + + log_info("rip engine exiting"); + _exit(0); +} + +void +ripe_iface_ctl(struct ctl_conn *c, unsigned int idx) +{ + struct iface *iface; + struct ctl_iface *ictl; + + LIST_FOREACH(iface, &oeconf->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 +ripe_nbr_ctl(struct ctl_conn *c) +{ + struct iface *iface; + struct nbr *nbr; + struct ctl_nbr *nctl; + + LIST_FOREACH(iface, &oeconf->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/ripd/ripe.h b/usr.sbin/ripd/ripe.h new file mode 100644 index 00000000000..cf7b8a1ea94 --- /dev/null +++ b/usr.sbin/ripd/ripe.h @@ -0,0 +1,146 @@ +/* $OpenBSD: ripe.h,v 1.1 2006/10/18 16:11:58 norby Exp $ */ + +/* + * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it> + * Copyright (c) 2004, 2005 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 _RIPE_H_ +#define _RIPE_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; +char *pkt_ptr; + +/* neighbor events */ +enum nbr_event { + NBR_EVT_RESPONSE_RCVD, + NBR_EVT_REQUEST_RCVD, + NBR_EVT_TIMEOUT, + NBR_EVT_KILL_NBR, + NBR_EVT_NOTHING +}; + +/* neighbor actions */ +enum nbr_action { + NBR_ACT_STRT_TIMER, + NBR_ACT_RST_TIMER, + NBR_ACT_DEL, + NBR_ACT_NOTHING +}; + +struct nbr_failed { + LIST_ENTRY(nbr_failed) entry; + struct in_addr addr; + u_int32_t auth_seq_num; +}; + +struct nbr { + LIST_ENTRY(nbr) entry, hash; + struct event timeout_timer; + struct in_addr addr; + struct in_addr id; + + struct packet_head rq_list; + struct packet_head rp_list; + + struct iface *iface; + + u_int32_t peerid; /* unique ID in DB */ + u_int32_t auth_seq_num; + u_int16_t port; + time_t uptime; + int state; + int flags; +}; + +/* packet */ +int send_packet(struct iface *, void *, size_t, struct sockaddr_in *); +void recv_packet(int, short, void *); +int gen_rip_hdr(struct buf *, u_int8_t); + +/* interface */ +void if_init(struct ripd_conf *, struct iface *); +int if_fsm(struct iface *, enum iface_event); +int if_set_mcast(struct iface *); +int if_set_mcast_ttl(int, u_int8_t); +int if_set_mcast_loop(int); +int if_set_opt(int); +int if_set_tos(int, int); +void if_set_recvbuf(int); +struct iface *if_new(struct kif *); +void if_del(struct iface *); +const char *if_event_name(int); +const char *if_action_name(int); +int if_join_group(struct iface *, struct in_addr *); +int if_leave_group(struct iface *, struct in_addr *); +struct ctl_iface *if_to_ctl(struct iface *); + +/* communications */ +void recv_request(struct iface *, struct nbr *, char *, u_int16_t); +void recv_response(struct iface *, struct nbr *, char *, u_int16_t); +void add_entry(struct packet_head *, struct rip_route *); +int send_triggered_update(struct iface *, struct rip_route *); +int send_request(struct packet_head *, struct iface *, struct nbr *); +int send_response(struct packet_head *, struct iface *, struct nbr *); +int start_report_timer(void); +void report_timer(int, short, void *); + +/* ripe.c */ +pid_t ripe(struct ripd_conf *, int [2], int [2], int [2]); +int ripe_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int ripe_imsg_compose_rde(int, u_int32_t, pid_t, void *, + u_int16_t); +void ripe_dispatch_main(int, short, void *); +void ripe_dispatch_rde(int, short, void *); +void ripe_iface_ctl(struct ctl_conn *, unsigned int); +void ripe_nbr_ctl(struct ctl_conn *); + +/* auth.c */ +int auth_validate(char **, u_int16_t *, struct iface *, struct nbr *, + struct nbr_failed *, u_int32_t *); +int auth_gen(struct buf *, struct iface *); +int auth_add_trailer(struct buf *, struct iface *); +void md_list_add(struct auth_md_head *, u_int8_t, char *); +void md_list_copy(struct auth_md_head *, struct auth_md_head *); +void md_list_clr(struct auth_md_head *); + +/* neighbor.c */ +void nbr_init(u_int32_t); +struct nbr *nbr_new(u_int32_t, struct iface *, int); +void nbr_act_del(struct nbr *); + +struct nbr *nbr_find_ip(struct iface *, u_int32_t); +struct nbr *nbr_find_peerid(u_int32_t); +struct nbr_failed *nbr_failed_find(struct iface *, u_int32_t); +void nbr_failed_delete(struct iface *, struct nbr_failed *); + +int nbr_fsm(struct nbr *, enum nbr_event); + +void nbr_timeout_timer(int, short, void *); + +void nbr_act_delete(struct nbr *); + +const char *nbr_event_name(int); +const char *nbr_action_name(int); + +struct ctl_nbr *nbr_to_ctl(struct nbr *); + +#endif /* _RIPE_H_ */ |