diff options
34 files changed, 14830 insertions, 0 deletions
diff --git a/usr.sbin/ospf6d/Makefile b/usr.sbin/ospf6d/Makefile new file mode 100644 index 00000000000..64f3c9968aa --- /dev/null +++ b/usr.sbin/ospf6d/Makefile @@ -0,0 +1,21 @@ +# $OpenBSD: Makefile,v 1.1 2007/10/08 10:44:50 norby Exp $ + + +PROG= ospf6d +SRCS= area.c buffer.c carp.c control.c database.c hello.c \ + imsg.c in_cksum.c interface.c iso_cksum.c kroute.c lsack.c \ + lsreq.c lsupdate.c log.c neighbor.c ospf6d.c ospfe.c packet.c \ + parse.y printconf.c rde.c rde_lsdb.c rde_spf.c name2id.c + +MAN= ospf6d.8 ospf6d.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 +DPADD+= ${LIBEVENT} + +.include <bsd.prog.mk> diff --git a/usr.sbin/ospf6d/area.c b/usr.sbin/ospf6d/area.c new file mode 100644 index 00000000000..31e09e6a030 --- /dev/null +++ b/usr.sbin/ospf6d/area.c @@ -0,0 +1,117 @@ +/* $OpenBSD: area.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2007 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/tree.h> + +#include <err.h> +#include <stdlib.h> + +#include "ospf6.h" +#include "ospf6d.h" +#include "ospfe.h" +#include "rde.h" +#include "log.h" + +struct area * +area_new(void) +{ + struct area *area = NULL; + + if ((area = calloc(1, sizeof(*area))) == NULL) + errx(1, "area_new: calloc"); + + LIST_INIT(&area->iface_list); + LIST_INIT(&area->nbr_list); + RB_INIT(&area->lsa_tree); + + return (area); +} + +int +area_del(struct area *area) +{ + struct iface *iface = NULL; + struct vertex *v, *nv; + struct rde_nbr *n; + + /* area is removed so neutralize the demotion done by the area */ + if (area->active == 0) + ospfe_demote_area(area, 1); + + /* clean lists */ + while ((iface = LIST_FIRST(&area->iface_list)) != NULL) { + LIST_REMOVE(iface, entry); + if_del(iface); + } + + while ((n = LIST_FIRST(&area->nbr_list)) != NULL) + rde_nbr_del(n); + + for (v = RB_MIN(lsa_tree, &area->lsa_tree); v != NULL; v = nv) { + nv = RB_NEXT(lsa_tree, &area->lsa_tree, v); + vertex_free(v); + } + + free(area); + + return (0); +} + +struct area * +area_find(struct ospfd_conf *conf, struct in_addr area_id) +{ + struct area *area; + + LIST_FOREACH(area, &conf->area_list, entry) { + if (area->id.s_addr == area_id.s_addr) { + return (area); + } + } + + return (NULL); +} + +void +area_track(struct area *area, int state) +{ + int old = area->active; + + if (state & NBR_STA_FULL) + area->active++; + else if (area->active == 0) + fatalx("area_track: area already inactive"); + else + area->active--; + + if (area->active == 0 || old == 0) + ospfe_demote_area(area, old == 0); +} + +int +area_border_router(struct ospfd_conf *conf) +{ + struct area *area; + int active = 0; + + LIST_FOREACH(area, &conf->area_list, entry) + if (area->active > 0) + active++; + + return (active > 1); +} diff --git a/usr.sbin/ospf6d/buffer.c b/usr.sbin/ospf6d/buffer.c new file mode 100644 index 00000000000..033ea9daef5 --- /dev/null +++ b/usr.sbin/ospf6d/buffer.c @@ -0,0 +1,218 @@ +/* $OpenBSD: buffer.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.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/ospf6d/carp.c b/usr.sbin/ospf6d/carp.c new file mode 100644 index 00000000000..d34c277deab --- /dev/null +++ b/usr.sbin/ospf6d/carp.c @@ -0,0 +1,181 @@ +/* $OpenBSD: carp.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2006 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/ioctl.h> +#include <net/if.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "ospf6d.h" +#include "log.h" + +struct carpgroup { + TAILQ_ENTRY(carpgroup) entry; + char *group; + int do_demote; + int changed_by; +}; + +TAILQ_HEAD(carpgroups, carpgroup) carpgroups = + TAILQ_HEAD_INITIALIZER(carpgroups); + +struct carpgroup *carp_group_find(char *group); +int carp_demote_ioctl(char *, int); + +struct carpgroup * +carp_group_find(char *group) +{ + struct carpgroup *c; + + TAILQ_FOREACH(c, &carpgroups, entry) + if (!strcmp(c->group, group)) + return (c); + + return (NULL); +} + +int +carp_demote_init(char *group, int force) +{ + struct carpgroup *c; + int level; + + if ((c = carp_group_find(group)) == NULL) { + if ((c = calloc(1, sizeof(struct carpgroup))) == NULL) { + log_warn("carp_demote_init calloc"); + return (-1); + } + if ((c->group = strdup(group)) == NULL) { + log_warn("carp_demote_init strdup"); + free(c); + return (-1); + } + + /* only demote if this group already is demoted */ + if ((level = carp_demote_get(group)) == -1) + return (-1); + if (level > 0 || force) + c->do_demote = 1; + + TAILQ_INSERT_TAIL(&carpgroups, c, entry); + } + + return (0); +} + +void +carp_demote_shutdown(void) +{ + struct carpgroup *c; + + while ((c = TAILQ_FIRST(&carpgroups)) != NULL) { + TAILQ_REMOVE(&carpgroups, c, entry); + for (; c->changed_by > 0; c->changed_by--) + if (c->do_demote) + carp_demote_ioctl(c->group, -1); + + free(c->group); + free(c); + } +} + +int +carp_demote_get(char *group) +{ + int s; + struct ifgroupreq ifgr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + log_warn("carp_demote_get: socket"); + return (-1); + } + + bzero(&ifgr, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, group, sizeof(ifgr.ifgr_name)); + + if (ioctl(s, SIOCGIFGATTR, (caddr_t)&ifgr) == -1) { + if (errno == ENOENT) + log_warnx("group \"%s\" does not exist", group); + else + log_warn("carp_demote_get: ioctl"); + close(s); + return (-1); + } + + close(s); + return ((int)ifgr.ifgr_attrib.ifg_carp_demoted); +} + +int +carp_demote_set(char *group, int demote) +{ + struct carpgroup *c; + + if ((c = carp_group_find(group)) == NULL) { + log_warnx("carp_group_find for %s returned NULL?!", group); + return (-1); + } + + if (c->changed_by + demote < 0) { + log_warnx("carp_demote_set: changed_by + demote < 0"); + return (-1); + } + + if (c->do_demote && carp_demote_ioctl(group, demote) == -1) + return (-1); + + c->changed_by += demote; + + /* enable demotion when we return to 0, i. e. all sessions up */ + if (demote < 0 && c->changed_by == 0) + c->do_demote = 1; + + return (0); +} + +int +carp_demote_ioctl(char *group, int demote) +{ + int s, res; + struct ifgroupreq ifgr; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + log_warn("carp_demote_get: socket"); + return (-1); + } + + bzero(&ifgr, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, group, sizeof(ifgr.ifgr_name)); + ifgr.ifgr_attrib.ifg_carp_demoted = demote; + + if ((res = ioctl(s, SIOCSIFGATTR, (caddr_t)&ifgr)) == -1) + log_warn("unable to %s the demote state " + "of group '%s'", (demote > 0) ? "increment" : "decrement", + group); + else + log_info("%s the demote state of group '%s'", + (demote > 0) ? "incremented" : "decremented", group); + + close(s); + return (res); +} diff --git a/usr.sbin/ospf6d/control.c b/usr.sbin/ospf6d/control.c new file mode 100644 index 00000000000..f7b14cfb4fc --- /dev/null +++ b/usr.sbin/ospf6d/control.c @@ -0,0 +1,303 @@ +/* $OpenBSD: control.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.h" +#include "ospf6.h" +#include "ospfe.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, OSPF6D_SOCKET, sizeof(sun.sun_path)); + + if (unlink(OSPF6D_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("control_init: unlink %s", OSPF6D_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", OSPF6D_SOCKET); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(OSPF6D_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("control_init: chmod"); + close(fd); + (void)unlink(OSPF6D_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(OSPF6D_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)) == -1 || n == 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_FIB_COUPLE: + case IMSG_CTL_FIB_DECOUPLE: + ospfe_fip_update(imsg.hdr.type); + /* FALLTHROUGH */ + case IMSG_CTL_RELOAD: + c->ibuf.pid = imsg.hdr.pid; + ospfe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + c->ibuf.pid = imsg.hdr.pid; + ospfe_imsg_compose_parent(imsg.hdr.type, + imsg.hdr.pid, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_INTERFACE: + if (imsg.hdr.len == IMSG_HEADER_SIZE + + sizeof(ifidx)) { + memcpy(&ifidx, imsg.data, sizeof(ifidx)); + ospfe_iface_ctl(c, ifidx); + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, + 0, NULL, 0); + } + break; + case IMSG_CTL_SHOW_DATABASE: + case IMSG_CTL_SHOW_DB_EXT: + case IMSG_CTL_SHOW_DB_NET: + case IMSG_CTL_SHOW_DB_RTR: + case IMSG_CTL_SHOW_DB_SELF: + case IMSG_CTL_SHOW_DB_SUM: + case IMSG_CTL_SHOW_DB_ASBR: + case IMSG_CTL_SHOW_RIB: + case IMSG_CTL_SHOW_SUM: + c->ibuf.pid = imsg.hdr.pid; + ospfe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid, + imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_NBR: + ospfe_nbr_ctl(c); + 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("fcntl F_GETFL"); + + if (bm == BM_NONBLOCK) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + fatal("fcntl F_SETFL"); +} diff --git a/usr.sbin/ospf6d/control.h b/usr.sbin/ospf6d/control.h new file mode 100644 index 00000000000..7d753010700 --- /dev/null +++ b/usr.sbin/ospf6d/control.h @@ -0,0 +1,50 @@ +/* $OpenBSD: control.h,v 1.1 2007/10/08 10:44:50 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/ospf6d/database.c b/usr.sbin/ospf6d/database.c new file mode 100644 index 00000000000..955bc469b40 --- /dev/null +++ b/usr.sbin/ospf6d/database.c @@ -0,0 +1,422 @@ +/* $OpenBSD: database.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +extern struct ospfd_conf *oeconf; + +void db_sum_list_next(struct nbr *); + +/* database description packet handling */ +int +send_db_description(struct nbr *nbr) +{ + struct sockaddr_in6 dst; + struct db_dscrp_hdr dd_hdr; + struct lsa_entry *le, *nle; + struct buf *buf; + int ret = 0; + + if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_db_description"); + + /* OSPF header */ + if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_DD)) + goto fail; + + /* reserve space for database description header */ + if (buf_reserve(buf, sizeof(dd_hdr)) == NULL) + goto fail; + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_SNAP: + log_debug("send_db_description: cannot send packet in state %s," + " neighbor ID %s", nbr_state_name(nbr->state), + inet_ntoa(nbr->id)); + ret = -1; + goto done; + case NBR_STA_XSTRT: + nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I; + break; + case NBR_STA_XCHNG: + if (nbr->master) { + /* master */ + nbr->options |= OSPF_DBD_MS; + } else { + /* slave */ + nbr->options &= ~OSPF_DBD_MS; + } + + if (TAILQ_EMPTY(&nbr->db_sum_list)) + nbr->options &= ~OSPF_DBD_M; + else + nbr->options |= OSPF_DBD_M; + + nbr->options &= ~OSPF_DBD_I; + + /* build LSA list, keep space for a possible md5 sum */ + for (le = TAILQ_FIRST(&nbr->db_sum_list); le != NULL && + buf->wpos + sizeof(struct lsa_hdr) < buf->max - + MD5_DIGEST_LENGTH; le = nle) { + nbr->dd_end = nle = TAILQ_NEXT(le, entry); + if (buf_add(buf, le->le_lsa, sizeof(struct lsa_hdr))) + goto fail; + } + break; + case NBR_STA_LOAD: + case NBR_STA_FULL: + if (nbr->master) { + /* master */ + nbr->options |= OSPF_DBD_MS; + } else { + /* slave */ + nbr->options &= ~OSPF_DBD_MS; + } + nbr->options &= ~OSPF_DBD_M; + nbr->options &= ~OSPF_DBD_I; + + break; + default: + fatalx("send_db_description: unknown neighbor state"); + } + + /* set destination */ + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + + switch (nbr->iface->type) { + case IF_TYPE_POINTOPOINT: + inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr); + dd_hdr.iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_BROADCAST: + dst.sin6_addr = nbr->addr; + dd_hdr.iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + /* XXX not supported */ + break; + case IF_TYPE_VIRTUALLINK: + dst.sin6_addr = nbr->iface->dst; + dd_hdr.iface_mtu = 0; + break; + default: + fatalx("send_db_description: unknown interface type"); + } + + dd_hdr.opts = oeconf->options; + dd_hdr.bits = nbr->options; + dd_hdr.dd_seq_num = htonl(nbr->dd_seq_num); + + memcpy(buf_seek(buf, sizeof(struct ospf_hdr), sizeof(dd_hdr)), + &dd_hdr, sizeof(dd_hdr)); + + /* calculate checksum */ + if (upd_ospf_hdr(buf, nbr->iface)) + goto fail; + + /* transmit packet */ + ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst); +done: + buf_free(buf); + return (ret); +fail: + log_warn("send_db_description"); + buf_free(buf); + return (-1); +} + +void +recv_db_description(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct db_dscrp_hdr dd_hdr; + int dupe = 0; + + if (len < sizeof(dd_hdr)) { + log_warnx("recv_dd_description: " + "bad packet size, neighbor ID %s", inet_ntoa(nbr->id)); + return; + } + memcpy(&dd_hdr, buf, sizeof(dd_hdr)); + buf += sizeof(dd_hdr); + len -= sizeof(dd_hdr); + + /* db description packet sanity checks */ + if (ntohs(dd_hdr.iface_mtu) > nbr->iface->mtu) { + log_warnx("recv_dd_description: invalid MTU %d sent by " + "neighbor ID %s, expected %d", ntohs(dd_hdr.iface_mtu), + inet_ntoa(nbr->id), nbr->iface->mtu); + return; + } + + if (nbr->last_rx_options == dd_hdr.opts && + nbr->last_rx_bits == dd_hdr.bits && + ntohl(dd_hdr.dd_seq_num) == nbr->dd_seq_num - nbr->master ? 1 : 0) { + log_debug("recv_db_description: dupe"); + dupe = 1; + } + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_2_WAY: + case NBR_STA_SNAP: + log_debug("recv_db_description: packet ignored in state %s, " + "neighbor ID %s", nbr_state_name(nbr->state), + inet_ntoa(nbr->id)); + return; + case NBR_STA_INIT: + /* evaluate dr and bdr after issuing a 2-Way event */ + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + if_fsm(nbr->iface, IF_EVT_NBR_CHNG); + if (nbr->state != NBR_STA_XSTRT) + return; + /* FALLTHROUGH */ + case NBR_STA_XSTRT: + if (dupe) + return; + /* + * check bits: either I,M,MS or only M + */ + if (dd_hdr.bits == (OSPF_DBD_I | OSPF_DBD_M | OSPF_DBD_MS)) { + /* if nbr Router ID is larger than own -> slave */ + if ((ntohl(nbr->id.s_addr)) > + ntohl(ospfe_router_id())) { + /* slave */ + nbr->master = 0; + nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num); + + /* event negotiation done */ + nbr_fsm(nbr, NBR_EVT_NEG_DONE); + } + } else if (!(dd_hdr.bits & (OSPF_DBD_I | OSPF_DBD_MS))) { + /* M only case: we are master */ + if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) { + log_warnx("recv_db_description: invalid " + "seq num, mine %x his %x", + nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num)); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + nbr->dd_seq_num++; + + /* this packet may already have data so pass it on */ + if (len > 0) { + nbr->dd_pending++; + ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid, + 0, buf, len); + } + + /* event negotiation done */ + nbr_fsm(nbr, NBR_EVT_NEG_DONE); + + } else { + /* ignore packet */ + log_debug("recv_db_description: packet ignored in " + "state %s (bad flags), neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + } + break; + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + if (dd_hdr.bits & OSPF_DBD_I || + !(dd_hdr.bits & OSPF_DBD_MS) == !nbr->master) { + log_warnx("recv_db_description: seq num mismatch, " + "bad flags"); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + + if (nbr->last_rx_options != dd_hdr.opts) { + log_warnx("recv_db_description: seq num mismatch, " + "bad options"); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + + if (dupe) { + if (!nbr->master) + /* retransmit */ + start_db_tx_timer(nbr); + return; + } + + if (nbr->state != NBR_STA_XCHNG) { + log_warnx("recv_db_description: invalid " + "seq num, mine %x his %x", + nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num)); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + + /* sanity check dd seq number */ + if (nbr->master) { + /* master */ + if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) { + log_warnx("recv_db_description: invalid " + "seq num, mine %x his %x", + nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num)); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + nbr->dd_seq_num++; + } else { + /* slave */ + if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num + 1) { + log_warnx("recv_db_description: invalid " + "seq num, mine %x his %x", + nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num)); + nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS); + return; + } + nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num); + } + + /* forward to RDE and let it decide which LSAs to request */ + if (len > 0) { + nbr->dd_pending++; + ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid, 0, + buf, len); + } + + /* next packet */ + db_sum_list_next(nbr); + start_db_tx_timer(nbr); + + if (!(dd_hdr.bits & OSPF_DBD_M) && + TAILQ_EMPTY(&nbr->db_sum_list)) + if (!nbr->master || !(nbr->options & OSPF_DBD_M)) + nbr_fsm(nbr, NBR_EVT_XCHNG_DONE); + break; + default: + fatalx("recv_db_description: unknown neighbor state"); + } + + nbr->last_rx_options = dd_hdr.opts; + nbr->last_rx_bits = dd_hdr.bits; +} + +void +db_sum_list_add(struct nbr *nbr, struct lsa_hdr *lsa) +{ + struct lsa_entry *le; + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("db_sum_list_add"); + + TAILQ_INSERT_TAIL(&nbr->db_sum_list, le, entry); + le->le_lsa = lsa; +} + +void +db_sum_list_next(struct nbr *nbr) +{ + struct lsa_entry *le; + + while ((le = TAILQ_FIRST(&nbr->db_sum_list)) != nbr->dd_end) { + TAILQ_REMOVE(&nbr->db_sum_list, le, entry); + free(le->le_lsa); + free(le); + } +} + +void +db_sum_list_clr(struct nbr *nbr) +{ + nbr->dd_end = NULL; + db_sum_list_next(nbr); +} + +/* timers */ +/* ARGSUSED */ +void +db_tx_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_SNAP: + return ; + case NBR_STA_XSTRT: + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + send_db_description(nbr); + break; + default: + log_debug("db_tx_timer: unknown neighbor state, " + "neighbor ID %s", inet_ntoa(nbr->id)); + break; + } + + /* reschedule db_tx_timer but only in master mode */ + if (nbr->master) { + timerclear(&tv); + tv.tv_sec = nbr->iface->rxmt_interval; + if (evtimer_add(&nbr->db_tx_timer, &tv) == -1) + fatal("db_tx_timer"); + } +} + +void +start_db_tx_timer(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return; + + timerclear(&tv); + if (evtimer_add(&nbr->db_tx_timer, &tv) == -1) + fatal("start_db_tx_timer"); +} + +void +stop_db_tx_timer(struct nbr *nbr) +{ + if (nbr == nbr->iface->self) + return; + + if (evtimer_del(&nbr->db_tx_timer) == -1) + fatal("stop_db_tx_timer"); +} diff --git a/usr.sbin/ospf6d/hello.c b/usr.sbin/ospf6d/hello.c new file mode 100644 index 00000000000..4e3f3d42710 --- /dev/null +++ b/usr.sbin/ospf6d/hello.c @@ -0,0 +1,281 @@ +/* $OpenBSD: hello.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * 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/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +extern struct ospfd_conf *oeconf; + +/* hello packet handling */ +int +send_hello(struct iface *iface) +{ + struct sockaddr_in6 dst; + struct hello_hdr hello; + struct nbr *nbr; + struct buf *buf; + int ret; + + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + log_debug("send_hello: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + case IF_TYPE_VIRTUALLINK: + dst.sin6_addr = iface->dst; + break; + default: + fatalx("send_hello: unknown interface type"); + } + + /* XXX READ_BUF_SIZE */ + if ((buf = buf_dynamic(PKG_DEF_SIZE, READ_BUF_SIZE)) == NULL) + fatal("send_hello"); + + /* OSPF header */ + if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) + goto fail; + + /* hello header */ + hello.iface_id = iface->ifindex; + hello.rtr_priority = iface->priority; + + /* XXX options */ + hello.opts1 = 0; + hello.opts2 = 0; + hello.opts3 = 0; + hello.opts3 |= OSPF_OPTION_R; /* XXX */ + + hello.hello_interval = htons(iface->hello_interval); + hello.rtr_dead_interval = htons(iface->dead_interval); + + if (iface->dr) { + hello.d_rtr = iface->dr->id.s_addr; + iface->self->dr.s_addr = iface->dr->id.s_addr; + } else + hello.d_rtr = 0; + if (iface->bdr) { + hello.bd_rtr = iface->bdr->id.s_addr; + iface->self->bdr.s_addr = iface->bdr->id.s_addr; + } else + hello.bd_rtr = 0; + + if (buf_add(buf, &hello, sizeof(hello))) + goto fail; + + /* active neighbor(s) */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) + if (buf_add(buf, &nbr->id, sizeof(nbr->id))) + goto fail; + } + + /* calculate checksum */ + if (upd_ospf_hdr(buf, iface)) + goto fail; + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + + buf_free(buf); + return (ret); +fail: + log_warn("send_hello"); + buf_free(buf); + return (-1); +} + +void +recv_hello(struct iface *iface, struct in6_addr src, u_int32_t rtr_id, + char *buf, u_int16_t len) +{ + struct hello_hdr hello; + struct nbr *nbr = NULL, *dr; + u_int32_t nbr_id; + int nbr_change = 0; + + if (len < sizeof(hello) && (len & 0x03)) { + log_warnx("recv_hello: bad packet size, interface %s", + iface->name); + return; + } + + memcpy(&hello, buf, sizeof(hello)); + buf += sizeof(hello); + len -= sizeof(hello); + +#if 0 +XXX + if (iface->type != IF_TYPE_POINTOPOINT && + iface->type != IF_TYPE_VIRTUALLINK) + if (hello.mask != iface->mask.s_addr) { + log_warnx("recv_hello: invalid netmask, interface %s", + iface->name); + return; + } +#endif + + if (ntohs(hello.hello_interval) != iface->hello_interval) { + log_warnx("recv_hello: invalid hello-interval %d, " + "interface %s", ntohs(hello.hello_interval), + iface->name); + return; + } + + if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) { + log_warnx("recv_hello: invalid router-dead-interval %d, " + "interface %s", ntohl(hello.rtr_dead_interval), + iface->name); + return; + } + + if ((hello.opts3 & OSPF_OPTION_E && iface->area->stub) || /* XXX */ + ((hello.opts3 & OSPF_OPTION_E) == 0 && !iface->area->stub)) { /* XXX */ + log_warnx("recv_hello: ExternalRoutingCapability mismatch, " + "interface %s", iface->name); + return; + } + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_VIRTUALLINK: + /* match router-id */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + if (nbr->id.s_addr == rtr_id) + break; + } + break; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + /* match src IP */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; +//XXX if (nbr->addr.s_addr == src.s_addr) + if (IN6_ARE_ADDR_EQUAL(&nbr->addr, &src)) + break; + } + break; + default: + fatalx("recv_hello: unknown interface type"); + } + + if (!nbr) { + nbr = nbr_new(rtr_id, iface, 0); + /* set neighbor parameters */ + nbr->dr.s_addr = hello.d_rtr; + nbr->bdr.s_addr = hello.bd_rtr; + nbr->priority = hello.rtr_priority; + nbr_change = 1; + } + + /* actually the neighbor address shouldn't be stored on virtual links */ +//XXX nbr->addr.s_addr = src.s_addr; + nbr->addr = src; + nbr->options = hello.opts3; /* XXX */ + + nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); + + while (len >= sizeof(nbr_id)) { + memcpy(&nbr_id, buf, sizeof(nbr_id)); + if (nbr_id == ospfe_router_id()) { + /* seen myself */ + if (nbr->state & NBR_STA_PRELIM) + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + break; + } + buf += sizeof(nbr_id); + len -= sizeof(nbr_id); + } + + if (len == 0) { + nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); + /* set neighbor parameters */ + nbr->dr.s_addr = hello.d_rtr; + nbr->bdr.s_addr = hello.bd_rtr; + nbr->priority = hello.rtr_priority; + return; + } + + if (nbr->priority != hello.rtr_priority) { + nbr->priority = hello.rtr_priority; + nbr_change = 1; + } + + if (iface->state & IF_STA_WAITING && +//XXX hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0) + hello.d_rtr == nbr->id.s_addr && hello.bd_rtr == 0) + if_fsm(iface, IF_EVT_BACKUP_SEEN); + +//XXX if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) { + if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->id.s_addr) { + /* + * In case we see the BDR make sure that the DR is around + * with a bidirectional (2_WAY or better) connection + */ + LIST_FOREACH(dr, &iface->nbr_list, entry) +//XXX if (hello.d_rtr == dr->addr.s_addr && + if (hello.d_rtr == dr->id.s_addr && + dr->state & NBR_STA_BIDIR) + if_fsm(iface, IF_EVT_BACKUP_SEEN); + } +#if 0 + if ((nbr->addr.s_addr == nbr->dr.s_addr && + nbr->addr.s_addr != hello.d_rtr) || + (nbr->addr.s_addr != nbr->dr.s_addr && + nbr->addr.s_addr == hello.d_rtr)) + /* neighbor changed from or to DR */ + nbr_change = 1; + if ((nbr->addr.s_addr == nbr->bdr.s_addr && + nbr->addr.s_addr != hello.bd_rtr) || + (nbr->addr.s_addr != nbr->bdr.s_addr && + nbr->addr.s_addr == hello.bd_rtr)) + /* neighbor changed from or to BDR */ + nbr_change = 1; +#endif + nbr->dr.s_addr = hello.d_rtr; + nbr->bdr.s_addr = hello.bd_rtr; + + if (nbr_change) + if_fsm(iface, IF_EVT_NBR_CHNG); + + /* TODO NBMA needs some special handling */ +} diff --git a/usr.sbin/ospf6d/imsg.c b/usr.sbin/ospf6d/imsg.c new file mode 100644 index 00000000000..1af8491ec67 --- /dev/null +++ b/usr.sbin/ospf6d/imsg.c @@ -0,0 +1,181 @@ +/* $OpenBSD: imsg.c,v 1.1 2007/10/08 10:44:50 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 "ospf6d.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 (-2); + } + + 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; + + datalen += IMSG_HEADER_SIZE; + if (datalen > MAX_IMSGSIZE) { + log_warnx("imsg_create: len %u > MAX_IMSGSIZE; " + "type %u peerid %lu", datalen + IMSG_HEADER_SIZE, + type, peerid); + return (NULL); + } + + hdr.type = type; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = buf_dynamic(datalen, MAX_IMSGSIZE)) == 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; + struct imsg_hdr *hdr; + + hdr = (struct imsg_hdr *)msg->buf; + hdr->len = (u_int16_t)msg->wpos; + 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/ospf6d/in_cksum.c b/usr.sbin/ospf6d/in_cksum.c new file mode 100644 index 00000000000..5fe4c3cdb3f --- /dev/null +++ b/usr.sbin/ospf6d/in_cksum.c @@ -0,0 +1,82 @@ +/* $OpenBSD: in_cksum.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ +/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */ + +/* + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL) + */ + +#include <sys/types.h> + +#include "ospf6d.h" +#include "log.h" + +/* + * Checksum routine for Internet Protocol family headers. + * This routine is very heavily used in the network + * code and should be modified for each CPU to be as fast as possible. + * In particular, it should not be this one. + */ +u_int16_t +in_cksum(void *p, size_t l) +{ + unsigned int sum = 0; + int len; + u_char *cp = p; + + /* ensure that < 2^16 bytes being summed */ + if (l >= (1 << 16)) + fatalx("in_cksum: packet to big"); + len = (int)l; + + if (((long)cp & 1) == 0) { + while (len > 1) { + sum += htons(*(u_short *)cp); + cp += 2; + len -= 2; + } + } else { + while (len > 1) { + sum += *cp++ << 8; + sum += *cp++; + len -= 2; + } + } + if (len == 1) + sum += *cp << 8; + + sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */ + sum += sum >> 16; /* add potential last carry */ + sum = ntohs(sum); + return (0xffff & ~sum); +} diff --git a/usr.sbin/ospf6d/interface.c b/usr.sbin/ospf6d/interface.c new file mode 100644 index 00000000000..be415114742 --- /dev/null +++ b/usr.sbin/ospf6d/interface.c @@ -0,0 +1,803 @@ +/* $OpenBSD: interface.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2007 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 "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +void if_hello_timer(int, short, void *); +void if_start_hello_timer(struct iface *); +void if_stop_hello_timer(struct iface *); +void if_stop_wait_timer(struct iface *); +void if_wait_timer(int, short, void *); +void if_start_wait_timer(struct iface *); +void if_stop_wait_timer(struct iface *); +struct nbr *if_elect(struct nbr *, struct nbr *); + +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_WAITING, IF_EVT_BACKUP_SEEN, IF_ACT_ELECT, 0}, + {IF_STA_WAITING, IF_EVT_WTIMER, IF_ACT_ELECT, 0}, + {IF_STA_ANY, IF_EVT_WTIMER, IF_ACT_NOTHING, 0}, + {IF_STA_WAITING, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0}, + {IF_STA_MULTI, IF_EVT_NBR_CHNG, IF_ACT_ELECT, 0}, + {IF_STA_ANY, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0}, + {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN}, + {IF_STA_ANY, IF_EVT_LOOP, IF_ACT_RST, IF_STA_LOOPBACK}, + {IF_STA_LOOPBACK, IF_EVT_UNLOOP, IF_ACT_NOTHING, IF_STA_DOWN}, + {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0}, +}; + +static int vlink_cnt = 0; + +const char * const if_event_names[] = { + "NOTHING", + "UP", + "WAITTIMER", + "BACKUPSEEN", + "NEIGHBORCHANGE", + "LOOP", + "UNLOOP", + "DOWN" +}; + +const char * const if_action_names[] = { + "NOTHING", + "START", + "ELECT", + "RESET" +}; + +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_names[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_ELECT: + ret = if_act_elect(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_names[event], + if_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + iface->state = new_state; + + if (iface->state != old_state) + orig_rtr_lsa(iface->area); + + if (old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT) && + (iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0) + ospfe_demote_iface(iface, 0); + if ((old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0 && + iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) + ospfe_demote_iface(iface, 1); + + log_debug("if_fsm: event %s resulted in action %s and changing " + "state for interface %s from %s to %s", + if_event_names[event], if_action_names[iface_fsm[i].action], + iface->name, if_state_name(old_state), if_state_name(iface->state)); + + return (ret); +} + +struct iface * +if_new(struct kif *kif, struct kif_addr *ka) +{ + struct iface *iface; + + 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->ls_ack_list); + + iface->crypt_seq_num = arc4random() & 0x0fffffff; + + if (kif == NULL) { + iface->type = IF_TYPE_VIRTUALLINK; + snprintf(iface->name, sizeof(iface->name), "vlink%d", + vlink_cnt++); + iface->flags |= IFF_UP; + iface->mtu = IP_MSS; + return (iface); + } + + strlcpy(iface->name, kif->ifname, sizeof(iface->name)); + + /* 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; + iface->state = IF_STA_LOOPBACK; + } + + /* 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; + iface->baudrate = kif->baudrate; + + /* set address, mask and p2p addr */ + iface->addr = ka->addr; + iface->mask = ka->mask; + if (kif->flags & IFF_POINTOPOINT) { + iface->dst = ka->dstbrd; + } + + return (iface); +} + +void +if_del(struct iface *iface) +{ + struct nbr *nbr = NULL; + + log_debug("if_del: interface %s", iface->name); + + /* revert the demotion when the interface is deleted */ + if ((iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0) + ospfe_demote_iface(iface, 1); + + /* clear lists etc */ + while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) + nbr_del(nbr); + + if (evtimer_pending(&iface->hello_timer, NULL)) + evtimer_del(&iface->hello_timer); + if (evtimer_pending(&iface->wait_timer, NULL)) + evtimer_del(&iface->wait_timer); + if (evtimer_pending(&iface->lsack_tx_timer, NULL)) + evtimer_del(&iface->lsack_tx_timer); + + ls_ack_list_clr(iface); + free(iface); +} + +void +if_init(struct ospfd_conf *xconf, struct iface *iface) +{ + /* init the dummy local neighbor */ + iface->self = nbr_new(ospfe_router_id(), iface, 1); + + /* set event handlers for interface */ + evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface); + evtimer_set(&iface->hello_timer, if_hello_timer, iface); + evtimer_set(&iface->wait_timer, if_wait_timer, iface); + + iface->fd = xconf->ospf_socket; + + ospfe_demote_iface(iface, 0); +} + +/* timers */ +/* ARGSUSED */ +void +if_hello_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + struct timeval tv; + + send_hello(iface); + + /* reschedule hello_timer */ + timerclear(&tv); + tv.tv_sec = iface->hello_interval; + if (evtimer_add(&iface->hello_timer, &tv) == -1) + fatal("if_hello_timer"); +} + +void +if_start_hello_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + if (evtimer_add(&iface->hello_timer, &tv) == -1) + fatal("if_start_hello_timer"); +} + +void +if_stop_hello_timer(struct iface *iface) +{ + if (evtimer_del(&iface->hello_timer) == -1) + fatal("if_stop_hello_timer"); +} + +/* ARGSUSED */ +void +if_wait_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + + if_fsm(iface, IF_EVT_WTIMER); +} + +void +if_start_wait_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = iface->dead_interval; + if (evtimer_add(&iface->wait_timer, &tv) == -1) + fatal("if_start_wait_timer"); +} + +void +if_stop_wait_timer(struct iface *iface) +{ + if (evtimer_del(&iface->wait_timer) == -1) + fatal("if_stop_wait_timer"); +} + +/* actions */ +int +if_act_start(struct iface *iface) +{ + struct in6_addr addr; + struct timeval now; + + if (!((iface->flags & IFF_UP) && + (LINK_STATE_IS_UP(iface->linkstate) || + (iface->linkstate == LINK_STATE_UNKNOWN && + iface->media_type != IFT_CARP)))) { + log_debug("if_act_start: interface %s link down", + iface->name); + return (0); + } + + if (iface->media_type == IFT_CARP && iface->passive == 0) { + /* force passive mode on carp interfaces */ + log_warnx("if_act_start: forcing interface %s to passive", + iface->name); + iface->passive = 1; + } + + if (iface->passive) { + /* for an update of stub network entries */ + orig_rtr_lsa(iface->area); + return (0); + } + + gettimeofday(&now, NULL); + iface->uptime = now.tv_sec; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + inet_pton(AF_INET6, AllSPFRouters, &addr); + + if (if_join_group(iface, &addr)) + return (-1); + iface->state = IF_STA_POINTTOPOINT; + break; + case IF_TYPE_VIRTUALLINK: + iface->state = IF_STA_POINTTOPOINT; + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_NBMA: + log_debug("if_act_start: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + case IF_TYPE_BROADCAST: + inet_pton(AF_INET6, AllSPFRouters, &addr); + + if (if_join_group(iface, &addr)) + return (-1); + if (iface->priority == 0) { + iface->state = IF_STA_DROTHER; + } else { + iface->state = IF_STA_WAITING; + if_start_wait_timer(iface); + } + break; + default: + fatalx("if_act_start: unknown interface type"); + } + + /* hello timer needs to be started in any case */ + if_start_hello_timer(iface); + return (0); +} + +struct nbr * +if_elect(struct nbr *a, struct nbr *b) +{ + if (a->priority > b->priority) + return (a); + if (a->priority < b->priority) + return (b); + if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr)) + return (a); + return (b); +} + +int +if_act_elect(struct iface *iface) +{ + struct in6_addr addr; + struct nbr *nbr, *bdr = NULL, *dr = NULL; + int round = 0; + int changed = 0; + int old_state; + char b1[16], b2[16], b3[16], b4[16]; + +start: + /* elect backup designated router */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->priority == 0 || nbr == dr || /* not electable */ + nbr->state & NBR_STA_PRELIM || /* not available */ + nbr->dr.s_addr == nbr->id.s_addr) /* don't elect DR */ + continue; + if (bdr != NULL) { + /* + * routers announcing themselves as BDR have higher + * precedence over those routers announcing a + * different BDR. + */ + if (nbr->bdr.s_addr == nbr->id.s_addr) { + if (bdr->bdr.s_addr == bdr->id.s_addr) + bdr = if_elect(bdr, nbr); + else + bdr = nbr; + } else if (bdr->bdr.s_addr != bdr->id.s_addr) + bdr = if_elect(bdr, nbr); + } else + bdr = nbr; + } + + /* elect designated router */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM || + (nbr != dr && nbr->dr.s_addr != nbr->id.s_addr)) + /* only DR may be elected check priority too */ + continue; + if (dr == NULL) + dr = nbr; + else + dr = if_elect(dr, nbr); + } + + if (dr == NULL) { + /* no designate router found use backup DR */ + dr = bdr; + bdr = NULL; + } + + /* + * if we are involved in the election (e.g. new DR or no + * longer BDR) redo the election + */ + if (round == 0 && + ((iface->self == dr && iface->self != iface->dr) || + (iface->self != dr && iface->self == iface->dr) || + (iface->self == bdr && iface->self != iface->bdr) || + (iface->self != bdr && iface->self == iface->bdr))) { + /* + * Reset announced DR/BDR to calculated one, so + * that we may get elected in the second round. + * This is needed to drop from a DR to a BDR. + */ + iface->self->dr.s_addr = dr->id.s_addr; + if (bdr) + iface->self->bdr.s_addr = bdr->id.s_addr; + round = 1; + goto start; + } + + log_debug("if_act_elect: interface %s old dr %s new dr %s, " + "old bdr %s new bdr %s", iface->name, + iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) : + "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none", + iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) : + "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) : + "none"); + + /* + * After the second round still DR or BDR change state to DR or BDR, + * etc. + */ + old_state = iface->state; + if (dr == iface->self) + iface->state = IF_STA_DR; + else if (bdr == iface->self) + iface->state = IF_STA_BACKUP; + else + iface->state = IF_STA_DROTHER; + + /* TODO if iface is NBMA send all non eligible neighbors event Start */ + + /* + * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way + */ + if (iface->dr != dr || iface->bdr != bdr) + changed = 1; + + iface->dr = dr; + iface->bdr = bdr; + + if (changed) { + inet_pton(AF_INET6, AllDRouters, &addr); + if (old_state & IF_STA_DRORBDR && + (iface->state & IF_STA_DRORBDR) == 0) { + if (if_leave_group(iface, &addr)) + return (-1); + } else if ((old_state & IF_STA_DRORBDR) == 0 && + iface->state & IF_STA_DRORBDR) { + if (if_join_group(iface, &addr)) + return (-1); + } + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->state & NBR_STA_BIDIR) + nbr_fsm(nbr, NBR_EVT_ADJ_OK); + } + + orig_rtr_lsa(iface->area); + if (iface->state & IF_STA_DR || old_state & IF_STA_DR) + orig_net_lsa(iface); + } + + if_start_hello_timer(iface); + return (0); +} + +int +if_act_reset(struct iface *iface) +{ + struct nbr *nbr = NULL; + struct in6_addr addr; + + if (iface->passive) { + /* for an update of stub network entries */ + orig_rtr_lsa(iface->area); + return (0); + } + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_pton(AF_INET6, AllSPFRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", log_in6addr(&addr), iface->name); + } + if (iface->state & IF_STA_DRORBDR) { + inet_pton(AF_INET6, AllDRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: " + "error leaving group %s, interface %s", + log_in6addr(&addr), iface->name); + } + } + break; + case IF_TYPE_VIRTUALLINK: + /* nothing */ + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + log_debug("if_act_reset: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + 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)); + } + } + + iface->dr = NULL; + iface->bdr = NULL; + + ls_ack_list_clr(iface); + stop_ls_ack_tx_timer(iface); + if_stop_hello_timer(iface); + if_stop_wait_timer(iface); + + /* send empty hello to tell everybody that we are going down */ + send_hello(iface); + + return (0); +} + +struct ctl_iface * +if_to_ctl(struct iface *iface) +{ + static struct ctl_iface ictl; + struct timeval tv, now, res; + struct nbr *nbr; + + 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.rtr_id.s_addr = ospfe_router_id(); + memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area)); + if (iface->dr) { + memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id)); + memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr)); + } else { + bzero(&ictl.dr_id, sizeof(ictl.dr_id)); + bzero(&ictl.dr_addr, sizeof(ictl.dr_addr)); + } + if (iface->bdr) { + memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id)); + memcpy(&ictl.bdr_addr, &iface->bdr->addr, + sizeof(ictl.bdr_addr)); + } else { + bzero(&ictl.bdr_id, sizeof(ictl.bdr_id)); + bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr)); + } + ictl.ifindex = iface->ifindex; + ictl.state = iface->state; + ictl.mtu = iface->mtu; + ictl.nbr_cnt = 0; + ictl.adj_cnt = 0; + ictl.baudrate = iface->baudrate; + ictl.dead_interval = iface->dead_interval; + ictl.transmit_delay = iface->transmit_delay; + ictl.hello_interval = iface->hello_interval; + ictl.flags = iface->flags; + ictl.metric = iface->metric; + ictl.rxmt_interval = iface->rxmt_interval; + ictl.type = iface->type; + ictl.linkstate = iface->linkstate; + ictl.mediatype = iface->media_type; + ictl.priority = iface->priority; + ictl.passive = iface->passive; + + gettimeofday(&now, NULL); + if (evtimer_pending(&iface->hello_timer, &tv)) { + timersub(&tv, &now, &res); + ictl.hello_timer = res.tv_sec; + } else + ictl.hello_timer = -1; + + if (iface->state != IF_STA_DOWN) { + ictl.uptime = now.tv_sec - iface->uptime; + } else + ictl.uptime = 0; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + ictl.nbr_cnt++; + if (nbr->state & NBR_STA_ADJFORM) + ictl.adj_cnt++; + } + + return (&ictl); +} + +/* misc */ +int +if_set_recvif(int fd, int enable) +{ +#if 0 + if (setsockopt(fd, IPPROTO_IPV6, IP_RECVIF, &enable, + sizeof(enable)) < 0) { + log_warn("if_set_recvif: error setting IP_RECVIF"); + return (-1); + } +#endif + 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 in6_addr *addr) +{ + struct ipv6_mreq mreq; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + log_debug("if_join_group: interface %s addr %s", + iface->name, log_in6addr(addr)); + mreq.ipv6mr_multiaddr = *addr; + mreq.ipv6mr_interface = iface->ifindex; + + if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + log_warn("if_join_group: error IPV6_JOIN_GROUP, " + "interface %s address %s", iface->name, + log_in6addr(addr)); + return (-1); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_join_group: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_join_group: unknown interface type"); + } + + return (0); +} + +int +if_leave_group(struct iface *iface, struct in6_addr *addr) +{ + struct ipv6_mreq mreq; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + log_debug("if_leave_group: interface %s addr %s", + iface->name, log_in6addr(addr)); + mreq.ipv6mr_multiaddr = *addr; + mreq.ipv6mr_interface = iface->ifindex; + + if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (void *)&mreq, sizeof(mreq)) < 0) { + log_warn("if_leave_group: error IPV6_LEAVE_GROUP, " + "interface %s address %s", iface->name, + log_in6addr(addr)); + return (-1); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_leave_group: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_leave_group: unknown interface type"); + } + return (0); +} + +int +if_set_mcast(struct iface *iface) +{ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + log_debug("if_set_mcast: iface %s", iface->name); + if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &iface->ifindex, sizeof(iface->ifindex)) < 0) { + log_debug("if_set_mcast: error setting " + "IP_MULTICAST_IF, interface %s", iface->name); + return (-1); + } + break; + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_NBMA: + log_debug("if_set_mcast: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_set_mcast: unknown interface type"); + } + + return (0); +} + +int +if_set_mcast_loop(int fd) +{ + u_int loop = 0; + + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + (u_int *)&loop, sizeof(loop)) < 0) { + log_warn("if_set_mcast_loop: error setting " + "IPV6_MULTICAST_LOOP"); + return (-1); + } + + return (0); +} + +int +if_set_ip_hdrincl(int fd) +{ +#if 0 + int hincl = 1; + + if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &hincl, + sizeof(hincl)) < 0) { + log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL"); + return (-1); + } +#endif + return (0); +} diff --git a/usr.sbin/ospf6d/iso_cksum.c b/usr.sbin/ospf6d/iso_cksum.c new file mode 100644 index 00000000000..f2eea518f9c --- /dev/null +++ b/usr.sbin/ospf6d/iso_cksum.c @@ -0,0 +1,57 @@ +/* $OpenBSD: iso_cksum.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@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 "ospf6d.h" + +/* implementation of Fletcher Checksum -- see RFC 1008 for more info */ + +/* pos needs to be 0 for verify and 2 <= pos < len for calculation */ +u_int16_t +iso_cksum(void *buf, u_int16_t len, u_int16_t pos) +{ + u_int8_t *p = buf; + int c0 = 0, c1 = 0, x; /* counters */ + u_int16_t sop; + + sop = len - pos - 1; /* pos is an offset (pos 2 is 3rd element) */ + p += 2; /* jump over age field */ + len -= 2; + while (len--) { + c0 += *p++; + c1 += c0; + if ((len & 0xfff) == 0) { + /* overflow protection */ + c0 %= 255; + c1 %= 255; + } + } + + if (pos) { + x = ((sop * c0 - c1)) % 255; + if (x <= 0) + x += 255; + c1 = 510 - c0 - x; + if (c1 > 255) + c1 -= 255; + c0 = x; + } + + return (c0 << 8 | c1); +} diff --git a/usr.sbin/ospf6d/kroute.c b/usr.sbin/ospf6d/kroute.c new file mode 100644 index 00000000000..fd0d99ec4a4 --- /dev/null +++ b/usr.sbin/ospf6d/kroute.c @@ -0,0 +1,1480 @@ +/* $OpenBSD: kroute.c,v 1.1 2007/10/08 10:44:50 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 <sys/uio.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 "ospf6d.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 kroute_node *next; +}; + +struct kif_node { + RB_ENTRY(kif_node) entry; + TAILQ_HEAD(, kif_addr) addrs; + struct kif k; +}; + +void kr_redist_remove(struct kroute_node *, struct kroute_node *); +int kr_redist_eval(struct kroute *, struct rroute *); +void kr_redistribute(struct kroute_node *); +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, u_int8_t); +struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr); +int kroute_insert(struct kroute_node *); +int kroute_remove(struct kroute_node *); +void kroute_clear(void); + +struct kif_node *kif_find(u_short); +struct kif_node *kif_insert(u_short); +int kif_remove(struct kif_node *); +void kif_clear(void); +struct kif *kif_update(u_short, int, struct if_data *, + struct sockaddr_dl *); +int kif_validate(u_short); + +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_newaddr(u_short, struct sockaddr_in6 *, + struct sockaddr_in6 *, struct sockaddr_in6 *); +void if_announce(void *); + +int send_rtmsg(int, int, struct kroute *); +int dispatch_rtmsg(void); +int fetchtable(void); +int fetchifs(u_short); + +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; + + kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag); + + if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen)) != + NULL) { + if (!(kr->r.flags & F_KERNEL)) + action = RTM_CHANGE; + else { /* a non-ospf route already exists. not a problem */ + if (!(kr->r.flags & F_BGPD_INSERTED)) { + do { + kr->r.flags |= F_OSPFD_INSERTED; + kr = kr->next; + } while (kr); + return (0); + } + /* + * XXX as long as there is no multipath support in + * bgpd this is safe else we end up in a bad situation. + */ + /* + * ospf route has higher pref + * - reset flags to the ospf ones + * - use RTM_CHANGE + * - zero out ifindex (this is no longer relevant) + */ + action = RTM_CHANGE; + kr->r.flags = kroute->flags | F_OSPFD_INSERTED; + kr->r.ifindex = 0; + rtlabel_unref(kr->r.rtlabel); + kr->r.ext_tag = kroute->ext_tag; + kr->r.rtlabel = kroute->rtlabel; + } + } + + /* nexthop within 127/8 -> ignore silently */ + if ((kroute->nexthop.s_addr & htonl(IN_CLASSA_NET)) == + htonl(INADDR_LOOPBACK & IN_CLASSA_NET)) + return (0); + + /* + * Ingnore updates that did not change the route. + * Currently only the nexthop can change. + */ + if (kr && kr->r.nexthop.s_addr == kroute->nexthop.s_addr) + 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.prefixlen = kroute->prefixlen; + kr->r.nexthop.s_addr = kroute->nexthop.s_addr; + kr->r.flags = kroute->flags | F_OSPFD_INSERTED; + kr->r.ext_tag = kroute->ext_tag; + kr->r.rtlabel = kroute->rtlabel; + + 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->prefixlen)) == + NULL) + return (0); + + if (!(kr->r.flags & F_OSPFD_INSERTED)) + return (0); + + if (kr->r.flags & F_KERNEL) { + /* remove F_OSPFD_INSERTED flag, route still exists in kernel */ + do { + kr->r.flags &= ~F_OSPFD_INSERTED; + kr = kr->next; + } while (kr); + 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; + struct kroute_node *kn; + 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) { + kn = kr; + do { + main_imsg_compose_ospfe(IMSG_CTL_KROUTE, + imsg->hdr.pid, + &kn->r, sizeof(kn->r)); + } while ((kn = kn->next) != NULL); + } + 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_ospfe(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_ospfe(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_ospfe(IMSG_CTL_IFINFO, + pid, &kif->k, sizeof(kif->k)); + } + + main_imsg_compose_ospfe(IMSG_CTL_END, pid, NULL, 0); +} + +void +kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn) +{ + struct rroute rr; + + /* was the route redistributed? */ + if ((kn->r.flags & F_REDISTRIBUTED) == 0) + return; + + /* remove redistributed flag */ + kn->r.flags &= ~F_REDISTRIBUTED; + rr.kr = kn->r; + rr.metric = DEFAULT_REDIST_METRIC; /* some dummy value */ + + /* probably inform the RDE (check if no other path is redistributed) */ + for (kn = kh; kn; kn = kn->next) + if (kn->r.flags & F_REDISTRIBUTED) + break; + + if (kn == NULL) + main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); +} + +int +kr_redist_eval(struct kroute *kr, struct rroute *rr) +{ + u_int32_t a, metric = 0; + + /* Only non-ospfd routes are considered for redistribution. */ + if (!(kr->flags & F_KERNEL)) + goto dont_redistribute; + + /* Dynamic routes are not redistributable. */ + if (kr->flags & F_DYNAMIC) + goto dont_redistribute; + + /* interface is not up and running so don't announce */ + if (kr->flags & F_DOWN) + goto dont_redistribute; + + /* + * 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) + goto dont_redistribute; + /* + * Consider networks with nexthop loopback as not redistributable. + */ + if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK)) + goto dont_redistribute; + + /* Should we redistrubute this route? */ + if (!ospf_redistribute(kr, &metric)) + goto dont_redistribute; + + /* prefix should be redistributed */ + kr->flags |= F_REDISTRIBUTED; + /* + * only on of all multipath routes can be redistributed so + * redistribute the best one. + */ + if (rr->metric > metric) { + rr->kr = *kr; + rr->metric = metric; + } + return (1); + +dont_redistribute: + /* was the route redistributed? */ + if ((kr->flags & F_REDISTRIBUTED) == 0) + return (0); + + kr->flags &= ~F_REDISTRIBUTED; + return (1); +} + +void +kr_redistribute(struct kroute_node *kh) +{ + struct kroute_node *kn; + struct rroute rr; + int redistribute = 0; + + bzero(&rr, sizeof(rr)); + rr.metric = UINT_MAX; + for (kn = kh; kn; kn = kn->next) + if (kr_redist_eval(&kn->r, &rr)) + redistribute = 1; + + if (!redistribute) + return; + + if (rr.kr.flags & F_REDISTRIBUTED) { + main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &rr, + sizeof(struct rroute)); + } else { + rr.metric = DEFAULT_REDIST_METRIC; /* some dummy value */ + rr.kr = kh->r; + main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); + } +} + +void +kr_reload(void) +{ + struct kroute_node *kr, *kn; + u_int32_t dummy; + int r; + + RB_FOREACH(kr, kroute_tree, &krt) { + for (kn = kr; kn; kn = kn->next) { + r = ospf_redistribute(&kn->r, &dummy); + /* + * if it is redistributed, redistribute again metric + * may have changed. + */ + if ((kn->r.flags & F_REDISTRIBUTED && !r) || r) + break; + } + if (kn) { + /* + * kr_redistribute copes with removes and RDE with + * duplicates + */ + kr_redistribute(kr); + } + } +} + +/* 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 (a->r.prefixlen < b->r.prefixlen) + return (-1); + if (a->r.prefixlen > b->r.prefixlen) + 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, u_int8_t prefixlen) +{ + struct kroute_node s; + + s.r.prefix.s_addr = prefix; + s.r.prefixlen = prefixlen; + + return (RB_FIND(kroute_tree, &krt, &s)); +} + +struct kroute_node * +kroute_matchgw(struct kroute_node *kr, struct in_addr nh) +{ + in_addr_t nexthop; + + nexthop = nh.s_addr; + + while (kr) { + if (kr->r.nexthop.s_addr == nexthop) + return (kr); + kr = kr->next; + } + + return (NULL); +} + +int +kroute_insert(struct kroute_node *kr) +{ + struct kroute_node *krm; + + if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { + /* + * Multipath route, add at end of list and clone the + * ospfd inserted flag. + */ + kr->r.flags |= krm->r.flags & F_OSPFD_INSERTED; + while (krm->next != NULL) + krm = krm->next; + krm->next = kr; + kr->next = NULL; /* to be sure */ + } else + krm = kr; + + if (!(kr->r.flags & F_KERNEL)) { + /* don't validate or redistribute ospf 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(krm); + return (0); +} + +int +kroute_remove(struct kroute_node *kr) +{ + struct kroute_node *krm; + + if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { + log_warnx("kroute_remove failed to find %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + + if (krm == kr) { + /* head element */ + if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + log_warnx("kroute_remove failed for %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + if (kr->next != NULL) { + if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { + log_warnx("kroute_remove failed to add %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + } + } else { + /* somewhere in the list */ + while (krm->next != kr && krm->next != NULL) + krm = krm->next; + if (krm->next == NULL) { + log_warnx("kroute_remove multipath list corrupted " + "for %s/%u", inet_ntoa(kr->r.prefix), + kr->r.prefixlen); + return (-1); + } + krm->next = kr->next; + } + + kr_redist_remove(krm, kr); + rtlabel_unref(kr->r.rtlabel); + + 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(u_short 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 in6_addr *addr, struct kif_addr **kap) +{ + struct kif_node *kif; + struct kif_addr *ka; + + RB_FOREACH(kif, kif_tree, &kit) + if (!strcmp(ifname, kif->k.ifname)) { + ka = TAILQ_FIRST(&kif->addrs); + if (!IN6_IS_ADDR_UNSPECIFIED(addr)) { + TAILQ_FOREACH(ka, &kif->addrs, entry) { + if (IN6_ARE_ADDR_EQUAL(addr, &ka->addr)) + break; + } + } + if (kap != NULL) + *kap = ka; + return (&kif->k); + } + + return (NULL); +} + +struct kif_node * +kif_insert(u_short ifindex) +{ + struct kif_node *kif; + + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) + return (NULL); + + kif->k.ifindex = ifindex; + TAILQ_INIT(&kif->addrs); + + if (RB_INSERT(kif_tree, &kit, kif) != NULL) + fatalx("kif_insert: RB_INSERT"); + + return (kif); +} + +int +kif_remove(struct kif_node *kif) +{ + struct kif_addr *ka; + + if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { + log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); + return (-1); + } + + while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) { + TAILQ_REMOVE(&kif->addrs, ka, entry); + free(ka); + } + free(kif); + return (0); +} + +void +kif_clear(void) +{ + struct kif_node *kif; + + while ((kif = RB_MIN(kif_tree, &kit)) != NULL) + kif_remove(kif); +} + +struct kif * +kif_update(u_short ifindex, int flags, struct if_data *ifd, + struct sockaddr_dl *sdl) +{ + struct kif_node *kif; + + if ((kif = kif_find(ifindex)) == NULL) + if ((kif = kif_insert(ifindex)) == NULL) + return (NULL); + + 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; + kif->k.mtu = ifd->ifi_mtu; + + if (sdl && sdl->sdl_family == AF_LINK) { + 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() */ + } + + return (&kif->k); +} + +int +kif_validate(u_short 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), 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.prefixlen = 8; + 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 kroute_node *kr, *tkr; + struct kif *kif; + u_int8_t reachable; + + if ((kif = kif_update(ifindex, flags, ifd, NULL)) == NULL) { + log_warn("if_change: kif_update(%u)", ifindex); + return; + } + + reachable = (kif->flags & IFF_UP) && + (LINK_STATE_IS_UP(kif->link_state) || + (kif->link_state == LINK_STATE_UNKNOWN && + kif->media_type != IFT_CARP)); + + if (reachable == kif->nh_reachable) + return; /* nothing changed wrt nexthop validity */ + + kif->nh_reachable = reachable; + + /* notify ospfe about interface link state */ + main_imsg_compose_ospfe(IMSG_IFINFO, 0, kif, sizeof(struct kif)); + + /* update redistribute list */ + RB_FOREACH(kr, kroute_tree, &krt) { + for (tkr = kr; tkr != NULL; tkr = tkr->next) { + if (tkr->r.ifindex == ifindex) { + if (reachable) + tkr->r.flags &= ~F_DOWN; + else + tkr->r.flags |= F_DOWN; + + } + } + kr_redistribute(kr); + } +} + +void +if_newaddr(u_short ifindex, struct sockaddr_in6 *ifa, struct sockaddr_in6 *mask, + struct sockaddr_in6 *brd) +{ + struct kif_node *kif; + struct kif_addr *ka; + + if (ifa == NULL || ifa->sin6_family != AF_INET6) + return; + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("if_newaddr: corresponding if %i not found", ifindex); + return; + } + if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL) + fatal("if_newaddr"); + ka->addr = ifa->sin6_addr; + if (mask) + ka->mask = mask->sin6_addr; + else + bzero(&ka->mask, sizeof(ka->mask)); + if (brd) + ka->dstbrd = brd->sin6_addr; + else + bzero(&ka->dstbrd, sizeof(ka->dstbrd)); + + log_debug("if_newaddr: ifindex %u, addr %s", ifindex, + log_in6addr(&ka->addr)); + TAILQ_INSERT_TAIL(&kif->addrs, ka, entry); +} + +void +if_announce(void *msg) +{ + struct if_announcemsghdr *ifan; + struct kif_node *kif; + + ifan = msg; + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + kif = kif_insert(ifan->ifan_index); + strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); + 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 iovec iov[5]; + struct rt_msghdr hdr; + struct sockaddr_in prefix; + struct sockaddr_in nexthop; + struct sockaddr_in mask; + struct sockaddr_rtlabel sa_rl; + int iovcnt = 0; + const char *label; + + if (kr_state.fib_sync == 0) + return (0); + + /* initialize header */ + bzero(&hdr, sizeof(hdr)); + hdr.rtm_version = RTM_VERSION; + hdr.rtm_type = action; + hdr.rtm_flags = RTF_PROTO2; + if (action == RTM_CHANGE) /* force PROTO2 reset the other flags */ + hdr.rtm_fmask = RTF_PROTO2|RTF_PROTO1|RTF_REJECT|RTF_BLACKHOLE; + hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + hdr.rtm_msglen = sizeof(hdr); + /* adjust iovec */ + iov[iovcnt].iov_base = &hdr; + iov[iovcnt++].iov_len = sizeof(hdr); + + bzero(&prefix, sizeof(prefix)); + prefix.sin_len = sizeof(prefix); + prefix.sin_family = AF_INET; + prefix.sin_addr.s_addr = kroute->prefix.s_addr; + /* adjust header */ + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof(prefix); + /* adjust iovec */ + iov[iovcnt].iov_base = &prefix; + iov[iovcnt++].iov_len = sizeof(prefix); + + if (kroute->nexthop.s_addr != 0) { + bzero(&nexthop, sizeof(nexthop)); + nexthop.sin_len = sizeof(nexthop); + nexthop.sin_family = AF_INET; + nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; + /* adjust header */ + hdr.rtm_flags |= RTF_GATEWAY; + hdr.rtm_addrs |= RTA_GATEWAY; + hdr.rtm_msglen += sizeof(nexthop); + /* adjust iovec */ + iov[iovcnt].iov_base = &nexthop; + iov[iovcnt++].iov_len = sizeof(nexthop); + } + + bzero(&mask, sizeof(mask)); + mask.sin_len = sizeof(mask); + mask.sin_family = AF_INET; + mask.sin_addr.s_addr = prefixlen2mask(kroute->prefixlen); + /* adjust header */ + hdr.rtm_addrs |= RTA_NETMASK; + hdr.rtm_msglen += sizeof(mask); + /* adjust iovec */ + iov[iovcnt].iov_base = &mask; + iov[iovcnt++].iov_len = sizeof(mask); + + if (kroute->rtlabel != 0) { + sa_rl.sr_len = sizeof(sa_rl); + sa_rl.sr_family = AF_UNSPEC; + label = rtlabel_id2name(kroute->rtlabel); + if (strlcpy(sa_rl.sr_label, label, + sizeof(sa_rl.sr_label)) >= sizeof(sa_rl.sr_label)) { + log_warnx("send_rtmsg: invalid rtlabel"); + return (-1); + } + /* adjust header */ + hdr.rtm_addrs |= RTA_LABEL; + hdr.rtm_msglen += sizeof(sa_rl); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_rl; + iov[iovcnt++].iov_len = sizeof(sa_rl); + } + +retry: + if (writev(fd, iov, iovcnt) == -1) { + switch (errno) { + case ESRCH: + if (hdr.rtm_type == RTM_CHANGE) { + hdr.rtm_type = RTM_ADD; + goto retry; + } else if (hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + inet_ntoa(kroute->prefix), + kroute->prefixlen); + return (0); + } else { + log_warnx("send_rtmsg: action %u, " + "prefix %s/%u: %s", hdr.rtm_type, + inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + break; + default: + log_warnx("send_rtmsg: action %u, prefix %s/%u: %s", + hdr.rtm_type, inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + return (0); +} + +int +fetchtable(void) +{ + size_t len; + int mib[7]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct sockaddr_rtlabel *label; + struct kroute_node *kr; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + mib[6] = 0; /* rtableid */ + + if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchtable"); + return (-1); + } + if (sysctl(mib, 7, 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; + + 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 (sa_in != NULL) { + if (sa_in->sin_len == 0) + break; + kr->r.prefixlen = + mask2prefixlen(sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + kr->r.prefixlen = 32; + else + kr->r.prefixlen = + prefixlen_classful(kr->r.prefix.s_addr); + break; + default: + free(kr); + continue; + } + + kr->r.ifindex = rtm->rtm_index; + 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_PROTO2) { + send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + free(kr); + } else { + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + kroute_insert(kr); + } + + } + free(buf); + return (0); +} + +int +fetchifs(u_short ifindex) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + struct ifa_msghdr *ifam; + struct kif *kif = NULL; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + 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 += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (rtm->rtm_version != RTM_VERSION) + continue; + switch (rtm->rtm_type) { + case RTM_IFINFO: + bcopy(rtm, &ifm, sizeof ifm); + sa = (struct sockaddr *)(next + sizeof(ifm)); + get_rtaddrs(ifm.ifm_addrs, sa, rti_info); + + if ((kif = kif_update(ifm.ifm_index, + ifm.ifm_flags, &ifm.ifm_data, + (struct sockaddr_dl *)rti_info[RTAX_IFP])) == NULL) + fatal("fetchifs"); + + kif->nh_reachable = (kif->flags & IFF_UP) && + (LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state) || + (ifm.ifm_data.ifi_link_state == + LINK_STATE_UNKNOWN && + ifm.ifm_data.ifi_type != IFT_CARP)); + break; + case RTM_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | + RTA_BRD)) == 0) + break; + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + + if_newaddr(ifam->ifam_index, + (struct sockaddr_in6 *)rti_info[RTAX_IFA], + (struct sockaddr_in6 *)rti_info[RTAX_NETMASK], + (struct sockaddr_in6 *)rti_info[RTAX_BRD]); + break; + } + } + 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 ifa_msghdr *ifam; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct sockaddr_rtlabel *label; + struct kroute_node *kr, *okr; + struct in_addr prefix, nexthop; + u_int8_t prefixlen; + int flags, mpath; + 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; + prefixlen = 0; + flags = F_KERNEL; + nexthop.s_addr = 0; + mpath = 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_tableid != 0) + continue; + + if (rtm->rtm_pid == kr_state.pid) /* caused by us */ + continue; + + if (rtm->rtm_errno) /* failed attempts... */ + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) + mpath = 1; +#endif + 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) + prefixlen = mask2prefixlen( + sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + prefixlen = 32; + else + prefixlen = + 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; + 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), prefixlen); + continue; + } + + if ((okr = kroute_find(prefix.s_addr, prefixlen)) != + NULL) { + /* just add new multipath routes */ + if (mpath && rtm->rtm_type == RTM_ADD) + goto add; + /* get the correct route */ + kr = okr; + if (mpath && (kr = kroute_matchgw(okr, + nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + /* add routes we missed out earlier */ + goto add; + } + + /* + * ospf route overridden by kernel. Preference + * of the route is not checked because this is + * forced -- most probably by a user. + */ + if (kr->r.flags & F_OSPFD_INSERTED) + flags |= F_OSPFD_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; + + rtlabel_unref(kr->r.rtlabel); + kr->r.rtlabel = 0; + kr->r.ext_tag = 0; + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + + 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(okr); + } else { +add: + 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.prefixlen = prefixlen; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + if ((label = (struct sockaddr_rtlabel *) + rti_info[RTAX_LABEL]) != NULL) { + kr->r.rtlabel = + rtlabel_name2id(label->sr_label); + kr->r.ext_tag = + rtlabel_id2tag(kr->r.rtlabel); + } + + kroute_insert(kr); + } + break; + case RTM_DELETE: + if ((kr = kroute_find(prefix.s_addr, prefixlen)) == + NULL) + continue; + if (!(kr->r.flags & F_KERNEL)) + continue; + /* get the correct route */ + okr = kr; + if (mpath && + (kr = kroute_matchgw(kr, nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + return (-1); + } + /* + * last route is getting removed request the + * ospf route from the RDE to insert instead + */ + if (okr == kr && kr->next == NULL && + kr->r.flags & F_OSPFD_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_NEWADDR: + ifam = (struct ifa_msghdr *)rtm; + if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | + RTA_BRD)) == 0) + break; + sa = (struct sockaddr *)(ifam + 1); + get_rtaddrs(ifam->ifam_addrs, sa, rti_info); + + if_newaddr(ifam->ifam_index, + (struct sockaddr_in6 *)rti_info[RTAX_IFA], + (struct sockaddr_in6 *)rti_info[RTAX_NETMASK], + (struct sockaddr_in6 *)rti_info[RTAX_BRD]); + break; + case RTM_IFANNOUNCE: + if_announce(next); + break; + default: + /* ignore for now */ + break; + } + } + return (0); +} diff --git a/usr.sbin/ospf6d/log.c b/usr.sbin/ospf6d/log.c new file mode 100644 index 00000000000..911aed34235 --- /dev/null +++ b/usr.sbin/ospf6d/log.c @@ -0,0 +1,305 @@ +/* $OpenBSD: log.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2006 Claudio Jeker <claudio@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 <netinet/in.h> +#include <arpa/inet.h> + +#include <netdb.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "ospf6d.h" +#include "log.h" + +static const char * const procnames[] = { + "parent", + "ospfe", + "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[ospfd_process], + strerror(errno)); + else + if (errno) + logit(LOG_CRIT, "fatal in %s: %s: %s", + procnames[ospfd_process], emsg, strerror(errno)); + else + logit(LOG_CRIT, "fatal in %s: %s", + procnames[ospfd_process], emsg); + + if (ospfd_process == PROC_MAIN) + exit(1); + else /* parent copes via SIGCHLD */ + _exit(1); +} + +void +fatalx(const char *emsg) +{ + errno = 0; + fatal(emsg); +} + +const char * +log_in6addr(const struct in6_addr *addr) +{ + struct sockaddr_in6 sa_in6; + u_int16_t tmp16; + + bzero(&sa_in6, sizeof(sa_in6)); + sa_in6.sin6_len = sizeof(sa_in6); + sa_in6.sin6_family = AF_INET6; + memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); + + /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ + if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { + memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); + sa_in6.sin6_scope_id = ntohs(tmp16); + sa_in6.sin6_addr.s6_addr[2] = 0; + sa_in6.sin6_addr.s6_addr[3] = 0; + } + + return (log_sockaddr((struct sockaddr *)&sa_in6)); +} + +const char * +log_sockaddr(struct sockaddr *sa) +{ + static char buf[NI_MAXHOST]; + + if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST)) + return ("(unknown)"); + else + return (buf); +} + +/* names */ +const char * +nbr_state_name(int state) +{ + switch (state) { + case NBR_STA_DOWN: + return ("DOWN"); + case NBR_STA_ATTEMPT: + return ("ATTMP"); + case NBR_STA_INIT: + return ("INIT"); + case NBR_STA_2_WAY: + return ("2-WAY"); + case NBR_STA_XSTRT: + return ("EXSTA"); + case NBR_STA_SNAP: + return ("SNAP"); + case NBR_STA_XCHNG: + return ("EXCHG"); + case NBR_STA_LOAD: + return ("LOAD"); + case NBR_STA_FULL: + return ("FULL"); + default: + return ("UNKNW"); + } +} + +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_LOOPBACK: + return ("LOOP"); + case IF_STA_WAITING: + return ("WAIT"); + case IF_STA_POINTTOPOINT: + return ("P2P"); + case IF_STA_DROTHER: + return ("OTHER"); + case IF_STA_BACKUP: + return ("BCKUP"); + case IF_STA_DR: + return ("DR"); + default: + return ("UNKNW"); + } +} + +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"); + case IF_TYPE_VIRTUALLINK: + return ("VIRTUALLINK"); + } + /* NOTREACHED */ + return ("UNKNOWN"); +} + +const char * +dst_type_name(enum dst_type type) +{ + switch (type) { + case DT_NET: + return ("Network"); + case DT_RTR: + return ("Router"); + } + /* NOTREACHED */ + return ("unknown"); +} + +const char * +path_type_name(enum path_type type) +{ + switch (type) { + case PT_INTRA_AREA: + return ("Intra-Area"); + case PT_INTER_AREA: + return ("Inter-Area"); + case PT_TYPE1_EXT: + return ("Type 1 ext"); + case PT_TYPE2_EXT: + return ("Type 2 ext"); + } + /* NOTREACHED */ + return ("unknown"); +} diff --git a/usr.sbin/ospf6d/log.h b/usr.sbin/ospf6d/log.h new file mode 100644 index 00000000000..f9541236d0a --- /dev/null +++ b/usr.sbin/ospf6d/log.h @@ -0,0 +1,36 @@ +/* $OpenBSD: log.h,v 1.1 2007/10/08 10:44:50 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 *log_in6addr(const struct in6_addr *); +const char *log_sockaddr(struct sockaddr *); + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/ospf6d/lsack.c b/usr.sbin/ospf6d/lsack.c new file mode 100644 index 00000000000..945d34467b5 --- /dev/null +++ b/usr.sbin/ospf6d/lsack.c @@ -0,0 +1,297 @@ +/* $OpenBSD: lsack.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <string.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +void start_ls_ack_tx_timer_now(struct iface *); + +/* link state acknowledgement packet handling */ +int +send_ls_ack(struct iface *iface, struct in6_addr addr, void *data, size_t len) +{ + struct sockaddr_in6 dst; + struct buf *buf; + int ret; + + /* XXX READ_BUF_SIZE */ + if ((buf = buf_dynamic(PKG_DEF_SIZE, READ_BUF_SIZE)) == NULL) + fatal("send_ls_ack"); + + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + dst.sin6_addr = addr; /* XXX */ + + /* OSPF header */ + if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_ACK)) + goto fail; + + /* LS ack(s) */ + if (buf_add(buf, data, len)) + goto fail; + + /* calculate checksum */ + if (upd_ospf_hdr(buf, iface)) + goto fail; + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + + buf_free(buf); + return (ret); +fail: + log_warn("send_ls_ack"); + buf_free(buf); + return (-1); +} + +void +recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct lsa_hdr lsa_hdr; + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_XSTRT: + case NBR_STA_SNAP: + log_debug("recv_ls_ack: packet ignored in state %s, " + "neighbor ID %s", nbr_state_name(nbr->state), + inet_ntoa(nbr->id)); + break; + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + while (len >= sizeof(lsa_hdr)) { + memcpy(&lsa_hdr, buf, sizeof(lsa_hdr)); + + if (lsa_hdr_check(nbr, &lsa_hdr)) { + /* try both list in case of DROTHER */ + if (nbr->iface->state & IF_STA_DROTHER) + (void)ls_retrans_list_del( + nbr->iface->self, &lsa_hdr); + (void)ls_retrans_list_del(nbr, &lsa_hdr); + } + + buf += sizeof(lsa_hdr); + len -= sizeof(lsa_hdr); + } + if (len > 0) { + log_warnx("recv_ls_ack: bad packet size, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return; + } + break; + default: + fatalx("recv_ls_ack: unknown neighbor state"); + } +} + +int +lsa_hdr_check(struct nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + /* invalid age */ + if ((ntohs(lsa_hdr->age) < 1) || (ntohs(lsa_hdr->age) > MAX_AGE)) { + log_debug("lsa_hdr_check: invalid age, neighbor ID %s", + inet_ntoa(nbr->id)); + return (0); + } + + /* invalid type */ + switch (lsa_hdr->type) { + case LSA_TYPE_ROUTER: + case LSA_TYPE_NETWORK: + case LSA_TYPE_SUM_NETWORK: + case LSA_TYPE_SUM_ROUTER: + case LSA_TYPE_EXTERNAL: + break; + default: + log_debug("lsa_hdr_check: invalid LSA type %d, neighbor ID %s", + lsa_hdr->type, inet_ntoa(nbr->id)); + return (0); + } + + /* invalid sequence number */ + if (ntohl(lsa_hdr->seq_num) == RESV_SEQ_NUM) { + log_debug("ls_hdr_check: invalid seq num, neighbor ID %s", + inet_ntoa(nbr->id)); + return (0); + } + + return (1); +} + +/* link state ack list */ +void +ls_ack_list_add(struct iface *iface, struct lsa_hdr *lsa) +{ + struct lsa_entry *le; + + if (lsa == NULL) + fatalx("ls_ack_list_add: no LSA header"); + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("ls_ack_list_add"); + + if (ls_ack_list_empty(iface)) + start_ls_ack_tx_timer(iface); + + TAILQ_INSERT_TAIL(&iface->ls_ack_list, le, entry); + le->le_lsa = lsa; + iface->ls_ack_cnt++; + + /* reschedule now if we have enough for a full packet */ + if (iface->ls_ack_cnt > + ((iface->mtu - PACKET_HDR) / sizeof(struct lsa_hdr))) { + start_ls_ack_tx_timer_now(iface); + } + +} + +void +ls_ack_list_free(struct iface *iface, struct lsa_entry *le) +{ + TAILQ_REMOVE(&iface->ls_ack_list, le, entry); + free(le->le_lsa); + free(le); + + iface->ls_ack_cnt--; +} + +void +ls_ack_list_clr(struct iface *iface) +{ + struct lsa_entry *le; + + while ((le = TAILQ_FIRST(&iface->ls_ack_list)) != NULL) { + TAILQ_REMOVE(&iface->ls_ack_list, le, entry); + free(le->le_lsa); + free(le); + } + iface->ls_ack_cnt = 0; +} + +int +ls_ack_list_empty(struct iface *iface) +{ + return (TAILQ_EMPTY(&iface->ls_ack_list)); +} + +/* timers */ +/* ARGSUSED */ +void +ls_ack_tx_timer(int fd, short event, void *arg) +{ + struct in6_addr addr; + struct iface *iface = arg; + struct lsa_hdr *lsa_hdr; + struct lsa_entry *le, *nle; + struct nbr *nbr; + char *buf; + char *ptr; + int cnt = 0; + + if ((buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("ls_ack_tx_timer"); + + while (!ls_ack_list_empty(iface)) { + ptr = buf; + cnt = 0; + for (le = TAILQ_FIRST(&iface->ls_ack_list); le != NULL && + (ptr - buf < iface->mtu - PACKET_HDR); le = nle) { + nle = TAILQ_NEXT(le, entry); + memcpy(ptr, le->le_lsa, sizeof(struct lsa_hdr)); + ptr += sizeof(*lsa_hdr); + ls_ack_list_free(iface, le); + cnt++; + } + + /* send LS ack(s) but first set correct destination */ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + inet_pton(AF_INET6, AllSPFRouters, &addr); + send_ls_ack(iface, addr, buf, ptr - buf); + break; + case IF_TYPE_BROADCAST: + if (iface->state & IF_STA_DRORBDR) + inet_pton(AF_INET6, AllSPFRouters, &addr); + else + inet_pton(AF_INET6, AllDRouters, &addr); + + send_ls_ack(iface, addr, buf, ptr - buf); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + if (!(nbr->state & NBR_STA_FLOOD)) + continue; + send_ls_ack(iface, nbr->addr, buf, ptr - buf); + } + break; + default: + fatalx("lsa_ack_tx_timer: unknown interface type"); + } + } + + free(buf); +} + +void +start_ls_ack_tx_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = iface->rxmt_interval / 2; + + if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) + fatal("start_ls_ack_tx_timer"); +} + +void +start_ls_ack_tx_timer_now(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) + fatal("start_ls_ack_tx_timer_now"); +} + +void +stop_ls_ack_tx_timer(struct iface *iface) +{ + if (evtimer_del(&iface->lsack_tx_timer) == -1) + fatal("stop_ls_ack_tx_timer"); +} diff --git a/usr.sbin/ospf6d/lsreq.c b/usr.sbin/ospf6d/lsreq.c new file mode 100644 index 00000000000..76c92129b65 --- /dev/null +++ b/usr.sbin/ospf6d/lsreq.c @@ -0,0 +1,251 @@ +/* $OpenBSD: lsreq.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +extern struct imsgbuf *ibuf_rde; + +/* link state request packet handling */ +int +send_ls_req(struct nbr *nbr) +{ + struct sockaddr_in6 dst; + struct ls_req_hdr ls_req_hdr; + struct lsa_entry *le, *nle; + struct buf *buf; + int ret; + + if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_ls_req"); + + /* set destination */ + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + + switch (nbr->iface->type) { + case IF_TYPE_POINTOPOINT: +//XXX inet_aton(AllSPFRouters, &dst.sin_addr); + inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr); + break; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: +//XXX dst.sin_addr.s_addr = nbr->addr.s_addr; + dst.sin6_addr = nbr->addr; + break; + default: + fatalx("send_ls_req: unknown interface type"); + } + + /* OSPF header */ + if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_LS_REQUEST)) + goto fail; + + /* LSA header(s), keep space for a possible md5 sum */ + for (le = TAILQ_FIRST(&nbr->ls_req_list); le != NULL && + buf->wpos + sizeof(struct ls_req_hdr) < buf->max - + MD5_DIGEST_LENGTH; le = nle) { + nbr->ls_req = nle = TAILQ_NEXT(le, entry); + ls_req_hdr.type = htonl(le->le_lsa->type); + ls_req_hdr.ls_id = le->le_lsa->ls_id; + ls_req_hdr.adv_rtr = le->le_lsa->adv_rtr; + if (buf_add(buf, &ls_req_hdr, sizeof(ls_req_hdr))) + goto fail; + } + + /* calculate checksum */ + if (upd_ospf_hdr(buf, nbr->iface)) + goto fail; + + ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst); + + buf_free(buf); + return (ret); +fail: + log_warn("send_ls_req"); + buf_free(buf); + return (-1); +} + +void +recv_ls_req(struct nbr *nbr, char *buf, u_int16_t len) +{ + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_XSTRT: + case NBR_STA_SNAP: + log_debug("recv_ls_req: packet ignored in state %s, " + "neighbor ID %s", nbr_state_name(nbr->state), + inet_ntoa(nbr->id)); + break; + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + imsg_compose(ibuf_rde, IMSG_LS_REQ, nbr->peerid, 0, buf, len); + break; + default: + fatalx("recv_ls_req: unknown neighbor state"); + } +} + +/* link state request list */ +void +ls_req_list_add(struct nbr *nbr, struct lsa_hdr *lsa) +{ + struct lsa_entry *le; + + if (lsa == NULL) + fatalx("ls_req_list_add: no LSA header"); + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("ls_req_list_add"); + + TAILQ_INSERT_TAIL(&nbr->ls_req_list, le, entry); + le->le_lsa = lsa; + nbr->ls_req_cnt++; +} + +struct lsa_entry * +ls_req_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct lsa_entry *le; + + TAILQ_FOREACH(le, &nbr->ls_req_list, entry) { + if ((lsa_hdr->type == le->le_lsa->type) && + (lsa_hdr->ls_id == le->le_lsa->ls_id) && + (lsa_hdr->adv_rtr == le->le_lsa->adv_rtr)) + return (le); + } + return (NULL); +} + +void +ls_req_list_free(struct nbr *nbr, struct lsa_entry *le) +{ + if (nbr->ls_req == le) { + nbr->ls_req = TAILQ_NEXT(le, entry); + } + + TAILQ_REMOVE(&nbr->ls_req_list, le, entry); + free(le->le_lsa); + free(le); + nbr->ls_req_cnt--; + + /* received all requested LSA(s), send a new LS req */ + if (nbr->ls_req != NULL && + nbr->ls_req == TAILQ_FIRST(&nbr->ls_req_list)) { + start_ls_req_tx_timer(nbr); + } + + if (ls_req_list_empty(nbr) && nbr->dd_pending == 0) + nbr_fsm(nbr, NBR_EVT_LOAD_DONE); +} + +void +ls_req_list_clr(struct nbr *nbr) +{ + struct lsa_entry *le; + + while ((le = TAILQ_FIRST(&nbr->ls_req_list)) != NULL) { + TAILQ_REMOVE(&nbr->ls_req_list, le, entry); + free(le->le_lsa); + free(le); + } + + nbr->ls_req_cnt = 0; + nbr->ls_req = NULL; +} + +int +ls_req_list_empty(struct nbr *nbr) +{ + return (TAILQ_EMPTY(&nbr->ls_req_list)); +} + +/* timers */ +/* ARGSUSED */ +void +ls_req_tx_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_SNAP: + case NBR_STA_XSTRT: + case NBR_STA_XCHNG: + return; + case NBR_STA_LOAD: + send_ls_req(nbr); + break; + case NBR_STA_FULL: + return; + default: + log_debug("ls_req_tx_timer: unknown neighbor state, " + "neighbor ID %s", inet_ntoa(nbr->id)); + break; + } + + /* reschedule lsreq_tx_timer */ + if (nbr->state == NBR_STA_LOAD) { + timerclear(&tv); + tv.tv_sec = nbr->iface->rxmt_interval; + if (evtimer_add(&nbr->lsreq_tx_timer, &tv) == -1) + fatal("ls_req_tx_timer"); + } +} + +void +start_ls_req_tx_timer(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return; + + timerclear(&tv); + if (evtimer_add(&nbr->lsreq_tx_timer, &tv) == -1) + fatal("start_ls_req_tx_timer"); +} + +void +stop_ls_req_tx_timer(struct nbr *nbr) +{ + if (nbr == nbr->iface->self) + return; + + if (evtimer_del(&nbr->lsreq_tx_timer) == -1) + fatal("stop_ls_req_tx_timer"); +} diff --git a/usr.sbin/ospf6d/lsupdate.c b/usr.sbin/ospf6d/lsupdate.c new file mode 100644 index 00000000000..9dcca18409a --- /dev/null +++ b/usr.sbin/ospf6d/lsupdate.c @@ -0,0 +1,600 @@ +/* $OpenBSD: lsupdate.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2007 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/hash.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <string.h> + +#include "ospf6.h" +#include "ospf6d.h" +#include "log.h" +#include "ospfe.h" +#include "rde.h" + +extern struct ospfd_conf *oeconf; +extern struct imsgbuf *ibuf_rde; + +struct buf *prepare_ls_update(struct iface *); +int add_ls_update(struct buf *, struct iface *, void *, int, u_int16_t); +int send_ls_update(struct buf *, struct iface *, struct in6_addr, u_int32_t); + +void ls_retrans_list_insert(struct nbr *, struct lsa_entry *); +void ls_retrans_list_remove(struct nbr *, struct lsa_entry *); + +/* link state update packet handling */ +int +lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr, + void *data) +{ + struct nbr *nbr; + struct lsa_entry *le = NULL; + int queued = 0, dont_ack = 0; + int r; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + if (!(nbr->state & NBR_STA_FLOOD)) + continue; + + if (iface->state & IF_STA_DROTHER && !queued) + while ((le = ls_retrans_list_get(iface->self, lsa_hdr))) + ls_retrans_list_free(iface->self, le); + + while ((le = ls_retrans_list_get(nbr, lsa_hdr))) + ls_retrans_list_free(nbr, le); + + if (!(nbr->state & NBR_STA_FULL) && + (le = ls_req_list_get(nbr, lsa_hdr)) != NULL) { + r = lsa_newer(lsa_hdr, le->le_lsa); + if (r > 0) { + /* to flood LSA is newer than requested */ + ls_req_list_free(nbr, le); + /* new needs to be flooded */ + } else if (r < 0) { + /* to flood LSA is older than requested */ + continue; + } else { + /* LSA are equal */ + ls_req_list_free(nbr, le); + continue; + } + } + + if (nbr == originator) { + dont_ack++; + continue; + } + + /* non DR or BDR router keep all lsa in one retrans list */ + if (iface->state & IF_STA_DROTHER) { + if (!queued) + ls_retrans_list_add(iface->self, data, + iface->rxmt_interval, 0); + queued = 1; + } else { + ls_retrans_list_add(nbr, data, iface->rxmt_interval, 0); + queued = 1; + } + } + + if (!queued) + return (0); + + if (iface == originator->iface && iface->self != originator) { + if (iface->dr == originator || iface->bdr == originator) + return (0); + if (iface->state & IF_STA_BACKUP) + return (0); + dont_ack++; + } + + /* + * initial flood needs to be queued separately, timeout is zero + * and oneshot has to be set because the retransimssion queues + * are already loaded. + */ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + ls_retrans_list_add(iface->self, data, 0, 1); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + if (!(nbr->state & NBR_STA_FLOOD)) + continue; + if (!TAILQ_EMPTY(&nbr->ls_retrans_list)) { + le = TAILQ_LAST(&nbr->ls_retrans_list, + lsa_head); + if (lsa_hdr->type != le->le_lsa->type || + lsa_hdr->ls_id != le->le_lsa->ls_id || + lsa_hdr->adv_rtr != le->le_lsa->adv_rtr) + continue; + } + ls_retrans_list_add(nbr, data, 0, 1); + } + break; + default: + fatalx("lsa_flood: unknown interface type"); + } + + return (dont_ack == 2); +} + +struct buf * +prepare_ls_update(struct iface *iface) +{ + struct buf *buf; + + if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_ls_update"); + + /* OSPF header */ + if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_UPDATE)) + goto fail; + + /* reserve space for number of lsa field */ + if (buf_reserve(buf, sizeof(u_int32_t)) == NULL) + goto fail; + + return (buf); +fail: + log_warn("prepare_ls_update"); + buf_free(buf); + return (NULL); +} + +int +add_ls_update(struct buf *buf, struct iface *iface, void *data, int len, + u_int16_t older) +{ + size_t pos; + u_int16_t age; + + if (buf->wpos + len >= buf->max - MD5_DIGEST_LENGTH) + return (0); + + pos = buf->wpos; + if (buf_add(buf, data, len)) { + log_warn("add_ls_update"); + return (0); + } + + /* age LSA before sending it out */ + memcpy(&age, data, sizeof(age)); + age = ntohs(age); + if ((age += older + iface->transmit_delay) >= MAX_AGE) + age = MAX_AGE; + age = htons(age); + memcpy(buf_seek(buf, pos, sizeof(age)), &age, sizeof(age)); + + return (1); +} + +int +send_ls_update(struct buf *buf, struct iface *iface, struct in6_addr addr, + u_int32_t nlsa) +{ + struct sockaddr_in6 dst; + int ret; + + nlsa = htonl(nlsa); + memcpy(buf_seek(buf, sizeof(struct ospf_hdr), sizeof(nlsa)), + &nlsa, sizeof(nlsa)); + /* calculate checksum */ + if (upd_ospf_hdr(buf, iface)) + goto fail; + + /* set destination */ + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + dst.sin6_addr = addr; + + ret = send_packet(iface, buf->buf, buf->wpos, &dst); + + buf_free(buf); + return (ret); +fail: + log_warn("send_ls_update"); + buf_free(buf); + return (-1); +} + +void +recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct lsa_hdr lsa; + u_int32_t nlsa; + + if (len < sizeof(nlsa)) { + log_warnx("recv_ls_update: bad packet size, neighbor ID %s", + inet_ntoa(nbr->id)); + return; + } + memcpy(&nlsa, buf, sizeof(nlsa)); + nlsa = ntohl(nlsa); + buf += sizeof(nlsa); + len -= sizeof(nlsa); + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_XSTRT: + case NBR_STA_SNAP: + log_debug("recv_ls_update: packet ignored in state %s, " + "neighbor ID %s", nbr_state_name(nbr->state), + inet_ntoa(nbr->id)); + break; + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + for (; nlsa > 0 && len > 0; nlsa--) { + if (len < sizeof(lsa)) { + log_warnx("recv_ls_update: bad packet size, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return; + } + memcpy(&lsa, buf, sizeof(lsa)); + if (len < ntohs(lsa.len)) { + log_warnx("recv_ls_update: bad packet size, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return; + } + imsg_compose(ibuf_rde, IMSG_LS_UPD, nbr->peerid, 0, + buf, ntohs(lsa.len)); + buf += ntohs(lsa.len); + len -= ntohs(lsa.len); + } + if (nlsa > 0 || len > 0) { + log_warnx("recv_ls_update: bad packet size, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return; + } + break; + default: + fatalx("recv_ls_update: unknown neighbor state"); + } +} + +/* link state retransmit list */ +void +ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa, + unsigned short timeout, unsigned short oneshot) +{ + struct timeval tv; + struct lsa_entry *le; + struct lsa_ref *ref; + + if ((ref = lsa_cache_get(lsa)) == NULL) + fatalx("King Bula sez: somebody forgot to lsa_cache_add"); + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("ls_retrans_list_add"); + + le->le_ref = ref; + le->le_when = timeout; + le->le_oneshot = oneshot; + + ls_retrans_list_insert(nbr, le); + + if (!evtimer_pending(&nbr->ls_retrans_timer, NULL)) { + timerclear(&tv); + tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when; + + if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) + fatal("ls_retrans_list_add"); + } +} + +int +ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct lsa_entry *le; + + if ((le = ls_retrans_list_get(nbr, lsa_hdr)) == NULL) + return (-1); + if (lsa_hdr->seq_num == le->le_ref->hdr.seq_num && + lsa_hdr->ls_chksum == le->le_ref->hdr.ls_chksum) { + ls_retrans_list_free(nbr, le); + return (0); + } + + return (-1); +} + +struct lsa_entry * +ls_retrans_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct lsa_entry *le; + + TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) { + if ((lsa_hdr->type == le->le_ref->hdr.type) && + (lsa_hdr->ls_id == le->le_ref->hdr.ls_id) && + (lsa_hdr->adv_rtr == le->le_ref->hdr.adv_rtr)) + return (le); + } + return (NULL); +} + +void +ls_retrans_list_insert(struct nbr *nbr, struct lsa_entry *new) +{ + struct lsa_entry *le; + unsigned short when = new->le_when; + + TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) { + if (when < le->le_when) { + new->le_when = when; + TAILQ_INSERT_BEFORE(le, new, entry); + nbr->ls_ret_cnt++; + return; + } + when -= le->le_when; + } + new->le_when = when; + TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, new, entry); + nbr->ls_ret_cnt++; +} + +void +ls_retrans_list_remove(struct nbr *nbr, struct lsa_entry *le) +{ + struct timeval tv; + struct lsa_entry *next = TAILQ_NEXT(le, entry); + int reset = 0; + + /* adjust timeout of next entry */ + if (next) + next->le_when += le->le_when; + + if (TAILQ_FIRST(&nbr->ls_retrans_list) == le && + evtimer_pending(&nbr->ls_retrans_timer, NULL)) + reset = 1; + + TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); + nbr->ls_ret_cnt--; + + if (reset && TAILQ_FIRST(&nbr->ls_retrans_list)) { + if (evtimer_del(&nbr->ls_retrans_timer) == -1) + fatal("ls_retrans_list_remove"); + + timerclear(&tv); + tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when; + + if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) + fatal("ls_retrans_list_remove"); + } +} + +void +ls_retrans_list_free(struct nbr *nbr, struct lsa_entry *le) +{ + ls_retrans_list_remove(nbr, le); + + lsa_cache_put(le->le_ref, nbr); + free(le); +} + +void +ls_retrans_list_clr(struct nbr *nbr) +{ + struct lsa_entry *le; + + while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) + ls_retrans_list_free(nbr, le); + + nbr->ls_ret_cnt = 0; +} + +/* ARGSUSED */ +void +ls_retrans_timer(int fd, short event, void *bula) +{ + struct timeval tv; + struct timespec tp; + struct in6_addr addr; + struct nbr *nbr = bula; + struct lsa_entry *le; + struct buf *buf; + time_t now; + int d; + u_int32_t nlsa = 0; + + if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) + le->le_when = 0; /* timer fired */ + else + return; /* queue empty, nothing to do */ + + clock_gettime(CLOCK_MONOTONIC, &tp); + now = tp.tv_sec; + + if (nbr->iface->self == nbr) { + /* + * oneshot needs to be set for lsa queued for flooding, + * if oneshot is not set then the lsa needs to be converted + * because the router switched lately to DR or BDR + */ + if (le->le_oneshot && nbr->iface->state & IF_STA_DRORBDR) + inet_pton(AF_INET6, AllSPFRouters, &addr); + else if (nbr->iface->state & IF_STA_DRORBDR) { + /* + * old retransmission needs to be converted into + * flood by rerunning the lsa_flood. + */ + lsa_flood(nbr->iface, nbr, &le->le_ref->hdr, + le->le_ref->data); + ls_retrans_list_free(nbr, le); + /* ls_retrans_list_free retriggers the timer */ + return; + } else + inet_pton(AF_INET6, AllDRouters, &addr); + } else + memcpy(&addr, &nbr->addr, sizeof(addr)); + + if ((buf = prepare_ls_update(nbr->iface)) == NULL) { + le->le_when = 1; + goto done; + } + + while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL && + le->le_when == 0) { + d = now - le->le_ref->stamp; + if (d < 0) + d = 0; + else if (d > MAX_AGE) + d = MAX_AGE; + + if (add_ls_update(buf, nbr->iface, le->le_ref->data, + le->le_ref->len, d) == 0) + break; + nlsa++; + if (le->le_oneshot) + ls_retrans_list_free(nbr, le); + else { + TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); + nbr->ls_ret_cnt--; + le->le_when = nbr->iface->rxmt_interval; + ls_retrans_list_insert(nbr, le); + } + } + send_ls_update(buf, nbr->iface, addr, nlsa); + +done: + if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) { + timerclear(&tv); + tv.tv_sec = le->le_when; + + if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) + fatal("ls_retrans_timer"); + } +} + +LIST_HEAD(lsa_cache_head, lsa_ref); + +struct lsa_cache { + struct lsa_cache_head *hashtbl; + u_int32_t hashmask; +} lsacache; + +struct lsa_ref *lsa_cache_look(struct lsa_hdr *); + +void +lsa_cache_init(u_int32_t hashsize) +{ + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + lsacache.hashtbl = calloc(hs, sizeof(struct lsa_cache_head)); + if (lsacache.hashtbl == NULL) + fatal("lsa_cache_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&lsacache.hashtbl[i]); + + lsacache.hashmask = hs - 1; +} + +struct lsa_ref * +lsa_cache_add(void *data, u_int16_t len) +{ + struct lsa_cache_head *head; + struct lsa_ref *ref, *old; + struct timespec tp; + + if ((ref = calloc(1, sizeof(*ref))) == NULL) + fatal("lsa_cache_add"); + memcpy(&ref->hdr, data, sizeof(ref->hdr)); + + if ((old = lsa_cache_look(&ref->hdr))) { + free(ref); + old->refcnt++; + return (old); + } + + if ((ref->data = malloc(len)) == NULL) + fatal("lsa_cache_add"); + memcpy(ref->data, data, len); + + clock_gettime(CLOCK_MONOTONIC, &tp); + ref->stamp = tp.tv_sec; + ref->len = len; + ref->refcnt = 1; + + head = &lsacache.hashtbl[hash32_buf(&ref->hdr, sizeof(ref->hdr), + HASHINIT) & lsacache.hashmask]; + LIST_INSERT_HEAD(head, ref, entry); + return (ref); +} + +struct lsa_ref * +lsa_cache_get(struct lsa_hdr *lsa_hdr) +{ + struct lsa_ref *ref; + + ref = lsa_cache_look(lsa_hdr); + if (ref) + ref->refcnt++; + + return (ref); +} + +void +lsa_cache_put(struct lsa_ref *ref, struct nbr *nbr) +{ + if (--ref->refcnt > 0) + return; + + if (ntohs(ref->hdr.age) >= MAX_AGE) + ospfe_imsg_compose_rde(IMSG_LS_MAXAGE, nbr->peerid, 0, + ref->data, sizeof(struct lsa_hdr)); + + free(ref->data); + LIST_REMOVE(ref, entry); + free(ref); +} + +struct lsa_ref * +lsa_cache_look(struct lsa_hdr *lsa_hdr) +{ + struct lsa_cache_head *head; + struct lsa_ref *ref; + + head = &lsacache.hashtbl[hash32_buf(lsa_hdr, sizeof(*lsa_hdr), + HASHINIT) & lsacache.hashmask]; + + LIST_FOREACH(ref, head, entry) { + if (memcmp(&ref->hdr, lsa_hdr, sizeof(*lsa_hdr)) == 0) + /* found match */ + return (ref); + } + + return (NULL); +} diff --git a/usr.sbin/ospf6d/name2id.c b/usr.sbin/ospf6d/name2id.c new file mode 100644 index 00000000000..4d614ec867e --- /dev/null +++ b/usr.sbin/ospf6d/name2id.c @@ -0,0 +1,228 @@ +/* $OpenBSD: name2id.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005 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 MIND, 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 <net/route.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "ospf6d.h" + +#define IDVAL_MAX 50000 + +u_int16_t _name2id(struct n2id_labels *, const char *); +const char *_id2name(struct n2id_labels *, u_int16_t); +u_int32_t _id2tag(struct n2id_labels *, u_int16_t); +u_int16_t _tag2id(struct n2id_labels *, u_int32_t); +void _tag(struct n2id_labels *, u_int16_t, u_int32_t); +void _unref(struct n2id_labels *, u_int16_t); +void _ref(struct n2id_labels *, u_int16_t); + +struct n2id_labels rt_labels = TAILQ_HEAD_INITIALIZER(rt_labels); + +u_int16_t +rtlabel_name2id(const char *name) +{ + return (_name2id(&rt_labels, name)); +} + +const char * +rtlabel_id2name(u_int16_t id) +{ + return (_id2name(&rt_labels, id)); +} + +u_int32_t +rtlabel_id2tag(u_int16_t id) +{ + return (_id2tag(&rt_labels, id)); +} + +u_int16_t +rtlabel_tag2id(u_int32_t tag) +{ + return (_tag2id(&rt_labels, tag)); +} + +void +rtlabel_tag(u_int16_t id, u_int32_t tag) +{ + _tag(&rt_labels, id, tag); +} + +void +rtlabel_unref(u_int16_t id) +{ + _unref(&rt_labels, id); +} + +/* +void +rtlabel_ref(u_int16_t id) +{ + _ref(&rt_labels, id); +} +*/ + +u_int16_t +_name2id(struct n2id_labels *head, const char *name) +{ + struct n2id_label *label, *p = NULL; + u_int16_t new_id = 1; + + if (!name[0]) { + errno = EINVAL; + return (0); + } + + TAILQ_FOREACH(label, head, entry) + if (strcmp(name, label->name) == 0) { + label->ref++; + return (label->id); + } + + /* + * to avoid fragmentation, we do a linear search from the beginning + * and take the first free slot we find. if there is none or the list + * is empty, append a new entry at the end. + */ + + if (!TAILQ_EMPTY(head)) + for (p = TAILQ_FIRST(head); p != NULL && + p->id == new_id; p = TAILQ_NEXT(p, entry)) + new_id = p->id + 1; + + if (new_id > IDVAL_MAX) { + errno = ERANGE; + return (0); + } + + if ((label = calloc(1, sizeof(struct n2id_label))) == NULL) + return (0); + if ((label->name = strdup(name)) == NULL) { + free(label); + return (0); + } + label->id = new_id; + label->ref++; + + if (p != NULL) /* insert new entry before p */ + TAILQ_INSERT_BEFORE(p, label, entry); + else /* either list empty or no free slot in between */ + TAILQ_INSERT_TAIL(head, label, entry); + + return (label->id); +} + +const char * +_id2name(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return (""); + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + return (label->name); + + return (""); +} + +u_int32_t +_id2tag(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return (0); + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + return (label->ext_tag); + + return (0); +} + +u_int16_t +_tag2id(struct n2id_labels *head, u_int32_t tag) +{ + struct n2id_label *label; + + if (tag == 0) + return (0); + + TAILQ_FOREACH(label, head, entry) + if (label->ext_tag == tag) + return (label->id); + + return (0); +} + +void +_tag(struct n2id_labels *head, u_int16_t id, u_int32_t tag) +{ + struct n2id_label *label; + + if (id == 0) + return; + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) + label->ext_tag = tag; +} + +void +_unref(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *p, *next; + + if (id == 0) + return; + + for (p = TAILQ_FIRST(head); p != NULL; p = next) { + next = TAILQ_NEXT(p, entry); + if (id == p->id) { + if (--p->ref == 0) { + TAILQ_REMOVE(head, p, entry); + free(p->name); + free(p); + } + break; + } + } +} + +void +_ref(struct n2id_labels *head, u_int16_t id) +{ + struct n2id_label *label; + + if (id == 0) + return; + + TAILQ_FOREACH(label, head, entry) + if (label->id == id) { + ++label->ref; + break; + } +} diff --git a/usr.sbin/ospf6d/neighbor.c b/usr.sbin/ospf6d/neighbor.c new file mode 100644 index 00000000000..0b154db78f5 --- /dev/null +++ b/usr.sbin/ospf6d/neighbor.c @@ -0,0 +1,705 @@ +/* $OpenBSD: neighbor.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2007 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 "ospf6d.h" +#include "ospf6.h" +#include "ospfe.h" +#include "log.h" +#include "rde.h" + +int nbr_adj_ok(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_ACTIVE, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, 0}, + {NBR_STA_BIDIR, NBR_EVT_2_WAY_RCVD, NBR_ACT_NOTHING, 0}, + {NBR_STA_INIT, NBR_EVT_1_WAY_RCVD, NBR_ACT_NOTHING, 0}, + {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_INIT}, + {NBR_STA_ATTEMPT, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, NBR_STA_INIT}, + {NBR_STA_INIT, NBR_EVT_2_WAY_RCVD, NBR_ACT_EVAL, 0}, + {NBR_STA_XSTRT, NBR_EVT_NEG_DONE, NBR_ACT_SNAP, NBR_STA_SNAP}, + {NBR_STA_SNAP, NBR_EVT_SNAP_DONE, NBR_ACT_SNAP_DONE, NBR_STA_XCHNG}, + {NBR_STA_XCHNG, NBR_EVT_XCHNG_DONE, NBR_ACT_XCHNG_DONE, 0}, + {NBR_STA_LOAD, NBR_EVT_LOAD_DONE, NBR_ACT_NOTHING, NBR_STA_FULL}, + {NBR_STA_2_WAY, NBR_EVT_ADJ_OK, NBR_ACT_EVAL, 0}, + {NBR_STA_ADJFORM, NBR_EVT_ADJ_OK, NBR_ACT_ADJ_OK, 0}, + {NBR_STA_PRELIM, NBR_EVT_ADJ_OK, NBR_ACT_HELLO_CHK, 0}, + {NBR_STA_ADJFORM, NBR_EVT_ADJTMOUT, NBR_ACT_RESTRT_DD, 0}, + {NBR_STA_FLOOD, NBR_EVT_SEQ_NUM_MIS, NBR_ACT_RESTRT_DD, NBR_STA_XSTRT}, + {NBR_STA_FLOOD, NBR_EVT_BAD_LS_REQ, NBR_ACT_RESTRT_DD, NBR_STA_XSTRT}, + {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_BIDIR, NBR_EVT_1_WAY_RCVD, NBR_ACT_CLR_LST, NBR_STA_INIT}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "HELLO_RECEIVED", + "2_WAY_RECEIVED", + "NEGOTIATION_DONE", + "SNAPSHOT_DONE", + "EXCHANGE_DONE", + "BAD_LS_REQ", + "LOADING_DONE", + "ADJ_OK", + "SEQ_NUM_MISMATCH", + "1_WAY_RECEIVED", + "KILL_NBR", + "INACTIVITY_TIMER", + "LL_DOWN", + "ADJ_TIMEOUT" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "RESET_INACTIVITY_TIMER", + "START_INACTIVITY_TIMER", + "EVAL", + "SNAPSHOT", + "SNAPSHOT_DONE", + "EXCHANGE_DONE", + "ADJ_OK", + "RESET_DD", + "DELETE", + "CLEAR_LISTS", + "HELLO_CHK" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i, ret = 0; + + if (nbr == nbr->iface->self) + return (0); + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if ((nbr_fsm_tbl[i].state & old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* 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_names[event], + nbr_state_name(old_state)); + return (0); + } + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_ITIMER: + ret = nbr_act_reset_itimer(nbr); + break; + case NBR_ACT_STRT_ITIMER: + ret = nbr_act_start_itimer(nbr); + break; + case NBR_ACT_EVAL: + ret = nbr_act_eval(nbr); + break; + case NBR_ACT_SNAP: + ret = nbr_act_snapshot(nbr); + break; + case NBR_ACT_SNAP_DONE: + /* start db exchange */ + start_db_tx_timer(nbr); + break; + case NBR_ACT_XCHNG_DONE: + ret = nbr_act_exchange_done(nbr); + break; + case NBR_ACT_ADJ_OK: + ret = nbr_act_adj_ok(nbr); + break; + case NBR_ACT_RESTRT_DD: + ret = nbr_act_restart_dd(nbr); + break; + case NBR_ACT_DEL: + ret = nbr_act_delete(nbr); + break; + case NBR_ACT_CLR_LST: + ret = nbr_act_clear_lists(nbr); + break; + case NBR_ACT_HELLO_CHK: + ret = nbr_act_hello_check(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_warnx("nbr_fsm: error changing state for neighbor ID %s, " + "event %s, state %s", inet_ntoa(nbr->id), + nbr_event_names[event], nbr_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + nbr->stats.sta_chng++; + /* state change inform RDE */ + ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE, + nbr->peerid, 0, &new_state, sizeof(new_state)); + + if (old_state & NBR_STA_FULL || nbr->state & NBR_STA_FULL) { + /* + * neighbor changed from/to FULL + * originate new rtr and net LSA + */ + area_track(nbr->iface->area, nbr->state); + orig_rtr_lsa(nbr->iface->area); + if (nbr->iface->state & IF_STA_DR) + orig_net_lsa(nbr->iface); + + gettimeofday(&now, NULL); + nbr->uptime = now.tv_sec; + } + + /* bidirectional communication lost */ + if (old_state & ~NBR_STA_PRELIM && nbr->state & NBR_STA_PRELIM) + if_fsm(nbr->iface, IF_EVT_NBR_CHNG); + + log_debug("nbr_fsm: event %s resulted in action %s and " + "changing state for neighbor ID %s from %s to %s", + nbr_event_names[event], + nbr_action_names[nbr_fsm_tbl[i].action], + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + + if (nbr->iface->type == IF_TYPE_VIRTUALLINK) { + orig_rtr_lsa(nbr->iface->area); + } + } + + return (ret); +} + +void +nbr_init(u_int32_t hashsize) +{ + struct nbr_head *head; + struct nbr *nbr; + 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; + + /* allocate a dummy neighbor used for self originated AS ext routes */ + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_init"); + + nbr->id.s_addr = ospfe_router_id(); + nbr->state = NBR_STA_DOWN; + nbr->peerid = NBR_IDSELF; + head = NBR_HASH(nbr->peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + TAILQ_INIT(&nbr->ls_retrans_list); + TAILQ_INIT(&nbr->db_sum_list); + TAILQ_INIT(&nbr->ls_req_list); +} + +struct nbr * +nbr_new(u_int32_t nbr_id, struct iface *iface, int self) +{ + struct nbr_head *head; + struct nbr *nbr; + struct rde_nbr rn; + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_new"); + + nbr->state = NBR_STA_DOWN; + nbr->master = 1; + nbr->dd_seq_num = arc4random(); /* RFC: some unique value */ + 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->ls_retrans_list); + TAILQ_INIT(&nbr->db_sum_list); + TAILQ_INIT(&nbr->ls_req_list); + + nbr->ls_req = NULL; + + if (self) { + nbr->state = NBR_STA_FULL; +//XXX nbr->addr.s_addr = iface->addr.s_addr; + nbr->addr = iface->addr; + nbr->priority = iface->priority; + } + + /* set event structures */ + evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr); + evtimer_set(&nbr->db_tx_timer, db_tx_timer, nbr); + evtimer_set(&nbr->lsreq_tx_timer, ls_req_tx_timer, nbr); + evtimer_set(&nbr->ls_retrans_timer, ls_retrans_timer, nbr); + evtimer_set(&nbr->adj_timer, nbr_adj_timer, nbr); + + bzero(&rn, sizeof(rn)); + rn.id.s_addr = nbr->id.s_addr; + rn.area_id.s_addr = nbr->iface->area->id.s_addr; + rn.state = nbr->state; + rn.self = self; + ospfe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn, + sizeof(rn)); + + return (nbr); +} + +void +nbr_del(struct nbr *nbr) +{ + ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); + + if (evtimer_pending(&nbr->inactivity_timer, NULL)) + evtimer_del(&nbr->inactivity_timer); + if (evtimer_pending(&nbr->db_tx_timer, NULL)) + evtimer_del(&nbr->db_tx_timer); + if (evtimer_pending(&nbr->lsreq_tx_timer, NULL)) + evtimer_del(&nbr->lsreq_tx_timer); + if (evtimer_pending(&nbr->ls_retrans_timer, NULL)) + evtimer_del(&nbr->ls_retrans_timer); + if (evtimer_pending(&nbr->adj_timer, NULL)) + evtimer_del(&nbr->adj_timer); + + /* clear lists */ + ls_retrans_list_clr(nbr); + db_sum_list_clr(nbr); + ls_req_list_clr(nbr); + + 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_id(struct iface *iface, u_int32_t rtr_id) +{ + struct nbr *nbr = NULL; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->id.s_addr == rtr_id) + return (nbr); + } + + return (NULL); +} + +/* timers */ +/* ARGSUSED */ +void +nbr_itimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + if (nbr->state == NBR_STA_DOWN) + nbr_del(nbr); + else + nbr_fsm(nbr, NBR_EVT_ITIMER); +} + +void +nbr_start_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + if (evtimer_add(&nbr->inactivity_timer, &tv) == -1) + fatal("nbr_start_itimer"); +} + +void +nbr_stop_itimer(struct nbr *nbr) +{ + if (evtimer_del(&nbr->inactivity_timer) == -1) + fatal("nbr_stop_itimer"); +} + +void +nbr_reset_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + if (evtimer_add(&nbr->inactivity_timer, &tv) == -1) + fatal("nbr_reset_itimer"); +} + +/* ARGSUSED */ +void +nbr_adj_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + if (nbr->state == NBR_STA_2_WAY) + return ; + + if (nbr->state & NBR_STA_ACTIVE && nbr->state != NBR_STA_FULL) { + log_warnx("nbr_adj_timer: failed to form adjacency with %s", + inet_ntoa(nbr->id)); + nbr_fsm(nbr, NBR_EVT_ADJTMOUT); + } +} + +void +nbr_start_adj_timer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = DEFAULT_ADJ_TMOUT; + + if (evtimer_add(&nbr->adj_timer, &tv) == -1) + fatal("nbr_start_adj_timer"); +} + +/* actions */ +int +nbr_act_reset_itimer(struct nbr *nbr) +{ + nbr_reset_itimer(nbr); + + return (0); +} + +int +nbr_act_start_itimer(struct nbr *nbr) +{ + nbr_start_itimer(nbr); + + return (0); +} + +int +nbr_adj_ok(struct nbr *nbr) +{ + struct iface *iface = nbr->iface; + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_VIRTUALLINK: + case IF_TYPE_POINTOMULTIPOINT: + /* always ok */ + break; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + /* + * if neighbor is dr, bdr or router self is dr or bdr + * start forming adjacency + */ + if (iface->dr == nbr || iface->bdr == nbr || + iface->state & IF_STA_DRORBDR) + break; + return (0); + default: + fatalx("nbr_act_ok: unknown interface type"); + } + return (1); +} + +int +nbr_act_eval(struct nbr *nbr) +{ + if (!nbr_adj_ok(nbr)) { + nbr->state = NBR_STA_2_WAY; + return (0); + } + + nbr->state = NBR_STA_XSTRT; + nbr->master = 1; + nbr->dd_seq_num++; /* as per RFC */ + nbr->dd_pending = 0; + /* initial db negotiation */ + start_db_tx_timer(nbr); + + nbr_start_adj_timer(nbr); + + return (0); +} + +int +nbr_act_snapshot(struct nbr *nbr) +{ + stop_db_tx_timer(nbr); + + ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0); + + return (0); +} + +int +nbr_act_exchange_done(struct nbr *nbr) +{ + if (nbr->master) + stop_db_tx_timer(nbr); + + if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG && + nbr->dd_pending == 0) { + nbr->state = NBR_STA_FULL; + return (0); + } + + nbr->state = NBR_STA_LOAD; + + if (!ls_req_list_empty(nbr)) + start_ls_req_tx_timer(nbr); + + return (0); +} + +int +nbr_act_adj_ok(struct nbr *nbr) +{ + if (nbr_adj_ok(nbr)) { + if (nbr->state == NBR_STA_2_WAY) + return (nbr_act_eval(nbr)); + } else { + nbr->state = NBR_STA_2_WAY; + return (nbr_act_clear_lists(nbr)); + } + + return (0); +} + +int +nbr_act_restart_dd(struct nbr *nbr) +{ + nbr_act_clear_lists(nbr); + + if (!nbr_adj_ok(nbr)) { + nbr->state = NBR_STA_2_WAY; + return (0); + } + + nbr->state = NBR_STA_XSTRT; + nbr->master = 1; + nbr->dd_seq_num += arc4random() & 0xffff; + nbr->dd_pending = 0; + + /* initial db negotiation */ + start_db_tx_timer(nbr); + + nbr_start_adj_timer(nbr); + + return (0); +} + +int +nbr_act_delete(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return (0); + + /* stop timers */ + nbr_stop_itimer(nbr); + + /* clear dr and bdr */ + nbr->dr.s_addr = 0; + nbr->bdr.s_addr = 0; + nbr->crypt_seq_num = 0; + + /* schedule kill timer */ + timerclear(&tv); + tv.tv_sec = DEFAULT_NBR_TMOUT; + + if (evtimer_add(&nbr->inactivity_timer, &tv)) { + log_warnx("nbr_act_delete: error scheduling neighbor ID %s " + "for removal", inet_ntoa(nbr->id)); + } + + return (nbr_act_clear_lists(nbr)); +} + +int +nbr_act_clear_lists(struct nbr *nbr) +{ + /* stop timers */ + stop_db_tx_timer(nbr); + stop_ls_req_tx_timer(nbr); + + /* clear lists */ + ls_retrans_list_clr(nbr); + db_sum_list_clr(nbr); + ls_req_list_clr(nbr); + + return (0); +} + +int +nbr_act_hello_check(struct nbr *nbr) +{ + log_debug("nbr_act_hello_check: neighbor ID %s", inet_ntoa(nbr->id)); + + return (-1); +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval tv, now, res; + struct lsa_entry *le; + + 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)); + memcpy(&nctl.dr, &nbr->dr, sizeof(nctl.dr)); + memcpy(&nctl.bdr, &nbr->bdr, sizeof(nctl.bdr)); + memcpy(&nctl.area, &nbr->iface->area->id, sizeof(nctl.area)); + + /* this list is 99% of the time empty so that's OK for now */ + nctl.db_sum_lst_cnt = 0; + TAILQ_FOREACH(le, &nbr->db_sum_list, entry) + nctl.db_sum_lst_cnt++; + + nctl.ls_req_lst_cnt = nbr->ls_req_cnt; + nctl.ls_retrans_lst_cnt = nbr->ls_ret_cnt; + + nctl.nbr_state = nbr->state; + + /* + * We need to trick a bit to show the remote iface state. + * The idea is to print DR, BDR or DROther dependent on + * the type of the neighbor. + */ + if (nbr->iface->dr == nbr) + nctl.iface_state = IF_STA_DR; + else if (nbr->iface->bdr == nbr) + nctl.iface_state = IF_STA_BACKUP; + else if (nbr->iface->state & IF_STA_MULTI) + nctl.iface_state = IF_STA_DROTHER; + else + nctl.iface_state = nbr->iface->state; + + nctl.state_chng_cnt = nbr->stats.sta_chng; + + nctl.priority = nbr->priority; + nctl.options = nbr->options; + + gettimeofday(&now, NULL); + if (evtimer_pending(&nbr->inactivity_timer, &tv)) { + timersub(&tv, &now, &res); + if (nbr->state & NBR_STA_DOWN) + nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec; + else + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; + + if (nbr->state == NBR_STA_FULL) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} + +struct lsa_hdr * +lsa_hdr_new(void) +{ + struct lsa_hdr *lsa_hdr = NULL; + + if ((lsa_hdr = calloc(1, sizeof(*lsa_hdr))) == NULL) + fatal("lsa_hdr_new"); + + return (lsa_hdr); +} diff --git a/usr.sbin/ospf6d/ospf6.h b/usr.sbin/ospf6d/ospf6.h new file mode 100644 index 00000000000..cd92a93f534 --- /dev/null +++ b/usr.sbin/ospf6d/ospf6.h @@ -0,0 +1,240 @@ +/* $OpenBSD: ospf6.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005, 2007 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. + */ + +/* OSPF protocol definitions */ + +#ifndef _OSPF_H_ +#define _OSPF_H_ + +#include <netinet/in.h> + +/* misc */ +#define OSPF6_VERSION 3 +#define IPPROTO_OSPF 89 +#define AllSPFRouters "ff02::5" +#define AllDRouters "ff02::6" + +#define PACKET_HDR 100 /* XXX used to calculate the IP payload */ + +#define DEFAULT_METRIC 10 +#define DEFAULT_REDIST_METRIC 100 +#define MIN_METRIC 1 +#define MAX_METRIC 65535 /* sum & as-ext lsa use 24bit metrics */ + +#define DEFAULT_PRIORITY 1 +#define MIN_PRIORITY 0 +#define MAX_PRIORITY 255 + +#define DEFAULT_HELLO_INTERVAL 10 +#define MIN_HELLO_INTERVAL 1 +#define MAX_HELLO_INTERVAL 65535 + +#define DEFAULT_RTR_DEAD_TIME 40 +#define MIN_RTR_DEAD_TIME 2 +#define MAX_RTR_DEAD_TIME 2147483647 + +#define DEFAULT_RXMT_INTERVAL 5 +#define MIN_RXMT_INTERVAL 5 +#define MAX_RXMT_INTERVAL 3600 + +#define DEFAULT_TRANSMIT_DELAY 1 +#define MIN_TRANSMIT_DELAY 1 +#define MAX_TRANSMIT_DELAY 3600 + +#define DEFAULT_ADJ_TMOUT 60 + +#define DEFAULT_NBR_TMOUT 86400 /* 24 hours */ + +#define DEFAULT_SPF_DELAY 1 +#define MIN_SPF_DELAY 1 +#define MAX_SPF_DELAY 10 + +#define DEFAULT_SPF_HOLDTIME 5 +#define MIN_SPF_HOLDTIME 1 +#define MAX_SPF_HOLDTIME 5 + +#define MIN_MD_ID 0 +#define MAX_MD_ID 255 + +#define DEFAULT_INSTANCE_ID 0 +#define MIN_INSTANCE_ID 0 +#define MAX_INSTANCE_ID 0 + +/* OSPF compatibility flags */ +#define OSPF_OPTION_V6 0x01 +#define OSPF_OPTION_E 0x02 +#define OSPF_OPTION_MC 0x04 +#define OSPF_OPTION_N 0x08 +#define OSPF_OPTION_R 0x10 +#define OSPF_OPTION_DC 0x20 + +/* OSPF packet types */ +#define PACKET_TYPE_HELLO 1 +#define PACKET_TYPE_DD 2 +#define PACKET_TYPE_LS_REQUEST 3 +#define PACKET_TYPE_LS_UPDATE 4 +#define PACKET_TYPE_LS_ACK 5 + +/* LSA */ +#define LS_REFRESH_TIME 1800 +#define MIN_LS_INTERVAL 5 +#define MIN_LS_ARRIVAL 1 +#define DEFAULT_AGE 0 +#define MAX_AGE 3600 +#define CHECK_AGE 300 +#define MAX_AGE_DIFF 900 +#define LS_INFINITY 0xffffff +#define RESV_SEQ_NUM 0x80000000 /* reserved and "unused" */ +#define INIT_SEQ_NUM 0x80000001 +#define MAX_SEQ_NUM 0x7fffffff + +/* OSPF header */ +struct crypt { + u_int16_t dummy; + u_int8_t keyid; + u_int8_t len; + u_int32_t seq_num; +}; + +struct ospf_hdr { + u_int8_t version; + u_int8_t type; + u_int16_t len; + u_int32_t rtr_id; + u_int32_t area_id; + u_int16_t chksum; + u_int8_t instance; + u_int8_t zero; /* must be zero */ +}; + +/* Hello header (type 1) */ +struct hello_hdr { + u_int32_t iface_id; + u_int8_t rtr_priority; + u_int8_t opts1; + u_int8_t opts2; + u_int8_t opts3; + u_int16_t hello_interval; + u_int16_t rtr_dead_interval; + u_int32_t d_rtr; + u_int32_t bd_rtr; +}; + +/* Database Description header (type 2) */ +struct db_dscrp_hdr { + u_int32_t opts; + u_int16_t iface_mtu; + u_int8_t zero; /* must be zero */ + u_int8_t bits; + u_int32_t dd_seq_num; +}; + +#define OSPF_DBD_MS 0x01 +#define OSPF_DBD_M 0x02 +#define OSPF_DBD_I 0x04 + +/* Link State Request header (type 3) */ +struct ls_req_hdr { + u_int16_t zero; + u_int16_t type; + u_int32_t ls_id; + u_int32_t adv_rtr; +}; + +/* Link State Update header (type 4) */ +struct ls_upd_hdr { + u_int32_t num_lsa; +}; + +#define LSA_TYPE_ROUTER 1 +#define LSA_TYPE_NETWORK 2 +#define LSA_TYPE_SUM_NETWORK 3 +#define LSA_TYPE_SUM_ROUTER 4 +#define LSA_TYPE_EXTERNAL 5 + +#define LINK_TYPE_POINTTOPOINT 1 +#define LINK_TYPE_TRANSIT_NET 2 +#define LINK_TYPE_STUB_NET 3 +#define LINK_TYPE_VIRTUAL 4 + +/* LSA headers */ +#define LSA_METRIC_MASK 0x00ffffff /* only for sum & as-ext */ +#define LSA_ASEXT_E_FLAG 0x80000000 + +#define OSPF_RTR_B 0x01 +#define OSPF_RTR_E 0x02 +#define OSPF_RTR_V 0x04 + +struct lsa_rtr { + u_int8_t flags; + u_int8_t dummy; + u_int16_t nlinks; +}; + +struct lsa_rtr_link { + u_int32_t id; + u_int32_t data; + u_int8_t type; + u_int8_t num_tos; + u_int16_t metric; +}; + +struct lsa_net { + u_int32_t mask; + u_int32_t att_rtr[1]; +}; + +struct lsa_net_link { + u_int32_t att_rtr; +}; + +struct lsa_sum { + u_int32_t mask; + u_int32_t metric; /* only lower 24 bit */ +}; + +struct lsa_asext { + u_int32_t mask; + u_int32_t metric; /* lower 24 bit plus E bit */ + u_int32_t fw_addr; + u_int32_t ext_tag; +}; + +struct lsa_hdr { + u_int16_t age; + u_int16_t type; + u_int32_t ls_id; + u_int32_t adv_rtr; + u_int32_t seq_num; + u_int16_t ls_chksum; + u_int16_t len; +}; + +#define LS_CKSUM_OFFSET ((u_int16_t)(&((struct lsa_hdr *)0)->ls_chksum)) + +struct lsa { + struct lsa_hdr hdr; + union { + struct lsa_rtr rtr; + struct lsa_net net; + struct lsa_sum sum; + struct lsa_asext asext; + } data; +}; + +#endif /* !_OSPF_H_ */ diff --git a/usr.sbin/ospf6d/ospf6d.8 b/usr.sbin/ospf6d/ospf6d.8 new file mode 100644 index 00000000000..026739491d6 --- /dev/null +++ b/usr.sbin/ospf6d/ospf6d.8 @@ -0,0 +1,160 @@ +.\" $OpenBSD: ospf6d.8,v 1.1 2007/10/08 10:44:50 norby Exp $ +.\" +.\" Copyright (c) 2004, 2005, 2007 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 $Mdocdate: October 8 2007 $ +.Dt OSPF6D 8 +.Os +.Sh NAME +.Nm ospf6d +.Nd "Open Shortest Path First daemon" +.Sh SYNOPSIS +.Nm +.Op Fl dnv +.Op Fl f Ar file +.Sh DESCRIPTION +.Nm +is an Open Shortest Path First +.Pq OSPF +daemon which manages routing tables. +.Pp +This implementation supports OSPF version 3, thus it is only capable of +maintaining IPv6 routing tables. + .Pp +OSPF is an interior gateway protocol designed to superceed RIP. +The OSPF daemon maintains a Link State Database +.Pq LSDB +consisting of routers and networks. +.Pp +Dijkstra's shortest path first algorithm is used to compute the Route +Information Base using the LSDB as input. +The Forwarding Information Base a.k.a. the kernel route table is updated +with information from the RIB. +.Pp +OSPF routers communicate via two multicast groups: ff02::5 all Shortest +Path First routers and ff02::6 all Designated Routers. +The IP protocol number 89 is reserved for OSPF, furthermore OSPF packets +omit the use of TCP and UDP. +.Pp +OSPF has several advantages over RIP. +For example every router has a complete network topology. +Response to changes in the network are faster. +Furthermore fail detection is much improved. +.Pp +All routers in an OSPF network spend most of their time keeping each others +LSDBs in sync. +All routers must have the same information in the LSDB at all times. +Every time the LSDB is updated the RIB is updated; if needed the FIB is +also updated. +.Pp +OSPF aware routers discover each other via OSPF hello packets. +.Pp +In a multiaccess network such as Ethernet it is unfeasible for all routers +to syncronize with all other routers in the network. +In such networks a Designated Router +.Pq DR +and a Backup Designated Router +.Pq BDR +is elected. +The DR's responsibility is to syncronize with all routers; the BDR will +not do much until the DR fails. +The first router in a network is automatically elected DR, the second +router BDR. +All routers have a FULL adjacency with the DR and the BDR. +A router not elected either DR or BDR will have 2-WAY adjacency with all +routers but the DR and BDR. +Routers that form 2-WAY adjacency recognize that they know each other. +In case a DR or BDR should fail another router is elected and all routers +change state on the newly elected router and syncronize to it. +.Pp +When routers are connected via point to point links, the DR and BDR +election is skipped since only two routers are connected. +.Pp +To limit the impact changes in the network have on the LSDB it is possible +to segement OSPF network into areas. +Area 0.0.0.0 a.k.a. the backbone area must always be present. +Routers can be configured as Area Border Router +.Pq ABR , +being part of multiple areas. +Every area must have direct access to the backbone area. +ABRs not directly connected to the backbone area need to establish a +virtual link to a router in the backbone area. +Virtual links are currently not available in +.Nm . +.Pp +AS Border Routers +.Pq ASBR +are connected to an OSPF network and other external networks, BGP, RIP, +or statically routed. +.Pp +.Nm +is usually started at boot time, and can be enabled by +setting the following in +.Pa /etc/rc.conf.local : +.Pp +.Dl ospf6d_flags=\&"\&" +.Pp +See +.Xr rc 8 +and +.Xr rc.conf 8 +for more information on the boot process +and enabling daemons. +.Pp +A running +.Nm +can be controlled with the +.Xr ospf6ctl 8 +utility. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground and log to +.Em stderr . +.It Fl f Ar file +Specify an alternative configuration file. +.It Fl n +Configtest mode. +Only check the configuration file for validity. +.It Fl v +Produce more verbose output. +.El +.Sh FILES +.Bl -tag -width "/var/run/ospf6d.sockXX" -compact +.It /etc/ospf6d.conf +Default +.Nm +configuration file. +.It /var/run/ospf6d.sock +Unix-domain socket used for communication with +.Xr ospf6ctl 8 . +.El +.Sh SEE ALSO +.Xr ospf6d.conf 5 , +.Xr ospf6ctl 8 +.Rs +.%R RFC 2740 +.%T "OSPF for IPv6" +.%D December 1999 +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 4.2 . diff --git a/usr.sbin/ospf6d/ospf6d.c b/usr.sbin/ospf6d/ospf6d.c new file mode 100644 index 00000000000..c8a5d576f11 --- /dev/null +++ b/usr.sbin/ospf6d/ospf6d.c @@ -0,0 +1,826 @@ +/* $OpenBSD: ospf6d.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2007 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <event.h> +#include <err.h> +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "ospfe.h" +#include "control.h" +#include "log.h" +#include "rde.h" + +void main_sig_handler(int, short, void *); +__dead void usage(void); +void ospfd_shutdown(void); +int check_child(pid_t, const char *); + +void main_dispatch_ospfe(int, short, void *); +void main_dispatch_rde(int, short, void *); + +int check_file_secrecy(int, const char *); +void ospf_redistribute_default(int); + +int ospf_reload(void); +int ospf_sendboth(enum imsg_type, void *, u_int16_t); +int merge_interfaces(struct area *, struct area *); +struct iface *iface_lookup(struct area *, struct iface *); + +int pipe_parent2ospfe[2]; +int pipe_parent2rde[2]; +int pipe_ospfe2rde[2]; + +struct ospfd_conf *ospfd_conf = NULL; +struct imsgbuf *ibuf_ospfe; +struct imsgbuf *ibuf_rde; +char *conffile; + +pid_t ospfe_pid = 0; +pid_t rde_pid = 0; + +/* ARGSUSED */ +void +main_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + int die = 0; + + switch (sig) { + case SIGTERM: + case SIGINT: + die = 1; + /* FALLTHROUGH */ + case SIGCHLD: + if (check_child(ospfe_pid, "ospf engine")) { + ospfe_pid = 0; + die = 1; + } + if (check_child(rde_pid, "route decision engine")) { + rde_pid = 0; + die = 1; + } + if (die) + ospfd_shutdown(); + break; + case SIGHUP: + if (ospf_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-cdnv] [-f file]\n", __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + struct area *a; + int ch, opts = 0; + int debug = 0; + int ipforwarding; + int mib[4]; + size_t len; + + conffile = CONF_FILE; + ospfd_process = PROC_MAIN; + + while ((ch = getopt(argc, argv, "cdf:nv")) != -1) { + switch (ch) { + case 'c': + opts |= OSPFD_OPT_FORCE_DEMOTE; + break; + case 'd': + debug = 1; + break; + case 'f': + conffile = optarg; + break; + case 'n': + opts |= OSPFD_OPT_NOACTION; + break; + case 'v': + if (opts & OSPFD_OPT_VERBOSE) + opts |= OSPFD_OPT_VERBOSE2; + opts |= OSPFD_OPT_VERBOSE; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + /* start logging */ + log_init(debug); + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1) + err(1, "sysctl"); + + if (ipforwarding != 1) { + log_warnx("WARNING: IPv6 forwarding NOT enabled, " + "running as stub router"); + opts |= OSPFD_OPT_STUB_ROUTER; + } + + /* fetch interfaces early */ + kif_init(); + + /* parse config file */ + if ((ospfd_conf = parse_config(conffile, opts)) == NULL ) + exit(1); + + if (ospfd_conf->opts & OSPFD_OPT_NOACTION) { + if (ospfd_conf->opts & OSPFD_OPT_VERBOSE) + print_config(ospfd_conf); + else + fprintf(stderr, "configuration OK\n"); + exit(0); + } + + /* check for root privileges */ + if (geteuid()) + errx(1, "need root privileges"); + + /* check for ospfd user */ + if (getpwnam(OSPF6D_USER) == NULL) + errx(1, "unknown user %s", OSPF6D_USER); + + if (!debug) + daemon(1, 0); + + log_info("startup"); + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, + pipe_parent2ospfe) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2rde) == -1) + fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_ospfe2rde) == -1) + fatal("socketpair"); + session_socket_blockmode(pipe_parent2ospfe[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2ospfe[1], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_parent2rde[1], BM_NONBLOCK); + session_socket_blockmode(pipe_ospfe2rde[0], BM_NONBLOCK); + session_socket_blockmode(pipe_ospfe2rde[1], BM_NONBLOCK); + + /* start children */ + rde_pid = rde(ospfd_conf, pipe_parent2rde, pipe_ospfe2rde, + pipe_parent2ospfe); + ospfe_pid = ospfe(ospfd_conf, pipe_parent2ospfe, pipe_ospfe2rde, + pipe_parent2rde); + + /* show who we are */ + setproctitle("parent"); + + event_init(); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sigchld, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + /* setup pipes to children */ + close(pipe_parent2ospfe[1]); + close(pipe_parent2rde[1]); + close(pipe_ospfe2rde[0]); + close(pipe_ospfe2rde[1]); + + if ((ibuf_ospfe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ospfe, pipe_parent2ospfe[0], main_dispatch_ospfe); + imsg_init(ibuf_rde, pipe_parent2rde[0], main_dispatch_rde); + + /* setup event handler */ + ibuf_ospfe->events = EV_READ; + event_set(&ibuf_ospfe->ev, ibuf_ospfe->fd, ibuf_ospfe->events, + ibuf_ospfe->handler, ibuf_ospfe); + event_add(&ibuf_ospfe->ev, NULL); + + ibuf_rde->events = EV_READ; + event_set(&ibuf_rde->ev, ibuf_rde->fd, ibuf_rde->events, + ibuf_rde->handler, ibuf_rde); + event_add(&ibuf_rde->ev, NULL); + + if (kr_init(!(ospfd_conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)) == -1) + fatalx("kr_init failed"); + + /* remove unneded stuff from config */ + while ((a = LIST_FIRST(&ospfd_conf->area_list)) != NULL) { + LIST_REMOVE(a, entry); + area_del(a); + } + + /* redistribute default */ + ospf_redistribute_default(IMSG_NETWORK_ADD); + + event_dispatch(); + + ospfd_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ospfd_shutdown(void) +{ + pid_t pid; + + if (ospfe_pid) + kill(ospfe_pid, SIGTERM); + + if (rde_pid) + kill(rde_pid, SIGTERM); + + control_cleanup(); + kr_shutdown(); + carp_demote_shutdown(); + + do { + if ((pid = wait(NULL)) == -1 && + errno != EINTR && errno != ECHILD) + fatal("wait"); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + msgbuf_clear(&ibuf_ospfe->w); + free(ibuf_ospfe); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + free(ospfd_conf); + + log_info("terminating"); + exit(0); +} + +int +check_child(pid_t pid, const char *pname) +{ + int status; + + if (waitpid(pid, &status, WNOHANG) > 0) { + if (WIFEXITED(status)) { + log_warnx("lost child: %s exited", pname); + return (1); + } + if (WIFSIGNALED(status)) { + log_warnx("lost child: %s terminated; signal %d", + pname, WTERMSIG(status)); + return (1); + } + } + + return (0); +} + +/* imsg handling */ +/* ARGSUSED */ +void +main_dispatch_ospfe(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + struct demote_msg dmsg; + ssize_t n; + int shut = 0; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_RELOAD: + if (ospf_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; + case IMSG_CTL_FIB_COUPLE: + kr_fib_couple(); + break; + case IMSG_CTL_FIB_DECOUPLE: + kr_fib_decouple(); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + kr_show_route(&imsg); + break; + case IMSG_CTL_IFINFO: + if (imsg.hdr.len == IMSG_HEADER_SIZE) + kr_ifinfo(NULL, imsg.hdr.pid); + else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ) + kr_ifinfo(imsg.data, imsg.hdr.pid); + else + log_warnx("IFINFO request with wrong len"); + break; + case IMSG_DEMOTE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(dmsg)) + fatalx("invalid size of OE request"); + memcpy(&dmsg, imsg.data, sizeof(dmsg)); + carp_demote_set(dmsg.demote_group, dmsg.level); + break; + default: + log_debug("main_dispatch_ospfe: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +main_dispatch_rde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + ssize_t n; + int shut = 0; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_KROUTE_CHANGE: + if (kr_change(imsg.data)) + log_warn("main_dispatch_rde: error changing " + "route"); + break; + case IMSG_KROUTE_DELETE: + if (kr_delete(imsg.data)) + log_warn("main_dispatch_rde: error deleting " + "route"); + break; + default: + log_debug("main_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +void +main_imsg_compose_ospfe(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_ospfe, type, 0, pid, data, datalen); +} + +void +main_imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen) +{ + imsg_compose(ibuf_rde, type, 0, pid, data, datalen); +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + warn("cannot stat %s", fname); + return (-1); + } + + if (st.st_uid != 0 && st.st_uid != getuid()) { + warnx("%s: owner not root or current user", fname); + return (-1); + } + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + warnx("%s: group/world readable/writeable", fname); + return (-1); + } + + return (0); +} + +/* this needs to be added here so that ospfctl can be used without libevent */ +void +imsg_event_add(struct imsgbuf *ibuf) +{ + ibuf->events = EV_READ; + if (ibuf->w.queued) + ibuf->events |= EV_WRITE; + + event_del(&ibuf->ev); + event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf); + event_add(&ibuf->ev, NULL); +} + +int +ospf_redistribute(struct kroute *kr, u_int32_t *metric) +{ + struct redistribute *r; + + /* stub area router? */ + if ((ospfd_conf->options & OSPF_OPTION_E) == 0) + return (0); + + /* only allow 0.0.0.0/0 via REDISTRIBUTE_DEFAULT */ + if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0) + return (0); + + SIMPLEQ_FOREACH(r, &ospfd_conf->redist_list, entry) { + switch (r->type & ~REDIST_NO) { + case REDIST_LABEL: + if (kr->rtlabel == r->label) { + *metric = r->metric; + return (r->type & REDIST_NO ? 0 : 1); + } + break; + case REDIST_STATIC: + /* + * Dynamic routes are not redistributable. Placed here + * so that link local addresses can be redistributed + * via a rtlabel. + */ + if (kr->flags & F_DYNAMIC) + continue; + if (kr->flags & F_STATIC) { + *metric = r->metric; + return (r->type & REDIST_NO ? 0 : 1); + } + break; + case REDIST_CONNECTED: + if (kr->flags & F_DYNAMIC) + continue; + if (kr->flags & F_CONNECTED) { + *metric = r->metric; + return (r->type & REDIST_NO ? 0 : 1); + } + break; + case REDIST_ADDR: + if (kr->flags & F_DYNAMIC) + continue; + if ((kr->prefix.s_addr & r->mask.s_addr) == + (r->addr.s_addr & r->mask.s_addr) && + kr->prefixlen >= mask2prefixlen(r->mask.s_addr)) { + *metric = r->metric; + return (r->type & REDIST_NO? 0 : 1); + } + break; + } + } + + return (0); +} + +void +ospf_redistribute_default(int type) +{ + struct rroute rr; + + if (!(ospfd_conf->redistribute & REDISTRIBUTE_DEFAULT)) + return; + + bzero(&rr, sizeof(rr)); + rr.metric = ospfd_conf->defaultmetric; + main_imsg_compose_rde(type, 0, &rr, sizeof(struct rroute)); +} + +int +ospf_reload(void) +{ + struct area *area; + struct iface *iface; + struct ospfd_conf *xconf; + + if ((xconf = parse_config(conffile, ospfd_conf->opts)) == NULL) + return (-1); + + /* send config to childs */ + if (ospf_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1) + return (-1); + + /* send interfaces */ + LIST_FOREACH(area, &xconf->area_list, entry) { + if (ospf_sendboth(IMSG_RECONF_AREA, area, sizeof(*area)) == -1) + return (-1); + + LIST_FOREACH(iface, &area->iface_list, entry) + if (ospf_sendboth(IMSG_RECONF_IFACE, iface, + sizeof(*iface)) == -1) + return (-1); + } + + if (ospf_sendboth(IMSG_RECONF_END, NULL, 0) == -1) + return (-1); + + merge_config(ospfd_conf, xconf); + /* update redistribute lists */ + kr_reload(); + return (0); +} + +int +ospf_sendboth(enum imsg_type type, void *buf, u_int16_t len) +{ + if (imsg_compose(ibuf_ospfe, type, 0, 0, buf, len) == -1) + return (-1); + if (imsg_compose(ibuf_rde, type, 0, 0, buf, len) == -1) + return (-1); + return (0); +} + +void +merge_config(struct ospfd_conf *conf, struct ospfd_conf *xconf) +{ + struct area *a, *xa, *na; + struct iface *iface; + struct redistribute *r; + + /* change of rtr_id needs a restart */ + conf->flags = xconf->flags; + conf->spf_delay = xconf->spf_delay; + conf->spf_hold_time = xconf->spf_hold_time; + conf->redistribute = xconf->redistribute; + + if (ospfd_process == PROC_MAIN) { + /* main process does neither use areas nor interfaces */ + while ((r = SIMPLEQ_FIRST(&conf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&conf->redist_list, entry); + free(r); + } + while ((r = SIMPLEQ_FIRST(&xconf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&xconf->redist_list, entry); + SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry); + } + goto done; + } + + /* merge areas and interfaces */ + for (a = LIST_FIRST(&conf->area_list); a != NULL; a = na) { + na = LIST_NEXT(a, entry); + /* find deleted areas */ + if ((xa = area_find(xconf, a->id)) == NULL) { + if (ospfd_process == PROC_OSPF_ENGINE) { + LIST_FOREACH(iface, &a->iface_list, entry) + if_fsm(iface, IF_EVT_DOWN); + } + LIST_REMOVE(a, entry); + area_del(a); + } + } + + for (xa = LIST_FIRST(&xconf->area_list); xa != NULL; xa = na) { + na = LIST_NEXT(xa, entry); + if ((a = area_find(conf, xa->id)) == NULL) { + LIST_REMOVE(xa, entry); + LIST_INSERT_HEAD(&conf->area_list, xa, entry); + if (ospfd_process == PROC_OSPF_ENGINE) { + /* start interfaces */ + ospfe_demote_area(xa, 0); + LIST_FOREACH(iface, &xa->iface_list, entry) { + if_init(conf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting " + "interface %s", + iface->name); + } + } + } + /* no need to merge interfaces */ + continue; + } + /* + * stub is not yet used but switching between stub and normal + * will be another painful job. + */ + a->stub = xa->stub; + a->stub_default_cost = xa->stub_default_cost; + if (ospfd_process == PROC_RDE_ENGINE) + a->dirty = 1; /* force SPF tree recalculation */ + + /* merge interfaces */ + if (merge_interfaces(a, xa) && + ospfd_process == PROC_OSPF_ENGINE) + a->dirty = 1; /* force rtr LSA update */ + } + + if (ospfd_process == PROC_OSPF_ENGINE) { + LIST_FOREACH(a, &conf->area_list, entry) { + LIST_FOREACH(iface, &a->iface_list, entry) { + if (iface->state == IF_STA_NEW) { + iface->state = IF_STA_DOWN; + if_init(conf, iface); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting " + "interface %s", + iface->name); + } + } + } + if (a->dirty) { + a->dirty = 0; + orig_rtr_lsa(a); + } + } + } + +done: + while ((a = LIST_FIRST(&xconf->area_list)) != NULL) { + LIST_REMOVE(a, entry); + area_del(a); + } + free(xconf); +} + +int +merge_interfaces(struct area *a, struct area *xa) +{ + struct iface *i, *xi, *ni; + int dirty = 0; + + /* problems: + * - new interfaces (easy) + * - deleted interfaces (needs to be done via fsm?) + * - changing passive (painful?) + */ + for (i = LIST_FIRST(&a->iface_list); i != NULL; i = ni) { + ni = LIST_NEXT(i, entry); + if (iface_lookup(xa, i) == NULL) { + log_debug("merge_config: proc %d area %s removing " + "interface %s", ospfd_process, inet_ntoa(a->id), + i->name); + if (ospfd_process == PROC_OSPF_ENGINE) + if_fsm(i, IF_EVT_DOWN); + LIST_REMOVE(i, entry); + if_del(i); + } + } + + for (xi = LIST_FIRST(&xa->iface_list); xi != NULL; xi = ni) { + ni = LIST_NEXT(xi, entry); + if ((i = iface_lookup(a, xi)) == NULL) { + /* new interface but delay initialisation */ + log_debug("merge_config: proc %d area %s adding " + "interface %s", ospfd_process, inet_ntoa(a->id), + xi->name); + LIST_REMOVE(xi, entry); + LIST_INSERT_HEAD(&a->iface_list, xi, entry); + xi->area = a; + if (ospfd_process == PROC_OSPF_ENGINE) + xi->state = IF_STA_NEW; + continue; + } + log_debug("merge_config: proc %d area %s merging interface %s", + ospfd_process, inet_ntoa(a->id), i->name); + i->addr = xi->addr; + i->dst = xi->dst; + i->mask = xi->mask; + i->abr_id = xi->abr_id; + i->baudrate = xi->baudrate; + i->dead_interval = xi->dead_interval; + i->mtu = xi->mtu; + i->transmit_delay = xi->transmit_delay; + i->hello_interval = xi->hello_interval; + i->rxmt_interval = xi->rxmt_interval; + if (i->metric != xi->metric) + dirty = 1; + i->metric = xi->metric; + i->priority = xi->priority; + i->flags = xi->flags; /* needed? */ + i->type = xi->type; /* needed? */ + i->media_type = xi->media_type; /* needed? */ + i->linkstate = xi->linkstate; /* needed? */ + + if (i->passive != xi->passive) { + /* need to restart interface to cope with this change */ + if (ospfd_process == PROC_OSPF_ENGINE) + if_fsm(i, IF_EVT_DOWN); + i->passive = xi->passive; + if (ospfd_process == PROC_OSPF_ENGINE) + if_fsm(i, IF_EVT_UP); + } + } + return (dirty); +} + +struct iface * +iface_lookup(struct area *area, struct iface *iface) +{ + struct iface *i; + + LIST_FOREACH(i, &area->iface_list, entry) + if (i->ifindex == iface->ifindex) + return (i); + return (NULL); +} diff --git a/usr.sbin/ospf6d/ospf6d.conf.5 b/usr.sbin/ospf6d/ospf6d.conf.5 new file mode 100644 index 00000000000..5404d7f9312 --- /dev/null +++ b/usr.sbin/ospf6d/ospf6d.conf.5 @@ -0,0 +1,311 @@ +.\" $OpenBSD: ospf6d.conf.5,v 1.1 2007/10/08 10:44:50 norby Exp $ +.\" +.\" Copyright (c) 2005 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 $Mdocdate: October 8 2007 $ +.Dt OSPF6D.CONF 5 +.Os +.Sh NAME +.Nm ospf6d.conf +.Nd Open Shortest Path First daemon configuration file +.Sh DESCRIPTION +The +.Xr ospf6d 8 +daemon implements the Open Shortest Path First protocol version 3 as described +in RFC 2740. +.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 ospf6d 8 . +A number of global settings can be overruled in specific areas or interfaces. +.It Sy Areas +An OSPF router must be a member of at least one area. +Areas are used to group interfaces, simplifying configuration. +.El +.Sh MACROS +Macros can be defined that will later be expanded in context. +Macro names must start with a letter, and may contain letters, digits, +and underscores. +Macro names may not be reserved words (for example, +.Ic area , +.Ic interface , +or +.Ic hello-interval ) . +Macros are not expanded inside quotes. +.Pp +For example: +.Bd -literal -offset indent +hi="5" +area 0.0.0.0 { + interface em0 { + hello-interval $hi + } +} +.Ed +.Pp +The same can be accomplished by specifying the hello-interval +globally or within the area declaration. +.Sh GLOBAL CONFIGURATION +All interface related settings can be configured globally, per area and per +interface. +The only settings that can be set globally and not overruled are listed below. +.Pp +.Bl -tag -width Ds -compact +.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 . +Setting +.Ic fib-update +to +.Ic \&no +will implicitly set the +.Ic stub Ic router +option to ensure that no trafic tries to transit via this router. +.Pp +.It Xo +.Op Ic no +.Ic redistribute +.Sm off +.Po Ic static Ns \&| Ns Ic connected Ns \&| Ns +.Ic default Pc +.Sm on +.Op Ic set ...\& +.Xc +.It Xo +.Op Ic no +.Ic redistribute Ar prefix Op Ic set ...\& +.Xc +.It Xo +.Op Ic no +.Ic redistribute rtlabel Ar label Op Ic set ...\& +.Xc +If set to +.Ic connected , +routes to directly attached networks will be announced over OSPF. +If set to +.Ic static , +static routes will be announced over OSPF. +If set to +.Ic default , +a default route pointing to this router will be announced over OSPF. +It is possible to specify a network range with +.Ar prefix ; +networks need to be part of that range to be redistributed. +Additionally it is possible to redistribute based on route labels +using the +.Ic rtlabel +keyword. +By default no additional routes will be announced over OSPF. +.Pp +.Ic redistribute +statements are evaluated in sequential order, from first to last. +The first matching rule decides if a route should be redistributed or not. +Matching rules starting with +.Ic no +will force the route to be not announced. +The only exception is +.Ic default , +which will be set no matter what, and additionally +.Ic no +cannot be used together with it. +.Pp +It is possible to set the route +.Ic metric +and +.Ic type +for each redistribute rule. +.Ic type +is either 1 or 2. +The default value for +.Ic type +is 1 and for +.Ic metric +is 100. +Setting more then one option needs curly brackets: +.Bd -literal -offset indent +redistribute static set { metric 300 type 2 } +.Ed +.Pp +.It Xo +.Ic rfc1583compat +.Pq Ic yes Ns \&| Ns Ic no +.Xc +If set to +.Ic yes , +decisions regarding AS-external routes are evaluated according to RFC 1583. +The default is +.Ic no . +.Pp +.It Ic router-id Ar address +Set the router ID; if not specified, the lowest IP address of the router +will be used. +.Pp +.It Ic rtlabel Ar label Ic external-tag Ar number +Map route labels to external route tags and vice versa. +The external route tag is a non-negative 32-bit number attached to +AS-external OSPF LSAs. +.Pp +.It Ic spf-delay Ar seconds +Set SPF delay in seconds. +The delay between receiving an update to the link +state database and starting the shortest path first calculation. +The default value is 1; valid range is 1\-10 seconds. +.Pp +.It Ic spf-holdtime Ar seconds +Set the SPF holdtime in seconds. +The minimum time between two consecutive +shortest path first calculations. +The default value is 5 seconds; the valid range is 1\-5 seconds. +.Pp +.It Xo +.Ic stub Ic router +.Pq Ic yes Ns \&| Ns Ic no +.Xc +If set to +.Ic yes , +all interfaces with active neighbors will have a metric of infinity. +This ensures that the other routers prefer routes around this router while +still being able to reach directly connected IP prefixes. +The +.Ic stub Ic router +option is automatically enabled if either the +.Xr sysctl 8 +variable +.Va net.inet.ip.forwarding +is set to a value different to 1 or if the FIB is not coupled. +.El +.Sh AREAS +Areas are used for grouping interfaces. +All interface specific parameters can +be configured per area, overruling the global settings. +.Bl -tag -width Ds +.It Ic area Ar address +Specify an area section, grouping one or more interfaces. +.Bd -literal -offset indent +area 0.0.0.0 { + interface em0 + interface em1 { + metric 10 + } +} +.Ed +.El +.Pp +Area specific parameters are listed below. +.Bl -tag -width Ds +.It Ic demote Ar group Op Ar count +Increase the +.Xr carp 4 +demotion counter by +.Ar count +on the given interface group, usually +.Ar carp , +when no neighbor in the area is in an active state. +The demotion counter will be decreased when one neighbor in that +area is in an active state. +The default value for +.Ar count +is 1. +.Pp +For more information on interface groups, +see the +.Ic group +keyword in +.Xr ifconfig 8 . +.El +.Sh INTERFACES +Each interface can have several parameters configured individually, otherwise +they are inherited. +An interface is specified by its name. +If multiple networks are configured an additional IP address can be supplied. +By default the first IP address is used. +.Bd -literal -offset indent +interface em0 { + ... +} +interface fxp0:192.168.1.3 +.Ed +.Pp +Interface specific parameters are listed below. +.Bl -tag -width Ds +.It Ic demote Ar group +Increase the +.Xr carp 4 +demotion counter by 1 on the given interface group, usually +.Ar carp , +when the interface state is going down. +The demotion counter will be decreased when the interface +state is active again. +.It Ic hello-interval Ar seconds +Set the hello interval. +The default value is 10; valid range is 1\-65535 seconds. +.It Ic metric Ar cost +Set the interface metric a.k.a. cost. +The default value is 10; valid range is 1\-65535. +.It Ic passive +Prevent transmission and reception of OSPF packets on this interface. +The specified interface will be announced as a stub network. +.It Ic retransmit-interval Ar seconds +Set retransmit interval. +The default value is 5 seconds; valid range is 5\-3600 seconds. +.It Ic router-dead-time Ar seconds +Set the router dead time, a.k.a. neighbor inactivity timer. +The default value is 40 seconds; valid range is 2\-2147483647 seconds. +When a neighbor has been +inactive for router-dead-time its state is set to DOWN. +Neighbors +that have been inactive for more than 24 hours are completely removed. +.It Ic router-priority Ar priority +Set the router priority. +The default value is 1; valid range is 0\-255. +If set +to 0 the router is not eligible as a Designated Router or Backup Designated +Router. +.It Ic transmit-delay Ar seconds +Set the transmit delay. +The default value is 1; valid range is 1\-3600 seconds. +.El +.Sh FILES +.Bl -tag -width "/etc/ospf6d.conf" -compact +.It Pa /etc/ospf6d.conf +.Xr ospf6d 8 +configuration file +.El +.Sh SEE ALSO +.Xr ospf6ctl 8 , +.Xr ospf6d 8 , +.Xr rc.conf.local 8 +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 4.2 . diff --git a/usr.sbin/ospf6d/ospf6d.h b/usr.sbin/ospf6d/ospf6d.h new file mode 100644 index 00000000000..7da73e70a57 --- /dev/null +++ b/usr.sbin/ospf6d/ospf6d.h @@ -0,0 +1,618 @@ +/* $OpenBSD: ospf6d.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2004, 2007 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 _OSPF6D_H_ +#define _OSPF6D_H_ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <md5.h> +#include <net/if.h> +#include <netinet/in.h> +#include <event.h> + +#include "ospf6.h" + +#define CONF_FILE "/etc/ospf6d.conf" +#define OSPF6D_SOCKET "/var/run/ospf6d.sock" +#define OSPF6D_USER "_ospf6d" + +#define NBR_HASHSIZE 128 +#define LSA_HASHSIZE 512 + +#define NBR_IDSELF 1 +#define NBR_CNTSTART (NBR_IDSELF + 1) + +#define READ_BUF_SIZE 65535 +#define PKG_DEF_SIZE 512 /* compromise */ +#define RT_BUF_SIZE 16384 +#define MAX_RTSOCK_BUF 128 * 1024 + +#define OSPFD_FLAG_NO_FIB_UPDATE 0x0001 +#define OSPFD_FLAG_STUB_ROUTER 0x0002 + +#define F_OSPFD_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_REDISTRIBUTED 0x0100 + +#define REDISTRIBUTE_ON 0x01 +#define REDISTRIBUTE_DEFAULT 0x02 + +/* 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_RELOAD, + IMSG_CTL_SHOW_INTERFACE, + IMSG_CTL_SHOW_DATABASE, + IMSG_CTL_SHOW_DB_EXT, + IMSG_CTL_SHOW_DB_NET, + IMSG_CTL_SHOW_DB_RTR, + IMSG_CTL_SHOW_DB_SELF, + IMSG_CTL_SHOW_DB_SUM, + IMSG_CTL_SHOW_DB_ASBR, + IMSG_CTL_SHOW_NBR, + IMSG_CTL_SHOW_RIB, + IMSG_CTL_SHOW_SUM, + IMSG_CTL_SHOW_SUM_AREA, + IMSG_CTL_FIB_COUPLE, + IMSG_CTL_FIB_DECOUPLE, + IMSG_CTL_AREA, + IMSG_CTL_KROUTE, + IMSG_CTL_KROUTE_ADDR, + IMSG_CTL_IFINFO, + IMSG_CTL_END, + IMSG_KROUTE_CHANGE, + IMSG_KROUTE_DELETE, + IMSG_KROUTE_GET, + IMSG_IFINFO, + IMSG_NEIGHBOR_UP, + IMSG_NEIGHBOR_DOWN, + IMSG_NEIGHBOR_CHANGE, + IMSG_NETWORK_ADD, + IMSG_NETWORK_DEL, + IMSG_DD, + IMSG_DD_END, + IMSG_DB_SNAPSHOT, + IMSG_DB_END, + IMSG_LS_REQ, + IMSG_LS_UPD, + IMSG_LS_ACK, + IMSG_LS_FLOOD, + IMSG_LS_BADREQ, + IMSG_LS_MAXAGE, + IMSG_ABR_UP, + IMSG_ABR_DOWN, + IMSG_RECONF_CONF, + IMSG_RECONF_AREA, + IMSG_RECONF_IFACE, + IMSG_RECONF_END, + IMSG_DEMOTE +}; + +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; +}; + +/* area */ +struct vertex; +struct rde_nbr; +RB_HEAD(lsa_tree, vertex); + +struct area { + LIST_ENTRY(area) entry; + struct in_addr id; + struct lsa_tree lsa_tree; + + LIST_HEAD(, iface) iface_list; + LIST_HEAD(, rde_nbr) nbr_list; +/* list addr_range_list; */ + char demote_group[IFNAMSIZ]; + u_int32_t stub_default_cost; + u_int32_t num_spf_calc; + int active; + u_int8_t transit; + u_int8_t stub; + u_int8_t dirty; + u_int8_t demote_level; +}; + +/* interface states */ +#define IF_STA_NEW 0x00 /* dummy state for reload */ +#define IF_STA_DOWN 0x01 +#define IF_STA_LOOPBACK 0x02 +#define IF_STA_WAITING 0x04 +#define IF_STA_POINTTOPOINT 0x08 +#define IF_STA_DROTHER 0x10 +#define IF_STA_BACKUP 0x20 +#define IF_STA_DR 0x40 +#define IF_STA_DRORBDR (IF_STA_DR | IF_STA_BACKUP) +#define IF_STA_MULTI (IF_STA_DROTHER | IF_STA_BACKUP | IF_STA_DR) +#define IF_STA_ANY 0x7f + +/* interface events */ +enum iface_event { + IF_EVT_NOTHING, + IF_EVT_UP, + IF_EVT_WTIMER, + IF_EVT_BACKUP_SEEN, + IF_EVT_NBR_CHNG, + IF_EVT_LOOP, + IF_EVT_UNLOOP, + IF_EVT_DOWN +}; + +/* interface actions */ +enum iface_action { + IF_ACT_NOTHING, + IF_ACT_STRT, + IF_ACT_ELECT, + IF_ACT_RST +}; + +/* interface types */ +enum iface_type { + IF_TYPE_POINTOPOINT, + IF_TYPE_BROADCAST, + IF_TYPE_NBMA, + IF_TYPE_POINTOMULTIPOINT, + IF_TYPE_VIRTUALLINK +}; + +/* neighbor states */ +#define NBR_STA_DOWN 0x0001 +#define NBR_STA_ATTEMPT 0x0002 +#define NBR_STA_INIT 0x0004 +#define NBR_STA_2_WAY 0x0008 +#define NBR_STA_XSTRT 0x0010 +#define NBR_STA_SNAP 0x0020 +#define NBR_STA_XCHNG 0x0040 +#define NBR_STA_LOAD 0x0080 +#define NBR_STA_FULL 0x0100 +#define NBR_STA_ACTIVE (~NBR_STA_DOWN) +#define NBR_STA_FLOOD (NBR_STA_XCHNG | NBR_STA_LOAD | NBR_STA_FULL) +#define NBR_STA_ADJFORM (NBR_STA_XSTRT | NBR_STA_SNAP | NBR_STA_FLOOD) +#define NBR_STA_BIDIR (NBR_STA_2_WAY | NBR_STA_ADJFORM) +#define NBR_STA_PRELIM (NBR_STA_DOWN | NBR_STA_ATTEMPT | NBR_STA_INIT) +#define NBR_STA_ANY 0xffff + +/* neighbor events */ +enum nbr_event { + NBR_EVT_NOTHING, + NBR_EVT_HELLO_RCVD, + NBR_EVT_2_WAY_RCVD, + NBR_EVT_NEG_DONE, + NBR_EVT_SNAP_DONE, + NBR_EVT_XCHNG_DONE, + NBR_EVT_BAD_LS_REQ, + NBR_EVT_LOAD_DONE, + NBR_EVT_ADJ_OK, + NBR_EVT_SEQ_NUM_MIS, + NBR_EVT_1_WAY_RCVD, + NBR_EVT_KILL_NBR, + NBR_EVT_ITIMER, + NBR_EVT_LL_DOWN, + NBR_EVT_ADJTMOUT +}; + +/* neighbor actions */ +enum nbr_action { + NBR_ACT_NOTHING, + NBR_ACT_RST_ITIMER, + NBR_ACT_STRT_ITIMER, + NBR_ACT_EVAL, + NBR_ACT_SNAP, + NBR_ACT_SNAP_DONE, + NBR_ACT_XCHNG_DONE, + NBR_ACT_ADJ_OK, + NBR_ACT_RESTRT_DD, + NBR_ACT_DEL, + NBR_ACT_CLR_LST, + NBR_ACT_HELLO_CHK +}; + +/* spf states */ +enum spf_state { + SPF_IDLE, + SPF_DELAY, + SPF_HOLD, + SPF_HOLDQUEUE +}; + +enum dst_type { + DT_NET, + DT_RTR +}; + +enum path_type { + PT_INTRA_AREA, + PT_INTER_AREA, + PT_TYPE1_EXT, + PT_TYPE2_EXT +}; + +enum rib_type { + RIB_NET = 1, + RIB_RTR, + RIB_EXT +}; + +/* lsa list used in RDE and OE */ +TAILQ_HEAD(lsa_head, lsa_entry); + +struct iface { + LIST_ENTRY(iface) entry; + struct event hello_timer; + struct event wait_timer; + struct event lsack_tx_timer; + + LIST_HEAD(, nbr) nbr_list; + struct lsa_head ls_ack_list; + + char name[IF_NAMESIZE]; + char demote_group[IFNAMSIZ]; + struct in6_addr addr; + struct in6_addr dst; + struct in6_addr mask; + struct in_addr abr_id; + struct nbr *dr; /* designated router */ + struct nbr *bdr; /* backup designated router */ + struct nbr *self; + struct area *area; + + u_int64_t baudrate; + u_int32_t dead_interval; + u_int32_t ls_ack_cnt; + u_int32_t crypt_seq_num; + time_t uptime; + unsigned int ifindex; + int fd; + int state; + int mtu; + u_int16_t flags; + u_int16_t transmit_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + enum iface_type type; + u_int8_t media_type; + u_int8_t linkstate; + u_int8_t priority; + u_int8_t passive; +}; + +/* ospf_conf */ +enum { + PROC_MAIN, + PROC_OSPF_ENGINE, + PROC_RDE_ENGINE +} ospfd_process; + +#define REDIST_CONNECTED 0x01 +#define REDIST_STATIC 0x02 +#define REDIST_LABEL 0x04 +#define REDIST_ADDR 0x08 +#define REDIST_NO 0x10 + +struct redistribute { + SIMPLEQ_ENTRY(redistribute) entry; + struct in_addr addr; + struct in_addr mask; + u_int32_t metric; + u_int16_t label; + u_int16_t type; +}; + +struct ospfd_conf { + struct event ev; + struct in_addr rtr_id; + LIST_HEAD(, area) area_list; + LIST_HEAD(, vertex) cand_list; + SIMPLEQ_HEAD(, redistribute) redist_list; + + u_int32_t defaultmetric; + u_int32_t opts; +#define OSPFD_OPT_VERBOSE 0x00000001 +#define OSPFD_OPT_VERBOSE2 0x00000002 +#define OSPFD_OPT_NOACTION 0x00000004 +#define OSPFD_OPT_STUB_ROUTER 0x00000008 +#define OSPFD_OPT_FORCE_DEMOTE 0x00000010 + u_int32_t spf_delay; + u_int32_t spf_hold_time; + time_t uptime; + int spf_state; + int ospf_socket; + int flags; + int options; /* OSPF options */ + u_int8_t border; + u_int8_t redistribute; +}; + +/* kroute */ +struct kroute { + struct in_addr prefix; + struct in_addr nexthop; + u_int16_t flags; + u_int16_t rtlabel; + u_int32_t ext_tag; + u_short ifindex; + u_int8_t prefixlen; +}; + +struct rroute { + struct kroute kr; + u_int32_t metric; +}; + +struct kif_addr { + TAILQ_ENTRY(kif_addr) entry; + struct in6_addr addr; + struct in6_addr mask; + struct in6_addr dstbrd; +}; + +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 */ +}; + +/* name2id */ +struct n2id_label { + TAILQ_ENTRY(n2id_label) entry; + char *name; + u_int16_t id; + u_int32_t ext_tag; + int ref; +}; + +TAILQ_HEAD(n2id_labels, n2id_label); +extern struct n2id_labels rt_labels; + +/* control data structures */ +struct ctl_iface { + char name[IF_NAMESIZE]; + struct in6_addr addr; + struct in6_addr mask; + struct in_addr area; + struct in_addr rtr_id; + struct in_addr dr_id; + struct in6_addr dr_addr; + struct in_addr bdr_id; + struct in6_addr bdr_addr; + time_t hello_timer; + time_t uptime; + u_int32_t baudrate; + u_int32_t dead_interval; + unsigned int ifindex; + int state; + int mtu; + int nbr_cnt; + int adj_cnt; + u_int16_t transmit_delay; + u_int16_t hello_interval; + u_int16_t flags; + u_int16_t metric; + u_int16_t rxmt_interval; + enum iface_type type; + u_int8_t linkstate; + u_int8_t mediatype; + u_int8_t priority; + u_int8_t passive; +}; + +struct ctl_nbr { + char name[IF_NAMESIZE]; + struct in_addr id; + struct in6_addr addr; + struct in_addr dr; + struct in_addr bdr; + struct in_addr area; + time_t dead_timer; + time_t uptime; + u_int32_t db_sum_lst_cnt; + u_int32_t ls_req_lst_cnt; + u_int32_t ls_retrans_lst_cnt; + u_int32_t state_chng_cnt; + int nbr_state; + int iface_state; + u_int8_t priority; + u_int8_t options; +}; + +struct ctl_rt { + struct in_addr prefix; + struct in_addr nexthop; + struct in_addr area; + struct in_addr adv_rtr; + time_t uptime; + u_int32_t cost; + u_int32_t cost2; + enum path_type p_type; + enum dst_type d_type; + u_int8_t flags; + u_int8_t prefixlen; +}; + +struct ctl_sum { + struct in_addr rtr_id; + u_int32_t spf_delay; + u_int32_t spf_hold_time; + u_int32_t num_ext_lsa; + u_int32_t num_area; + time_t uptime; +}; + +struct ctl_sum_area { + struct in_addr area; + u_int32_t num_iface; + u_int32_t num_adj_nbr; + u_int32_t num_spf_calc; + u_int32_t num_lsa; +}; + +struct demote_msg { + char demote_group[IF_NAMESIZE]; + int level; +}; + +/* area.c */ +struct area *area_new(void); +int area_del(struct area *); +struct area *area_find(struct ospfd_conf *, struct in_addr); +void area_track(struct area *, int); +int area_border_router(struct ospfd_conf *); + +/* 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 *); + +/* carp.c */ +int carp_demote_init(char *, int); +void carp_demote_shutdown(void); +int carp_demote_get(char *); +int carp_demote_set(char *, int); + +/* parse.y */ +struct ospfd_conf *parse_config(char *, int); +int cmdline_symset(char *); + +/* imsg.c */ +void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *)); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + void *, u_int16_t); +struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, + u_int16_t); +int imsg_add(struct buf *, void *, u_int16_t); +int imsg_close(struct imsgbuf *, struct buf *); +void imsg_free(struct imsg *); +void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */ + +/* in_cksum.c */ +u_int16_t in_cksum(void *, size_t); + +/* iso_cksum.c */ +u_int16_t iso_cksum(void *, u_int16_t, u_int16_t); + +/* kroute.c */ +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 *, struct in6_addr *, struct kif_addr **); +void kr_reload(void); + +u_int8_t mask2prefixlen(in_addr_t); +in_addr_t prefixlen2mask(u_int8_t); + +/* log.h */ +const char *nbr_state_name(int); +const char *if_state_name(int); +const char *if_type_name(enum iface_type); +const char *dst_type_name(enum dst_type); +const char *path_type_name(enum path_type); + +/* name2id.c */ +u_int16_t rtlabel_name2id(const char *); +const char *rtlabel_id2name(u_int16_t); +void rtlabel_unref(u_int16_t); +u_int32_t rtlabel_id2tag(u_int16_t); +u_int16_t rtlabel_tag2id(u_int32_t); +void rtlabel_tag(u_int16_t, u_int32_t); + +/* ospfd.c */ +void main_imsg_compose_ospfe(int, pid_t, void *, u_int16_t); +void main_imsg_compose_rde(int, pid_t, void *, u_int16_t); +int ospf_redistribute(struct kroute *, u_int32_t *); +void merge_config(struct ospfd_conf *, struct ospfd_conf *); + +/* printconf.c */ +void print_config(struct ospfd_conf *); + +#endif /* _OSPF6D_H_ */ diff --git a/usr.sbin/ospf6d/ospfe.c b/usr.sbin/ospf6d/ospfe.c new file mode 100644 index 00000000000..9b09c98ea6e --- /dev/null +++ b/usr.sbin/ospf6d/ospfe.c @@ -0,0 +1,1094 @@ +/* $OpenBSD: ospfe.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * 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 "ospf6.h" +#include "ospf6d.h" +#include "ospfe.h" +#include "rde.h" +#include "control.h" +#include "log.h" + +void ospfe_sig_handler(int, short, void *); +void ospfe_shutdown(void); +void orig_rtr_lsa_all(struct area *); +struct iface *find_vlink(struct abr_rtr *); + +struct ospfd_conf *oeconf = NULL, *nconf; +struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_rde; +int oe_nofib; + +/* ARGSUSED */ +void +ospfe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + ospfe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + } +} + +/* ospf engine */ +pid_t +ospfe(struct ospfd_conf *xconf, int pipe_parent2ospfe[2], int pipe_ospfe2rde[2], + int pipe_parent2rde[2]) +{ + struct area *area; + struct iface *iface; + struct redistribute *r; + 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 ospfd control socket outside chroot */ + if (control_init() == -1) + fatalx("control socket setup failed"); + + /* create the raw ip socket */ + if ((xconf->ospf_socket = socket(AF_INET6, SOCK_RAW, + IPPROTO_OSPF)) == -1) + fatal("error creating raw socket"); + + /* set some defaults */ + if (if_set_mcast_loop(xconf->ospf_socket) == -1) + fatal("if_set_mcast_loop"); + if (if_set_ip_hdrincl(xconf->ospf_socket) == -1) + fatal("if_set_ip_hdrincl"); + if (if_set_recvif(xconf->ospf_socket, 1) == -1) + fatal("if_set_recvif"); + if_set_recvbuf(xconf->ospf_socket); + + oeconf = xconf; + if (oeconf->flags & OSPFD_FLAG_NO_FIB_UPDATE) + oe_nofib = 1; + + if ((pw = getpwnam(OSPF6D_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("ospf engine"); + ospfd_process = PROC_OSPF_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); + lsa_cache_init(LSA_HASHSIZE); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, ospfe_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, ospfe_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipes */ + close(pipe_parent2ospfe[0]); + close(pipe_ospfe2rde[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_ospfe2rde[0], ospfe_dispatch_rde); + imsg_init(ibuf_main, pipe_parent2ospfe[1], ospfe_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->ospf_socket, EV_READ|EV_PERSIST, + recv_packet, oeconf); + event_add(&oeconf->ev, NULL); + + /* remove unneeded config stuff */ + while ((r = SIMPLEQ_FIRST(&oeconf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&oeconf->redist_list, entry); + free(r); + } + + /* listen on ospfd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("ospfe"); + + /* start interfaces */ + LIST_FOREACH(area, &oeconf->area_list, entry) { + ospfe_demote_area(area, 0); + LIST_FOREACH(iface, &area->iface_list, entry) { + if_init(xconf, iface); + log_debug("fire up the barbeque on %s", iface->name); + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting interface %s", + iface->name); + } + } + } + + event_dispatch(); + + ospfe_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ospfe_shutdown(void) +{ + struct area *area; + struct iface *iface; + + /* stop all interfaces and remove all areas */ + while ((area = LIST_FIRST(&oeconf->area_list)) != NULL) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + } + LIST_REMOVE(area, entry); + area_del(area); + } + + close(oeconf->ospf_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("ospf engine exiting"); + _exit(0); +} + +/* imesg */ +int +ospfe_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 +ospfe_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 +ospfe_dispatch_main(int fd, short event, void *bula) +{ + static struct area *narea; + static struct iface *niface; + struct imsg imsg; + struct imsgbuf *ibuf = bula; + struct area *area = NULL; + struct iface *iface = NULL; + struct kif *kif; + int n, link_ok, stub_changed, shut = 0; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + 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("ospfe_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) && + (LINK_STATE_IS_UP(kif->link_state) || + (kif->link_state == LINK_STATE_UNKNOWN && + kif->media_type != IFT_CARP)); + + LIST_FOREACH(area, &oeconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (kif->ifindex == iface->ifindex && + iface->type != + IF_TYPE_VIRTUALLINK) { + 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_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ospfd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ospfd_conf)); + + LIST_INIT(&nconf->area_list); + LIST_INIT(&nconf->cand_list); + break; + case IMSG_RECONF_AREA: + if ((narea = area_new()) == NULL) + fatal(NULL); + memcpy(narea, imsg.data, sizeof(struct area)); + + LIST_INIT(&narea->iface_list); + LIST_INIT(&narea->nbr_list); + RB_INIT(&narea->lsa_tree); + + LIST_INSERT_HEAD(&nconf->area_list, narea, entry); + break; + case IMSG_RECONF_IFACE: + if ((niface = malloc(sizeof(struct iface))) == NULL) + fatal(NULL); + memcpy(niface, imsg.data, sizeof(struct iface)); + + LIST_INIT(&niface->nbr_list); + TAILQ_INIT(&niface->ls_ack_list); + + niface->area = narea; + LIST_INSERT_HEAD(&narea->iface_list, niface, entry); + break; + case IMSG_RECONF_END: + if ((oeconf->flags & OSPFD_FLAG_STUB_ROUTER) != + (nconf->flags & OSPFD_FLAG_STUB_ROUTER)) + stub_changed = 1; + else + stub_changed = 0; + merge_config(oeconf, nconf); + nconf = NULL; + if (stub_changed) + orig_rtr_lsa_all(NULL); + break; + case IMSG_CTL_KROUTE: + case IMSG_CTL_KROUTE_ADDR: + case IMSG_CTL_IFINFO: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("ospfe_dispatch_main: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +ospfe_dispatch_rde(int fd, short event, void *bula) +{ + struct lsa_hdr lsa_hdr; + struct imsgbuf *ibuf = bula; + struct nbr *nbr; + struct lsa_hdr *lhp; + struct lsa_ref *ref; + struct area *area; + struct iface *iface; + struct lsa_entry *le; + struct imsg imsg; + struct abr_rtr ar; + int n, noack = 0, shut = 0; + u_int16_t l, age; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + 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("ospfe_dispatch_rde: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_DD: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + /* put these on my ls_req_list for retrieval */ + lhp = lsa_hdr_new(); + memcpy(lhp, imsg.data, sizeof(*lhp)); + ls_req_list_add(nbr, lhp); + break; + case IMSG_DD_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + nbr->dd_pending--; + if (nbr->dd_pending == 0 && nbr->state & NBR_STA_LOAD) { + if (ls_req_list_empty(nbr)) + nbr_fsm(nbr, NBR_EVT_LOAD_DONE); + else + start_ls_req_tx_timer(nbr); + } + break; + case IMSG_DB_SNAPSHOT: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + /* add LSA header to the neighbor db_sum_list */ + lhp = lsa_hdr_new(); + memcpy(lhp, imsg.data, sizeof(*lhp)); + db_sum_list_add(nbr, lhp); + break; + case IMSG_DB_END: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + /* snapshot done, start tx of dd packets */ + nbr_fsm(nbr, NBR_EVT_SNAP_DONE); + break; + case IMSG_LS_FLOOD: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + l = imsg.hdr.len - IMSG_HEADER_SIZE; + if (l < sizeof(lsa_hdr)) + fatalx("ospfe_dispatch_rde: " + "bad imsg size"); + memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr)); + + ref = lsa_cache_add(imsg.data, l); + + if (lsa_hdr.type == LSA_TYPE_EXTERNAL) { + /* + * flood on all areas but stub areas and + * virtual links + */ + LIST_FOREACH(area, &oeconf->area_list, entry) { + if (area->stub) + continue; + LIST_FOREACH(iface, &area->iface_list, + entry) { + noack += lsa_flood(iface, nbr, + &lsa_hdr, imsg.data); + } + } + } else { + /* + * flood on all area interfaces on + * area 0.0.0.0 include also virtual links. + */ + area = nbr->iface->area; + LIST_FOREACH(iface, &area->iface_list, entry) { + noack += lsa_flood(iface, nbr, + &lsa_hdr, imsg.data); + } + /* XXX virtual links */ + } + + /* remove from ls_req_list */ + le = ls_req_list_get(nbr, &lsa_hdr); + if (!(nbr->state & NBR_STA_FULL) && le != NULL) { + ls_req_list_free(nbr, le); + /* + * XXX no need to ack requested lsa + * the problem is that the RFC is very + * unclear about this. + */ + noack = 1; + } + + if (!noack && nbr->iface != NULL && + nbr->iface->self != nbr) { + if (!(nbr->iface->state & IF_STA_BACKUP) || + nbr->iface->dr == nbr) { + /* delayed ack */ + lhp = lsa_hdr_new(); + memcpy(lhp, &lsa_hdr, sizeof(*lhp)); + ls_ack_list_add(nbr->iface, lhp); + } + } + + lsa_cache_put(ref, nbr); + break; + case IMSG_LS_UPD: + /* + * IMSG_LS_UPD is used in three cases: + * 1. as response to ls requests + * 2. as response to ls updates where the DB + * is newer then the sent LSA + * 3. in EXSTART when the LSA has age MaxAge + */ + l = imsg.hdr.len - IMSG_HEADER_SIZE; + if (l < sizeof(lsa_hdr)) + fatalx("ospfe_dispatch_rde: " + "bad imsg size"); + + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + if (nbr->iface->self == nbr) + break; + + memcpy(&age, imsg.data, sizeof(age)); + ref = lsa_cache_add(imsg.data, l); + if (ntohs(age) >= MAX_AGE) + /* add to retransmit list */ + ls_retrans_list_add(nbr, imsg.data, 0, 0); + else + ls_retrans_list_add(nbr, imsg.data, 0, 1); + + lsa_cache_put(ref, nbr); + break; + case IMSG_LS_ACK: + /* + * IMSG_LS_ACK is used in two cases: + * 1. LSA was a duplicate + * 2. LS age is MaxAge and there is no current + * instance in the DB plus no neighbor in state + * Exchange or Loading + */ + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + if (nbr->iface->self == nbr) + break; + + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lsa_hdr)) + fatalx("ospfe_dispatch_rde: bad imsg size"); + memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr)); + + /* for case one check for implied acks */ + if (nbr->iface->state & IF_STA_DROTHER) + if (ls_retrans_list_del(nbr->iface->self, + &lsa_hdr) == 0) + break; + if (ls_retrans_list_del(nbr, &lsa_hdr) == 0) + break; + + /* send a direct acknowledgement */ + send_ls_ack(nbr->iface, nbr->addr, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); + + break; + case IMSG_LS_BADREQ: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + break; + + if (nbr->iface->self == nbr) + fatalx("ospfe_dispatch_rde: " + "dummy neighbor got BADREQ"); + + nbr_fsm(nbr, NBR_EVT_BAD_LS_REQ); + break; + case IMSG_ABR_UP: + memcpy(&ar, imsg.data, sizeof(ar)); + + if ((iface = find_vlink(&ar)) != NULL && + iface->state == IF_STA_DOWN) + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting interface %s", + iface->name); + } + break; + case IMSG_ABR_DOWN: + memcpy(&ar, imsg.data, sizeof(ar)); + + if ((iface = find_vlink(&ar)) != NULL && + iface->state == IF_STA_POINTTOPOINT) + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + break; + case IMSG_CTL_AREA: + case IMSG_CTL_END: + case IMSG_CTL_SHOW_DATABASE: + case IMSG_CTL_SHOW_DB_EXT: + case IMSG_CTL_SHOW_DB_NET: + case IMSG_CTL_SHOW_DB_RTR: + case IMSG_CTL_SHOW_DB_SELF: + case IMSG_CTL_SHOW_DB_SUM: + case IMSG_CTL_SHOW_DB_ASBR: + case IMSG_CTL_SHOW_RIB: + case IMSG_CTL_SHOW_SUM: + case IMSG_CTL_SHOW_SUM_AREA: + control_imsg_relay(&imsg); + break; + default: + log_debug("ospfe_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +struct iface * +find_vlink(struct abr_rtr *ar) +{ + struct area *area; + struct iface *iface = NULL; + + LIST_FOREACH(area, &oeconf->area_list, entry) + LIST_FOREACH(iface, &area->iface_list, entry) + if (iface->abr_id.s_addr == ar->abr_id.s_addr && + iface->type == IF_TYPE_VIRTUALLINK && +//XXX iface->area->id.s_addr == ar->area.s_addr) { + iface->area->id.s_addr == ar->area.s_addr) { +//XXX iface->dst.s_addr = ar->dst_ip.s_addr; + iface->dst = ar->dst_ip; +//XXX iface->addr.s_addr = ar->addr.s_addr; + iface->addr = ar->addr; + iface->metric = ar->metric; + + return (iface); + } + + return (iface); +} + +void +orig_rtr_lsa_all(struct area *area) +{ + struct area *a; + + /* + * update all router LSA in all areas except area itself, + * as this update is already running. + */ + LIST_FOREACH(a, &oeconf->area_list, entry) + if (a != area) + orig_rtr_lsa(a); +} + +void +orig_rtr_lsa(struct area *area) +{ + struct lsa_hdr lsa_hdr; + struct lsa_rtr lsa_rtr; + struct lsa_rtr_link rtr_link; + struct iface *iface; + struct buf *buf; + struct nbr *nbr, *self = NULL; + u_int16_t num_links = 0; + u_int16_t chksum; + u_int8_t border, virtual = 0; + + log_debug("orig_rtr_lsa: area %s", inet_ntoa(area->id)); + + /* XXX READ_BUF_SIZE */ + if ((buf = buf_dynamic(sizeof(lsa_hdr), READ_BUF_SIZE)) == NULL) + fatal("orig_rtr_lsa"); + + /* reserve space for LSA header and LSA Router header */ + if (buf_reserve(buf, sizeof(lsa_hdr)) == NULL) + fatal("orig_rtr_lsa: buf_reserve failed"); + + if (buf_reserve(buf, sizeof(lsa_rtr)) == NULL) + fatal("orig_rtr_lsa: buf_reserve failed"); + + /* links */ + LIST_FOREACH(iface, &area->iface_list, entry) { + if (self == NULL && iface->self != NULL) + self = iface->self; + + bzero(&rtr_link, sizeof(rtr_link)); + + if (iface->state & IF_STA_LOOPBACK) { +//XXX rtr_link.id = iface->addr.s_addr; + rtr_link.data = 0xffffffff; + rtr_link.type = LINK_TYPE_STUB_NET; + rtr_link.metric = htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + continue; + } + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + LIST_FOREACH(nbr, &iface->nbr_list, entry) + if (nbr != iface->self && + nbr->state & NBR_STA_FULL) + break; + if (nbr) { + log_debug("orig_rtr_lsa: point-to-point, " + "interface %s", iface->name); + rtr_link.id = nbr->id.s_addr; +//XXX rtr_link.data = iface->addr.s_addr; + rtr_link.type = LINK_TYPE_POINTTOPOINT; + /* RFC 3137: stub router support */ + if (oeconf->flags & OSPFD_FLAG_STUB_ROUTER || + oe_nofib) + rtr_link.metric = 0xffff; + else + rtr_link.metric = htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + } + if (iface->state & IF_STA_POINTTOPOINT) { + log_debug("orig_rtr_lsa: stub net, " + "interface %s", iface->name); + bzero(&rtr_link, sizeof(rtr_link)); + if (nbr) { +//XXX rtr_link.id = nbr->addr.s_addr; + rtr_link.data = 0xffffffff; + } else { +//XXX rtr_link.id = iface->addr.s_addr; +//XXX rtr_link.data = iface->mask.s_addr; + } + rtr_link.type = LINK_TYPE_STUB_NET; + rtr_link.metric = htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + } + continue; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + if ((iface->state & IF_STA_MULTI)) { + if (iface->dr == iface->self) { + LIST_FOREACH(nbr, &iface->nbr_list, + entry) + if (nbr != iface->self && + nbr->state & NBR_STA_FULL) + break; + } else + nbr = iface->dr; + + if (nbr && nbr->state & NBR_STA_FULL) { + log_debug("orig_rtr_lsa: transit net, " + "interface %s", iface->name); + +//XXX rtr_link.id = iface->dr->addr.s_addr; +//XXX rtr_link.data = iface->addr.s_addr; + rtr_link.type = LINK_TYPE_TRANSIT_NET; + break; + } + } + + if ((iface->flags & IFF_UP) == 0 || + iface->linkstate == LINK_STATE_DOWN || + (!LINK_STATE_IS_UP(iface->linkstate) && + iface->media_type == IFT_CARP)) + continue; + + log_debug("orig_rtr_lsa: stub net, " + "interface %s", iface->name); + +/*XXX rtr_link.id = + iface->addr.s_addr & iface->mask.s_addr; + rtr_link.data = iface->mask.s_addr; +XXX*/ + rtr_link.type = LINK_TYPE_STUB_NET; + break; + case IF_TYPE_VIRTUALLINK: + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr != iface->self && + nbr->state & NBR_STA_FULL) + break; + } + if (nbr) { + rtr_link.id = nbr->id.s_addr; +//XXX rtr_link.data = iface->addr.s_addr; + rtr_link.type = LINK_TYPE_VIRTUAL; + /* RFC 3137: stub router support */ + if (oeconf->flags & OSPFD_FLAG_STUB_ROUTER || + oe_nofib) + rtr_link.metric = 0xffff; + else + rtr_link.metric = htons(iface->metric); + num_links++; + virtual = 1; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + + log_debug("orig_rtr_lsa: virtual link, " + "interface %s", iface->name); + } + continue; + case IF_TYPE_POINTOMULTIPOINT: + log_debug("orig_rtr_lsa: stub net, " + "interface %s", iface->name); +//XXX rtr_link.id = iface->addr.s_addr; + rtr_link.data = 0xffffffff; + rtr_link.type = LINK_TYPE_STUB_NET; + rtr_link.metric = htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr != iface->self && + nbr->state & NBR_STA_FULL) { + bzero(&rtr_link, sizeof(rtr_link)); + log_debug("orig_rtr_lsa: " + "point-to-multipoint, interface %s", + iface->name); +//XXX rtr_link.id = nbr->addr.s_addr; +//XXX rtr_link.data = iface->addr.s_addr; + rtr_link.type = LINK_TYPE_POINTTOPOINT; + /* RFC 3137: stub router support */ + if (oe_nofib || oeconf->flags & + OSPFD_FLAG_STUB_ROUTER) + rtr_link.metric = 0xffff; + else + rtr_link.metric = + htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, + sizeof(rtr_link))) + fatalx("orig_rtr_lsa: " + "buf_add failed"); + } + } + continue; + default: + fatalx("orig_rtr_lsa: unknown interface type"); + } + + rtr_link.num_tos = 0; + /* RFC 3137: stub router support */ + if ((oeconf->flags & OSPFD_FLAG_STUB_ROUTER || oe_nofib) && + rtr_link.type != LINK_TYPE_STUB_NET) + rtr_link.metric = 0xffff; + else + rtr_link.metric = htons(iface->metric); + num_links++; + if (buf_add(buf, &rtr_link, sizeof(rtr_link))) + fatalx("orig_rtr_lsa: buf_add failed"); + } + + /* LSA router header */ + lsa_rtr.flags = 0; + /* + * Set the E bit as soon as an as-ext lsa may be redistributed, only + * setting it in case we redistribute something is not worth the fuss. + */ + if (oeconf->redistribute && (oeconf->options & OSPF_OPTION_E)) + lsa_rtr.flags |= OSPF_RTR_E; + + border = (area_border_router(oeconf) != 0); + if (border != oeconf->border) { + oeconf->border = border; + orig_rtr_lsa_all(area); + } + + if (oeconf->border) + lsa_rtr.flags |= OSPF_RTR_B; + /* TODO set V flag if a active virtual link ends here and the + * area is the tranist area for this link. */ + if (virtual) + lsa_rtr.flags |= OSPF_RTR_V; + + lsa_rtr.dummy = 0; + lsa_rtr.nlinks = htons(num_links); + memcpy(buf_seek(buf, sizeof(lsa_hdr), sizeof(lsa_rtr)), + &lsa_rtr, sizeof(lsa_rtr)); + + /* LSA header */ + lsa_hdr.age = htons(DEFAULT_AGE); + lsa_hdr.type = LSA_TYPE_ROUTER; + lsa_hdr.ls_id = oeconf->rtr_id.s_addr; + lsa_hdr.adv_rtr = oeconf->rtr_id.s_addr; + lsa_hdr.seq_num = htonl(INIT_SEQ_NUM); + lsa_hdr.len = htons(buf->wpos); + lsa_hdr.ls_chksum = 0; /* updated later */ + memcpy(buf_seek(buf, 0, sizeof(lsa_hdr)), &lsa_hdr, sizeof(lsa_hdr)); + + chksum = htons(iso_cksum(buf->buf, buf->wpos, LS_CKSUM_OFFSET)); + memcpy(buf_seek(buf, LS_CKSUM_OFFSET, sizeof(chksum)), + &chksum, sizeof(chksum)); + + if (self) + imsg_compose(ibuf_rde, IMSG_LS_UPD, self->peerid, 0, + buf->buf, buf->wpos); + else + log_warnx("orig_rtr_lsa: empty area %s", + inet_ntoa(area->id)); + + buf_free(buf); +} + +void +orig_net_lsa(struct iface *iface) +{ + struct lsa_hdr lsa_hdr; + struct nbr *nbr; + struct buf *buf; + int num_rtr = 0; + u_int16_t chksum; + + /* XXX READ_BUF_SIZE */ + if ((buf = buf_dynamic(sizeof(lsa_hdr), READ_BUF_SIZE)) == NULL) + fatal("orig_net_lsa"); + + /* reserve space for LSA header and LSA Router header */ + if (buf_reserve(buf, sizeof(lsa_hdr)) == NULL) + fatal("orig_net_lsa: buf_reserve failed"); + + /* LSA net mask and then all fully adjacent routers */ + if (buf_add(buf, &iface->mask, sizeof(iface->mask))) + fatal("orig_net_lsa: buf_add failed"); + + /* fully adjacent neighbors + self */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) + if (nbr->state & NBR_STA_FULL) { + if (buf_add(buf, &nbr->id, sizeof(nbr->id))) + fatal("orig_net_lsa: buf_add failed"); + num_rtr++; + } + + if (num_rtr == 1) { + /* non transit net therefor no need to generate a net lsa */ + buf_free(buf); + return; + } + + /* LSA header */ + if (iface->state & IF_STA_DR) + lsa_hdr.age = htons(DEFAULT_AGE); + else + lsa_hdr.age = htons(MAX_AGE); + + lsa_hdr.type = LSA_TYPE_NETWORK; +//XXX lsa_hdr.ls_id = iface->addr.s_addr; + lsa_hdr.adv_rtr = oeconf->rtr_id.s_addr; + lsa_hdr.seq_num = htonl(INIT_SEQ_NUM); + lsa_hdr.len = htons(buf->wpos); + lsa_hdr.ls_chksum = 0; /* updated later */ + memcpy(buf_seek(buf, 0, sizeof(lsa_hdr)), &lsa_hdr, sizeof(lsa_hdr)); + + chksum = htons(iso_cksum(buf->buf, buf->wpos, LS_CKSUM_OFFSET)); + memcpy(buf_seek(buf, LS_CKSUM_OFFSET, sizeof(chksum)), + &chksum, sizeof(chksum)); + + imsg_compose(ibuf_rde, IMSG_LS_UPD, iface->self->peerid, 0, + buf->buf, buf->wpos); + + buf_free(buf); +} + +u_int32_t +ospfe_router_id(void) +{ + return (oeconf->rtr_id.s_addr); +} + +void +ospfe_fip_update(int type) +{ + int old = oe_nofib; + + if (type == IMSG_CTL_FIB_COUPLE) + oe_nofib = 0; + if (type == IMSG_CTL_FIB_DECOUPLE) + oe_nofib = 1; + if (old != oe_nofib) + orig_rtr_lsa_all(NULL); +} + +void +ospfe_iface_ctl(struct ctl_conn *c, unsigned int idx) +{ + struct area *area; + struct iface *iface; + struct ctl_iface *ictl; + + LIST_FOREACH(area, &oeconf->area_list, entry) + LIST_FOREACH(iface, &area->iface_list, entry) + if (idx == 0 || idx == iface->ifindex) { + ictl = if_to_ctl(iface); + imsg_compose(&c->ibuf, IMSG_CTL_SHOW_INTERFACE, + 0, 0, ictl, sizeof(struct ctl_iface)); + } +} + +void +ospfe_nbr_ctl(struct ctl_conn *c) +{ + struct area *area; + struct iface *iface; + struct nbr *nbr; + struct ctl_nbr *nctl; + + LIST_FOREACH(area, &oeconf->area_list, entry) + LIST_FOREACH(iface, &area->iface_list, entry) + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (iface->self != nbr) { + 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); +} + +void +ospfe_demote_area(struct area *area, int active) +{ + struct demote_msg dmsg; + + if (ospfd_process != PROC_OSPF_ENGINE || + area->demote_group[0] == '\0') + return; + + bzero(&dmsg, sizeof(dmsg)); + strlcpy(dmsg.demote_group, area->demote_group, + sizeof(dmsg.demote_group)); + dmsg.level = area->demote_level; + if (active) + dmsg.level = -dmsg.level; + + ospfe_imsg_compose_parent(IMSG_DEMOTE, 0, &dmsg, sizeof(dmsg)); +} + +void +ospfe_demote_iface(struct iface *iface, int active) +{ + struct demote_msg dmsg; + + if (ospfd_process != PROC_OSPF_ENGINE || + iface->demote_group[0] == '\0') + return; + + bzero(&dmsg, sizeof(dmsg)); + strlcpy(dmsg.demote_group, iface->demote_group, + sizeof(dmsg.demote_group)); + if (active) + dmsg.level = -1; + else + dmsg.level = 1; + + ospfe_imsg_compose_parent(IMSG_DEMOTE, 0, &dmsg, sizeof(dmsg)); +} diff --git a/usr.sbin/ospf6d/ospfe.h b/usr.sbin/ospf6d/ospfe.h new file mode 100644 index 00000000000..ccbe153ae4e --- /dev/null +++ b/usr.sbin/ospf6d/ospfe.h @@ -0,0 +1,233 @@ +/* $OpenBSD: ospfe.h,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * 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 _OSPFE_H_ +#define _OSPFE_H_ + +#define max(x,y) ((x) > (y) ? (x) : (y)) + +#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; + +struct lsa_entry { + TAILQ_ENTRY(lsa_entry) entry; + union { + struct lsa_hdr *lu_lsa; + struct lsa_ref *lu_ref; + } le_data; + unsigned short le_when; + unsigned short le_oneshot; +}; +#define le_lsa le_data.lu_lsa +#define le_ref le_data.lu_ref + +struct lsa_ref { + LIST_ENTRY(lsa_ref) entry; + struct lsa_hdr hdr; + void *data; + time_t stamp; + int refcnt; + u_int16_t len; +}; + +struct nbr_stats { + u_int32_t sta_chng; +}; + +struct nbr { + LIST_ENTRY(nbr) entry, hash; + struct event inactivity_timer; + struct event db_tx_timer; + struct event lsreq_tx_timer; + struct event ls_retrans_timer; + struct event adj_timer; + + struct nbr_stats stats; + + struct lsa_head ls_retrans_list; + struct lsa_head db_sum_list; + struct lsa_head ls_req_list; + + struct in6_addr addr; /* ip6 address */ + struct in_addr id; /* router id */ + struct in_addr dr; /* designated router */ + struct in_addr bdr; /* backup designated router */ + + struct iface *iface; + struct lsa_entry *ls_req; + struct lsa_entry *dd_end; + + u_int32_t dd_seq_num; + u_int32_t dd_pending; + u_int32_t peerid; /* unique ID in DB */ + u_int32_t ls_req_cnt; + u_int32_t ls_ret_cnt; + u_int32_t crypt_seq_num; + + time_t uptime; + int state; + u_int8_t priority; + u_int8_t options; + u_int8_t last_rx_options; + u_int8_t last_rx_bits; + u_int8_t master; +}; + +/* database.c */ +int send_db_description(struct nbr *); +void recv_db_description(struct nbr *, char *, u_int16_t); +void db_sum_list_add(struct nbr *, struct lsa_hdr *); +int db_sum_list_del(struct nbr *, struct lsa_hdr *); +void db_sum_list_clr(struct nbr *); +void db_tx_timer(int, short, void *); +void start_db_tx_timer(struct nbr *); +void stop_db_tx_timer(struct nbr *); + +/* hello.c */ +int send_hello(struct iface *); +void recv_hello(struct iface *, struct in6_addr, u_int32_t, + char *, u_int16_t); + +/* ospfe.c */ +pid_t ospfe(struct ospfd_conf *, int[2], int[2], int[2]); +void ospfe_dispatch_main(int, short, void *); +void ospfe_dispatch_rde(int, short, void *); +int ospfe_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int ospfe_imsg_compose_rde(int, u_int32_t, pid_t, void *, + u_int16_t); +u_int32_t ospfe_router_id(void); +void ospfe_fip_update(int); +void ospfe_iface_ctl(struct ctl_conn *, unsigned int); +void ospfe_nbr_ctl(struct ctl_conn *); +void orig_rtr_lsa(struct area *); +void orig_net_lsa(struct iface *); +void ospfe_demote_area(struct area *, int); +void ospfe_demote_iface(struct iface *, int); + +/* interface.c */ +int if_fsm(struct iface *, enum iface_event); + +struct iface *if_new(struct kif *, struct kif_addr *); +void if_del(struct iface *); +void if_init(struct ospfd_conf *, struct iface *); + +int if_act_start(struct iface *); +int if_act_elect(struct iface *); +int if_act_reset(struct iface *); + +struct ctl_iface *if_to_ctl(struct iface *); + +int if_join_group(struct iface *, struct in6_addr *); +int if_leave_group(struct iface *, struct in6_addr *); +int if_set_mcast(struct iface *); +int if_set_recvif(int, int); +void if_set_recvbuf(int); +int if_set_mcast_loop(int); +int if_set_ip_hdrincl(int); + +/* lsack.c */ +int delay_lsa_ack(struct iface *, struct lsa_hdr *); +int send_ls_ack(struct iface *, struct in6_addr, void *, size_t); +void recv_ls_ack(struct nbr *, char *, u_int16_t); +int lsa_hdr_check(struct nbr *, struct lsa_hdr *); +void ls_ack_list_add(struct iface *, struct lsa_hdr *); +void ls_ack_list_free(struct iface *, struct lsa_entry *); +void ls_ack_list_clr(struct iface *); +int ls_ack_list_empty(struct iface *); +void ls_ack_tx_timer(int, short, void *); +void start_ls_ack_tx_timer(struct iface *); +void stop_ls_ack_tx_timer(struct iface *); + +/* lsreq.c */ +int send_ls_req(struct nbr *); +void recv_ls_req(struct nbr *, char *, u_int16_t); +void ls_req_list_add(struct nbr *, struct lsa_hdr *); +int ls_req_list_del(struct nbr *, struct lsa_hdr *); +struct lsa_entry *ls_req_list_get(struct nbr *, struct lsa_hdr *); +void ls_req_list_free(struct nbr *, struct lsa_entry *); +void ls_req_list_clr(struct nbr *); +int ls_req_list_empty(struct nbr *); +void ls_req_tx_timer(int, short, void *); +void start_ls_req_tx_timer(struct nbr *); +void stop_ls_req_tx_timer(struct nbr *); + +/* lsupdate.c */ +int lsa_flood(struct iface *, struct nbr *, struct lsa_hdr *, + void *); +void recv_ls_update(struct nbr *, char *, u_int16_t); + +void ls_retrans_list_add(struct nbr *, struct lsa_hdr *, + unsigned short, unsigned short); +int ls_retrans_list_del(struct nbr *, struct lsa_hdr *); +struct lsa_entry *ls_retrans_list_get(struct nbr *, struct lsa_hdr *); +void ls_retrans_list_free(struct nbr *, struct lsa_entry *); +void ls_retrans_list_clr(struct nbr *); +void ls_retrans_timer(int, short, void *); + +void lsa_cache_init(u_int32_t); +struct lsa_ref *lsa_cache_add(void *, u_int16_t); +struct lsa_ref *lsa_cache_get(struct lsa_hdr *); +void lsa_cache_put(struct lsa_ref *, struct nbr *); + +/* neighbor.c */ +void nbr_init(u_int32_t); +struct nbr *nbr_new(u_int32_t, struct iface *, int); +void nbr_del(struct nbr *); + +struct nbr *nbr_find_id(struct iface *, u_int32_t); +struct nbr *nbr_find_peerid(u_int32_t); + +int nbr_fsm(struct nbr *, enum nbr_event); + +void nbr_itimer(int, short, void *); +void nbr_start_itimer(struct nbr *); +void nbr_stop_itimer(struct nbr *); +void nbr_reset_itimer(struct nbr *); + +void nbr_adj_timer(int, short, void *); +void nbr_start_adj_timer(struct nbr *); + +int nbr_act_reset_itimer(struct nbr *); +int nbr_act_start_itimer(struct nbr *); +int nbr_act_eval(struct nbr *); +int nbr_act_snapshot(struct nbr *); +int nbr_act_exchange_done(struct nbr *); +int nbr_act_adj_ok(struct nbr *); +int nbr_act_restart_dd(struct nbr *); +int nbr_act_delete(struct nbr *); +int nbr_act_clear_lists(struct nbr *); +int nbr_act_hello_check(struct nbr *); + +struct ctl_nbr *nbr_to_ctl(struct nbr *); + +struct lsa_hdr *lsa_hdr_new(void); + +/* packet.c */ +int gen_ospf_hdr(struct buf *, struct iface *, u_int8_t); +int upd_ospf_hdr(struct buf *, struct iface *); +int send_packet(struct iface *, void *, size_t, struct sockaddr_in6 *); +void recv_packet(int, short, void *); + +char *pkt_ptr; /* packet buffer */ + +#endif /* _OSPFE_H_ */ diff --git a/usr.sbin/ospf6d/packet.c b/usr.sbin/ospf6d/packet.c new file mode 100644 index 00000000000..2e833c48c27 --- /dev/null +++ b/usr.sbin/ospf6d/packet.c @@ -0,0 +1,361 @@ +/* $OpenBSD: packet.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * 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/ip6.h> +#include <arpa/inet.h> +#include <net/if_dl.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <string.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "ospfe.h" + +int ip_hdr_sanity_check(const struct ip6_hdr *, u_int16_t); +int ospf_hdr_sanity_check(const struct ip6_hdr *, + struct ospf_hdr *, u_int16_t, const struct iface *); +struct iface *find_iface(struct ospfd_conf *, unsigned int, struct in6_addr); + +int +gen_ospf_hdr(struct buf *buf, struct iface *iface, u_int8_t type) +{ + struct ospf_hdr ospf_hdr; + + bzero(&ospf_hdr, sizeof(ospf_hdr)); + ospf_hdr.version = OSPF6_VERSION; + ospf_hdr.type = type; + ospf_hdr.rtr_id = ospfe_router_id(); + if (iface->type != IF_TYPE_VIRTUALLINK) + ospf_hdr.area_id = iface->area->id.s_addr; + ospf_hdr.instance = DEFAULT_INSTANCE_ID; + ospf_hdr.zero = 0; /* must be zero */ + + return (buf_add(buf, &ospf_hdr, sizeof(ospf_hdr))); +} + +int +upd_ospf_hdr(struct buf *buf, struct iface *iface) +{ + struct ospf_hdr *ospf_hdr; + + if ((ospf_hdr = buf_seek(buf, 0, sizeof(ospf_hdr))) == NULL) + fatalx("upd_ospf_hdr: buf_seek failed"); + + /* update length */ + if (buf->wpos > USHRT_MAX) + fatalx("upd_ospf_hdr: resulting ospf packet too big"); + ospf_hdr->len = htons((u_int16_t)buf->wpos); + + ospf_hdr->chksum = in_cksum(buf->buf, buf->wpos); /* XXX */ + + return (0); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, void *pkt, size_t len, + struct sockaddr_in6 *dst) +{ + struct msghdr msg; + struct iovec iov[1]; + /* setup buffer */ + bzero(&msg, sizeof(msg)); + iov[0].iov_base = pkt; + iov[0].iov_len = len; + msg.msg_name = dst; + msg.msg_namelen = sizeof(*dst); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + /* set outgoing interface for multicast traffic */ + if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) + if (if_set_mcast(iface) == -1) { + log_warn("send_packet: error setting multicast " + "interface, %s", iface->name); + return (-1); + } + + + log_debug("send_packet: iface %d addr %s", iface->ifindex, + log_in6addr(&iface->addr)); + log_debug("send_packet: dest %s", log_in6addr(&dst->sin6_addr)); + if (sendmsg(iface->fd, &msg, MSG_DONTROUTE) == -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 msghdr msg; + struct iovec iov; + struct ip6_hdr ip_hdr; + struct in6_addr addr; + struct ospfd_conf *xconf = bula; + struct ospf_hdr *ospf_hdr; + struct iface *iface; + struct nbr *nbr = NULL; + char *buf; + struct cmsghdr *cmsg; + ssize_t r; + u_int16_t len; + int l; + unsigned int ifindex = 0; + + if (event != EV_READ) + return; + + /* setup buffer */ + bzero(&msg, sizeof(msg)); + iov.iov_base = buf = pkt_ptr; + iov.iov_len = READ_BUF_SIZE; + 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 != EAGAIN && errno != EINTR) + 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_IPV6 && + cmsg->cmsg_type == IP_RECVIF) { + ifindex = ((struct sockaddr_dl *) + CMSG_DATA(cmsg))->sdl_index; + break; + } + } + + len = (u_int16_t)r; + + /* IP header sanity checks */ + if (len < sizeof(ip_hdr)) { + log_warnx("recv_packet: bad packet size"); + return; + } + memcpy(&ip_hdr, buf, sizeof(ip_hdr)); + if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) + return; + buf += l; + len -= l; + + /* find a matching interface */ + if ((iface = find_iface(xconf, ifindex, ip_hdr.ip6_src)) == NULL) { + /* XXX add a counter here */ + return; + } +#if 1 + /* + * Packet needs to be sent to AllSPFRouters or AllDRouters + * or to the address of the interface itself. + * AllDRouters is only valid for DR and BDR but this is checked later. + */ + inet_pton(AF_INET6, AllSPFRouters, &addr); + + if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) { + inet_pton(AF_INET6, AllDRouters, &addr); + if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) { + if (!IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, + &iface->addr)) { + log_debug("recv_packet: packet sent to wrong " + "address %s, interface %s", + log_in6addr(&ip_hdr.ip6_dst), iface->name); + return; + } + } + } +#endif + /* OSPF header sanity checks */ + if (len < sizeof(*ospf_hdr)) { + log_debug("recv_packet: bad packet size"); + return; + } + ospf_hdr = (struct ospf_hdr *)buf; + + if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1) + return; + + nbr = nbr_find_id(iface, ospf_hdr->rtr_id); + if (ospf_hdr->type != PACKET_TYPE_HELLO && nbr == NULL) { + log_debug("recv_packet: unknown neighbor ID"); + return; + } + + if (in_cksum(buf, len)) { + log_warnx("recv_packet: invalid checksum, " + "interface %s", iface->name); + return; + } + + buf += sizeof(*ospf_hdr); + len = l - sizeof(*ospf_hdr); + + /* switch OSPF packet type */ + switch (ospf_hdr->type) { + case PACKET_TYPE_HELLO: + inet_pton(AF_INET6, AllDRouters, &addr); + if (IN6_ARE_ADDR_EQUAL(&ip_hdr.ip6_dst, &addr)) { + log_debug("recv_packet: invalid destination IP " + "address"); + break; + } + + recv_hello(iface, ip_hdr.ip6_src, ospf_hdr->rtr_id, buf, len); + break; + case PACKET_TYPE_DD: + recv_db_description(nbr, buf, len); + break; + case PACKET_TYPE_LS_REQUEST: + recv_ls_req(nbr, buf, len); + break; + case PACKET_TYPE_LS_UPDATE: + recv_ls_update(nbr, buf, len); + break; + case PACKET_TYPE_LS_ACK: + recv_ls_ack(nbr, buf, len); + break; + default: + log_debug("recv_packet: unknown OSPF packet type, interface %s", + iface->name); + } +} + +int +ip_hdr_sanity_check(const struct ip6_hdr *ip_hdr, u_int16_t len) +{ + if (ntohs(ip_hdr->ip6_plen) != len) { + log_debug("recv_packet: invalid IP packet length %u", + ntohs(ip_hdr->ip6_plen)); + return (-1); + } + + if (ip_hdr->ip6_nxt != IPPROTO_OSPF) + /* this is enforced by the socket itself */ + fatalx("recv_packet: invalid IP proto"); + +//XXX return (ip_hdr->ip_hl << 2); + return (sizeof(struct ip6_hdr)); +} + +int +ospf_hdr_sanity_check(const struct ip6_hdr *ip_hdr, struct ospf_hdr *ospf_hdr, + u_int16_t len, const struct iface *iface) +{ + struct in6_addr addr; + struct in_addr id; + + if (ospf_hdr->version != OSPF6_VERSION) { + log_debug("recv_packet: invalid OSPF version %d", + ospf_hdr->version); + return (-1); + } + + if (ntohs(ospf_hdr->len) > len || + len <= sizeof(struct ospf_hdr)) { + log_debug("recv_packet: invalid OSPF packet length %d", + ntohs(ospf_hdr->len)); + return (-1); + } + + if (iface->type != IF_TYPE_VIRTUALLINK) { + if (ospf_hdr->area_id != iface->area->id.s_addr) { + id.s_addr = ospf_hdr->area_id; + log_debug("recv_packet: invalid area ID %s, " + "interface %s", inet_ntoa(id), iface->name); + return (-1); + } + } else { + if (ospf_hdr->area_id != 0) { + id.s_addr = ospf_hdr->area_id; + log_debug("recv_packet: invalid area ID %s, " + "interface %s", inet_ntoa(id), iface->name); + return (-1); + } + } + + if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) { + if (inet_pton(AF_INET6, AllDRouters, &addr) == 0) + fatalx("recv_packet: inet_pton"); + if (IN6_ARE_ADDR_EQUAL(&ip_hdr->ip6_dst, &addr) && + (iface->state & IF_STA_DRORBDR) == 0) { + log_debug("recv_packet: invalid destination IP in " + "state %s, interface %s", + if_state_name(iface->state), iface->name); + return (-1); + } + } + + return (ntohs(ospf_hdr->len)); +} + +struct iface * +find_iface(struct ospfd_conf *xconf, unsigned int ifindex, struct in6_addr src) +{ + struct area *area = NULL; + struct iface *iface = NULL; + + /* returned interface needs to be active */ + LIST_FOREACH(area, &xconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + switch (iface->type) { + case IF_TYPE_VIRTUALLINK: + if (IN6_ARE_ADDR_EQUAL(&src, &iface->dst) && + !iface->passive) + return (iface); + break; + case IF_TYPE_POINTOPOINT: + if (ifindex == iface->ifindex && + IN6_ARE_ADDR_EQUAL(&iface->dst, &src) && + !iface->passive) + return (iface); + break; + default: +#if 0 + if (ifindex == iface->ifindex && + (iface->addr.s_addr & iface->mask.s_addr) == + (src.s_addr & iface->mask.s_addr) && + !iface->passive) + return (iface); +#endif + break; + } + } + } + + return (NULL); +} diff --git a/usr.sbin/ospf6d/parse.y b/usr.sbin/ospf6d/parse.y new file mode 100644 index 00000000000..60f8d8ffb37 --- /dev/null +++ b/usr.sbin/ospf6d/parse.y @@ -0,0 +1,1061 @@ +/* $OpenBSD: parse.y,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * 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 "ospf6.h" +#include "ospf6d.h" +#include "ospfe.h" +#include "log.h" + +static struct ospfd_conf *conf; +static FILE *fin = NULL; +static int lineno = 1; +static int errors = 0; +char *infile; + +struct area *area = NULL; +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 ospfd_conf *xconf); +int check_file_secrecy(int fd, const char *fname); +u_int32_t get_rtr_id(void); +int host(const char *, struct in_addr *, struct in_addr *); + +struct config_defaults { + u_int32_t dead_interval; + u_int16_t transmit_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + u_int8_t priority; +}; + +struct config_defaults globaldefs; +struct config_defaults areadefs; +struct config_defaults ifacedefs; +struct config_defaults *defs; + +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 *); +struct area *conf_get_area(struct in_addr); +struct iface *conf_get_if(struct kif *, struct kif_addr *); + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token AREA INTERFACE ROUTERID FIBUPDATE REDISTRIBUTE RTLABEL +%token STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG +%token METRIC PASSIVE +%token HELLOINTERVAL TRANSMITDELAY +%token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY +%token SET TYPE +%token YES NO +%token DEMOTE +%token ERROR +%token <v.string> STRING +%token <v.number> NUMBER +%type <v.number> yesno no optlist, optlist_l option demotecount +%type <v.string> string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar conf_main '\n' + | grammar varset '\n' + | grammar area '\n' + | grammar error '\n' { errors++; } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : YES { $$ = 1; } + | NO { $$ = 0; } + ; + +no : /* empty */ { $$ = 0; } + | NO { $$ = 1; } + +varset : STRING '=' string { + if (conf->opts & OSPFD_OPT_VERBOSE) + printf("%s = \"%s\"\n", $1, $3); + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +conf_main : ROUTERID STRING { + if (!inet_aton($2, &conf->rtr_id)) { + yyerror("error parsing router-id"); + free($2); + YYERROR; + } + free($2); + } + | FIBUPDATE yesno { + if ($2 == 0) + conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE; + else + conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE; + } + | no REDISTRIBUTE NUMBER '/' NUMBER optlist { + struct redistribute *r; + + if ((r = calloc(1, sizeof(*r))) == NULL) + fatal(NULL); + r->type = REDIST_ADDR; + if ($3 < 0 || $3 > 255 || $5 < 1 || $5 > 32) { + yyerror("bad network: %llu/%llu", $3, $5); + free(r); + YYERROR; + } + r->addr.s_addr = htonl($3 << IN_CLASSA_NSHIFT); + r->mask.s_addr = prefixlen2mask($5); + + if ($1) + r->type |= REDIST_NO; + r->metric = $6; + + SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry); + conf->redistribute |= REDISTRIBUTE_ON; + } + | no REDISTRIBUTE STRING optlist { + struct redistribute *r; + + if (!strcmp($3, "default")) { + if (!$1) + conf->redistribute |= + REDISTRIBUTE_DEFAULT; + else + conf->redistribute &= + ~REDISTRIBUTE_DEFAULT; + conf->defaultmetric = $4; + } else { + if ((r = calloc(1, sizeof(*r))) == NULL) + fatal(NULL); + if (!strcmp($3, "static")) + r->type = REDIST_STATIC; + else if (!strcmp($3, "connected")) + r->type = REDIST_CONNECTED; + else if (host($3, &r->addr, &r->mask)) + r->type = REDIST_ADDR; + else { + yyerror("unknown redistribute type"); + free($3); + free(r); + YYERROR; + } + + if ($1) + r->type |= REDIST_NO; + r->metric = $4; + + SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, + entry); + } + conf->redistribute |= REDISTRIBUTE_ON; + free($3); + } + | no REDISTRIBUTE RTLABEL STRING optlist { + struct redistribute *r; + + if ((r = calloc(1, sizeof(*r))) == NULL) + fatal(NULL); + r->type = REDIST_LABEL; + r->label = rtlabel_name2id($4); + if ($1) + r->type |= REDIST_NO; + r->metric = $5; + free($4); + + SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry); + conf->redistribute |= REDISTRIBUTE_ON; + } + | RTLABEL STRING EXTTAG NUMBER { + if ($4 < 0 || $4 > UINT_MAX) { + yyerror("invalid external route tag"); + free($2); + YYERROR; + } + rtlabel_tag(rtlabel_name2id($2), $4); + free($2); + } + | SPFDELAY NUMBER { + if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) { + yyerror("spf-delay out of range " + "(%d-%d)", MIN_SPF_DELAY, + MAX_SPF_DELAY); + YYERROR; + } + conf->spf_delay = $2; + } + | SPFHOLDTIME NUMBER { + if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) { + yyerror("spf-holdtime out of range " + "(%d-%d)", MIN_SPF_HOLDTIME, + MAX_SPF_HOLDTIME); + YYERROR; + } + conf->spf_hold_time = $2; + } + | STUB ROUTER yesno { + if ($3) + conf->flags |= OSPFD_FLAG_STUB_ROUTER; + else + /* allow to force non stub mode */ + conf->flags &= ~OSPFD_FLAG_STUB_ROUTER; + } + | defaults + ; + +optlist : /* empty */ { $$ = DEFAULT_REDIST_METRIC; } + | SET option { + $$ = $2; + if (($$ & LSA_METRIC_MASK) == 0) + $$ |= DEFAULT_REDIST_METRIC; + } + | SET optnl '{' optnl optlist_l optnl '}' { + $$ = $5; + if (($$ & LSA_METRIC_MASK) == 0) + $$ |= DEFAULT_REDIST_METRIC; + } + ; + +optlist_l : optlist_l comma option { + if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) { + yyerror("redistribute type already defined"); + YYERROR; + } + if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) { + yyerror("redistribute metric already defined"); + YYERROR; + } + $$ = $1 | $3; + } + | option { $$ = $1; } + ; + +option : METRIC NUMBER { + if ($2 == 0 || $2 > MAX_METRIC) { + yyerror("invalid redistribute metric"); + YYERROR; + } + $$ = $2; + } + | TYPE NUMBER { + switch ($2) { + case 1: + $$ = 0; + break; + case 2: + $$ = LSA_ASEXT_E_FLAG; + break; + default: + yyerror("only external type 1 and 2 allowed"); + YYERROR; + } + } + ; + +defaults : METRIC NUMBER { + if ($2 < MIN_METRIC || $2 > MAX_METRIC) { + yyerror("metric out of range (%d-%d)", + MIN_METRIC, MAX_METRIC); + YYERROR; + } + defs->metric = $2; + } + | ROUTERPRIORITY NUMBER { + if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) { + yyerror("router-priority out of range (%d-%d)", + MIN_PRIORITY, MAX_PRIORITY); + YYERROR; + } + defs->priority = $2; + } + | ROUTERDEADTIME NUMBER { + if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) { + yyerror("router-dead-time out of range (%d-%d)", + MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME); + YYERROR; + } + defs->dead_interval = $2; + } + | TRANSMITDELAY NUMBER { + if ($2 < MIN_TRANSMIT_DELAY || + $2 > MAX_TRANSMIT_DELAY) { + yyerror("transmit-delay out of range (%d-%d)", + MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY); + YYERROR; + } + defs->transmit_delay = $2; + } + | HELLOINTERVAL NUMBER { + if ($2 < MIN_HELLO_INTERVAL || + $2 > MAX_HELLO_INTERVAL) { + yyerror("hello-interval out of range (%d-%d)", + MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); + YYERROR; + } + defs->hello_interval = $2; + } + | RETRANSMITINTERVAL NUMBER { + if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) { + yyerror("retransmit-interval out of range " + "(%d-%d)", MIN_RXMT_INTERVAL, + MAX_RXMT_INTERVAL); + YYERROR; + } + defs->rxmt_interval = $2; + } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl /* one newline or more */ + ; + +comma : ',' + | /*empty*/ + ; + +area : AREA STRING { + struct in_addr id; + if (inet_aton($2, &id) == 0) { + yyerror("error parsing area"); + free($2); + YYERROR; + } + free($2); + area = conf_get_area(id); + + memcpy(&areadefs, defs, sizeof(areadefs)); + defs = &areadefs; + } '{' optnl areaopts_l '}' { + area = NULL; + defs = &globaldefs; + } + ; + +demotecount : NUMBER { $$ = $1; } + | /*empty*/ { $$ = 1; } + ; + +areaopts_l : areaopts_l areaoptsl nl + | areaoptsl optnl + ; + +areaoptsl : interface + | DEMOTE STRING demotecount { + if ($3 < 1 || $3 > 255) { + yyerror("demote count out of range (1-255)"); + free($2); + YYERROR; + } + area->demote_level = $3; + if (strlcpy(area->demote_group, $2, + sizeof(area->demote_group)) >= + sizeof(area->demote_group)) { + yyerror("demote group name \"%s\" too long"); + free($2); + YYERROR; + } + free($2); + if (carp_demote_init(area->demote_group, + conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) { + yyerror("error initializing group \"%s\"", + area->demote_group); + YYERROR; + } + } + | defaults + ; + +interface : INTERFACE STRING { + struct kif *kif; + struct kif_addr *ka = NULL; + char *s; + struct in6_addr addr; + + s = strchr($2, ':'); + if (s) { + *s++ = '\0'; + if (inet_pton(AF_INET6, s, &addr) == 0) { + yyerror( + "error parsing interface address"); + free($2); + YYERROR; + } + } else + bzero(&addr, sizeof(addr)); + + if ((kif = kif_findname($2, &addr, &ka)) == NULL) { + yyerror("unknown interface %s", $2); + free($2); + YYERROR; + } + if (ka == NULL) { + if (s) + yyerror("address %s not configured on " + "interface %s", s, $2); + else + yyerror("unnumbered interface %s", $2); + free($2); + YYERROR; + } + free($2); + iface = conf_get_if(kif, ka); + if (iface == NULL) + YYERROR; + iface->area = area; + LIST_INSERT_HEAD(&area->iface_list, iface, entry); + + memcpy(&ifacedefs, defs, sizeof(ifacedefs)); + defs = &ifacedefs; + } interface_block { + iface->dead_interval = defs->dead_interval; + iface->transmit_delay = defs->transmit_delay; + iface->hello_interval = defs->hello_interval; + iface->rxmt_interval = defs->rxmt_interval; + iface->metric = defs->metric; + iface->priority = defs->priority; + iface = NULL; + /* interface is always part of an area */ + defs = &areadefs; + } + ; + +interface_block : '{' optnl interfaceopts_l '}' + | '{' optnl '}' + | + ; + +interfaceopts_l : interfaceopts_l interfaceoptsl nl + | interfaceoptsl optnl + ; + +interfaceoptsl : PASSIVE { iface->passive = 1; } + | DEMOTE STRING { + if (strlcpy(iface->demote_group, $2, + sizeof(iface->demote_group)) >= + sizeof(iface->demote_group)) { + yyerror("demote group name \"%s\" too long"); + free($2); + YYERROR; + } + free($2); + if (carp_demote_init(iface->demote_group, + conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) { + yyerror("error initializing group \"%s\"", + iface->demote_group); + YYERROR; + } + } + | defaults + ; + +%% + +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[] = { + {"area", AREA}, + {"demote", DEMOTE}, + {"external-tag", EXTTAG}, + {"fib-update", FIBUPDATE}, + {"hello-interval", HELLOINTERVAL}, + {"interface", INTERFACE}, + {"metric", METRIC}, + {"no", NO}, + {"passive", PASSIVE}, + {"redistribute", REDISTRIBUTE}, + {"retransmit-interval", RETRANSMITINTERVAL}, + {"router", ROUTER}, + {"router-dead-time", ROUTERDEADTIME}, + {"router-id", ROUTERID}, + {"router-priority", ROUTERPRIORITY}, + {"rtlabel", RTLABEL}, + {"set", SET}, + {"spf-delay", SPFDELAY}, + {"spf-holdtime", SPFHOLDTIME}, + {"stub", STUB}, + {"transmit-delay", TRANSMITDELAY}, + {"type", TYPE}, + {"yes", YES} + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + 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') { + c = next; + 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_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(fin)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, LLONG_MAX, + &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", buf, + errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#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 ospfd_conf * +parse_config(char *filename, int opts) +{ + struct sym *sym, *next; + + if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL) + fatal("parse_config"); + + bzero(&globaldefs, sizeof(globaldefs)); + defs = &globaldefs; + defs->dead_interval = DEFAULT_RTR_DEAD_TIME; + defs->transmit_delay = DEFAULT_TRANSMIT_DELAY; + defs->hello_interval = DEFAULT_HELLO_INTERVAL; + defs->rxmt_interval = DEFAULT_RXMT_INTERVAL; + defs->metric = DEFAULT_METRIC; + defs->priority = DEFAULT_PRIORITY; + + conf->options = OSPF_OPTION_E; + conf->spf_delay = DEFAULT_SPF_DELAY; + conf->spf_hold_time = DEFAULT_SPF_HOLDTIME; + conf->spf_state = SPF_IDLE; + + if ((fin = fopen(filename, "r")) == NULL) { + warn("%s", filename); + free(conf); + return (NULL); + } + infile = filename; + + conf->opts = opts; + if (conf->opts & OSPFD_OPT_STUB_ROUTER) + conf->flags |= OSPFD_FLAG_STUB_ROUTER; + LIST_INIT(&conf->area_list); + LIST_INIT(&conf->cand_list); + SIMPLEQ_INIT(&conf->redist_list); + + if (!(conf->opts & OSPFD_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 & OSPFD_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 */ + if (errors) { + clear_config(conf); + return (NULL); + } + + if (conf->rtr_id.s_addr == 0) + conf->rtr_id.s_addr = get_rtr_id(); + + 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); +} + +struct area * +conf_get_area(struct in_addr id) +{ + struct area *a; + + a = area_find(conf, id); + if (a) + return (a); + a = area_new(); + LIST_INSERT_HEAD(&conf->area_list, a, entry); + + a->id.s_addr = id.s_addr; + + return (a); +} + +struct iface * +conf_get_if(struct kif *kif, struct kif_addr *ka) +{ + struct area *a; + struct iface *i; + + LIST_FOREACH(a, &conf->area_list, entry) + LIST_FOREACH(i, &a->iface_list, entry) + if (i->ifindex == kif->ifindex /*&& XXX + i->addr.s_addr == ka->addr.s_addr*/) { + yyerror("interface %s already configured", + kif->ifname); + return (NULL); + } + + i = if_new(kif, ka); + + return (i); +} + +void +clear_config(struct ospfd_conf *xconf) +{ + struct area *a; + + while ((a = LIST_FIRST(&xconf->area_list)) != NULL) { + LIST_REMOVE(a, entry); + area_del(a); + } + + free(xconf); +} + +u_int32_t +get_rtr_id(void) +{ + struct ifaddrs *ifap, *ifa; + u_int32_t ip = 0, cur, localnet; + + localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); + + if (getifaddrs(&ifap) == -1) + fatal("getifaddrs"); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + if ((cur & localnet) == localnet) /* skip 127/8 */ + continue; + if (cur > ip || ip == 0) + ip = cur; + } + freeifaddrs(ifap); + + if (ip == 0) + fatal("router-id is 0.0.0.0"); + + return (ip); +} + +int +host(const char *s, struct in_addr *addr, struct in_addr *mask) +{ + struct in_addr ina; + int bits = 32; + + bzero(&ina, sizeof(struct in_addr)); + if (strrchr(s, '/') != NULL) { + if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) + return (0); + } else { + if (inet_pton(AF_INET, s, &ina) != 1) + return (0); + } + + addr->s_addr = ina.s_addr; + mask->s_addr = prefixlen2mask(bits); + + return (1); +} diff --git a/usr.sbin/ospf6d/printconf.c b/usr.sbin/ospf6d/printconf.c new file mode 100644 index 00000000000..ab00b8884ae --- /dev/null +++ b/usr.sbin/ospf6d/printconf.c @@ -0,0 +1,147 @@ +/* $OpenBSD: printconf.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * 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/queue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <stdio.h> + +#include "ospf6.h" +#include "ospf6d.h" +#include "ospfe.h" + +void print_mainconf(struct ospfd_conf *); +const char *print_no(u_int16_t); +void print_redistribute(struct ospfd_conf *); +void print_rtlabel(struct ospfd_conf *); +void print_iface(struct iface *); + +void +print_mainconf(struct ospfd_conf *conf) +{ + printf("router-id %s\n", inet_ntoa(conf->rtr_id)); + + if (conf->flags & OSPFD_FLAG_NO_FIB_UPDATE) + printf("fib-update no\n"); + else + printf("fib-update yes\n"); + + if (conf->flags & OSPFD_FLAG_STUB_ROUTER) + printf("stub router yes\n"); + + print_redistribute(conf); + print_rtlabel(conf); + + printf("spf-delay %u\n", conf->spf_delay); + printf("spf-holdtime %u\n", conf->spf_hold_time); +} + +const char * +print_no(u_int16_t type) +{ + if (type & REDIST_NO) + return ("no "); + else + return (""); +} + +void +print_redistribute(struct ospfd_conf *conf) +{ + struct redistribute *r; + + if (conf->redistribute & REDISTRIBUTE_DEFAULT) + printf("redistribute default\n"); + + SIMPLEQ_FOREACH(r, &conf->redist_list, entry) { + switch (r->type & ~REDIST_NO) { + case REDIST_STATIC: + printf("%sredistribute static\n", print_no(r->type)); + break; + case REDIST_CONNECTED: + printf("%sredistribute connected\n", print_no(r->type)); + break; + case REDIST_LABEL: + printf("%sredistribute rtlabel %s\n", + print_no(r->type), rtlabel_id2name(r->label)); + break; + case REDIST_ADDR: + printf("%sredistribute %s/%d\n", + print_no(r->type), inet_ntoa(r->addr), + mask2prefixlen(r->mask.s_addr)); + break; + } + } +} + +void +print_rtlabel(struct ospfd_conf *conf) +{ + struct n2id_label *label; + + TAILQ_FOREACH(label, &rt_labels, entry) + if (label->ext_tag) + printf("rtlabel \"%s\" external-tag %u\n", + label->name, label->ext_tag); +} + +void +print_iface(struct iface *iface) +{ + printf("\tinterface %s {\n", iface->name); + + printf("\t\thello-interval %d\n", iface->hello_interval); + printf("\t\tmetric %d\n", iface->metric); + + if (iface->passive) + printf("\t\tpassive\n"); + if (*iface->demote_group) + printf("\t\tdemote %s\n", iface->demote_group); + + printf("\t\tretransmit-interval %d\n", iface->rxmt_interval); + printf("\t\trouter-dead-time %d\n", iface->dead_interval); + printf("\t\trouter-priority %d\n", iface->priority); + printf("\t\ttransmit-delay %d\n", iface->transmit_delay); + + printf("\t}\n"); +} + +void +print_config(struct ospfd_conf *conf) +{ + struct area *area; + struct iface *iface; + + printf("\n"); + print_mainconf(conf); + printf("\n"); + + LIST_FOREACH(area, &conf->area_list, entry) { + printf("area %s {\n", inet_ntoa(area->id)); + if (*area->demote_group) + printf("\tdemote %s %d\n", area->demote_group, + area->demote_level); + LIST_FOREACH(iface, &area->iface_list, entry) { + print_iface(iface); + } + printf("}\n\n"); + } +} diff --git a/usr.sbin/ospf6d/rde.c b/usr.sbin/ospf6d/rde.c new file mode 100644 index 00000000000..61a0ece00b4 --- /dev/null +++ b/usr.sbin/ospf6d/rde.c @@ -0,0 +1,1178 @@ +/* $OpenBSD: rde.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * 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 "ospf6.h" +#include "ospf6d.h" +#include "ospfe.h" +#include "log.h" +#include "rde.h" + +void rde_sig_handler(int sig, short, void *); +void rde_shutdown(void); +void rde_dispatch_imsg(int, short, void *); +void rde_dispatch_parent(int, short, void *); + +void rde_send_summary(pid_t); +void rde_send_summary_area(struct area *, pid_t); +void rde_nbr_init(u_int32_t); +void rde_nbr_free(void); +struct rde_nbr *rde_nbr_find(u_int32_t); +struct rde_nbr *rde_nbr_new(u_int32_t, struct rde_nbr *); +void rde_nbr_del(struct rde_nbr *); + +void rde_req_list_add(struct rde_nbr *, struct lsa_hdr *); +int rde_req_list_exists(struct rde_nbr *, struct lsa_hdr *); +void rde_req_list_del(struct rde_nbr *, struct lsa_hdr *); +void rde_req_list_free(struct rde_nbr *); + +struct lsa *rde_asext_get(struct rroute *); +struct lsa *rde_asext_put(struct rroute *); + +struct lsa *orig_asext_lsa(struct rroute *, u_int16_t); +struct lsa *orig_sum_lsa(struct rt_node *, u_int8_t, int); + +struct ospfd_conf *rdeconf = NULL, *nconf = NULL; +struct imsgbuf *ibuf_ospfe; +struct imsgbuf *ibuf_main; +struct rde_nbr *nbrself; +struct lsa_tree asext_tree; + +/* 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 ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2], + int pipe_parent2ospfe[2]) +{ + struct event ev_sigint, ev_sigterm; + struct timeval now; + struct passwd *pw; + struct redistribute *r; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + /* NOTREACHED */ + case 0: + break; + default: + return (pid); + } + + rdeconf = xconf; + + if ((pw = getpwnam(OSPF6D_USER)) == NULL) + fatal("getpwnam"); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("route decision engine"); + ospfd_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(); + rde_nbr_init(NBR_HASHSIZE); + lsa_init(&asext_tree); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, rde_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, rde_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* setup pipes */ + close(pipe_ospfe2rde[0]); + close(pipe_parent2rde[0]); + close(pipe_parent2ospfe[0]); + close(pipe_parent2ospfe[1]); + + if ((ibuf_ospfe = malloc(sizeof(struct imsgbuf))) == NULL || + (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) + fatal(NULL); + imsg_init(ibuf_ospfe, pipe_ospfe2rde[1], rde_dispatch_imsg); + imsg_init(ibuf_main, pipe_parent2rde[1], rde_dispatch_parent); + + /* setup event handler */ + ibuf_ospfe->events = EV_READ; + event_set(&ibuf_ospfe->ev, ibuf_ospfe->fd, ibuf_ospfe->events, + ibuf_ospfe->handler, ibuf_ospfe); + event_add(&ibuf_ospfe->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); + + evtimer_set(&rdeconf->ev, spf_timer, rdeconf); + cand_list_init(); + rt_init(); + + while ((r = SIMPLEQ_FIRST(&rdeconf->redist_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&rdeconf->redist_list, entry); + free(r); + } + + gettimeofday(&now, NULL); + rdeconf->uptime = now.tv_sec; + + event_dispatch(); + + rde_shutdown(); + /* NOTREACHED */ + + return (0); +} + +void +rde_shutdown(void) +{ + struct area *a; + + stop_spf_timer(rdeconf); + cand_list_clr(); + rt_clear(); + + while ((a = LIST_FIRST(&rdeconf->area_list)) != NULL) { + LIST_REMOVE(a, entry); + area_del(a); + } + rde_nbr_free(); + + msgbuf_clear(&ibuf_ospfe->w); + free(ibuf_ospfe); + msgbuf_clear(&ibuf_main->w); + free(ibuf_main); + free(rdeconf); + + log_info("route decision engine exiting"); + _exit(0); +} + +int +rde_imsg_compose_ospfe(int type, u_int32_t peerid, pid_t pid, void *data, + u_int16_t datalen) +{ + return (imsg_compose(ibuf_ospfe, type, peerid, pid, data, datalen)); +} + +/* ARGSUSED */ +void +rde_dispatch_imsg(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + struct in_addr aid; + struct ls_req_hdr req_hdr; + struct lsa_hdr lsa_hdr, *db_hdr; + struct rde_nbr rn, *nbr; + struct timespec tp; + struct lsa *lsa; + struct area *area; + struct vertex *v; + char *buf; + ssize_t n; + time_t now; + int r, state, self, shut = 0; + u_int16_t l; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + clock_gettime(CLOCK_MONOTONIC, &tp); + now = tp.tv_sec; + + 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_NEIGHBOR_UP: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rn)) + fatalx("invalid size of OE request"); + memcpy(&rn, imsg.data, sizeof(rn)); + + if (rde_nbr_find(imsg.hdr.peerid)) + fatalx("rde_dispatch_imsg: " + "neighbor already exists"); + rde_nbr_new(imsg.hdr.peerid, &rn); + break; + case IMSG_NEIGHBOR_DOWN: + rde_nbr_del(rde_nbr_find(imsg.hdr.peerid)); + break; + case IMSG_NEIGHBOR_CHANGE: + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(state)) + fatalx("invalid size of OE request"); + memcpy(&state, imsg.data, sizeof(state)); + + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + if (state != nbr->state && (nbr->state & NBR_STA_FULL || + state & NBR_STA_FULL)) + area_track(nbr->area, state); + + nbr->state = state; + if (nbr->state & NBR_STA_FULL) + rde_req_list_free(nbr); + break; + case IMSG_DB_SNAPSHOT: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + lsa_snap(nbr->area, imsg.hdr.peerid); + + imsg_compose(ibuf_ospfe, IMSG_DB_END, imsg.hdr.peerid, + 0, NULL, 0); + break; + case IMSG_DD: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + buf = imsg.data; + for (l = imsg.hdr.len - IMSG_HEADER_SIZE; + l >= sizeof(lsa_hdr); l -= sizeof(lsa_hdr)) { + memcpy(&lsa_hdr, buf, sizeof(lsa_hdr)); + buf += sizeof(lsa_hdr); + + v = lsa_find(nbr->area, lsa_hdr.type, + lsa_hdr.ls_id, lsa_hdr.adv_rtr); + if (v == NULL) + db_hdr = NULL; + else + db_hdr = &v->lsa->hdr; + + if (lsa_newer(&lsa_hdr, db_hdr) > 0) { + /* + * only request LSAs that are + * newer or missing + */ + rde_req_list_add(nbr, &lsa_hdr); + imsg_compose(ibuf_ospfe, IMSG_DD, + imsg.hdr.peerid, 0, &lsa_hdr, + sizeof(lsa_hdr)); + } + } + if (l != 0) + log_warnx("rde_dispatch_imsg: peerid %lu, " + "trailing garbage in Database Description " + "packet", imsg.hdr.peerid); + + imsg_compose(ibuf_ospfe, IMSG_DD_END, imsg.hdr.peerid, + 0, NULL, 0); + break; + case IMSG_LS_REQ: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + buf = imsg.data; + for (l = imsg.hdr.len - IMSG_HEADER_SIZE; + l >= sizeof(req_hdr); l -= sizeof(req_hdr)) { + memcpy(&req_hdr, buf, sizeof(req_hdr)); + buf += sizeof(req_hdr); + + if ((v = lsa_find(nbr->area, + ntohl(req_hdr.type), req_hdr.ls_id, + req_hdr.adv_rtr)) == NULL) { + imsg_compose(ibuf_ospfe, IMSG_LS_BADREQ, + imsg.hdr.peerid, 0, NULL, 0); + continue; + } + imsg_compose(ibuf_ospfe, IMSG_LS_UPD, + imsg.hdr.peerid, 0, v->lsa, + ntohs(v->lsa->hdr.len)); + } + if (l != 0) + log_warnx("rde_dispatch_imsg: peerid %lu, " + "trailing garbage in LS Request " + "packet", imsg.hdr.peerid); + break; + case IMSG_LS_UPD: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + lsa = malloc(imsg.hdr.len - IMSG_HEADER_SIZE); + if (lsa == NULL) + fatal(NULL); + memcpy(lsa, imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + + if (!lsa_check(nbr, lsa, + imsg.hdr.len - IMSG_HEADER_SIZE)) { + free(lsa); + break; + } + + v = lsa_find(nbr->area, lsa->hdr.type, lsa->hdr.ls_id, + lsa->hdr.adv_rtr); + if (v == NULL) + db_hdr = NULL; + else + db_hdr = &v->lsa->hdr; + + if (nbr->self) { + lsa_merge(nbr, lsa, v); + /* lsa_merge frees the right lsa */ + break; + } + + r = lsa_newer(&lsa->hdr, db_hdr); + if (r > 0) { + /* new LSA newer than DB */ + if (v && v->flooded && + v->changed + MIN_LS_ARRIVAL >= now) { + free(lsa); + break; + } + + rde_req_list_del(nbr, &lsa->hdr); + + if (!(self = lsa_self(nbr, lsa, v))) + if (lsa_add(nbr, lsa)) + /* delayed lsa */ + break; + + /* flood and perhaps ack LSA */ + imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD, + imsg.hdr.peerid, 0, lsa, + ntohs(lsa->hdr.len)); + + /* reflood self originated LSA */ + if (self && v) + imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD, + v->peerid, 0, v->lsa, + ntohs(v->lsa->hdr.len)); + /* lsa not added so free it */ + if (self) + free(lsa); + } else if (r < 0) { + /* lsa no longer needed */ + free(lsa); + + /* + * point 6 of "The Flooding Procedure" + * We are violating the RFC here because + * it does not make sense to reset a session + * because an equal LSA is already in the table. + * Only if the LSA sent is older than the one + * in the table we should reset the session. + */ + if (rde_req_list_exists(nbr, &lsa->hdr)) { + imsg_compose(ibuf_ospfe, IMSG_LS_BADREQ, + imsg.hdr.peerid, 0, NULL, 0); + break; + } + + /* new LSA older than DB */ + if (ntohl(db_hdr->seq_num) == MAX_SEQ_NUM && + ntohs(db_hdr->age) == MAX_AGE) + /* seq-num wrap */ + break; + + if (v->changed + MIN_LS_ARRIVAL >= now) + break; + + /* directly send current LSA, no ack */ + imsg_compose(ibuf_ospfe, IMSG_LS_UPD, + imsg.hdr.peerid, 0, v->lsa, + ntohs(v->lsa->hdr.len)); + } else { + /* LSA equal send direct ack */ + imsg_compose(ibuf_ospfe, IMSG_LS_ACK, + imsg.hdr.peerid, 0, &lsa->hdr, + sizeof(lsa->hdr)); + free(lsa); + } + break; + case IMSG_LS_MAXAGE: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + break; + + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct lsa_hdr)) + fatalx("invalid size of OE request"); + memcpy(&lsa_hdr, imsg.data, sizeof(lsa_hdr)); + + if (rde_nbr_loading(nbr->area)) + break; + + v = lsa_find(nbr->area, lsa_hdr.type, lsa_hdr.ls_id, + lsa_hdr.adv_rtr); + if (v == NULL) + db_hdr = NULL; + else + db_hdr = &v->lsa->hdr; + + /* + * only delete LSA if the one in the db is not newer + */ + if (lsa_newer(db_hdr, &lsa_hdr) <= 0) + lsa_del(nbr, &lsa_hdr); + break; + case IMSG_CTL_SHOW_DATABASE: + case IMSG_CTL_SHOW_DB_EXT: + case IMSG_CTL_SHOW_DB_NET: + case IMSG_CTL_SHOW_DB_RTR: + case IMSG_CTL_SHOW_DB_SELF: + case IMSG_CTL_SHOW_DB_SUM: + case IMSG_CTL_SHOW_DB_ASBR: + if (imsg.hdr.len != IMSG_HEADER_SIZE && + imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(aid)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + if (imsg.hdr.len == IMSG_HEADER_SIZE) { + LIST_FOREACH(area, &rdeconf->area_list, entry) { + imsg_compose(ibuf_ospfe, IMSG_CTL_AREA, + 0, imsg.hdr.pid, area, + sizeof(*area)); + lsa_dump(&area->lsa_tree, imsg.hdr.type, + imsg.hdr.pid); + } + lsa_dump(&asext_tree, imsg.hdr.type, + imsg.hdr.pid); + } else { + memcpy(&aid, imsg.data, sizeof(aid)); + if ((area = area_find(rdeconf, aid)) != NULL) { + imsg_compose(ibuf_ospfe, IMSG_CTL_AREA, + 0, imsg.hdr.pid, area, + sizeof(*area)); + lsa_dump(&area->lsa_tree, imsg.hdr.type, + imsg.hdr.pid); + if (!area->stub) + lsa_dump(&asext_tree, + imsg.hdr.type, + imsg.hdr.pid); + } + } + imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid, + NULL, 0); + break; + case IMSG_CTL_SHOW_RIB: + LIST_FOREACH(area, &rdeconf->area_list, entry) { + imsg_compose(ibuf_ospfe, IMSG_CTL_AREA, + 0, imsg.hdr.pid, area, sizeof(*area)); + + rt_dump(area->id, imsg.hdr.pid, RIB_RTR); + rt_dump(area->id, imsg.hdr.pid, RIB_NET); + } + aid.s_addr = 0; + rt_dump(aid, imsg.hdr.pid, RIB_EXT); + + imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid, + NULL, 0); + break; + case IMSG_CTL_SHOW_SUM: + rde_send_summary(imsg.hdr.pid); + LIST_FOREACH(area, &rdeconf->area_list, entry) + rde_send_summary_area(area, imsg.hdr.pid); + imsg_compose(ibuf_ospfe, 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); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +/* ARGSUSED */ +void +rde_dispatch_parent(int fd, short event, void *bula) +{ + static struct area *narea; + struct iface *niface; + struct imsg imsg; + struct kroute kr; + struct rroute rr; + struct imsgbuf *ibuf = bula; + struct lsa *lsa; + struct vertex *v; + struct rt_node *rn; + ssize_t n; + int shut = 0; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + shut = 1; + 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(rr)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + memcpy(&rr, imsg.data, sizeof(rr)); + + if ((lsa = rde_asext_get(&rr)) != NULL) { + v = lsa_find(NULL, lsa->hdr.type, + lsa->hdr.ls_id, lsa->hdr.adv_rtr); + + lsa_merge(nbrself, lsa, v); + } + break; + case IMSG_NETWORK_DEL: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(rr)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + memcpy(&rr, imsg.data, sizeof(rr)); + + if ((lsa = rde_asext_put(&rr)) != NULL) { + v = lsa_find(NULL, lsa->hdr.type, + lsa->hdr.ls_id, lsa->hdr.adv_rtr); + + /* + * if v == NULL no LSA is in the table and + * nothing has to be done. + */ + if (v) + lsa_merge(nbrself, lsa, v); + } + 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 ((rn = rt_find(kr.prefix.s_addr, kr.prefixlen, + DT_NET)) != NULL) + rde_send_change_kroute(rn); + else + /* should not happen */ + imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, + 0, &kr, sizeof(kr)); + break; + case IMSG_RECONF_CONF: + if ((nconf = malloc(sizeof(struct ospfd_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, sizeof(struct ospfd_conf)); + + LIST_INIT(&nconf->area_list); + LIST_INIT(&nconf->cand_list); + break; + case IMSG_RECONF_AREA: + if ((narea = area_new()) == NULL) + fatal(NULL); + memcpy(narea, imsg.data, sizeof(struct area)); + + LIST_INIT(&narea->iface_list); + LIST_INIT(&narea->nbr_list); + RB_INIT(&narea->lsa_tree); + + LIST_INSERT_HEAD(&nconf->area_list, narea, entry); + break; + case IMSG_RECONF_IFACE: + if ((niface = malloc(sizeof(struct iface))) == NULL) + fatal(NULL); + memcpy(niface, imsg.data, sizeof(struct iface)); + + LIST_INIT(&niface->nbr_list); + TAILQ_INIT(&niface->ls_ack_list); + + niface->area = narea; + LIST_INSERT_HEAD(&narea->iface_list, niface, entry); + + break; + case IMSG_RECONF_END: + merge_config(rdeconf, nconf); + nconf = NULL; + break; + default: + log_debug("rde_dispatch_parent: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(ibuf); + else { + /* this pipe is dead, so remove the event handler */ + event_del(&ibuf->ev); + event_loopexit(NULL); + } +} + +u_int32_t +rde_router_id(void) +{ + return (rdeconf->rtr_id.s_addr); +} + +void +rde_send_change_kroute(struct rt_node *r) +{ + struct kroute kr; + struct rt_nexthop *rn; + + TAILQ_FOREACH(rn, &r->nexthop, entry) { + if (!rn->invalid) + break; + } + if (!rn) + fatalx("rde_send_change_kroute: no valid nexthop found"); + + bzero(&kr, sizeof(kr)); + kr.prefix.s_addr = r->prefix.s_addr; + kr.nexthop.s_addr = rn->nexthop.s_addr; + kr.prefixlen = r->prefixlen; + kr.ext_tag = r->ext_tag; + + 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.prefixlen = r->prefixlen; + + imsg_compose(ibuf_main, IMSG_KROUTE_DELETE, 0, 0, &kr, sizeof(kr)); +} + +void +rde_send_summary(pid_t pid) +{ + static struct ctl_sum sumctl; + struct timeval now; + struct area *area; + struct vertex *v; + + bzero(&sumctl, sizeof(struct ctl_sum)); + + sumctl.rtr_id.s_addr = rde_router_id(); + sumctl.spf_delay = rdeconf->spf_delay; + sumctl.spf_hold_time = rdeconf->spf_hold_time; + + LIST_FOREACH(area, &rdeconf->area_list, entry) + sumctl.num_area++; + + RB_FOREACH(v, lsa_tree, &asext_tree) + sumctl.num_ext_lsa++; + + gettimeofday(&now, NULL); + if (rdeconf->uptime < now.tv_sec) + sumctl.uptime = now.tv_sec - rdeconf->uptime; + else + sumctl.uptime = 0; + + rde_imsg_compose_ospfe(IMSG_CTL_SHOW_SUM, 0, pid, &sumctl, + sizeof(sumctl)); +} + +void +rde_send_summary_area(struct area *area, pid_t pid) +{ + static struct ctl_sum_area sumareactl; + struct iface *iface; + struct rde_nbr *nbr; + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + bzero(&sumareactl, sizeof(struct ctl_sum_area)); + + sumareactl.area.s_addr = area->id.s_addr; + sumareactl.num_spf_calc = area->num_spf_calc; + + LIST_FOREACH(iface, &area->iface_list, entry) + sumareactl.num_iface++; + + LIST_FOREACH(nbr, &area->nbr_list, entry) + if (nbr->state == NBR_STA_FULL && !nbr->self) + sumareactl.num_adj_nbr++; + + RB_FOREACH(v, lsa_tree, tree) + sumareactl.num_lsa++; + + rde_imsg_compose_ospfe(IMSG_CTL_SHOW_SUM_AREA, 0, pid, &sumareactl, + sizeof(sumareactl)); +} + +LIST_HEAD(rde_nbr_head, rde_nbr); + +struct nbr_table { + struct rde_nbr_head *hashtbl; + u_int32_t hashmask; +} rdenbrtable; + +#define RDE_NBR_HASH(x) \ + &rdenbrtable.hashtbl[(x) & rdenbrtable.hashmask] + +void +rde_nbr_init(u_int32_t hashsize) +{ + struct rde_nbr_head *head; + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + rdenbrtable.hashtbl = calloc(hs, sizeof(struct rde_nbr_head)); + if (rdenbrtable.hashtbl == NULL) + fatal("rde_nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&rdenbrtable.hashtbl[i]); + + rdenbrtable.hashmask = hs - 1; + + if ((nbrself = calloc(1, sizeof(*nbrself))) == NULL) + fatal("rde_nbr_init"); + + nbrself->id.s_addr = rde_router_id(); + nbrself->peerid = NBR_IDSELF; + nbrself->state = NBR_STA_DOWN; + nbrself->self = 1; + head = RDE_NBR_HASH(NBR_IDSELF); + LIST_INSERT_HEAD(head, nbrself, hash); +} + +void +rde_nbr_free(void) +{ + free(nbrself); + free(rdenbrtable.hashtbl); +} + +struct rde_nbr * +rde_nbr_find(u_int32_t peerid) +{ + struct rde_nbr_head *head; + struct rde_nbr *nbr; + + head = RDE_NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct rde_nbr * +rde_nbr_new(u_int32_t peerid, struct rde_nbr *new) +{ + struct rde_nbr_head *head; + struct rde_nbr *nbr; + struct area *area; + + if (rde_nbr_find(peerid)) + return (NULL); + if ((area = area_find(rdeconf, new->area_id)) == NULL) + fatalx("rde_nbr_new: unknown area"); + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("rde_nbr_new"); + + memcpy(nbr, new, sizeof(*nbr)); + nbr->peerid = peerid; + nbr->area = area; + + TAILQ_INIT(&nbr->req_list); + + head = RDE_NBR_HASH(peerid); + LIST_INSERT_HEAD(head, nbr, hash); + LIST_INSERT_HEAD(&area->nbr_list, nbr, entry); + + return (nbr); +} + +void +rde_nbr_del(struct rde_nbr *nbr) +{ + if (nbr == NULL) + return; + + rde_req_list_free(nbr); + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); +} + +int +rde_nbr_loading(struct area *area) +{ + struct rde_nbr *nbr; + int checkall = 0; + + if (area == NULL) { + area = LIST_FIRST(&rdeconf->area_list); + checkall = 1; + } + + while (area != NULL) { + LIST_FOREACH(nbr, &area->nbr_list, entry) { + if (nbr->self) + continue; + if (nbr->state & NBR_STA_XCHNG || + nbr->state & NBR_STA_LOAD) + return (1); + } + if (!checkall) + break; + area = LIST_NEXT(area, entry); + } + + return (0); +} + +struct rde_nbr * +rde_nbr_self(struct area *area) +{ + struct rde_nbr *nbr; + + LIST_FOREACH(nbr, &area->nbr_list, entry) + if (nbr->self) + return (nbr); + + /* this may not happen */ + fatalx("rde_nbr_self: area without self"); + return (NULL); +} + +/* + * LSA req list + */ +void +rde_req_list_add(struct rde_nbr *nbr, struct lsa_hdr *lsa) +{ + struct rde_req_entry *le; + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("rde_req_list_add"); + + TAILQ_INSERT_TAIL(&nbr->req_list, le, entry); + le->type = lsa->type; + le->ls_id = lsa->ls_id; + le->adv_rtr = lsa->adv_rtr; +} + +int +rde_req_list_exists(struct rde_nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct rde_req_entry *le; + + TAILQ_FOREACH(le, &nbr->req_list, entry) { + if ((lsa_hdr->type == le->type) && + (lsa_hdr->ls_id == le->ls_id) && + (lsa_hdr->adv_rtr == le->adv_rtr)) + return (1); + } + return (0); +} + +void +rde_req_list_del(struct rde_nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct rde_req_entry *le; + + TAILQ_FOREACH(le, &nbr->req_list, entry) { + if ((lsa_hdr->type == le->type) && + (lsa_hdr->ls_id == le->ls_id) && + (lsa_hdr->adv_rtr == le->adv_rtr)) { + TAILQ_REMOVE(&nbr->req_list, le, entry); + free(le); + return; + } + } +} + +void +rde_req_list_free(struct rde_nbr *nbr) +{ + struct rde_req_entry *le; + + while ((le = TAILQ_FIRST(&nbr->req_list)) != NULL) { + TAILQ_REMOVE(&nbr->req_list, le, entry); + free(le); + } +} + +/* + * as-external LSA handling + */ +struct lsa * +rde_asext_get(struct rroute *rr) +{ +#if 0 + struct area *area; + struct iface *iface; +XXX + LIST_FOREACH(area, &rdeconf->area_list, entry) + LIST_FOREACH(iface, &area->iface_list, entry) { + if ((iface->addr.s_addr & iface->mask.s_addr) == + rr->kr.prefix.s_addr && iface->mask.s_addr == + prefixlen2mask(rr->kr.prefixlen)) { + /* already announced as (stub) net LSA */ + log_debug("rde_asext_get: %s/%d is net LSA", + inet_ntoa(rr->kr.prefix), rr->kr.prefixlen); + return (NULL); + } + } +#endif + /* update of seqnum is done by lsa_merge */ + return (orig_asext_lsa(rr, DEFAULT_AGE)); +} + +struct lsa * +rde_asext_put(struct rroute *rr) +{ + /* + * just try to remove the LSA. If the prefix is announced as + * stub net LSA lsa_find() will fail later and nothing will happen. + */ + + /* remove by reflooding with MAX_AGE */ + return (orig_asext_lsa(rr, MAX_AGE)); +} + +/* + * summary LSA stuff + */ +void +rde_summary_update(struct rt_node *rte, struct area *area) +{ + struct vertex *v = NULL; + struct lsa *lsa; + u_int8_t type = 0; + + /* first check if we actually need to announce this route */ + if (!(rte->d_type == DT_NET || rte->flags & OSPF_RTR_E)) + return; + /* never create summaries for as-ext LSA */ + if (rte->p_type == PT_TYPE1_EXT || rte->p_type == PT_TYPE2_EXT) + return; + /* no need for summary LSA in the originating area */ + if (rte->area.s_addr == area->id.s_addr) + return; + /* no need to originate inter-area routes to the backbone */ + if (rte->p_type == PT_INTER_AREA && area->id.s_addr == INADDR_ANY) + return; + /* TODO nexthop check, nexthop part of area -> no summary */ + if (rte->cost >= LS_INFINITY) + return; + /* TODO AS border router specific checks */ + /* TODO inter-area network route stuff */ + /* TODO intra-area stuff -- condense LSA ??? */ + + if (rte->d_type == DT_NET) { + type = LSA_TYPE_SUM_NETWORK; + } else if (rte->d_type == DT_RTR) { + type = LSA_TYPE_SUM_ROUTER; + } else + fatalx("rde_summary_update: unknown route type"); + + /* update lsa but only if it was changed */ + v = lsa_find(area, type, rte->prefix.s_addr, rde_router_id()); + lsa = orig_sum_lsa(rte, type, rte->invalid); + lsa_merge(rde_nbr_self(area), lsa, v); + + if (v == NULL) + v = lsa_find(area, type, rte->prefix.s_addr, rde_router_id()); + + /* suppressed/deleted routes are not found in the second lsa_find */ + if (v) + v->cost = rte->cost; +} + + +/* + * functions for self-originated LSA + */ +struct lsa * +orig_asext_lsa(struct rroute *rr, u_int16_t age) +{ + struct lsa *lsa; + u_int16_t len; + + len = sizeof(struct lsa_hdr) + sizeof(struct lsa_asext); + if ((lsa = calloc(1, len)) == NULL) + fatal("orig_asext_lsa"); + + log_debug("orig_asext_lsa: %s/%d age %d", + inet_ntoa(rr->kr.prefix), rr->kr.prefixlen, age); + + /* LSA header */ + lsa->hdr.age = htons(age); + lsa->hdr.type = LSA_TYPE_EXTERNAL; + lsa->hdr.adv_rtr = rdeconf->rtr_id.s_addr; + lsa->hdr.seq_num = htonl(INIT_SEQ_NUM); + lsa->hdr.len = htons(len); + + /* prefix and mask */ + /* + * TODO ls_id must be unique, for overlapping routes this may + * not be true. In this case a hack needs to be done to + * make the ls_id unique. + */ + lsa->hdr.ls_id = rr->kr.prefix.s_addr; + lsa->data.asext.mask = prefixlen2mask(rr->kr.prefixlen); + + /* + * nexthop -- on connected routes we are the nexthop, + * on all other cases we announce the true nexthop. + * XXX this is wrong as the true nexthop may be outside + * of the ospf cloud and so unreachable. For now we force + * all traffic to be directed to us. + */ + lsa->data.asext.fw_addr = 0; + + lsa->data.asext.metric = htonl(rr->metric); + lsa->data.asext.ext_tag = htonl(rr->kr.ext_tag); + + lsa->hdr.ls_chksum = 0; + lsa->hdr.ls_chksum = + htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET)); + + return (lsa); +} + +struct lsa * +orig_sum_lsa(struct rt_node *rte, u_int8_t type, int invalid) +{ + struct lsa *lsa; + u_int16_t len; + + len = sizeof(struct lsa_hdr) + sizeof(struct lsa_sum); + if ((lsa = calloc(1, len)) == NULL) + fatal("orig_sum_lsa"); + + /* LSA header */ + lsa->hdr.age = htons(invalid ? MAX_AGE : DEFAULT_AGE); + lsa->hdr.type = type; + lsa->hdr.adv_rtr = rdeconf->rtr_id.s_addr; + lsa->hdr.seq_num = htonl(INIT_SEQ_NUM); + lsa->hdr.len = htons(len); + + /* prefix and mask */ + /* + * TODO ls_id must be unique, for overlapping routes this may + * not be true. In this case a hack needs to be done to + * make the ls_id unique. + */ + lsa->hdr.ls_id = rte->prefix.s_addr; + if (type == LSA_TYPE_SUM_NETWORK) + lsa->data.sum.mask = prefixlen2mask(rte->prefixlen); + else + lsa->data.sum.mask = 0; /* must be zero per RFC */ + + lsa->data.sum.metric = htonl(rte->cost & LSA_METRIC_MASK); + + lsa->hdr.ls_chksum = 0; + lsa->hdr.ls_chksum = + htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET)); + + return (lsa); +} diff --git a/usr.sbin/ospf6d/rde.h b/usr.sbin/ospf6d/rde.h new file mode 100644 index 00000000000..3d8d0d00c1a --- /dev/null +++ b/usr.sbin/ospf6d/rde.h @@ -0,0 +1,169 @@ +/* $OpenBSD: rde.h,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * 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 v_nexthop { + TAILQ_ENTRY(v_nexthop) entry; + struct vertex *prev; + struct in_addr nexthop; +}; + +TAILQ_HEAD(v_nexthead, v_nexthop); + +struct vertex { + RB_ENTRY(vertex) entry; + TAILQ_ENTRY(vertex) cand; + struct v_nexthead nexthop; + struct event ev; + struct area *area; + struct lsa *lsa; + time_t changed; + time_t stamp; + u_int32_t cost; + u_int32_t peerid; /* neighbor unique imsg ID */ + u_int32_t ls_id; + u_int32_t adv_rtr; + u_int8_t type; + u_int8_t flooded; + u_int8_t deleted; + u_int8_t self; +}; + +struct rde_req_entry { + TAILQ_ENTRY(rde_req_entry) entry; + u_int32_t ls_id; + u_int32_t adv_rtr; + u_int8_t type; +}; + +/* just the info RDE needs */ +struct rde_nbr { + LIST_ENTRY(rde_nbr) entry, hash; + struct in_addr id; + struct in_addr area_id; + TAILQ_HEAD(, rde_req_entry) req_list; + struct area *area; + u_int32_t peerid; /* unique ID in DB */ + int state; + int self; +}; + +struct rt_nexthop { + TAILQ_ENTRY(rt_nexthop) entry; + struct in_addr nexthop; + struct in_addr adv_rtr; + time_t uptime; + u_int8_t connected; + u_int8_t invalid; +}; + +struct rt_node { + RB_ENTRY(rt_node) entry; + TAILQ_HEAD(,rt_nexthop) nexthop; + struct in_addr prefix; + struct in_addr area; + u_int32_t cost; + u_int32_t cost2; + u_int32_t ext_tag; + enum path_type p_type; + enum dst_type d_type; + u_int8_t flags; + u_int8_t prefixlen; + u_int8_t invalid; +}; + +struct abr_rtr { + struct in6_addr addr; + struct in_addr abr_id; + struct in6_addr dst_ip; + struct in_addr area; + u_int16_t metric; +}; + +extern struct lsa_tree asext_tree; + +/* rde.c */ +pid_t rde(struct ospfd_conf *, int [2], int [2], int [2]); +int rde_imsg_compose_ospfe(int, u_int32_t, pid_t, void *, + u_int16_t); +u_int32_t rde_router_id(void); +void rde_send_change_kroute(struct rt_node *); +void rde_send_delete_kroute(struct rt_node *); +void rde_nbr_del(struct rde_nbr *); +int rde_nbr_loading(struct area *); +struct rde_nbr *rde_nbr_self(struct area *); +void rde_summary_update(struct rt_node *, struct area *); + +/* rde_lsdb.c */ +void lsa_init(struct lsa_tree *); +int lsa_compare(struct vertex *, struct vertex *); +void vertex_free(struct vertex *); +int lsa_newer(struct lsa_hdr *, struct lsa_hdr *); +int lsa_check(struct rde_nbr *, struct lsa *, u_int16_t); +int lsa_self(struct rde_nbr *, struct lsa *, struct vertex *); +int lsa_add(struct rde_nbr *, struct lsa *); +void lsa_del(struct rde_nbr *, struct lsa_hdr *); +void lsa_age(struct vertex *); +struct vertex *lsa_find(struct area *, u_int8_t, u_int32_t, u_int32_t); +struct vertex *lsa_find_net(struct area *area, u_int32_t); +u_int16_t lsa_num_links(struct vertex *); +void lsa_snap(struct area *, u_int32_t); +void lsa_dump(struct lsa_tree *, int, pid_t); +void lsa_merge(struct rde_nbr *, struct lsa *, struct vertex *); +void lsa_remove_invalid_sums(struct area *); + +/* rde_spf.c */ +void spf_calc(struct area *); +void rt_calc(struct vertex *, struct area *, struct ospfd_conf *); +void asext_calc(struct vertex *); +void spf_tree_clr(struct area *); + +void cand_list_init(void); +void cand_list_add(struct vertex *); +struct vertex *cand_list_pop(void); +int cand_list_present(struct vertex *); +void cand_list_clr(void); + +void spf_timer(int, short, void *); +void start_spf_timer(void); +void stop_spf_timer(struct ospfd_conf *); +void start_spf_holdtimer(struct ospfd_conf *); + +void rt_init(void); +int rt_compare(struct rt_node *, struct rt_node *); +struct rt_node *rt_find(in_addr_t, u_int8_t, enum dst_type); +int rt_insert(struct rt_node *); +int rt_remove(struct rt_node *); +void rt_clear(void); +void rt_dump(struct in_addr, pid_t, u_int8_t); + +struct lsa_rtr_link *get_rtr_link(struct vertex *, int); +struct lsa_net_link *get_net_link(struct vertex *, int); + +RB_PROTOTYPE(lsa_tree, vertex, entry, lsa_compare) + +#endif /* _RDE_H_ */ diff --git a/usr.sbin/ospf6d/rde_lsdb.c b/usr.sbin/ospf6d/rde_lsdb.c new file mode 100644 index 00000000000..70c23652f16 --- /dev/null +++ b/usr.sbin/ospf6d/rde_lsdb.c @@ -0,0 +1,737 @@ +/* $OpenBSD: rde_lsdb.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@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/tree.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ospf6.h" +#include "ospf6d.h" +#include "rde.h" +#include "log.h" + +struct vertex *vertex_get(struct lsa *, struct rde_nbr *); + +int lsa_router_check(struct lsa *, u_int16_t); +void lsa_timeout(int, short, void *); +void lsa_refresh(struct vertex *); +int lsa_equal(struct lsa *, struct lsa *); + +RB_GENERATE(lsa_tree, vertex, entry, lsa_compare) + +void +lsa_init(struct lsa_tree *t) +{ + RB_INIT(t); +} + +int +lsa_compare(struct vertex *a, struct vertex *b) +{ + if (a->type < b->type) + return (-1); + if (a->type > b->type) + return (1); + if (a->ls_id < b->ls_id) + return (-1); + if (a->ls_id > b->ls_id) + return (1); + if (a->adv_rtr < b->adv_rtr) + return (-1); + if (a->adv_rtr > b->adv_rtr) + return (1); + return (0); +} + + +struct vertex * +vertex_get(struct lsa *lsa, struct rde_nbr *nbr) +{ + struct vertex *v; + struct timespec tp; + + if ((v = calloc(1, sizeof(struct vertex))) == NULL) + fatal(NULL); + TAILQ_INIT(&v->nexthop); + v->area = nbr->area; + v->peerid = nbr->peerid; + v->lsa = lsa; + clock_gettime(CLOCK_MONOTONIC, &tp); + v->changed = v->stamp = tp.tv_sec; + v->cost = LS_INFINITY; + v->ls_id = ntohl(lsa->hdr.ls_id); + v->adv_rtr = ntohl(lsa->hdr.adv_rtr); + v->type = lsa->hdr.type; + + if (!nbr->self) + v->flooded = 1; /* XXX fix me */ + v->self = nbr->self; + + evtimer_set(&v->ev, lsa_timeout, v); + + return (v); +} + +void +vertex_free(struct vertex *v) +{ + if (v->type == LSA_TYPE_EXTERNAL) + RB_REMOVE(lsa_tree, &asext_tree, v); + else + RB_REMOVE(lsa_tree, &v->area->lsa_tree, v); + + (void)evtimer_del(&v->ev); + free(v->lsa); + free(v); +} + +/* returns -1 if a is older, 1 if newer and 0 if equal to b */ +int +lsa_newer(struct lsa_hdr *a, struct lsa_hdr *b) +{ + int32_t a32, b32; + u_int16_t a16, b16; + int i; + + if (a == NULL) + return (-1); + if (b == NULL) + return (1); + + /* + * The sequence number is defined as signed 32-bit integer, + * no idea how IETF came up with such a stupid idea. + */ + a32 = (int32_t)ntohl(a->seq_num); + b32 = (int32_t)ntohl(b->seq_num); + + if (a32 > b32) + return (1); + if (a32 < b32) + return (-1); + + a16 = ntohs(a->ls_chksum); + b16 = ntohs(b->ls_chksum); + + if (a16 > b16) + return (1); + if (a16 < b16) + return (-1); + + a16 = ntohs(a->age); + b16 = ntohs(b->age); + + if (a16 >= MAX_AGE && b16 >= MAX_AGE) + return (0); + if (b16 >= MAX_AGE) + return (-1); + if (a16 >= MAX_AGE) + return (1); + + i = b16 - a16; + if (abs(i) > MAX_AGE_DIFF) + return (i > 0 ? 1 : -1); + + return (0); +} + +int +lsa_check(struct rde_nbr *nbr, struct lsa *lsa, u_int16_t len) +{ + struct area *area = nbr->area; + u_int32_t metric; + + if (len < sizeof(lsa->hdr)) { + log_warnx("lsa_check: bad packet size"); + return (0); + } + if (ntohs(lsa->hdr.len) != len) { + log_warnx("lsa_check: bad packet size"); + return (0); + } + + if (iso_cksum(lsa, len, 0)) { + log_warnx("lsa_check: bad packet checksum"); + return (0); + } + + /* invalid ages */ + if ((ntohs(lsa->hdr.age) < 1 && !nbr->self) || + ntohs(lsa->hdr.age) > MAX_AGE) { + log_warnx("lsa_check: bad age"); + return (0); + } + + /* invalid sequence number */ + if (ntohl(lsa->hdr.seq_num) == RESV_SEQ_NUM) { + log_warnx("ls_check: bad seq num"); + return (0); + } + + switch (lsa->hdr.type) { + case LSA_TYPE_ROUTER: + if (!lsa_router_check(lsa, len)) + return (0); + break; + case LSA_TYPE_NETWORK: + if ((len % sizeof(u_int32_t)) || + len < sizeof(lsa->hdr) + sizeof(u_int32_t)) { + log_warnx("lsa_check: bad LSA network packet"); + return (0); + } + break; + case LSA_TYPE_SUM_NETWORK: + case LSA_TYPE_SUM_ROUTER: + if ((len % sizeof(u_int32_t)) || + len < sizeof(lsa->hdr) + sizeof(lsa->data.sum)) { + log_warnx("lsa_check: bad LSA summary packet"); + return (0); + } + metric = ntohl(lsa->data.sum.metric); + if (metric & ~LSA_METRIC_MASK) { + log_warnx("lsa_check: bad LSA summary metric"); + return (0); + } + break; + case LSA_TYPE_EXTERNAL: + if ((len % (3 * sizeof(u_int32_t))) || + len < sizeof(lsa->hdr) + sizeof(lsa->data.asext)) { + log_warnx("lsa_check: bad LSA as-external packet"); + return (0); + } + metric = ntohl(lsa->data.asext.metric); + if (metric & ~(LSA_METRIC_MASK | LSA_ASEXT_E_FLAG)) { + log_warnx("lsa_check: bad LSA as-external metric"); + return (0); + } + /* AS-external-LSA are silently discarded in stub areas */ + if (area->stub) + return (0); + break; + default: + log_warnx("lsa_check: unknown type %u", lsa->hdr.type); + return (0); + } + + /* MaxAge handling */ + if (lsa->hdr.age == htons(MAX_AGE) && !nbr->self && lsa_find(area, + lsa->hdr.type, lsa->hdr.ls_id, lsa->hdr.adv_rtr) == NULL && + !rde_nbr_loading(area)) { + /* + * if no neighbor in state Exchange or Loading + * ack LSA but don't add it. Needs to be a direct ack. + */ + rde_imsg_compose_ospfe(IMSG_LS_ACK, nbr->peerid, 0, &lsa->hdr, + sizeof(struct lsa_hdr)); + return (0); + } + + return (1); +} + +int +lsa_router_check(struct lsa *lsa, u_int16_t len) +{ + struct lsa_rtr_link *rtr_link; + char *buf = (char *)lsa; + u_int16_t i, off, nlinks; + + off = sizeof(lsa->hdr) + sizeof(struct lsa_rtr); + if (off > len) { + log_warnx("lsa_check: invalid LSA router packet"); + return (0); + } + + nlinks = ntohs(lsa->data.rtr.nlinks); + for (i = 0; i < nlinks; i++) { + rtr_link = (struct lsa_rtr_link *)(buf + off); + off += sizeof(struct lsa_rtr_link); + if (off > len) { + log_warnx("lsa_check: invalid LSA router packet"); + return (0); + } + off += rtr_link->num_tos * sizeof(u_int32_t); + if (off > len) { + log_warnx("lsa_check: invalid LSA router packet"); + return (0); + } + } + + if (i != nlinks) { + log_warnx("lsa_check: invalid LSA router packet"); + return (0); + } + return (1); +} + +int +lsa_self(struct rde_nbr *nbr, struct lsa *new, struct vertex *v) +{ + struct lsa *dummy; +#if 0 + struct iface *iface; + + if (nbr->self) + return (0); + + if (rde_router_id() == new->hdr.adv_rtr) + goto self; + + if (new->hdr.type == LSA_TYPE_NETWORK) + LIST_FOREACH(iface, &nbr->area->iface_list, entry) + if (iface->addr.s_addr == new->hdr.ls_id) + goto self; + + return (0); +self: +#endif + if (v == NULL) { + /* + * LSA is no longer announced, remove by premature aging. + * The problem is that new may not be altered so a copy + * needs to be added to the LSA DB first. + */ + if ((dummy = malloc(ntohs(new->hdr.len))) == NULL) + fatal("lsa_self"); + memcpy(dummy, new, ntohs(new->hdr.len)); + dummy->hdr.age = htons(MAX_AGE); + /* + * The clue is that by using the remote nbr as originator + * the dummy LSA will be reflooded via the default timeout + * handler. + */ + (void)lsa_add(rde_nbr_self(nbr->area), dummy); + return (1); + } + + /* + * LSA is still originated, just reflood it. But we need to create + * a new instance by setting the LSA sequence number equal to the + * one of new and calling lsa_refresh(). Flooding will be done by the + * caller. + */ + v->lsa->hdr.seq_num = new->hdr.seq_num; + lsa_refresh(v); + return (1); +} + +int +lsa_add(struct rde_nbr *nbr, struct lsa *lsa) +{ + struct lsa_tree *tree; + struct vertex *new, *old; + struct timeval tv, now, res; + + if (lsa->hdr.type == LSA_TYPE_EXTERNAL) + tree = &asext_tree; + else + tree = &nbr->area->lsa_tree; + + new = vertex_get(lsa, nbr); + old = RB_INSERT(lsa_tree, tree, new); + + if (old != NULL) { + if (old->deleted && evtimer_pending(&old->ev, &tv)) { + /* new update added before hold time expired */ + gettimeofday(&now, NULL); + timersub(&tv, &now, &res); + + /* remove old LSA and insert new LSA with delay */ + vertex_free(old); + RB_INSERT(lsa_tree, tree, new); + new->deleted = 1; + + if (evtimer_add(&new->ev, &res) != 0) + fatal("lsa_add"); + return (1); + } + if (!lsa_equal(new->lsa, old->lsa)) { + if (lsa->hdr.type != LSA_TYPE_EXTERNAL) + nbr->area->dirty = 1; + start_spf_timer(); + } + vertex_free(old); + RB_INSERT(lsa_tree, tree, new); + } else { + if (lsa->hdr.type != LSA_TYPE_EXTERNAL) + nbr->area->dirty = 1; + start_spf_timer(); + } + + /* timeout handling either MAX_AGE or LS_REFRESH_TIME */ + timerclear(&tv); + + if (nbr->self && ntohs(new->lsa->hdr.age) == DEFAULT_AGE) + tv.tv_sec = LS_REFRESH_TIME; + else + tv.tv_sec = MAX_AGE - ntohs(new->lsa->hdr.age); + + if (evtimer_add(&new->ev, &tv) != 0) + fatal("lsa_add"); + return (0); +} + +void +lsa_del(struct rde_nbr *nbr, struct lsa_hdr *lsa) +{ + struct vertex *v; + struct timeval tv; + + v = lsa_find(nbr->area, lsa->type, lsa->ls_id, lsa->adv_rtr); + if (v == NULL) + return; + + v->deleted = 1; + /* hold time to make sure that a new lsa is not added premature */ + timerclear(&tv); + tv.tv_sec = MIN_LS_INTERVAL; + if (evtimer_add(&v->ev, &tv) == -1) + fatal("lsa_del"); +} + +void +lsa_age(struct vertex *v) +{ + struct timespec tp; + time_t now; + int d; + u_int16_t age; + + clock_gettime(CLOCK_MONOTONIC, &tp); + now = tp.tv_sec; + + d = now - v->stamp; + /* set stamp so that at least new calls work */ + v->stamp = now; + + if (d < 0) { + log_warnx("lsa_age: time went backwards"); + return; + } + + age = ntohs(v->lsa->hdr.age); + if (age + d > MAX_AGE) + age = MAX_AGE; + else + age += d; + + v->lsa->hdr.age = htons(age); +} + +struct vertex * +lsa_find(struct area *area, u_int8_t type, u_int32_t ls_id, u_int32_t adv_rtr) +{ + struct vertex key; + struct vertex *v; + struct lsa_tree *tree; + + key.ls_id = ntohl(ls_id); + key.adv_rtr = ntohl(adv_rtr); + key.type = type; + + if (type == LSA_TYPE_EXTERNAL) + tree = &asext_tree; + else + tree = &area->lsa_tree; + + v = RB_FIND(lsa_tree, tree, &key); + + /* LSA that are deleted are not findable */ + if (v && v->deleted) + return (NULL); + + if (v) + lsa_age(v); + + return (v); +} + +struct vertex * +lsa_find_net(struct area *area, u_int32_t ls_id) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + /* XXX speed me up */ + RB_FOREACH(v, lsa_tree, tree) { + if (v->lsa->hdr.type == LSA_TYPE_NETWORK && + v->lsa->hdr.ls_id == ls_id) { + /* LSA that are deleted are not findable */ + if (v->deleted) + return (NULL); + lsa_age(v); + return (v); + } + } + + return (NULL); +} + +u_int16_t +lsa_num_links(struct vertex *v) +{ + switch (v->type) { + case LSA_TYPE_ROUTER: + return (ntohs(v->lsa->data.rtr.nlinks)); + case LSA_TYPE_NETWORK: + return ((ntohs(v->lsa->hdr.len) - sizeof(struct lsa_hdr) + - sizeof(u_int32_t)) / sizeof(struct lsa_net_link)); + default: + fatalx("lsa_num_links: invalid LSA type"); + } + + return (0); +} + +void +lsa_snap(struct area *area, u_int32_t peerid) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + do { + RB_FOREACH(v, lsa_tree, tree) { + if (v->deleted) + continue; + lsa_age(v); + if (ntohs(v->lsa->hdr.age) >= MAX_AGE) + rde_imsg_compose_ospfe(IMSG_LS_UPD, peerid, + 0, &v->lsa->hdr, ntohs(v->lsa->hdr.len)); + else + rde_imsg_compose_ospfe(IMSG_DB_SNAPSHOT, peerid, + 0, &v->lsa->hdr, sizeof(struct lsa_hdr)); + } + if (tree != &area->lsa_tree) + break; + tree = &asext_tree; + } while (1); +} + +void +lsa_dump(struct lsa_tree *tree, int imsg_type, pid_t pid) +{ + struct vertex *v; + + RB_FOREACH(v, lsa_tree, tree) { + if (v->deleted) + continue; + lsa_age(v); + switch (imsg_type) { + case IMSG_CTL_SHOW_DATABASE: + rde_imsg_compose_ospfe(IMSG_CTL_SHOW_DATABASE, 0, pid, + &v->lsa->hdr, ntohs(v->lsa->hdr.len)); + continue; + case IMSG_CTL_SHOW_DB_SELF: + if (v->lsa->hdr.adv_rtr == rde_router_id()) + break; + continue; + case IMSG_CTL_SHOW_DB_EXT: + if (v->type == LSA_TYPE_EXTERNAL) + break; + continue; + case IMSG_CTL_SHOW_DB_NET: + if (v->type == LSA_TYPE_NETWORK) + break; + continue; + case IMSG_CTL_SHOW_DB_RTR: + if (v->type == LSA_TYPE_ROUTER) + break; + continue; + case IMSG_CTL_SHOW_DB_SUM: + if (v->type == LSA_TYPE_SUM_NETWORK) + break; + continue; + case IMSG_CTL_SHOW_DB_ASBR: + if (v->type == LSA_TYPE_SUM_ROUTER) + break; + continue; + default: + log_warnx("lsa_dump: unknown imsg type"); + return; + } + rde_imsg_compose_ospfe(imsg_type, 0, pid, &v->lsa->hdr, + ntohs(v->lsa->hdr.len)); + } +} + +/* ARGSUSED */ +void +lsa_timeout(int fd, short event, void *bula) +{ + struct vertex *v = bula; + struct timeval tv; + + lsa_age(v); + + if (v->deleted) { + if (ntohs(v->lsa->hdr.age) >= MAX_AGE) { + vertex_free(v); + } else { + v->deleted = 0; + + /* schedule recalculation of the RIB */ + if (v->lsa->hdr.type != LSA_TYPE_EXTERNAL) + v->area->dirty = 1; + start_spf_timer(); + + rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->peerid, 0, + v->lsa, ntohs(v->lsa->hdr.len)); + + /* timeout handling either MAX_AGE or LS_REFRESH_TIME */ + timerclear(&tv); + if (v->self) + tv.tv_sec = LS_REFRESH_TIME; + else + tv.tv_sec = MAX_AGE - ntohs(v->lsa->hdr.age); + + if (evtimer_add(&v->ev, &tv) != 0) + fatal("lsa_add"); + } + return; + } + + if (v->self && ntohs(v->lsa->hdr.age) < MAX_AGE) + lsa_refresh(v); + + rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->peerid, 0, + v->lsa, ntohs(v->lsa->hdr.len)); +} + +void +lsa_refresh(struct vertex *v) +{ + struct timeval tv; + struct timespec tp; + u_int32_t seqnum; + u_int16_t len; + + /* refresh LSA by increasing sequence number by one */ + v->lsa->hdr.age = htons(DEFAULT_AGE); + seqnum = ntohl(v->lsa->hdr.seq_num); + if (seqnum++ == MAX_SEQ_NUM) + /* XXX fix me */ + fatalx("sequence number wrapping"); + v->lsa->hdr.seq_num = htonl(seqnum); + + /* recalculate checksum */ + len = ntohs(v->lsa->hdr.len); + v->lsa->hdr.ls_chksum = 0; + v->lsa->hdr.ls_chksum = htons(iso_cksum(v->lsa, len, LS_CKSUM_OFFSET)); + + clock_gettime(CLOCK_MONOTONIC, &tp); + v->changed = v->stamp = tp.tv_sec; + + timerclear(&tv); + tv.tv_sec = LS_REFRESH_TIME; + if (evtimer_add(&v->ev, &tv) == -1) + fatal("lsa_refresh"); +} + +void +lsa_merge(struct rde_nbr *nbr, struct lsa *lsa, struct vertex *v) +{ + struct timeval tv; + struct timespec tp; + time_t now; + u_int16_t len; + + if (v == NULL) { + if (lsa_add(nbr, lsa)) + /* delayed update */ + return; + rde_imsg_compose_ospfe(IMSG_LS_FLOOD, nbr->peerid, 0, + lsa, ntohs(lsa->hdr.len)); + return; + } + + /* set the seq_num to the current one. lsa_refresh() will do the ++ */ + lsa->hdr.seq_num = v->lsa->hdr.seq_num; + /* recalculate checksum */ + len = ntohs(lsa->hdr.len); + lsa->hdr.ls_chksum = 0; + lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET)); + + /* compare LSA most header fields are equal so don't check them */ + if (lsa_equal(lsa, v->lsa)) { + free(lsa); + return; + } + + /* overwrite the lsa all other fields are unaffected */ + free(v->lsa); + v->lsa = lsa; + start_spf_timer(); + if (lsa->hdr.type != LSA_TYPE_EXTERNAL) + nbr->area->dirty = 1; + + /* set correct timeout for reflooding the LSA */ + clock_gettime(CLOCK_MONOTONIC, &tp); + now = tp.tv_sec; + timerclear(&tv); + if (v->changed + MIN_LS_INTERVAL >= now) + tv.tv_sec = MIN_LS_INTERVAL; + if (evtimer_add(&v->ev, &tv) == -1) + fatal("lsa_merge"); +} + +void +lsa_remove_invalid_sums(struct area *area) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v, *nv; + + /* XXX speed me up */ + for (v = RB_MIN(lsa_tree, tree); v != NULL; v = nv) { + nv = RB_NEXT(lsa_tree, tree, v); + if ((v->lsa->hdr.type == LSA_TYPE_SUM_NETWORK || + v->lsa->hdr.type == LSA_TYPE_SUM_ROUTER) && + v->self && v->cost == LS_INFINITY && + v->deleted == 0) { + /* + * age the lsa and call lsa_timeout() which will + * actually remove it from the database. + */ + v->lsa->hdr.age = htons(MAX_AGE); + lsa_timeout(0, 0, v); + } + } +} + +int +lsa_equal(struct lsa *a, struct lsa *b) +{ + /* + * compare LSA that already have same type, adv_rtr and ls_id + * so not all header need to be compared + */ + if (a == NULL || b == NULL) + return (0); + if (a->hdr.len != b->hdr.len) + return (0); + /* LSA with age MAX_AGE are never equal */ + if (a->hdr.age == htons(MAX_AGE) || b->hdr.age == htons(MAX_AGE)) + return (0); + if (memcmp(&a->data, &b->data, ntohs(a->hdr.len) - + sizeof(struct lsa_hdr))) + return (0); + + return (1); +} + diff --git a/usr.sbin/ospf6d/rde_spf.c b/usr.sbin/ospf6d/rde_spf.c new file mode 100644 index 00000000000..7ef02ef6206 --- /dev/null +++ b/usr.sbin/ospf6d/rde_spf.c @@ -0,0 +1,1077 @@ +/* $OpenBSD: rde_spf.c,v 1.1 2007/10/08 10:44:51 norby Exp $ */ + +/* + * Copyright (c) 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 <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "ospf6d.h" +#include "ospf6.h" +#include "log.h" +#include "rde.h" + +extern struct ospfd_conf *rdeconf; +TAILQ_HEAD(, vertex) cand_list; +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) +struct vertex *spf_root = NULL; + +void calc_nexthop_clear(struct vertex *); +void calc_nexthop_add(struct vertex *, struct vertex *, u_int32_t); +void calc_nexthop(struct vertex *, struct vertex *); +void rt_nexthop_clear(struct rt_node *); +void rt_nexthop_add(struct rt_node *, struct v_nexthead *, + struct in_addr); +void rt_update(struct in_addr, u_int8_t, struct v_nexthead *, + u_int32_t, u_int32_t, struct in_addr, struct in_addr, + enum path_type, enum dst_type, u_int8_t, u_int32_t); +struct rt_node *rt_lookup(enum dst_type, in_addr_t); +void rt_invalidate(struct area *); +int linked(struct vertex *, struct vertex *); + +void +spf_calc(struct area *area) +{ + struct vertex *v, *w; + struct lsa_rtr_link *rtr_link = NULL; + struct lsa_net_link *net_link; + u_int32_t d; + int i; + struct in_addr addr; + + /* clear SPF tree */ + spf_tree_clr(area); + cand_list_clr(); + + /* initialize SPF tree */ + if ((v = spf_root = lsa_find(area, LSA_TYPE_ROUTER, rde_router_id(), + rde_router_id())) == NULL) + /* empty area because no interface is active */ + return; + + area->transit = 0; + spf_root->cost = 0; + w = NULL; + + /* calculate SPF tree */ + do { + /* loop links */ + for (i = 0; i < lsa_num_links(v); i++) { + switch (v->type) { + case LSA_TYPE_ROUTER: + rtr_link = get_rtr_link(v, i); + switch (rtr_link->type) { + case LINK_TYPE_STUB_NET: + /* skip */ + continue; + case LINK_TYPE_POINTTOPOINT: + case LINK_TYPE_VIRTUAL: + /* find router LSA */ + w = lsa_find(area, LSA_TYPE_ROUTER, + rtr_link->id, rtr_link->id); + break; + case LINK_TYPE_TRANSIT_NET: + /* find network LSA */ + w = lsa_find_net(area, rtr_link->id); + break; + default: + fatalx("spf_calc: invalid link type"); + } + break; + case LSA_TYPE_NETWORK: + net_link = get_net_link(v, i); + /* find router LSA */ + w = lsa_find(area, LSA_TYPE_ROUTER, + net_link->att_rtr, net_link->att_rtr); + break; + default: + fatalx("spf_calc: invalid LSA type"); + } + + if (w == NULL) + continue; + + if (w->lsa->hdr.age == MAX_AGE) + continue; + + if (!linked(w, v)) { + addr.s_addr = htonl(w->ls_id); + log_debug("spf_calc: w id %s type %d has ", + inet_ntoa(addr), w->type); + addr.s_addr = htonl(v->ls_id); + log_debug(" no link to v id %s type %d", + inet_ntoa(addr), v->type); + continue; + } + + if (v->type == LSA_TYPE_ROUTER) + d = v->cost + ntohs(rtr_link->metric); + else + d = v->cost; + + if (cand_list_present(w)) { + if (d > w->cost) + continue; + if (d < w->cost) { + w->cost = d; + calc_nexthop_clear(w); + calc_nexthop(w, v); + /* + * need to readd to candidate list + * because the list is sorted + */ + TAILQ_REMOVE(&cand_list, w, cand); + cand_list_add(w); + } else + /* equal cost path */ + calc_nexthop(w, v); + } else if (w->cost == LS_INFINITY && d < LS_INFINITY) { + w->cost = d; + + calc_nexthop_clear(w); + calc_nexthop(w, v); + cand_list_add(w); + } + } + + /* get next vertex */ + v = cand_list_pop(); + w = NULL; + } while (v != NULL); + + /* spf_dump(area); */ + log_debug("spf_calc: area %s calculated", + inet_ntoa(area->id)); + + area->num_spf_calc++; + start_spf_timer(); +} + +void +rt_calc(struct vertex *v, struct area *area, struct ospfd_conf *conf) +{ + struct vertex *w; + struct v_nexthop *vn; + struct lsa_rtr_link *rtr_link = NULL; + int i; + struct in_addr addr, adv_rtr; + + lsa_age(v); + if (ntohs(v->lsa->hdr.age) == MAX_AGE) + return; + + switch (v->type) { + case LSA_TYPE_ROUTER: + /* stub networks */ + if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop)) + return; + + for (i = 0; i < lsa_num_links(v); i++) { + rtr_link = get_rtr_link(v, i); + if (rtr_link->type != LINK_TYPE_STUB_NET) + continue; + + addr.s_addr = rtr_link->id; + adv_rtr.s_addr = htonl(v->adv_rtr); + + rt_update(addr, mask2prefixlen(rtr_link->data), + &v->nexthop, v->cost + ntohs(rtr_link->metric), 0, + area->id, adv_rtr, PT_INTRA_AREA, DT_NET, + v->lsa->data.rtr.flags, 0); + } + + /* router, only add border and as-external routers */ + if ((v->lsa->data.rtr.flags & (OSPF_RTR_B | OSPF_RTR_E)) == 0) + return; + + addr.s_addr = htonl(v->ls_id); + adv_rtr.s_addr = htonl(v->adv_rtr); + + rt_update(addr, 32, &v->nexthop, v->cost, 0, area->id, + adv_rtr, PT_INTRA_AREA, DT_RTR, v->lsa->data.rtr.flags, 0); + break; + case LSA_TYPE_NETWORK: + if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop)) + return; + + addr.s_addr = htonl(v->ls_id) & v->lsa->data.net.mask; + adv_rtr.s_addr = htonl(v->adv_rtr); + rt_update(addr, mask2prefixlen(v->lsa->data.net.mask), + &v->nexthop, v->cost, 0, area->id, adv_rtr, PT_INTRA_AREA, + DT_NET, 0, 0); + break; + case LSA_TYPE_SUM_NETWORK: + case LSA_TYPE_SUM_ROUTER: + /* if ABR only look at area 0.0.0.0 LSA */ + if (area_border_router(conf) && area->id.s_addr != INADDR_ANY) + return; + + /* ignore self-originated stuff */ + if (v->self) + return; + + /* TODO type 3 area address range check */ + + if ((w = lsa_find(area, LSA_TYPE_ROUTER, + htonl(v->adv_rtr), + htonl(v->adv_rtr))) == NULL) + return; + + /* copy nexthops */ + calc_nexthop_clear(v); /* XXX needed ??? */ + TAILQ_FOREACH(vn, &w->nexthop, entry) + calc_nexthop_add(v, w, vn->nexthop.s_addr); + + v->cost = w->cost + + (ntohl(v->lsa->data.sum.metric) & LSA_METRIC_MASK); + + if (v->cost >= LS_INFINITY || TAILQ_EMPTY(&v->nexthop)) + return; + + adv_rtr.s_addr = htonl(v->adv_rtr); + if (v->type == LSA_TYPE_SUM_NETWORK) { + addr.s_addr = htonl(v->ls_id) & v->lsa->data.sum.mask; + rt_update(addr, mask2prefixlen(v->lsa->data.sum.mask), + &v->nexthop, v->cost, 0, area->id, adv_rtr, + PT_INTER_AREA, DT_NET, 0, 0); + } else { + addr.s_addr = htonl(v->ls_id); + rt_update(addr, 32, &v->nexthop, v->cost, 0, area->id, + adv_rtr, PT_INTER_AREA, DT_RTR, + v->lsa->data.rtr.flags, 0); + } + + break; + default: + /* as-external LSA are stored in a different tree */ + fatalx("rt_calc: invalid LSA type"); + } +} + +void +asext_calc(struct vertex *v) +{ + struct rt_node *r; + struct rt_nexthop *rn; + u_int32_t cost2; + struct in_addr addr, adv_rtr, a; + enum path_type type; + + lsa_age(v); + if (ntohs(v->lsa->hdr.age) == MAX_AGE || + (ntohl(v->lsa->data.asext.metric) & LSA_METRIC_MASK) >= + LS_INFINITY) + return; + + switch (v->type) { + case LSA_TYPE_EXTERNAL: + /* ignore self-originated stuff */ + if (v->self) + return; + + if ((r = rt_lookup(DT_RTR, htonl(v->adv_rtr))) == NULL) + return; + + /* XXX RFC1583Compatibility */ + if (v->lsa->data.asext.fw_addr != 0 && + (r = rt_lookup(DT_NET, v->lsa->data.asext.fw_addr)) == NULL) + return; + + if (v->lsa->data.asext.fw_addr != 0 && + r->p_type != PT_INTRA_AREA && + r->p_type != PT_INTER_AREA) + return; + + if (ntohl(v->lsa->data.asext.metric) & LSA_ASEXT_E_FLAG) { + v->cost = r->cost; + cost2 = ntohl(v->lsa->data.asext.metric) & + LSA_METRIC_MASK; + type = PT_TYPE2_EXT; + } else { + v->cost = r->cost + (ntohl(v->lsa->data.asext.metric) & + LSA_METRIC_MASK); + cost2 = 0; + type = PT_TYPE1_EXT; + } + + a.s_addr = 0; + adv_rtr.s_addr = htonl(v->adv_rtr); + addr.s_addr = htonl(v->ls_id) & v->lsa->data.asext.mask; + + calc_nexthop_clear(v); + TAILQ_FOREACH(rn, &r->nexthop, entry) { + if (rn->invalid) + continue; + + if (rn->connected && r->d_type == DT_NET) { + if (v->lsa->data.asext.fw_addr != 0) + calc_nexthop_add(v, NULL, + v->lsa->data.asext.fw_addr); + else + calc_nexthop_add(v, NULL, + htonl(v->adv_rtr)); + } else + calc_nexthop_add(v, NULL, rn->nexthop.s_addr); + } + + rt_update(addr, mask2prefixlen(v->lsa->data.asext.mask), + &v->nexthop, v->cost, cost2, a, adv_rtr, type, + DT_NET, 0, ntohl(v->lsa->data.asext.ext_tag)); + break; + default: + fatalx("asext_calc: invalid LSA type"); + } +} + +void +spf_tree_clr(struct area *area) +{ + struct lsa_tree *tree = &area->lsa_tree; + struct vertex *v; + + RB_FOREACH(v, lsa_tree, tree) { + v->cost = LS_INFINITY; + calc_nexthop_clear(v); + } +} + +void +calc_nexthop_clear(struct vertex *v) +{ + struct v_nexthop *vn; + + while ((vn = TAILQ_FIRST(&v->nexthop))) { + TAILQ_REMOVE(&v->nexthop, vn, entry); + free(vn); + } +} + +void +calc_nexthop_add(struct vertex *dst, struct vertex *parent, u_int32_t nexthop) +{ + struct v_nexthop *vn; + + if (nexthop == 0) + /* invalid nexthop, skip it */ + return; + + if ((vn = calloc(1, sizeof(*vn))) == NULL) + fatal("calc_nexthop_add"); + + vn->prev = parent; + vn->nexthop.s_addr = nexthop; + + TAILQ_INSERT_TAIL(&dst->nexthop, vn, entry); +} + +void +calc_nexthop(struct vertex *dst, struct vertex *parent) +{ + struct lsa_rtr_link *rtr_link = NULL; + struct v_nexthop *vn; + int i; + + /* case 1 */ + if (parent == spf_root) { + switch (dst->type) { + case LSA_TYPE_ROUTER: + for (i = 0; i < lsa_num_links(dst); i++) { + rtr_link = get_rtr_link(dst, i); + if (rtr_link->type == LINK_TYPE_POINTTOPOINT && + ntohl(rtr_link->id) == parent->ls_id) { + calc_nexthop_add(dst, parent, + rtr_link->data); + break; + } + } + return; + case LSA_TYPE_NETWORK: + for (i = 0; i < lsa_num_links(parent); i++) { + rtr_link = get_rtr_link(parent, i); + switch (rtr_link->type) { + case LINK_TYPE_POINTTOPOINT: + /* ignore */ + break; + case LINK_TYPE_TRANSIT_NET: + if ((htonl(dst->ls_id) & + dst->lsa->data.net.mask) == + (rtr_link->data & + dst->lsa->data.net.mask)) { + calc_nexthop_add(dst, parent, + rtr_link->data); + } + break; + case LINK_TYPE_STUB_NET: + break; + + default: + fatalx("calc_nexthop: invalid link " + "type"); + } + } + return; + default: + fatalx("calc_nexthop: invalid dst type"); + } + } + + /* case 2 */ + if (parent->type == LSA_TYPE_NETWORK && dst->type == LSA_TYPE_ROUTER) { + TAILQ_FOREACH(vn, &parent->nexthop, entry) { + if (vn->prev == spf_root) { + for (i = 0; i < lsa_num_links(dst); i++) { + rtr_link = get_rtr_link(dst, i); + if ((rtr_link->type == + LINK_TYPE_TRANSIT_NET) && + (rtr_link->data & + parent->lsa->data.net.mask) == + (htonl(parent->ls_id) & + parent->lsa->data.net.mask)) + calc_nexthop_add(dst, parent, + rtr_link->data); + } + } else { + calc_nexthop_add(dst, parent, + vn->nexthop.s_addr); + } + } + return; + } + + /* case 3 */ + TAILQ_FOREACH(vn, &parent->nexthop, entry) + calc_nexthop_add(dst, parent, vn->nexthop.s_addr); +} + +/* candidate list */ +void +cand_list_init(void) +{ + TAILQ_INIT(&cand_list); +} + +void +cand_list_add(struct vertex *v) +{ + struct vertex *c = NULL; + + TAILQ_FOREACH(c, &cand_list, cand) { + if (c->cost > v->cost) { + TAILQ_INSERT_BEFORE(c, v, cand); + return; + } else if (c->cost == v->cost && c->type == LSA_TYPE_ROUTER && + v->type == LSA_TYPE_NETWORK) { + TAILQ_INSERT_BEFORE(c, v, cand); + return; + } + } + TAILQ_INSERT_TAIL(&cand_list, v, cand); +} + +struct vertex * +cand_list_pop(void) +{ + struct vertex *c; + + if ((c = TAILQ_FIRST(&cand_list)) != NULL) { + TAILQ_REMOVE(&cand_list, c, cand); + } + + return (c); +} + +int +cand_list_present(struct vertex *v) +{ + struct vertex *c; + + TAILQ_FOREACH(c, &cand_list, cand) { + if (c == v) + return (1); + } + + return (0); +} + +void +cand_list_clr(void) +{ + struct vertex *c; + + while ((c = TAILQ_FIRST(&cand_list)) != NULL) { + TAILQ_REMOVE(&cand_list, c, cand); + } +} + +/* timers */ +/* ARGSUSED */ +void +spf_timer(int fd, short event, void *arg) +{ + struct vertex *v; + struct ospfd_conf *conf = arg; + struct area *area; + struct rt_node *r; + + switch (conf->spf_state) { + case SPF_IDLE: + fatalx("spf_timer: invalid state IDLE"); + case SPF_HOLDQUEUE: + conf->spf_state = SPF_DELAY; + /* FALLTHROUGH */ + case SPF_DELAY: + LIST_FOREACH(area, &conf->area_list, entry) { + if (area->dirty) { + /* invalidate RIB entries of this area */ + rt_invalidate(area); + + /* calculate SPF tree */ + spf_calc(area); + + /* calculate route table */ + RB_FOREACH(v, lsa_tree, &area->lsa_tree) { + rt_calc(v, area, conf); + } + + area->dirty = 0; + } + } + + /* calculate as-external routes, first invalidate them */ + rt_invalidate(NULL); + RB_FOREACH(v, lsa_tree, &asext_tree) { + asext_calc(v); + } + + RB_FOREACH(r, rt_tree, &rt) { + LIST_FOREACH(area, &conf->area_list, entry) + rde_summary_update(r, area); + + if (r->d_type != DT_NET) + continue; + + if (r->invalid) + rde_send_delete_kroute(r); + else + rde_send_change_kroute(r); + } + + LIST_FOREACH(area, &conf->area_list, entry) + lsa_remove_invalid_sums(area); + + start_spf_holdtimer(conf); + break; + case SPF_HOLD: + conf->spf_state = SPF_IDLE; + break; + default: + fatalx("spf_timer: unknown state"); + } +} + +void +start_spf_timer(void) +{ + struct timeval tv; + + switch (rdeconf->spf_state) { + case SPF_IDLE: + timerclear(&tv); + tv.tv_sec = rdeconf->spf_delay; + rdeconf->spf_state = SPF_DELAY; + if (evtimer_add(&rdeconf->ev, &tv) == -1) + fatal("start_spf_timer"); + break; + case SPF_DELAY: + /* ignore */ + break; + case SPF_HOLD: + rdeconf->spf_state = SPF_HOLDQUEUE; + break; + case SPF_HOLDQUEUE: + /* ignore */ + break; + default: + fatalx("start_spf_timer: invalid spf_state"); + } +} + +void +stop_spf_timer(struct ospfd_conf *conf) +{ + if (evtimer_del(&conf->ev) == -1) + fatal("stop_spf_timer"); +} + +void +start_spf_holdtimer(struct ospfd_conf *conf) +{ + struct timeval tv; + + switch (conf->spf_state) { + case SPF_DELAY: + timerclear(&tv); + tv.tv_sec = conf->spf_hold_time; + conf->spf_state = SPF_HOLD; + if (evtimer_add(&conf->ev, &tv) == -1) + fatal("start_spf_holdtimer"); + break; + case SPF_IDLE: + case SPF_HOLD: + case SPF_HOLDQUEUE: + fatalx("start_spf_holdtimer: invalid state"); + default: + fatalx("spf_start_holdtimer: unknown state"); + } +} + +/* 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 (a->prefixlen < b->prefixlen) + return (-1); + if (a->prefixlen > b->prefixlen) + return (1); + if (a->d_type > b->d_type) + return (-1); + if (a->d_type < b->d_type) + return (1); + return (0); +} + +struct rt_node * +rt_find(in_addr_t prefix, u_int8_t prefixlen, enum dst_type d_type) +{ + struct rt_node s; + + s.prefix.s_addr = prefix; + s.prefixlen = prefixlen; + s.d_type = d_type; + + return (RB_FIND(rt_tree, &rt, &s)); +} + +int +rt_insert(struct rt_node *r) +{ + if (RB_INSERT(rt_tree, &rt, r) != NULL) { + log_warnx("rt_insert failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + free(r); + return (-1); + } + + return (0); +} + +int +rt_remove(struct rt_node *r) +{ + if (RB_REMOVE(rt_tree, &rt, r) == NULL) { + log_warnx("rt_remove failed for %s/%u", + inet_ntoa(r->prefix), r->prefixlen); + return (-1); + } + + rt_nexthop_clear(r); + free(r); + return (0); +} + +void +rt_invalidate(struct area *area) +{ + struct rt_node *r, *nr; + struct rt_nexthop *rn, *nrn; + + for (r = RB_MIN(rt_tree, &rt); r != NULL; r = nr) { + nr = RB_NEXT(rt_tree, &rt, r); + if (area == NULL) { + /* look only at as_ext routes */ + if (r->p_type != PT_TYPE1_EXT && + r->p_type != PT_TYPE2_EXT) + continue; + } else { + /* ignore all as_ext routes */ + if (r->p_type == PT_TYPE1_EXT || + r->p_type == PT_TYPE2_EXT) + continue; + + /* look only at routes matching the area */ + if (r->area.s_addr != area->id.s_addr) + continue; + } + r->invalid = 1; + for (rn = TAILQ_FIRST(&r->nexthop); rn != NULL; rn = nrn) { + nrn = TAILQ_NEXT(rn, entry); + if (rn->invalid) { + TAILQ_REMOVE(&r->nexthop, rn, entry); + free(rn); + } else + rn->invalid = 1; + } + if (TAILQ_EMPTY(&r->nexthop)) + rt_remove(r); + } +} + +void +rt_nexthop_clear(struct rt_node *r) +{ + struct rt_nexthop *rn; + + while ((rn = TAILQ_FIRST(&r->nexthop)) != NULL) { + TAILQ_REMOVE(&r->nexthop, rn, entry); + free(rn); + } +} + +void +rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, + struct in_addr adv_rtr) +{ + struct v_nexthop *vn; + struct rt_nexthop *rn; + struct timespec now; + + TAILQ_FOREACH(vn, vnh, entry) { + TAILQ_FOREACH(rn, &r->nexthop, entry) { + if (rn->nexthop.s_addr != vn->nexthop.s_addr) + continue; + + rn->adv_rtr.s_addr = adv_rtr.s_addr; + rn->connected = vn->prev == spf_root; + rn->invalid = 0; + + r->invalid = 0; + break; + } + if (rn) + continue; + + if ((rn = calloc(1, sizeof(struct rt_nexthop))) == NULL) + fatal("rt_nexthop_add"); + + clock_gettime(CLOCK_MONOTONIC, &now); + rn->nexthop.s_addr = vn->nexthop.s_addr; + rn->adv_rtr.s_addr = adv_rtr.s_addr; + rn->uptime = now.tv_sec; + rn->connected = vn->prev == spf_root; + rn->invalid = 0; + + r->invalid = 0; + TAILQ_INSERT_TAIL(&r->nexthop, rn, entry); + } +} + +void +rt_clear(void) +{ + struct rt_node *r; + + while ((r = RB_MIN(rt_tree, &rt)) != NULL) + rt_remove(r); +} + +void +rt_dump(struct in_addr area, pid_t pid, u_int8_t r_type) +{ + static struct ctl_rt rtctl; + struct timespec now; + struct rt_node *r; + struct rt_nexthop *rn; + + clock_gettime(CLOCK_MONOTONIC, &now); + + RB_FOREACH(r, rt_tree, &rt) { + if (r->invalid) + continue; + + if (r->area.s_addr != area.s_addr) + continue; + + switch (r_type) { + case RIB_RTR: + if (r->d_type != DT_RTR) + continue; + break; + case RIB_NET: + if (r->d_type != DT_NET) + continue; + if (r->p_type == PT_TYPE1_EXT || + r->p_type == PT_TYPE2_EXT) + continue; + break; + case RIB_EXT: + if (r->p_type != PT_TYPE1_EXT && + r->p_type != PT_TYPE2_EXT) + continue; + break; + default: + fatalx("rt_dump: invalid RIB type"); + } + + TAILQ_FOREACH(rn, &r->nexthop, entry) { + if (rn->invalid) + continue; + + rtctl.prefix.s_addr = r->prefix.s_addr; + rtctl.nexthop.s_addr = rn->nexthop.s_addr; + rtctl.area.s_addr = r->area.s_addr; + rtctl.adv_rtr.s_addr = rn->adv_rtr.s_addr; + rtctl.cost = r->cost; + rtctl.cost2 = r->cost2; + rtctl.p_type = r->p_type; + rtctl.d_type = r->d_type; + rtctl.flags = r->flags; + rtctl.prefixlen = r->prefixlen; + rtctl.uptime = now.tv_sec - rn->uptime; + + rde_imsg_compose_ospfe(IMSG_CTL_SHOW_RIB, 0, pid, + &rtctl, sizeof(rtctl)); + } + } +} + +void +rt_update(struct in_addr prefix, u_int8_t prefixlen, struct v_nexthead *vnh, + u_int32_t cost, u_int32_t cost2, struct in_addr area, + struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type, + u_int8_t flags, u_int32_t tag) +{ + struct rt_node *rte; + struct rt_nexthop *rn; + int better = 0, equal = 0; + + if (vnh == NULL || TAILQ_EMPTY(vnh)) /* XXX remove */ + fatalx("rt_update: invalid nexthop"); + + if ((rte = rt_find(prefix.s_addr, prefixlen, d_type)) == NULL) { + if ((rte = calloc(1, sizeof(struct rt_node))) == NULL) + fatal("rt_update"); + + TAILQ_INIT(&rte->nexthop); + rte->prefix.s_addr = prefix.s_addr; + rte->prefixlen = prefixlen; + rte->cost = cost; + rte->cost2 = cost2; + rte->area = area; + rte->p_type = p_type; + rte->d_type = d_type; + rte->flags = flags; + rte->ext_tag = tag; + + rt_nexthop_add(rte, vnh, adv_rtr); + + rt_insert(rte); + } else { + /* order: + * 1. intra-area + * 2. inter-area + * 3. type 1 as ext + * 4. type 2 as ext + */ + if (rte->invalid) /* everything is better than invalid */ + better = 1; + else if (p_type < rte->p_type) + better = 1; + else if (p_type == rte->p_type) + switch (p_type) { + case PT_INTRA_AREA: + case PT_INTER_AREA: + if (cost < rte->cost) + better = 1; + else if (cost == rte->cost && + rte->area.s_addr == area.s_addr) + equal = 1; + break; + case PT_TYPE1_EXT: + if (cost < rte->cost) + better = 1; + else if (cost == rte->cost) + equal = 1; + break; + case PT_TYPE2_EXT: + if (cost2 < rte->cost2) + better = 1; + else if (cost2 == rte->cost2 && + cost < rte->cost) + better = 1; + else if (cost2 == rte->cost2 && + cost == rte->cost) + equal = 1; + break; + } + + if (!better && !equal) + return; + + if (better) { + TAILQ_FOREACH(rn, &rte->nexthop, entry) + rn->invalid = 1; + + rte->area = area; + rte->cost = cost; + rte->cost2 = cost2; + rte->p_type = p_type; + rte->flags = flags; + rte->ext_tag = tag; + } + + if (equal || better) + rt_nexthop_add(rte, vnh, adv_rtr); + } +} + +struct rt_node * +rt_lookup(enum dst_type type, in_addr_t addr) +{ + struct rt_node *rn; + u_int8_t i = 32; + + if (type == DT_RTR) { + rn = rt_find(addr, 32, type); + if (rn && rn->invalid == 0) + return (rn); + return (NULL); + } + + /* type == DT_NET */ + do { + if ((rn = rt_find(addr & prefixlen2mask(i), i, type)) && + rn->invalid == 0) + return (rn); + } while (i-- != 0); + + return (NULL); +} + +/* router LSA links */ +struct lsa_rtr_link * +get_rtr_link(struct vertex *v, int idx) +{ + struct lsa_rtr_link *rtr_link = NULL; + char *buf = (char *)v->lsa; + u_int16_t i, off, nlinks; + + if (v->type != LSA_TYPE_ROUTER) + fatalx("get_rtr_link: invalid LSA type"); + + off = sizeof(v->lsa->hdr) + sizeof(struct lsa_rtr); + + /* nlinks validated earlier by lsa_check() */ + nlinks = lsa_num_links(v); + for (i = 0; i < nlinks; i++) { + rtr_link = (struct lsa_rtr_link *)(buf + off); + if (i == idx) + return (rtr_link); + + off += sizeof(struct lsa_rtr_link) + + rtr_link->num_tos * sizeof(u_int32_t); + } + + return (NULL); +} + +/* network LSA links */ +struct lsa_net_link * +get_net_link(struct vertex *v, int idx) +{ + struct lsa_net_link *net_link = NULL; + char *buf = (char *)v->lsa; + u_int16_t i, off, nlinks; + + if (v->type != LSA_TYPE_NETWORK) + fatalx("get_net_link: invalid LSA type"); + + off = sizeof(v->lsa->hdr) + sizeof(u_int32_t); + + /* nlinks validated earlier by lsa_check() */ + nlinks = lsa_num_links(v); + for (i = 0; i < nlinks; i++) { + net_link = (struct lsa_net_link *)(buf + off); + if (i == idx) + return (net_link); + + off += sizeof(struct lsa_net_link); + } + + return (NULL); +} + +/* misc */ +int +linked(struct vertex *w, struct vertex *v) +{ + struct lsa_rtr_link *rtr_link = NULL; + struct lsa_net_link *net_link = NULL; + int i; + + switch (w->type) { + case LSA_TYPE_ROUTER: + for (i = 0; i < lsa_num_links(w); i++) { + rtr_link = get_rtr_link(w, i); + switch (v->type) { + case LSA_TYPE_ROUTER: + if (rtr_link->type == LINK_TYPE_POINTTOPOINT && + rtr_link->id == htonl(v->ls_id)) + return (1); + break; + case LSA_TYPE_NETWORK: + if (rtr_link->id == htonl(v->ls_id)) + return (1); + break; + default: + fatalx("spf_calc: invalid type"); + } + } + return (0); + case LSA_TYPE_NETWORK: + for (i = 0; i < lsa_num_links(w); i++) { + net_link = get_net_link(w, i); + switch (v->type) { + case LSA_TYPE_ROUTER: + if (net_link->att_rtr == htonl(v->ls_id)) + return (1); + break; + default: + fatalx("spf_calc: invalid type"); + } + } + return (0); + default: + fatalx("spf_calc: invalid LSA type"); + } + + return (0); +} |