diff options
31 files changed, 10493 insertions, 0 deletions
diff --git a/usr.sbin/ospfd/Makefile b/usr.sbin/ospfd/Makefile new file mode 100644 index 00000000000..6d77c3006fc --- /dev/null +++ b/usr.sbin/ospfd/Makefile @@ -0,0 +1,20 @@ +# $OpenBSD: Makefile,v 1.1 2005/01/28 14:05:40 claudio Exp $ + + +PROG= ospfd +SRCS= area.c auth.c buffer.c config.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 ospfd.c ospfe.c packet.c \ + parse.y rde.c rde_lsdb.c + +MAN= ospfd.8 + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +YFLAGS= +LDADD+= -levent + +.include <bsd.prog.mk> diff --git a/usr.sbin/ospfd/area.c b/usr.sbin/ospfd/area.c new file mode 100644 index 00000000000..3fbec52532d --- /dev/null +++ b/usr.sbin/ospfd/area.c @@ -0,0 +1,91 @@ +/* $OpenBSD: area.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/tree.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "ospf.h" +#include "ospfd.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; + + log_debug("area_del: area ID %s", inet_ntoa(area->id)); + + /* 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); + RB_REMOVE(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 = NULL; + + LIST_FOREACH(area, &conf->area_list, entry) { + if (area->id.s_addr == area_id.s_addr) { + return (area); + } + } + + log_debug("area_find: area ID %s not found", inet_ntoa(area_id)); + return (NULL); +} diff --git a/usr.sbin/ospfd/auth.c b/usr.sbin/ospfd/auth.c new file mode 100644 index 00000000000..67cd52a1472 --- /dev/null +++ b/usr.sbin/ospfd/auth.c @@ -0,0 +1,97 @@ +/* $OpenBSD: auth.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 <string.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +int +auth_validate(struct ospf_hdr *pkt, const struct iface *iface) +{ + if (ntohs(pkt->auth_type) != (u_int16_t)iface->auth_type) { + log_debug("auth_validate: wrong auth type, interface %s", + iface->name); + return (-1); + } + + switch (iface->auth_type) { + case AUTH_NONE: + break; + case AUTH_SIMPLE: + if (bcmp(pkt->auth_key.simple, iface->auth_key, + sizeof(pkt->auth_key.simple))) { + log_debug("auth_validate: wrong password, interface %s", + iface->name); + return (-1); + } + /* clear the key before chksum */ + bzero(pkt->auth_key.simple, + sizeof(pkt->auth_key.simple)); + break; + case AUTH_CRYPT: + log_debug("auth_validate: not supported, interface %s", + iface->name); + return (-1); + default: + log_debug("auth_validate: unknown auth type, interface %s", + iface->name); + return (-1); + } + + if (in_cksum(pkt, ntohs(pkt->len))) { + log_debug("recv_packet: invalid checksum, interface %s", + iface->name); + return (-1); + } + + return (0); +} + +int +auth_gen(void *buf, u_int16_t len, const struct iface *iface) +{ + struct ospf_hdr *ospf_hdr = buf; + + /* update length, and checksum */ + ospf_hdr->len = htons(len); + ospf_hdr->chksum = in_cksum(buf, len); + + switch (iface->auth_type) { + case AUTH_NONE: + break; + case AUTH_SIMPLE: + strncpy(ospf_hdr->auth_key.simple, iface->auth_key, + sizeof(ospf_hdr->auth_key.simple)); + break; + case AUTH_CRYPT: + log_debug("auth_gen: not supported, interface %s", + iface->name); + return (-1); + default: + log_debug("auth_gen: unknown auth type, interface %s", + iface->name); + return (-1); + } + + return (0); +} diff --git a/usr.sbin/ospfd/buffer.c b/usr.sbin/ospfd/buffer.c new file mode 100644 index 00000000000..f8fa58797d6 --- /dev/null +++ b/usr.sbin/ospfd/buffer.c @@ -0,0 +1,257 @@ +/* $OpenBSD: buffer.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h> +#include <sys/uio.h> +#include <stdio.h> +#include <stdlib.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <unistd.h> + +#include "ospfd.h" + +void buf_enqueue(struct msgbuf *, struct buf *); +void buf_dequeue(struct msgbuf *, struct buf *); + +struct buf * +buf_open(ssize_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 = len; + buf->fd = -1; + + return (buf); +} + +int +buf_add(struct buf *buf, void *data, ssize_t len) +{ + if (buf->wpos + len > buf->size) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +buf_reserve(struct buf *buf, ssize_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +int +buf_close(struct msgbuf *msgbuf, struct buf *buf) +{ + buf_enqueue(msgbuf, buf); + return (1); +} + +int +buf_write(int sock, struct buf *buf) +{ + ssize_t n; + + if ((n = write(sock, buf->buf + buf->rpos, + buf->size - buf->rpos)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS) /* try later */ + return (0); + else + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (-2); + } + + if (n < buf->size - buf->rpos) { /* not all data written yet */ + buf->rpos += n; + return (0); + } else + 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) +{ + /* + * possible race here + * when we cannot write out data completely from a buffer, + * we MUST return and NOT try to write out stuff from later buffers - + * the socket might have become writeable again + */ + struct iovec iov[IOV_MAX]; + struct buf *buf, *next; + int i = 0; + ssize_t n; + struct msghdr msg; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + + 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++; + if (buf->fd != -1) + break; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if (buf != NULL && buf->fd != -1) { + msg.msg_control = (caddr_t)cmsgbuf; + msg.msg_controllen = CMSG_LEN(sizeof(int)); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = buf->fd; + } + + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS) /* 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 (n >= buf->size - buf->rpos) { + n -= buf->size - buf->rpos; + buf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } + + return (0); +} + +int +msgbuf_writebound(struct msgbuf *msgbuf) +{ + /* + * possible race here + * when we cannot write out data completely from a buffer, + * we MUST return and NOT try to write out stuff from later buffers - + * the socket might have become writeable again + */ + struct buf *buf; + int n; + + if (!msgbuf_unbounded(msgbuf)) + return (1); + + buf = TAILQ_FIRST(&msgbuf->bufs); + if ((n = buf_write(msgbuf->fd, buf)) < 0) + return (n); + + if (n == 1) { /* everything written out */ + buf_dequeue(msgbuf, buf); + return (1); + } else + return (0); +} + +int +msgbuf_unbounded(struct msgbuf *msgbuf) +{ + struct buf *buf; + + /* return 1 if last buffer was not completely written. */ + buf = TAILQ_FIRST(&msgbuf->bufs); + if (buf != NULL && buf->rpos != 0) + return (1); + else + 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); + + if (buf->fd != -1) + close(buf->fd); + + msgbuf->queued--; + buf_free(buf); +} diff --git a/usr.sbin/ospfd/config.c b/usr.sbin/ospfd/config.c new file mode 100644 index 00000000000..5db51be9084 --- /dev/null +++ b/usr.sbin/ospfd/config.c @@ -0,0 +1,153 @@ +/* $OpenBSD: config.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 "ospf.h" +#include "ospfd.h" +#include "ospfe.h" +#include "log.h" + +void show_db_sum(struct lsa_hdr *); +void show_neighbor(struct nbr *); +void show_interface(struct iface *); +void show_area(struct area *); + +extern char *__progname; + +void +show_db_sum(struct lsa_hdr *db_sum) +{ + + log_debug(" age %d", db_sum->age); + log_debug(" opts %d", db_sum->opts); + log_debug(" type %d", db_sum->type); + log_debug(" ls_id %s", (db_sum->ls_id)); + log_debug(" adv_rtr %s", (db_sum->adv_rtr)); + log_debug(" seq_num 0x%x", db_sum->seq_num); + log_debug(" chksum 0x%x", db_sum->ls_chksum); + log_debug(" len %d", db_sum->len); +} + +void +show_neighbor(struct nbr *nbr) +{ + struct lsa_entry *lsa_entry = NULL; + + log_debug(" state: %s", nbr_state_name(nbr->state)); + log_debug(" inactivity timer: "); + log_debug(" master: %d", nbr->master); + log_debug(" dd seq num: %d", nbr->dd_seq_num); + log_debug(" last rx options: %d", nbr->last_rx_options); + log_debug(" id: %s", inet_ntoa(nbr->id)); + log_debug(" priority: %d", nbr->priority); + log_debug(" address: %s", inet_ntoa(nbr->addr)); + log_debug(" options: %d", nbr->options); + log_debug(" dr: %s", inet_ntoa(nbr->dr)); + log_debug(" bdr: %s", inet_ntoa(nbr->bdr)); + + log_debug(" ls retrans: "); + + log_debug(" db sum list: "); + TAILQ_FOREACH(lsa_entry, &nbr->db_sum_list, entry) { + show_db_sum(lsa_entry->le_lsa); + } + + log_debug(" ls request: "); + TAILQ_FOREACH(lsa_entry, &nbr->ls_req_list, entry) { + show_db_sum(lsa_entry->le_lsa); + } +} + +void +show_interface(struct iface *iface) +{ + struct nbr *nbr = NULL; + + log_debug(" interface: %s", iface->name); + log_debug(" type: %s", if_type_name(iface->type)); + log_debug(" state: %s", if_state_name(iface->state)); + log_debug(" address: %s", inet_ntoa(iface->addr)); + log_debug(" mask: %s", inet_ntoa(iface->mask)); + log_debug(" area: %s", inet_ntoa(iface->area->id)); + log_debug(" hello interval: %d", iface->hello_interval); + log_debug(" dead interval: %d", iface->dead_interval); + log_debug(" transfer delay: %d", iface->transfer_delay); + log_debug(" priority: %d", iface->priority); + log_debug(" hello timer: "); + log_debug(" wait timer: "); + log_debug(" neighbor:"); + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + show_neighbor(nbr); + } + + log_debug(" dr: "); + log_debug(" bdr: "); + log_debug(" metric: %d", iface->metric); + log_debug(" rxmt interval: %d", iface->rxmt_interval); + log_debug(" auth type: %s", if_auth_name(iface->auth_type)); + if (iface->auth_type == AUTH_TYPE_SIMPLE) { + log_debug(" auth key: '%s'", iface->auth_key); + } else { + log_debug(" auth key:" ); + } + + log_debug(" mtu: %d", iface->mtu); + log_debug(" fd: %d", iface->fd); + log_debug(" passive: %d", iface->passive); + log_debug(" ifindex: %d", iface->ifindex); +} + +void +show_area(struct area *area) +{ + struct iface *iface = NULL; + + log_debug("area: %s", inet_ntoa(area->id)); + + LIST_FOREACH(iface, &area->iface_list, entry) { + show_interface(iface); + } + + log_debug(" transit: %d", area->transit); + log_debug(" stub: %d", area->stub); + log_debug(" stub default cost: %d", area->stub_default_cost); +} + +void +show_config(struct ospfd_conf *xconf) +{ + struct area *area = NULL; + + log_debug("--------------------------------------------------------"); + log_debug("dumping %s configuration", __progname); + log_debug("--------------------------------------------------------"); + + log_debug("router-id: %s", inet_ntoa(xconf->rtr_id)); + log_debug("ospf socket: %d", xconf->ospf_socket); + + LIST_FOREACH(area, &xconf->area_list, entry) { + show_area(area); + } + log_debug("--------------------------------------------------------"); +} diff --git a/usr.sbin/ospfd/control.c b/usr.sbin/ospfd/control.c new file mode 100644 index 00000000000..c426d270a98 --- /dev/null +++ b/usr.sbin/ospfd/control.c @@ -0,0 +1,286 @@ +/* $OpenBSD: control.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospfd.h" +#include "ospf.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); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, OSPFD_SOCKET, sizeof(sun.sun_path)); + + if (unlink(OSPFD_SOCKET) == -1) + if (errno != ENOENT) { + log_warn("control_init: unlink %s", OSPFD_SOCKET); + close(fd); + return (-1); + } + + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("control_init: bind: %s", OSPFD_SOCKET); + close(fd); + return (-1); + } + + if (chmod(OSPFD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("control_init: chmod"); + close(fd); + return (-1); + } + + umask(old_umask); + + 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(OSPFD_SOCKET); +} + +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); +} + +void +control_dispatch_imsg(int fd, short event, void *bula) +{ + struct ctl_conn *c; + struct imsg imsg; + int n; + unsigned int ifidx; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warn("control_dispatch_imsg: fd %d: not found", fd); + return; + } + + switch (event) { + case EV_READ: + if ((n = imsg_read(&c->ibuf)) <= 0) { + control_close(fd); + return; + } + break; + case EV_WRITE: + if (msgbuf_write(&c->ibuf.w) < 0) { + control_close(fd); + return; + } + imsg_event_add(&c->ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(&c->ibuf, &imsg)) == -1) { + control_close(fd); + return; + } + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_FIB_COUPLE: + case IMSG_CTL_FIB_DECOUPLE: + case IMSG_CTL_RELOAD: + ospfe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0); + 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, -1, NULL, 0); + } + break; + case IMSG_CTL_SHOW_DATABASE: + 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) { + log_debug("control_imsg_relay: no connection pid"); + return (0); + } + + return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1, + imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); +} + +void +session_socket_blockmode(int fd, enum blockmodes bm) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + fatal("fnctl F_GETFL"); + + if (bm == BM_NONBLOCK) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + fatal("fnctl F_SETFL"); +} diff --git a/usr.sbin/ospfd/control.h b/usr.sbin/ospfd/control.h new file mode 100644 index 00000000000..47947f01593 --- /dev/null +++ b/usr.sbin/ospfd/control.h @@ -0,0 +1,50 @@ +/* $OpenBSD: control.h,v 1.1 2005/01/28 14:05:40 claudio 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 _CONTORL_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/ospfd/database.c b/usr.sbin/ospfd/database.c new file mode 100644 index 00000000000..8d893b75cf1 --- /dev/null +++ b/usr.sbin/ospfd/database.c @@ -0,0 +1,454 @@ +/* $OpenBSD: database.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 <string.h> +#include <unistd.h> + +#include "ospfd.h" +#include "ospf.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_in dst; + struct db_dscrp_hdr *dd_hdr; + struct lsa_hdr *lsa_hdr; + struct lsa_entry *le, *nle; + char *buf; + char *ptr; + int ret = 0; + + log_debug("send_db_description: neighbor ID %s, seq_num %x", + inet_ntoa(nbr->id), nbr->dd_seq_num); + + if (nbr->iface->passive) + return (0); + + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("send_db_description"); + + /* OSPF header */ + gen_ospf_hdr(ptr, nbr->iface, PACKET_TYPE_DD); + ptr += sizeof(struct ospf_hdr); + + /* database description header */ + dd_hdr = (struct db_dscrp_hdr *)ptr; + dd_hdr->opts = oeconf->options; + dd_hdr->dd_seq_num = htonl(nbr->dd_seq_num); + + ptr += sizeof(*dd_hdr); + + 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: + log_debug("send_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I; + break; + case NBR_STA_XCHNG: + log_debug("send_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + 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 */ + lsa_hdr = (struct lsa_hdr *)ptr; + + for (le = TAILQ_FIRST(&nbr->db_sum_list); (le != NULL) && + ((ptr - buf) < nbr->iface->mtu - PACKET_HDR); le = nle) { + nbr->dd_end = nle = TAILQ_NEXT(le, entry); + memcpy(ptr, le->le_lsa, sizeof(struct lsa_hdr)); + ptr += sizeof(*lsa_hdr); + } + break; + case NBR_STA_LOAD: + case NBR_STA_FULL: + log_debug("send_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + 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: + log_debug("send_db_description: unknown neighbor state, " + "neighbor ID %s", inet_ntoa(nbr->id)); + ret = -1; + goto done; + break; + } + + /* set destination */ + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + + switch (nbr->iface->type) { + case IF_TYPE_POINTOPOINT: + inet_aton(AllSPFRouters, &dst.sin_addr); + dd_hdr->iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_BROADCAST: + dst.sin_addr = nbr->addr; + dd_hdr->iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + dst.sin_addr = nbr->addr; + dd_hdr->iface_mtu = 0; + break; + default: + fatalx("send_db_description: unknown interface type"); + } + + dd_hdr->bits = nbr->options; + + /* update authentication and calculate checksum */ + auth_gen(buf, ptr - buf, nbr->iface); + + /* transmit packet */ + if ((ret = send_packet(nbr->iface, buf, (ptr - buf), &dst)) == -1) + log_warnx("send_db_description: error sending packet on " + "interface %s", nbr->iface->name); + +done: + free(buf); + + return (ret); +} + +void +recv_db_description(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct db_dscrp_hdr dd_hdr; + int dupe = 0; + + log_debug("recv_db_description: neighbor ID %s, seq_num %x", + inet_ntoa(nbr->id), nbr->dd_seq_num); + + 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, neighbor ID %s", + inet_ntoa(nbr->id)); + 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; + } + + log_debug("recv_db_description: seq_num %x", ntohl(dd_hdr.dd_seq_num)); + 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: + log_debug("recv_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + /* evaluate dr and bdr before issuing a 2-Way event */ + if_fsm(nbr->iface, IF_EVT_NBR_CHNG); + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + if (nbr->state != NBR_STA_XSTRT) + return; + /* FALLTHROUGH */ + case NBR_STA_XSTRT: + log_debug("recv_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + if (dupe) + return; + /* + * check bits: either I,M,MS or only M + * I,M,MS is checked here the M only case is a fall-through + */ + 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(nbr->iface->rtr_id.s_addr)) { + /* slave */ + log_debug("recv_db_description: slave, " + "neighbor ID %s", inet_ntoa(nbr->id)); + nbr->master = false; + 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_M) { + /* 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) + 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: + log_debug("recv_db_description: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + 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 LSA's to request */ + if (len > 0) + 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)) + 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; + return; +} + +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 */ +void +db_tx_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + log_debug("db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + + 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_LOAD: + return ; + case NBR_STA_XSTRT: + case NBR_STA_XCHNG: + 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; + log_debug("db_tx_timer: reschedule neighbor ID %s", + inet_ntoa(nbr->id)); + evtimer_add(&nbr->db_tx_timer, &tv); + } +} + +int +start_db_tx_timer(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return (0); + + log_debug("start_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + + timerclear(&tv); + + return (evtimer_add(&nbr->db_tx_timer, &tv)); +} + +int +stop_db_tx_timer(struct nbr *nbr) +{ + if (nbr == nbr->iface->self) + return (0); + + log_debug("stop_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + + return (evtimer_del(&nbr->db_tx_timer)); +} diff --git a/usr.sbin/ospfd/hello.c b/usr.sbin/ospfd/hello.c new file mode 100644 index 00000000000..25506b19125 --- /dev/null +++ b/usr.sbin/ospfd/hello.c @@ -0,0 +1,259 @@ +/* $OpenBSD: hello.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +extern struct ospfd_conf *oeconf; + +/* hello packet handling */ +int +send_hello(struct iface *iface) +{ + struct sockaddr_in dst; + struct hello_hdr *hello; + struct nbr *nbr; + char *buf; + char *ptr; + int ret = 0; + + if (iface->passive) + return (0); + + /* XXX use buffer API instead for better decoupling */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("send_hello"); + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_BROADCAST: + inet_aton(AllSPFRouters, &dst.sin_addr); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + /* XXX these networks need to be preconfigured */ + /* dst.sin_addr.s_addr = nbr->addr.s_addr; */ + inet_aton(AllSPFRouters, &dst.sin_addr); + break; + default: + fatalx("send_hello: unknown interface type"); + } + + /* OSPF header */ + gen_ospf_hdr(ptr, iface, PACKET_TYPE_HELLO); + ptr += sizeof(struct ospf_hdr); + + /* hello header */ + hello = (struct hello_hdr *)ptr; + hello->mask = iface->mask.s_addr; + hello->hello_interval = htons(iface->hello_interval); + hello->opts = oeconf->options; + hello->rtr_priority = iface->priority; + hello->rtr_dead_interval = htonl(iface->dead_interval); + + if (iface->dr) + hello->d_rtr = iface->dr->addr.s_addr; + if (iface->bdr) + hello->bd_rtr = iface->bdr->addr.s_addr; + ptr += sizeof(*hello); + + /* active neighbor(s) */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (ptr - buf > iface->mtu - PACKET_HDR) { + log_warnx("send_hello: too many neighbors on " + "interface %s", iface->name); + break; + } + if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) { + memcpy(ptr, &nbr->id, sizeof(nbr->id)); + ptr += sizeof(nbr->id); + } + } + + /* update authentication and calculate checksum */ + auth_gen(buf, ptr - buf, iface); + + if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1) + log_warnx("send_hello: error sending packet on " + "interface %s", iface->name); + + free(buf); + return (ret); +} + +void +recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf, + u_int16_t len) +{ + struct hello_hdr hello; + struct nbr *nbr = NULL; + u_int32_t nbr_id; + int twoway = 0, 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 (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; + } + + 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.opts & OSPF_OPTION_E && iface->area->stub) || + ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) { + 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; + if (nbr->addr.s_addr == src.s_addr) + break; + } + break; + default: + fatalx("recv_hello: unknown interface type"); + } + + if (!nbr) + nbr = nbr_new(rtr_id, iface, 0); + /* actually the neighbor address shouldn't be stored on virtual links */ + nbr->addr.s_addr = src.s_addr; + nbr->options = hello.opts; + + nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); + + while (len >= sizeof(nbr_id)) { + memcpy(&nbr_id, buf, sizeof(nbr_id)); + if (nbr_id == iface->rtr_id.s_addr) { + /* seen myself */ + if (nbr->state < NBR_STA_XSTRT) + twoway = 1; + 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 && + ((nbr->dr.s_addr == nbr->addr.s_addr && + nbr->bdr.s_addr == 0) || nbr->bdr.s_addr == nbr->addr.s_addr)) + if_fsm(iface, IF_EVT_BACKUP_SEEN); + + 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; + + nbr->dr.s_addr = hello.d_rtr; + nbr->bdr.s_addr = hello.bd_rtr; + + if (twoway) { + /* + * event 2 way recieved needs to be delayed after the + * interface neighbor change check else the DR and BDR + * may not be set correctly. + */ + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + } + + if (nbr_change) + if_fsm(iface, IF_EVT_NBR_CHNG); + + /* TODO NBMA needs some special handling */ + return; +} diff --git a/usr.sbin/ospfd/imsg.c b/usr.sbin/ospfd/imsg.c new file mode 100644 index 00000000000..b8634ebd62e --- /dev/null +++ b/usr.sbin/ospfd/imsg.c @@ -0,0 +1,223 @@ +/* $OpenBSD: imsg.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h> +#include <sys/uio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ospfd.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); +} + +int +imsg_read(struct imsgbuf *ibuf) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(int) * 16)]; + struct iovec iov; + ssize_t n; + int fd; + struct imsg_fd *ifd; + + bzero(&msg, sizeof(msg)); + iov.iov_base = ibuf->r.buf + ibuf->r.wpos; + iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + log_warn("imsg_read: pipe read error"); + return (-1); + } + return (0); + } + + ibuf->r.wpos += n; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fd = (*(int *)CMSG_DATA(cmsg)); + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) + fatal("imsg_read"); + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); + } else + log_warn("imsg_read: got unexpected ctl data level %d " + "type %d", cmsg->cmsg_level, cmsg->cmsg_type); + } + + return (n); +} + +int +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + ssize_t datalen = 0; + size_t av, left; + + 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; + memcpy(&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, int fd, 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); + + wbuf->fd = fd; + + if ((n = imsg_close(ibuf, wbuf)) < 0) + return (-1); + + return (n); +} + +struct buf * +imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid, + pid_t pid, u_int16_t datalen) +{ + struct buf *wbuf; + struct imsg_hdr hdr; + + if (datalen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { + log_warnx("imsg_create_core: len %u > MAX_IMSGSIZE; " + "type %u peerid %lu", datalen + IMSG_HEADER_SIZE, + type, peerid); + return (NULL); + } + + hdr.len = datalen + IMSG_HEADER_SIZE; + hdr.type = type; + hdr.peerid = peerid; + hdr.pid = pid; + if ((wbuf = buf_open(hdr.len)) == NULL) { + log_warn("imsg_create: buf_open"); + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct buf *msg, void *data, u_int16_t datalen) +{ + if (datalen) + if (buf_add(msg, data, datalen) == -1) { + log_warnx("imsg_add: buf_add error"); + buf_free(msg); + return (-1); + } + return (datalen); +} + +int +imsg_close(struct imsgbuf *ibuf, struct buf *msg) +{ + int n; + + if ((n = buf_close(&ibuf->w, msg)) < 0) { + log_warnx("imsg_close: buf_close error"); + buf_free(msg); + return (-1); + } + imsg_event_add(ibuf); + + return (n); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} + +int +imsg_get_fd(struct imsgbuf *ibuf) +{ + int fd; + struct imsg_fd *ifd; + + if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) + return (-1); + + fd = ifd->fd; + TAILQ_REMOVE(&ibuf->fds, ifd, entry); + free(ifd); + + return (fd); +} diff --git a/usr.sbin/ospfd/in_cksum.c b/usr.sbin/ospfd/in_cksum.c new file mode 100644 index 00000000000..3bb43c80718 --- /dev/null +++ b/usr.sbin/ospfd/in_cksum.c @@ -0,0 +1,90 @@ +/* $OpenBSD: in_cksum.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ +/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */ + +/* + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL) + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> + +#include <netinet/if_ether.h> +#include <netinet/in_systm.h> + +#include "ospfd.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. + */ +int +in_cksum(void *p, int len) +{ + int sum = 0, oddbyte = 0, v = 0; + u_char *cp = p; + + /* we assume < 2^16 bytes being summed */ + while (len > 0) { + if (oddbyte) { + sum += v + *cp++; + len--; + } + if (((long)cp & 1) == 0) { + while ((len -= 2) >= 0) { + sum += *(u_short *)cp; + cp += 2; + } + } else { + while ((len -= 2) >= 0) { + sum += *cp++ << 8; + sum += *cp++; + } + } + if ((oddbyte = len & 1) != 0) + v = *cp << 8; + } + if (oddbyte) + sum += v; + sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */ + sum += sum >> 16; /* add potential last carry */ + return (0xffff & ~sum); +} diff --git a/usr.sbin/ospfd/interface.c b/usr.sbin/ospfd/interface.c new file mode 100644 index 00000000000..4120c537e17 --- /dev/null +++ b/usr.sbin/ospfd/interface.c @@ -0,0 +1,824 @@ +/* $OpenBSD: interface.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 <unistd.h> +#include <string.h> +#include <event.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +void if_hello_timer(int, short, void *); +int if_start_hello_timer(struct iface *); +int if_stop_hello_timer(struct iface *); +int if_stop_wait_timer(struct iface *); +void if_wait_timer(int, short, void *); +int if_start_wait_timer(struct iface *); +int if_stop_wait_timer(struct iface *); +struct nbr *if_elect(struct nbr *, struct nbr *); +int if_set_mcast_ttl(int, u_int8_t); +int if_set_tos(int, int); +int if_set_mcast_loop(int); + +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_WAITING, IF_EVT_NBR_CHNG, 0, 0}, + {IF_STA_MULTI, IF_EVT_NBR_CHNG, IF_ACT_ELECT, 0}, + {IF_STA_ANY, IF_EVT_NBR_CHNG, 0, 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, 0, IF_STA_DOWN}, + {-1, 0, -1, 0}, +}; + +const char * const if_action_names[] = { + "START", + "ELECT", + "RESET" +}; + +const char * const if_type_names[] = { + "POINTOPOINT", + "BROADCAST", + "NBMA", + "POINTOMULTIPOINT", + "VIRTUALLINK" +}; + +const char * const if_auth_names[] = { + "NONE", + "SIMPLE", + "CRYPT" +}; + +int +if_fsm(struct iface *iface, enum iface_event event) +{ + int old_state; + int new_state = 0; + int i, ret = 0; + + old_state = iface->state; + + for (i = 0; iface_fsm[i].state != -1; i++) + if ((iface_fsm[i].state & old_state) && + (iface_fsm[i].event == event)) { + new_state = iface_fsm[i].new_state; + break; + } + + if (iface_fsm[i].state == -1) { + /* XXX event outside of the defined fsm, ignore it. */ + log_debug("fsm_if: interface %s, " + "event %s not expected in state %s", iface->name, + nbr_event_name(event), if_state_name(old_state)); + return (0); + } + + switch (iface_fsm[i].action) { + case IF_ACT_STRT: + ret = if_act_start(iface); + break; + case IF_ACT_ELECT: + ret = if_act_elect(iface); + break; + case IF_ACT_RST: + ret = if_act_reset(iface); + break; + default: + /* do nothing */ + break; + } + + if (ret) { + log_debug("fsm_if: error changing state for interface %s, " + "event %s, state %s", iface->name, if_event_name(event), + if_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + iface->state = new_state; + + log_debug("fsm_if: event %s resulted in action %s and changing " + "state for interface %s from %s to %s", + if_event_name(event), if_action_name(iface_fsm[i].action), + iface->name, if_state_name(old_state), if_state_name(iface->state)); + + return (ret); +} + +struct iface * +if_new(char *name, unsigned int idx) +{ + struct sockaddr_in *sain; + struct iface *iface = NULL; + struct ifreq *ifr; + int s; + + if ((iface = calloc(1, sizeof(*iface))) == NULL) + errx(1, "if_new: calloc"); + + iface->state = IF_STA_DOWN; + iface->passive = true; + + LIST_INIT(&iface->nbr_list); + TAILQ_INIT(&iface->ls_ack_list); + + evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface); + + strlcpy(iface->name, name, sizeof(iface->name)); + + if ((ifr = calloc(1, sizeof(*ifr))) == NULL) + errx(1, "if_new: calloc"); + + /* set up ifreq */ + strlcpy(ifr->ifr_name, name, sizeof(ifr->ifr_name)); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + errx(1, "if_new: socket"); + + /* get type */ + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)ifr) < 0) + errx(1, "if_new: cannot get type"); + if ((ifr->ifr_flags & IFF_POINTOPOINT)) + iface->type = IF_TYPE_POINTOPOINT; + + if ((ifr->ifr_flags & IFF_BROADCAST) && + (ifr->ifr_flags & IFF_MULTICAST)) + iface->type = IF_TYPE_BROADCAST; + + iface->flags = ifr->ifr_flags; + + /* get address */ + if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) < 0) + errx(1, "if_new: cannot get address"); + sain = (struct sockaddr_in *) &ifr->ifr_addr; + iface->addr = sain->sin_addr; + + /* get mask */ + if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) < 0) + errx(1, "if_new: cannot get mask"); + sain = (struct sockaddr_in *) &ifr->ifr_addr; + iface->mask = sain->sin_addr; + + /* get mtu */ + if (ioctl(s, SIOCGIFMTU, (caddr_t)ifr) < 0) + errx(1, "if_new: cannot get mtu"); + + iface->mtu = ifr->ifr_mtu; + iface->ifindex = idx; + + /* set event handlers for interface */ + evtimer_set(&iface->hello_timer, if_hello_timer, iface); + evtimer_set(&iface->wait_timer, if_wait_timer, iface); + + free(ifr); + close(s); + + return (iface); +} + +int +if_del(struct iface *iface) +{ + struct nbr *nbr = NULL; + + log_debug("if_del: interface %s", iface->name); + + /* clear lists etc */ + iface->self = NULL; /* trick neighbor.c code to remove self too */ + while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) { + LIST_REMOVE(nbr, entry); + nbr_del(nbr); + } + + ls_ack_list_clr(iface); + + return (-1); +} + +int +if_init(struct ospfd_conf *xconf) +{ + struct area *area = NULL; + struct iface *iface = NULL; + + if ((xconf->ospf_socket = socket(AF_INET, SOCK_RAW, + IPPROTO_OSPF)) == -1) { + log_warn("if_init: error creating socket"); + return (-1); + } + + /* set some defaults */ + if (if_set_mcast_ttl(xconf->ospf_socket, + IP_DEFAULT_MULTICAST_TTL) == -1) + return (-1); + + if (if_set_mcast_loop(xconf->ospf_socket) == -1) + return (-1); + + if (if_set_tos(xconf->ospf_socket, IPTOS_PREC_INTERNETCONTROL) == -1) + return (-1); + + + LIST_FOREACH(area, &xconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + break; + case IF_TYPE_BROADCAST: + /* all bcast interfaces use the same socket */ + iface->fd = xconf->ospf_socket; + break; + case IF_TYPE_NBMA: + break; + case IF_TYPE_POINTOMULTIPOINT: + break; + case IF_TYPE_VIRTUALLINK: + break; + default: + fatalx("if_init: unknown interface type"); + } + } + } + + return (0); +} + +int +if_shutdown(struct ospfd_conf *xconf) +{ + int ret = 0; + + ret = close(xconf->ospf_socket); + + return (ret); +} + +/* timers */ +void +if_hello_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + struct timeval tv; + + send_hello(iface); + + /* reschedule hello_timer */ + if (!iface->passive) { + timerclear(&tv); + tv.tv_sec = iface->hello_interval; + evtimer_add(&iface->hello_timer, &tv); + } +} + +int +if_start_hello_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + return (evtimer_add(&iface->hello_timer, &tv)); +} + +int +if_stop_hello_timer(struct iface *iface) +{ + return (evtimer_del(&iface->hello_timer)); +} + +void +if_wait_timer(int fd, short event, void *arg) +{ + struct iface *iface = arg; + + if_fsm(iface, IF_EVT_WTIMER); +} + +int +if_start_wait_timer(struct iface *iface) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = iface->dead_interval; + return (evtimer_add(&iface->wait_timer, &tv)); +} + +int +if_stop_wait_timer(struct iface *iface) +{ + return (evtimer_del(&iface->wait_timer)); +} + +/* actions */ +int +if_act_start(struct iface *iface) +{ + struct in_addr addr; + + if (iface->passive) { + log_debug("if_act_start: cannot start passive interface %s", + iface->name); + return (-1); + } + + /* init the dummy local neighbor */ + iface->self = nbr_new(ospfe_router_id(), iface, 1); + + /* up interface */ + /* ... */ + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + 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_aton(AllSPFRouters, &addr); + if (if_join_group(iface, &addr)) { + log_warnx("if_act_start: error joining group %s, " + "interface %s", inet_ntoa(addr), iface->name); + } + if (iface->priority == 0) { + iface->state = IF_STA_DROTHER; + if (if_start_hello_timer(iface)) + log_warnx("if_act_start: cannot schedule hello " + "timer, interface %s", iface->name); + } else { + iface->state = IF_STA_WAITING; + if (if_start_wait_timer(iface)) + log_warnx("if_act_start: cannot schedule wait " + "timer, interface %s", iface->name); + } + break; + case IF_TYPE_NBMA: + log_debug("if_act_start: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + default: + fatalx("if_act_start: unknown interface type"); + } + + 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 nbr *nbr, *bdr = NULL, *dr = NULL; + int round = 0; + int changed = 0; + char b1[16], b2[16], b3[16], b4[16]; + + log_debug("if_act_elect: interface %s", iface->name); + +start: + /* elect backup designated router */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->priority == 0 || nbr == dr || /* not electable */ + nbr->dr.s_addr == nbr->addr.s_addr) /* don't elect DR */ + continue; + if (bdr != NULL) { + if (nbr->bdr.s_addr == nbr->addr.s_addr) { + if (bdr->bdr.s_addr == bdr->addr.s_addr) + bdr = if_elect(bdr, nbr); + else + bdr = nbr; + } else if (bdr->bdr.s_addr != bdr->addr.s_addr) + bdr = if_elect(bdr, nbr); + } else + bdr = nbr; + } + log_debug("if_act_elect: bdr %s", bdr ? + inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) : "none"); + /* elect designated router */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->priority == 0 || (nbr != dr && + nbr->dr.s_addr != nbr->addr.s_addr)) + /* only DR may be elected check priority too */ + continue; + if (dr == NULL || bdr == NULL) + dr = nbr; + else + dr = if_elect(bdr, nbr); + } + log_debug("if_act_elect: dr %s", dr ? + inet_ntop(AF_INET, &dr->addr, b4, sizeof(b4)) : "none"); + 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))) { + log_debug("if_act_elect: round two"); + 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. + */ + 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) + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->state & NBR_STA_BIDIR) + nbr_fsm(nbr, NBR_EVT_ADJ_OK); + } + + if (if_start_hello_timer(iface)) { + log_warnx("if_act_elect: cannot schedule hello_timer"); + return (-1); + } + + return (0); +} + +int +if_act_reset(struct iface *iface) +{ + struct nbr *nbr = NULL; + struct in_addr addr; + + if (if_stop_hello_timer(iface)) { + log_warnx("if_act_reset: error removing hello_timer, " + "interface %s", iface->name); + return (-1); + } + + if (if_stop_wait_timer(iface)) { + log_warnx("if_act_reset: error removing wait_timer, " + "interface %s", iface->name); + return (-1); + } + + if (stop_ls_ack_tx_timer(iface)) { + log_warnx("if_act_reset: error removing ls_ack_tx_timer, " + "interface %s", iface->name); + return (-1); + } + + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + log_debug("if_act_reset: type %s not supported, interface %s", + if_type_name(iface->type), iface->name); + return (-1); + case IF_TYPE_BROADCAST: + inet_aton(AllSPFRouters, &addr); + if (if_leave_group(iface, &addr)) { + log_warnx("if_act_reset: error leaving group %s, " + "interface %s", inet_ntoa(addr), iface->name); + } + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + 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)); + } + } + + while ((nbr = LIST_FIRST(&iface->nbr_list))) { + LIST_REMOVE(nbr, entry); + nbr_del(nbr); + } + + iface->dr = NULL; + iface->bdr = NULL; + + 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)); + memcpy(&ictl.rtr_id, &iface->rtr_id, sizeof(ictl.rtr_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.transfer_delay = iface->transfer_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.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; + + 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); +} + +/* names */ +const char * +if_state_name(int state) +{ + switch (state) { + case IF_STA_DOWN: + return ("DOWN"); + case IF_STA_LOOPBACK: + return ("LOOPBACK"); + case IF_STA_WAITING: + return ("WAITING"); + case IF_STA_POINTTOPOINT: + return ("POINT-TO-POINT"); + case IF_STA_DROTHER: + return ("DROTHER"); + case IF_STA_BACKUP: + return ("BACKUP"); + case IF_STA_DR: + return ("DR"); + default: + return ("UNKNOWN"); + } +} + +const char * +if_event_name(int event) +{ + return (if_event_names[event]); +} + +const char * +if_action_name(int action) +{ + return (if_action_names[action]); +} + +const char * +if_type_name(int type) +{ + return (if_type_names[type]); +} + +const char * +if_auth_name(int type) +{ + return (if_auth_names[type]); +} + +/* misc */ +int +if_set_mcast_ttl(int fd, u_int8_t ttl) +{ + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&ttl, sizeof(ttl)) < 0) { + log_warn("if_set_mcast_ttl: error setting " + "IP_MULTICAST_TTL to %d", ttl); + return (-1); + } + + return (0); +} + +int +if_set_tos(int fd, int tos) +{ + if (setsockopt(fd, IPPROTO_IP, IP_TOS, + (int *)&tos, sizeof(tos)) < 0) { + log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos); + return (-1); + } + + return (0); +} + +int +if_join_group(struct iface *iface, struct in_addr *addr) +{ + struct ip_mreq mreq; + + switch (iface->type) { + case IF_TYPE_BROADCAST: + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)) < 0) { + log_debug("if_join_group: error IP_ADD_MEMBERSHIP, " + "interface %s", iface->name); + return (-1); + } + break; + case IF_TYPE_POINTOPOINT: + 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 in_addr *addr) +{ + struct ip_mreq mreq; + + switch (iface->type) { + case IF_TYPE_BROADCAST: + mreq.imr_multiaddr.s_addr = addr->s_addr; + mreq.imr_interface.s_addr = iface->addr.s_addr; + + if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)) < 0) { + log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, " + "interface %s", iface->name); + return (-1); + } + break; + case IF_TYPE_POINTOPOINT: + 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_BROADCAST: + if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&iface->addr.s_addr, + sizeof(iface->addr.s_addr)) < 0) { + log_debug("if_set_mcast: error setting " + "IP_MULTICAST_IF, interface %s", iface->name); + return (-1); + } + break; + case IF_TYPE_POINTOPOINT: + 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_int8_t loop = 0; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&loop, sizeof(loop)) < 0) { + log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP"); + return (-1); + } + + return (0); +} diff --git a/usr.sbin/ospfd/iso_cksum.c b/usr.sbin/ospfd/iso_cksum.c new file mode 100644 index 00000000000..f3a050b37fe --- /dev/null +++ b/usr.sbin/ospfd/iso_cksum.c @@ -0,0 +1,58 @@ +/* $OpenBSD: iso_cksum.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospfd.h" +#include "log.h" + +/* 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; /* counters */ + int r0, r1; /* results */ + u_int16_t sop; + + sop = len - pos - 1; /* pos is an offset (pos 2 is at len 3) */ + p += 2; + len -= 2; + while (len--) { + c0 += *p++; + c1 += c0; + if ((len & 0xfff) == 0) { + /* overflow protection */ + c0 %= 255; + c1 %= 255; + } + } + r0 = c0 = c0 % 255; + r1 = c1 % 255; + + if (pos) { + r0 = ((sop * r0 - r1)) % 255; + if (r0 <= 0) + r0 += 255; + r1 = 510 - r0 - c0; + if (r1 > 255) + r1 -= 255; + } + + return (r0 << 8 | r1); +} diff --git a/usr.sbin/ospfd/kroute.c b/usr.sbin/ospfd/kroute.c new file mode 100644 index 00000000000..2aa833051b4 --- /dev/null +++ b/usr.sbin/ospfd/kroute.c @@ -0,0 +1,1270 @@ +/* $OpenBSD: kroute.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/tree.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ospfd.h" +#include "ospfd.h" +#include "log.h" + +/* XXX */ +void main_imsg_compose_ospfe(int, pid_t, void *, u_int16_t); +void main_imsg_compose_rde(int, pid_t, void *, u_int16_t); +extern struct ospfd_conf *conf; + +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 knexthop_node { + RB_ENTRY(knexthop_node) entry; + struct in_addr nexthop; + struct kroute_node *kroute; +}; + +struct kif_kr { + LIST_ENTRY(kif_kr) entry; + struct kroute_node *kr; +}; + +LIST_HEAD(kif_kr_head, kif_kr); + +struct kif_node { + RB_ENTRY(kif_node) entry; + struct kif k; + struct kif_kr_head kroute_l; +}; + +int kroute_compare(struct kroute_node *, struct kroute_node *); +int knexthop_compare(struct knexthop_node *, struct knexthop_node *); +int kif_compare(struct kif_node *, struct kif_node *); + +struct kroute_node *kroute_find(in_addr_t, u_int8_t); +int kroute_insert(struct kroute_node *); +int kroute_remove(struct kroute_node *); +void kroute_clear(void); + +struct knexthop_node *knexthop_find(struct in_addr); +int knexthop_insert(struct knexthop_node *); +int knexthop_remove(struct knexthop_node *); +void knexthop_clear(void); + +struct kif_node *kif_find(int); +int kif_insert(struct kif_node *); +int kif_remove(struct kif_node *); +void kif_clear(void); + +int kif_kr_insert(struct kroute_node *); +int kif_kr_remove(struct kroute_node *); + +void knexthop_validate(struct knexthop_node *); +struct kroute_node *kroute_match(in_addr_t); +void kroute_attach_nexthop(struct knexthop_node *, + struct kroute_node *); +void kroute_detach_nexthop(struct knexthop_node *); + +int protect_lo(void); +u_int8_t prefixlen_classful(in_addr_t); +u_int8_t mask2prefixlen(in_addr_t); +in_addr_t prefixlen2mask(u_int8_t); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +void if_change(u_short, int, struct if_data *); +void if_announce(void *); + +int send_rtmsg(int, int, struct kroute *); +int dispatch_rtmsg(void); +int fetchtable(void); +int fetchifs(int); + +RB_HEAD(kroute_tree, kroute_node) kroute_tree, krt; +RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) +RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) + +RB_HEAD(knexthop_tree, knexthop_node) knexthop_tree, knt; +RB_PROTOTYPE(knexthop_tree, knexthop_node, entry, knexthop_compare) +RB_GENERATE(knexthop_tree, knexthop_node, entry, knexthop_compare) + +RB_HEAD(kif_tree, kif_node) kif_tree, kit; +RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) +RB_GENERATE(kif_tree, kif_node, entry, kif_compare) + +int +kr_init(int fs) +{ + int opt; + + 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 */ + + kr_state.pid = getpid(); + kr_state.rtseq = 1; + + RB_INIT(&krt); + RB_INIT(&knt); + RB_INIT(&kit); + + if (fetchifs(0) == -1) + return (-1); + + if (fetchtable() == -1) + return (-1); + + if (protect_lo() == -1) + return (-1); + + event_set(&kr_state.ev, kr_state.fd, EV_READ, kr_dispatch_msg, NULL); + event_add(&kr_state.ev, NULL); + + return (0); +} + +int +kr_change(struct kroute *kroute) +{ + struct kroute_node *kr; + int action = RTM_ADD; + + if ((kr = kroute_find(kroute->prefix.s_addr, kroute->prefixlen)) != + NULL) { + if (kr->r.flags & F_OSPFD_INSERTED) + action = RTM_CHANGE; + else /* a non-bgp route already exists. not a problem */ + return (0); + } + + /* nexthop within 127/8 -> ignore silently */ + if ((kroute->nexthop.s_addr & htonl(0xff000000)) == + inet_addr("127.0.0.0")) + 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; + + if (kroute_insert(kr) == -1) + free(kr); + } else { + kr->r.nexthop.s_addr = kroute->nexthop.s_addr; + if (kroute->flags & F_BLACKHOLE) + kr->r.flags |= F_BLACKHOLE; + else + kr->r.flags &= ~F_BLACKHOLE; + if (kroute->flags & F_REJECT) + kr->r.flags |= F_REJECT; + else + kr->r.flags &= ~F_REJECT; + } + + 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); + + /* nexthop within 127/8 -> ignore silently */ + if ((kroute->nexthop.s_addr & htonl(0xff000000)) == + inet_addr("127.0.0.0")) + 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(); + knexthop_clear(); + 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_OSPFD_INSERTED)) + 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_OSPFD_INSERTED)) + send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + + kr_state.fib_sync = 0; + + log_info("kernel routing table decoupled"); +} + +void +kr_dispatch_msg(int fd, short event, void *bula) +{ + dispatch_rtmsg(); +} + +int +kr_nexthop_add(struct in_addr addr) +{ + struct knexthop_node *h; + + if ((h = knexthop_find(addr)) != NULL) { + /* should not happen... this is really an error path */ + struct kroute_nexthop nh; + + bzero(&nh, sizeof(nh)); + memcpy(&nh.nexthop, &addr, sizeof(nh.nexthop)); + if (h->kroute != NULL) { + nh.valid = 1; + nh.connected = h->kroute->r.flags & F_CONNECTED; + if (h->kroute->r.nexthop.s_addr != 0) + nh.gateway.s_addr = + h->kroute->r.nexthop.s_addr; + memcpy(&nh.kr, &h->kroute->r, sizeof(nh.kr)); + } + send_nexthop_update(&nh); + } else { + if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) { + log_warn("kr_nexthop_add"); + return (-1); + } + memcpy(&h->nexthop, &addr, sizeof(h->nexthop)); + + if (knexthop_insert(h) == -1) + return (-1); + } + + return (0); +} + +void +kr_nexthop_delete(struct in_addr addr) +{ + struct knexthop_node *kn; + + if ((kn = knexthop_find(addr)) == NULL) + return; + + knexthop_remove(kn); +} + +void +kr_show_route(struct imsg *imsg) +{ + struct area *area = NULL; + struct iface *iface = NULL; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_INTERFACE: + LIST_FOREACH(area, &conf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + main_imsg_compose_ospfe(IMSG_CTL_SHOW_INTERFACE, + imsg->hdr.pid, iface, sizeof(*iface)); + } + } + 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) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + if (!strcmp(ifname, kif->k.ifname)) { + main_imsg_compose_ospfe(IMSG_IFINFO, 0, + &kif->k, sizeof(kif->k)); + return; + } +} + +/* 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 +knexthop_compare(struct knexthop_node *a, struct knexthop_node *b) +{ + u_int32_t r; + + if ((r = b->nexthop.s_addr - a->nexthop.s_addr) != 0) + return (r); + + 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)); +} + +int +kroute_insert(struct kroute_node *kr) +{ + struct knexthop_node *h; + in_addr_t mask, ina; + + if (RB_INSERT(kroute_tree, &krt, kr) != NULL) { + log_warnx("kroute_tree insert failed for %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + free(kr); + return (-1); + } + + if (kr->r.flags & F_KERNEL) { + if (!(kr->r.flags & F_CONNECTED)) + kr->r.flags |= F_STATIC; + + mask = prefixlen2mask(kr->r.prefixlen); + ina = ntohl(kr->r.prefix.s_addr); + RB_FOREACH(h, knexthop_tree, &knt) + if ((ntohl(h->nexthop.s_addr) & mask) == ina) + knexthop_validate(h); + + if (kr->r.flags & F_CONNECTED) + if (kif_kr_insert(kr) == -1) + return (-1); + } + return (0); +} + +int +kroute_remove(struct kroute_node *kr) +{ + struct knexthop_node *s; + + 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); + } + + /* check wether a nexthop depends on this kroute */ + if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP)) + RB_FOREACH(s, knexthop_tree, &knt) + if (s->kroute == kr) + knexthop_validate(s); + + if (kr->r.flags & F_CONNECTED) + if (kif_kr_remove(kr) == -1) { + free(kr); + return (-1); + } + + free(kr); + return (0); +} + +void +kroute_clear(void) +{ + struct kroute_node *kr; + + while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) + kroute_remove(kr); +} + +struct knexthop_node * +knexthop_find(struct in_addr addr) +{ + struct knexthop_node s; + + memcpy(&s.nexthop, &addr, sizeof(s.nexthop)); + + return (RB_FIND(knexthop_tree, &knt, &s)); +} + +int +knexthop_insert(struct knexthop_node *kn) +{ + if (RB_INSERT(knexthop_tree, &knt, kn) != NULL) { + log_warnx("knexthop_tree insert failed for %s", + inet_ntoa(kn->nexthop)); + free(kn); + return (-1); + } + + knexthop_validate(kn); + + return (0); +} + +int +knexthop_remove(struct knexthop_node *kn) +{ + kroute_detach_nexthop(kn); + + if (RB_REMOVE(knexthop_tree, &knt, kn) == NULL) { + log_warnx("knexthop_remove failed for %s", + inet_ntoa(kn->nexthop)); + return (-1); + } + + free(kn); + return (0); +} + +void +knexthop_clear(void) +{ + struct knexthop_node *kn; + + while ((kn = RB_MIN(knexthop_tree, &knt)) != NULL) + knexthop_remove(kn); +} + +struct kif_node * +kif_find(int ifindex) +{ + struct kif_node s; + + bzero(&s, sizeof(s)); + s.k.ifindex = ifindex; + + return (RB_FIND(kif_tree, &kit, &s)); +} + +int +kif_insert(struct kif_node *kif) +{ + LIST_INIT(&kif->kroute_l); + + if (RB_INSERT(kif_tree, &kit, kif) != NULL) { + log_warnx("RB_INSERT(kif_tree, &kit, kif)"); + free(kif); + return (-1); + } + + return (0); +} + +int +kif_remove(struct kif_node *kif) +{ + struct kif_kr *kkr; + + if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { + log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); + return (-1); + } + + while ((kkr = LIST_FIRST(&kif->kroute_l)) != NULL) { + LIST_REMOVE(kkr, entry); + kkr->kr->r.flags &= ~F_NEXTHOP; + kroute_remove(kkr->kr); + free(kkr); + } + + free(kif); + return (0); +} + +void +kif_clear(void) +{ + struct kif_node *kif; + + while ((kif = RB_MIN(kif_tree, &kit)) != NULL) + kif_remove(kif); +} + +int +kif_kr_insert(struct kroute_node *kr) +{ + struct kif_node *kif; + struct kif_kr *kkr; + + if ((kif = kif_find(kr->r.ifindex)) == NULL) { + if (kr->r.ifindex) + log_warnx("interface with index %u not found", + kr->r.ifindex); + return (0); + } + + if ((kkr = calloc(1, sizeof(struct kif_kr))) == NULL) { + log_warn("kif_kr_insert"); + return (-1); + } + + kkr->kr = kr; + + LIST_INSERT_HEAD(&kif->kroute_l, kkr, entry); + + return (0); +} + +int +kif_kr_remove(struct kroute_node *kr) +{ + struct kif_node *kif; + struct kif_kr *kkr; + + if ((kif = kif_find(kr->r.ifindex)) == NULL) { + if (kr->r.ifindex) + log_warnx("interface with index %u not found", + kr->r.ifindex); + return (0); + } + + for (kkr = LIST_FIRST(&kif->kroute_l); kkr != NULL && kkr->kr != kr; + kkr = LIST_NEXT(kkr, entry)) + ; /* nothing */ + + if (kkr == NULL) { + log_warnx("can't remove connected route from interface " + "with index %u: not found", kr->r.ifindex); + return (-1); + } + + LIST_REMOVE(kkr, entry); + free(kkr); + + return (0); +} + +/* nexthop validation */ +void +knexthop_validate(struct knexthop_node *kn) +{ + struct kroute_node *kr; + struct kroute_nexthop n; + int was_valid = 0; + + if (kn->kroute != NULL && (!(kn->kroute->r.flags & F_DOWN))) + was_valid = 1; + + bzero(&n, sizeof(n)); + memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop)); + kroute_detach_nexthop(kn); + + if ((kr = kroute_match(kn->nexthop.s_addr)) == NULL) { + if (was_valid) + send_nexthop_update(&n); + } else { /* match */ + if (kr->r.flags & F_DOWN) { /* is down */ + if (was_valid) + send_nexthop_update(&n); + } else { /* valid */ + n.valid = 1; + n.connected = kr->r.flags & F_CONNECTED; + n.gateway.s_addr = kr->r.nexthop.s_addr; + memcpy(&n.kr, &kr->r, sizeof(n.kr)); + send_nexthop_update(&n); + } + kroute_attach_nexthop(kn, kr); + } +} + +struct kroute_node * +kroute_match(in_addr_t key) +{ + int i; + struct kroute_node *kr; + in_addr_t ina; + + ina = ntohl(key); + + /* we will never match the default route */ + for (i = 32; i > 0; i--) + if ((kr = + kroute_find(htonl(ina & 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); +} + +void +kroute_attach_nexthop(struct knexthop_node *kn, struct kroute_node *kr) +{ + kn->kroute = kr; + kr->r.flags |= F_NEXTHOP; +} + +void +kroute_detach_nexthop(struct knexthop_node *kn) +{ + struct knexthop_node *s; + + /* + * check wether there's another nexthop depending on this kroute + * if not remove the flag + */ + + if (kn->kroute == NULL) + return; + + for (s = RB_MIN(knexthop_tree, &knt); s != NULL && + s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, &knt, s)) + ; /* nothing */ + + if (s == NULL) + kn->kroute->r.flags &= ~F_NEXTHOP; + + kn->kroute = 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 = inet_addr("127.0.0.1"); + 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 (0xffffffff << (32 - prefixlen)); +} + +#define ROUNDUP(a, size) \ + (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) + +void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + ROUNDUP(sa->sa_len, sizeof(long))); + } else + rti_info[i] = NULL; + } +} + +void +if_change(u_short ifindex, int flags, struct if_data *ifd) +{ + struct kif_node *kif; + struct kif_kr *kkr; + struct kroute_nexthop nh; + struct knexthop_node *n; + u_int8_t reachable; + + if ((kif = kif_find(ifindex)) == NULL) { + log_warnx("interface with index %u not found", + ifindex); + return; + } + + kif->k.flags = flags; + kif->k.link_state = ifd->ifi_link_state; + kif->k.media_type = ifd->ifi_type; + kif->k.baudrate = ifd->ifi_baudrate; + + main_imsg_compose_ospfe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); + + if ((reachable = (flags & IFF_UP) && + (ifd->ifi_link_state != LINK_STATE_DOWN)) == kif->k.nh_reachable) + return; /* nothing changed wrt nexthop validity */ + + kif->k.nh_reachable = reachable; + + LIST_FOREACH(kkr, &kif->kroute_l, entry) { + /* + * we treat link_state == LINK_STATE_UNKNOWN as valid + * not all interfaces have a concept of "link state" and/or + * do not report up + */ + if (reachable) + kkr->kr->r.flags &= ~F_DOWN; + else + kkr->kr->r.flags |= F_DOWN; + + RB_FOREACH(n, knexthop_tree, &knt) + if (n->kroute == kkr->kr) { + bzero(&nh, sizeof(nh)); + memcpy(&nh.nexthop, &n->nexthop, + sizeof(nh.nexthop)); + if (!(kkr->kr->r.flags & F_DOWN)) { + nh.valid = 1; + nh.connected = 1; + nh.gateway.s_addr = + kkr->kr->r.nexthop.s_addr; + } + memcpy(&nh.kr, &kkr->kr->r, sizeof(nh.kr)); + send_nexthop_update(&nh); + } + } +} + +void +if_announce(void *msg) +{ + struct if_announcemsghdr *ifan; + struct kif_node *kif; + + ifan = msg; + + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { + log_warn("if_announce"); + return; + } + + kif->k.ifindex = ifan->ifan_index; + strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); + kif_insert(kif); + break; + case IFAN_DEPARTURE: + kif = kif_find(ifan->ifan_index); + kif_remove(kif); + break; + } +} + +/* rtsock */ +int +send_rtmsg(int fd, int action, struct kroute *kroute) +{ + struct { + struct rt_msghdr hdr; + struct sockaddr_in prefix; + struct sockaddr_in nexthop; + struct sockaddr_in mask; + } r; + + if (kr_state.fib_sync == 0) + return (0); + + bzero(&r, sizeof(r)); + r.hdr.rtm_msglen = sizeof(r); + r.hdr.rtm_version = RTM_VERSION; + r.hdr.rtm_type = action; + r.hdr.rtm_flags = RTF_PROTO1; + if (kroute->flags & F_BLACKHOLE) + r.hdr.rtm_flags |= RTF_BLACKHOLE; + if (kroute->flags & F_REJECT) + r.hdr.rtm_flags |= RTF_REJECT; + r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK; + r.prefix.sin_len = sizeof(r.prefix); + r.prefix.sin_family = AF_INET; + r.prefix.sin_addr.s_addr = kroute->prefix.s_addr; + + r.nexthop.sin_len = sizeof(r.nexthop); + r.nexthop.sin_family = AF_INET; + r.nexthop.sin_addr.s_addr = kroute->nexthop.s_addr; + if (kroute->nexthop.s_addr != 0) + r.hdr.rtm_flags |= RTF_GATEWAY; + + r.mask.sin_len = sizeof(r.mask); + r.mask.sin_family = AF_INET; + r.mask.sin_addr.s_addr = htonl(prefixlen2mask(kroute->prefixlen)); + +retry: + if (write(fd, &r, sizeof(r)) == -1) { + switch (errno) { + case ESRCH: + if (r.hdr.rtm_type == RTM_CHANGE) { + r.hdr.rtm_type = RTM_ADD; + goto retry; + } else if (r.hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + inet_ntoa(kroute->prefix), + kroute->prefixlen); + return (0); + } else { + log_warnx("send_rtmsg: action %u, " + "prefix %s/%u: %s", r.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", + r.hdr.rtm_type, inet_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + return (0); +} + +int +fetchtable(void) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct kroute_node *kr; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchtable"); + return (-1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if ((sa = rti_info[RTAX_DST]) == NULL) + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) /* multipath */ + continue; +#endif + + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("fetchtable"); + free(buf); + return (-1); + } + + kr->r.flags = F_KERNEL; + + switch (sa->sa_family) { + case AF_INET: + kr->r.prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; + if (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; + /* not reached */ + } + + 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; + kr->r.ifindex = rtm->rtm_index; + break; + } + + kroute_insert(kr); + + } + free(buf); + return (0); +} + +int +fetchifs(int ifindex) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct if_msghdr ifm; + struct kif_node *kif; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_dl *sdl; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = ifindex; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchif"); + return (-1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm.ifm_msglen) { + memcpy(&ifm, next, sizeof(ifm)); + sa = (struct sockaddr *)(next + sizeof(ifm)); + get_rtaddrs(ifm.ifm_addrs, sa, rti_info); + + if (ifm.ifm_type != RTM_IFINFO) + continue; + + if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) { + log_warn("fetchifs"); + free(buf); + return (-1); + } + + kif->k.ifindex = ifm.ifm_index; + kif->k.flags = ifm.ifm_flags; + kif->k.link_state = ifm.ifm_data.ifi_link_state; + kif->k.media_type = ifm.ifm_data.ifi_type; + kif->k.baudrate = ifm.ifm_data.ifi_baudrate; + kif->k.nh_reachable = (kif->k.flags & IFF_UP) && + (ifm.ifm_data.ifi_link_state != LINK_STATE_DOWN); + + if ((sa = rti_info[RTAX_IFP]) != NULL) + if (sa->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)sa; + if (sdl->sdl_nlen > 0) + strlcpy(kif->k.ifname, sdl->sdl_data, + sizeof(kif->k.ifname)); + } + + kif_insert(kif); + } + free(buf); + return (0); +} + +int +dispatch_rtmsg(void) +{ + char buf[RT_BUF_SIZE]; + ssize_t n; + char *next, *lim; + struct rt_msghdr *rtm; + struct if_msghdr ifm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in *sa_in; + struct kroute_node *kr; + struct in_addr prefix, nexthop; + u_int8_t prefixlen; + int flags; + u_short ifindex; + + 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; + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + prefix.s_addr = 0; + prefixlen = 0; + flags = F_KERNEL; + nexthop.s_addr = 0; + ifindex = 0; + + if (rtm->rtm_pid == kr_state.pid) /* cause by us */ + continue; + + if (rtm->rtm_errno) /* failed attempts... */ + continue; + + if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE || + rtm->rtm_type == RTM_DELETE) { + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + switch (sa->sa_family) { + case AF_INET: + prefix.s_addr = + ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *) + rti_info[RTAX_NETMASK]; + if (sa_in != NULL) { + if (sa_in->sin_len != 0) + prefixlen = mask2prefixlen( + sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + prefixlen = 32; + else + prefixlen = + prefixlen_classful(prefix.s_addr); + break; + default: + continue; + /* not reached */ + } + } + + 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; + ifindex = rtm->rtm_index; + 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 ((kr = kroute_find(prefix.s_addr, prefixlen)) != + NULL) { + if (kr->r.flags & F_KERNEL) { + kr->r.nexthop.s_addr = nexthop.s_addr; + if (kr->r.flags & F_NEXTHOP) + flags |= F_NEXTHOP; + if ((kr->r.flags & F_CONNECTED) && + !(flags & F_CONNECTED)) + kif_kr_remove(kr); + if ((flags & F_CONNECTED) && + !(kr->r.flags & F_CONNECTED)) + kif_kr_insert(kr); + kr->r.flags = flags; + } + } else if (rtm->rtm_type == RTM_CHANGE) { + log_warnx("change req for %s/%u: not " + "in table", inet_ntoa(prefix), + prefixlen); + continue; + } else { + if ((kr = calloc(1, + sizeof(struct kroute_node))) == NULL) { + log_warn("dispatch_rtmsg"); + return (-1); + } + kr->r.prefix.s_addr = prefix.s_addr; + kr->r.prefixlen = prefixlen; + kr->r.nexthop.s_addr = nexthop.s_addr; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + kroute_insert(kr); + } + break; + case RTM_DELETE: + if ((kr = kroute_find(prefix.s_addr, prefixlen)) == + NULL) + continue; + if (!(kr->r.flags & F_KERNEL)) + continue; + if (kroute_remove(kr) == -1) + return (-1); + break; + case RTM_IFINFO: + memcpy(&ifm, next, sizeof(ifm)); + if_change(ifm.ifm_index, ifm.ifm_flags, + &ifm.ifm_data); + break; + case RTM_IFANNOUNCE: + if_announce(next); + break; + default: + /* ignore for now */ + break; + } + } + return (0); +} diff --git a/usr.sbin/ospfd/log.c b/usr.sbin/ospfd/log.c new file mode 100644 index 00000000000..30f725b9609 --- /dev/null +++ b/usr.sbin/ospfd/log.c @@ -0,0 +1,164 @@ +/* $OpenBSD: log.c,v 1.1 2005/01/28 14:05:40 claudio 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/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "ospfd.h" +#include "log.h" + +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); +} + diff --git a/usr.sbin/ospfd/log.h b/usr.sbin/ospfd/log.h new file mode 100644 index 00000000000..65caffcad90 --- /dev/null +++ b/usr.sbin/ospfd/log.h @@ -0,0 +1,39 @@ +/* $OpenBSD: log.h,v 1.1 2005/01/28 14:05:40 claudio 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 *); +void fatalx(const char *); + +static const char * const procnames[] = { + "parent", + "ospfe", + "rde" +}; + +#endif /* _LOG_H_ */ diff --git a/usr.sbin/ospfd/lsack.c b/usr.sbin/ospfd/lsack.c new file mode 100644 index 00000000000..dbbb4f43ad0 --- /dev/null +++ b/usr.sbin/ospfd/lsack.c @@ -0,0 +1,305 @@ +/* $OpenBSD: lsack.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <strings.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +int start_ls_ack_tx_timer_now(struct iface *); + +/* link state acknowledgement packet handling */ +int +send_ls_ack(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + char *buf; + char *ptr; + int ret = 0; + + log_debug("send_ls_ack: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + /* XXX use buffer API instead for better decoupling */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("send_ls_ack"); + + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* OSPF header */ + gen_ospf_hdr(ptr, iface, PACKET_TYPE_LS_ACK); + ptr += sizeof(struct ospf_hdr); + + /* LS ack(s) */ + memcpy(ptr, data, len); /* XXX size check ??? */ + ptr += len; + + /* update authentication and calculate checksum */ + auth_gen(buf, ptr - buf, iface); + + if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1) + log_warnx("send_ls_ack: error sending packet on " + "interface %s", iface->name); + + free(buf); + return (ret); +} + +void +recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct lsa_hdr lsa_hdr; + + log_debug("recv_ls_ack: neighbor ID %s", inet_ntoa(nbr->id)); + + 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: + log_debug("recv_ls_ack: state %s, neighbor ID %s", + nbr_state_name(nbr->state), inet_ntoa(nbr->id)); + + while (len >= sizeof(lsa_hdr)) { + memcpy(&lsa_hdr, buf, sizeof(lsa_hdr)); + + if (lsa_hdr_check(nbr, &lsa_hdr)) + 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"); + } + + return; +} + +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) == 0x80000000)) { + 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; +} + +bool +ls_ack_list_empty(struct iface *iface) +{ + return (TAILQ_EMPTY(&iface->ls_ack_list)); +} + +/* timers */ +void +ls_ack_tx_timer(int fd, short event, void *arg) +{ + struct in_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++; + } + + log_debug("ls_ack_tx_timer: sending %d ack(s), interface %s", + cnt, iface->name); + + /* send LS ack(s) but first set correct destination */ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + inet_aton(AllSPFRouters, &addr); + send_ls_ack(iface, addr, buf, ptr - buf); + break; + case IF_TYPE_BROADCAST: + if (iface->state & IF_STA_DRORBDR) + inet_aton(AllSPFRouters, &addr); + else + inet_aton(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, ptr, ptr - buf); + } + break; + default: + fatalx("lsa_flood: unknown interface type"); + } + } + + free(buf); +} + +int +start_ls_ack_tx_timer(struct iface *iface) +{ + struct timeval tv; + + log_debug("start_ls_ack_tx_timer: interface %s", iface->name); + timerclear(&tv); + tv.tv_sec = iface->rxmt_interval / 2; + return (evtimer_add(&iface->lsack_tx_timer, &tv)); +} + +int +start_ls_ack_tx_timer_now(struct iface *iface) +{ + struct timeval tv; + + log_debug("start_ls_ack_tx_timer_now: interface %s", iface->name); + timerclear(&tv); + + return (evtimer_add(&iface->lsack_tx_timer, &tv)); +} + +int +stop_ls_ack_tx_timer(struct iface *iface) +{ + log_debug("stop_ls_ack_tx_timer: interface %s", iface->name); + + return (evtimer_del(&iface->lsack_tx_timer)); +} diff --git a/usr.sbin/ospfd/lsreq.c b/usr.sbin/ospfd/lsreq.c new file mode 100644 index 00000000000..8018c240ad5 --- /dev/null +++ b/usr.sbin/ospfd/lsreq.c @@ -0,0 +1,259 @@ +/* $OpenBSD: lsreq.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 "ospfd.h" +#include "ospf.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_in dst; + struct ls_req_hdr *ls_req_hdr; + struct lsa_entry *le, *nle; + char *buf = NULL; + char *ptr; + int ret = 0; + + log_debug("send_ls_req: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->iface->passive) + return (0); + + /* XXX use buffer API instead for better decoupling */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("send_ls_req"); + + /* set destination */ + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + + switch (nbr->iface->type) { + case IF_TYPE_POINTOPOINT: + inet_aton(AllSPFRouters, &dst.sin_addr); + break; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + case IF_TYPE_VIRTUALLINK: + dst.sin_addr.s_addr = nbr->addr.s_addr; + break; + default: + fatalx("send_ls_req: unknown interface type"); + } + + /* OSPF header */ + gen_ospf_hdr(ptr, nbr->iface, PACKET_TYPE_LS_REQUEST); + ptr += sizeof(struct ospf_hdr); + + /* LSA header(s) */ + for (le = TAILQ_FIRST(&nbr->ls_req_list); le != NULL && + (ptr - buf) < nbr->iface->mtu - PACKET_HDR; le = nle) { + nbr->ls_req = nle = TAILQ_NEXT(le, entry); + ls_req_hdr = (struct ls_req_hdr *)ptr; + 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; + ptr += sizeof(*ls_req_hdr); + } + + /* update authentication and calculate checksum */ + auth_gen(buf, ptr - buf, nbr->iface); + + if ((ret = send_packet(nbr->iface, buf, (ptr - buf), &dst)) == -1) + log_warnx("send_ls_req: error sending packet on " + "interface %s", nbr->iface->name); + free(buf); + return (ret); +} + +void +recv_ls_req(struct nbr *nbr, char *buf, u_int16_t len) +{ + log_debug("recv_ls_req: neighbor ID %s", inet_ntoa(nbr->id)); + + 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)); + nbr_fsm(nbr, NBR_EVT_ADJ_OK); + break; + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + case NBR_STA_FULL: + imsg_compose(ibuf_rde, IMSG_LS_REQ, nbr->peerid, 0, -1, + 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_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; +} + +bool +ls_req_list_empty(struct nbr *nbr) +{ + return (TAILQ_EMPTY(&nbr->ls_req_list)); +} + +/* timers */ +void +ls_req_tx_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + log_debug("ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + + 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; + log_debug("ls_req_tx_timer: reschedule neighbor ID %s", + inet_ntoa(nbr->id)); + evtimer_add(&nbr->lsreq_tx_timer, &tv); + } +} + +int +start_ls_req_tx_timer(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return (0); + + log_debug("start_ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + timerclear(&tv); + + return (evtimer_add(&nbr->lsreq_tx_timer, &tv)); +} + +int +stop_ls_req_tx_timer(struct nbr *nbr) +{ + if (nbr == nbr->iface->self) + return (0); + + log_debug("stop_ls_req_tx_timer: neighbor ID %s", inet_ntoa(nbr->id)); + + return (evtimer_del(&nbr->lsreq_tx_timer)); +} diff --git a/usr.sbin/ospfd/lsupdate.c b/usr.sbin/ospfd/lsupdate.c new file mode 100644 index 00000000000..5795d4c8702 --- /dev/null +++ b/usr.sbin/ospfd/lsupdate.c @@ -0,0 +1,447 @@ +/* $OpenBSD: lsupdate.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 <strings.h> + +#include "ospf.h" +#include "ospfd.h" +#include "log.h" +#include "ospfe.h" +#include "rde.h" + +extern struct ospfd_conf *oeconf; +extern struct imsgbuf *ibuf_rde; + +/* link state update packet handling */ +int +lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr, + void *data, u_int16_t len) +{ + struct in_addr addr; + struct nbr *nbr; + struct lsa_entry *le; + int queued = 0, dont_ack = 0; + int r; + + if (iface->passive) + return (0); + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr == iface->self) + continue; + if (!(nbr->state & NBR_STA_FLOOD)) + continue; + + le = ls_retrans_list_get(nbr, lsa_hdr); + if (le) + 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; + } + + ls_retrans_list_add(nbr, data); /* XXX error handling */ + queued = 1; + } + if (!queued) + return (0); + + if (iface == originator->iface) { + if (iface->dr == originator || iface->bdr == originator) + return (0); + if (iface->state & IF_STA_BACKUP) + return (0); + dont_ack++; + } + + /* flood LSA but first set correct destination */ + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + inet_aton(AllSPFRouters, &addr); + send_ls_update(iface, addr, data, len); + break; + case IF_TYPE_BROADCAST: + if (iface->state & IF_STA_DRORBDR) + inet_aton(AllSPFRouters, &addr); + else + inet_aton(AllDRouters, &addr); + send_ls_update(iface, addr, data, len); + 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; + } + send_ls_update(iface, nbr->addr, data, len); + } + break; + default: + fatalx("lsa_flood: unknown interface type"); + } + + + return (dont_ack == 2); +} + +int +send_ls_update(struct iface *iface, struct in_addr addr, void *data, int len) +{ + struct sockaddr_in dst; + char *buf; + char *ptr; + u_int32_t nlsa; + u_int16_t age; + int ret = 0; + + log_debug("send_ls_update: interface %s addr %s", + iface->name, inet_ntoa(addr)); + + if (iface->passive) + return (0); + + /* XXX use buffer API instead for better decoupling */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("send_ls_update"); + + /* set destination */ + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + dst.sin_addr.s_addr = addr.s_addr; + + /* OSPF header */ + gen_ospf_hdr(ptr, iface, PACKET_TYPE_LS_UPDATE); + ptr += sizeof(struct ospf_hdr); + + nlsa = htonl(1); + memcpy(ptr, &nlsa, sizeof(nlsa)); + ptr += sizeof(nlsa); + + memcpy(ptr, data, len); /* XXX */ + + /* age LSA befor sending it out */ + memcpy(&age, ptr, sizeof(age)); + age = ntohs(age); + if ((age += iface->transfer_delay) >= MAX_AGE) age = MAX_AGE; + age = ntohs(age); + memcpy(ptr, &age, sizeof(age)); + + ptr += len; + + /* update authentication and calculate checksum */ + auth_gen(buf, ptr - buf, iface); + + if ((ret = send_packet(iface, buf, (ptr - buf), &dst)) == -1) + log_warnx("send_ls_update: error sending packet on " + "interface %s", iface->name); + + free(buf); + return (ret); +} + +void +recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct lsa_hdr lsa; + u_int32_t nlsa; + + log_debug("recv_ls_update: neighbor ID %s", inet_ntoa(nbr->id)); + + 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, -1, + 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"); + } + + return; +} + +/* link state retransmit list */ +int +ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa) +{ + struct lsa_entry *le; + struct lsa_ref *ref; + + ref = lsa_cache_get(lsa); + if (ref == NULL) + return (1); + + if ((le = calloc(1, sizeof(*le))) == NULL) + fatal("ls_retrans_list_add"); + + le->le_ref = ref; + TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, le, entry); + + return (0); +} + +int +ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr) +{ + struct lsa_entry *le; + + le = ls_retrans_list_get(nbr, lsa_hdr); + if (le == 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); + } + + log_warnx("ls_retrans_list_del: invalid LS ack received, neigbor %s", + inet_ntoa(nbr->id)); + + 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_free(struct nbr *nbr, struct lsa_entry *le) +{ + TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); + + 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); +} + +bool +ls_retrans_list_empty(struct nbr *nbr) +{ + return (TAILQ_EMPTY(&nbr->ls_retrans_list)); +} + +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 *); +struct lsa_cache_head *lsa_cache_hash(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; + + 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); + ref->stamp = time(NULL); + ref->len = len; + ref->refcnt = 1; + + head = lsa_cache_hash(&ref->hdr); + 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 = lsa_cache_hash(lsa_hdr); + LIST_FOREACH(ref, head, entry) { + if (ref->hdr.type == lsa_hdr->type && + ref->hdr.ls_id == lsa_hdr->ls_id && + ref->hdr.adv_rtr == lsa_hdr->adv_rtr && + ref->hdr.seq_num == lsa_hdr->seq_num && + ref->hdr.ls_chksum == lsa_hdr->ls_chksum) { + /* found match */ + return (ref); + } + } + return (NULL); +} + +struct lsa_cache_head * +lsa_cache_hash(struct lsa_hdr *lsa_hdr) +{ + u_int32_t hash = 8271; + + hash ^= lsa_hdr->type; + hash ^= lsa_hdr->ls_id; + hash ^= lsa_hdr->adv_rtr; + hash ^= lsa_hdr->seq_num; + hash ^= lsa_hdr->ls_chksum; + hash &= lsacache.hashmask; + + return (&lsacache.hashtbl[hash]); +} diff --git a/usr.sbin/ospfd/neighbor.c b/usr.sbin/ospfd/neighbor.c new file mode 100644 index 00000000000..3d60e9417a5 --- /dev/null +++ b/usr.sbin/ospfd/neighbor.c @@ -0,0 +1,698 @@ +/* $OpenBSD: neighbor.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 "ospfd.h" +#include "ospf.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; + +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, 0, 0}, + {NBR_STA_INIT, NBR_EVT_1_WAY_RCVD, 0, 0}, + {NBR_STA_DOWN, NBR_EVT_HELLO_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_INIT}, + {NBR_STA_DOWN, NBR_EVT_STRT, NBR_ACT_STRT, NBR_STA_ATTEMPT}, + {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, 0, 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, NBR_STA_2_WAY}, + {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, 0, -1, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "HELLO_RECEIVED", + "START", + "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", + "START", + "RESET_INACTIVITY_TIMER", + "START_INACTIVITY_TIMER", + "EVAL", + "SNAPSHOT", + "SNAPSHOT_DONE", + "EXCHANGE_DONE", + "ADJ_OK", + "RESET_DD", + "DELETE", + "CLEAR_LISTS" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + 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) { + /* XXX event outside of the defined fsm, ignore it. */ + log_debug("nbr_fsm: neighbor ID %s, " + "event %s not expected in state %s", + inet_ntoa(nbr->id), nbr_event_name(event), + nbr_state_name(old_state)); + return (0); + } + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_STRT: + ret = nbr_act_start(nbr); + break; + 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; + default: + /* do nothing */ + break; + } + + if (ret) { + log_debug("nbr_fsm: error changing state for neighbor ID %s, " + "event %s, state %s", inet_ntoa(nbr->id), + nbr_event_name(event), nbr_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + nbr->state = new_state; + + if (nbr->state != old_state) + ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE, + nbr->peerid, 0, &new_state, sizeof(new_state)); + + if (old_state & ~NBR_STA_PRELIM && new_state & NBR_STA_PRELIM) + /* bidirectional communication lost */ + if_fsm(nbr->iface, IF_EVT_NBR_CHNG); + + if (old_state != nbr->state) { + nbr->stats.sta_chng++; + log_debug("nbr_fsm: event %s resulted in action %s and " + "changing state for neighbor ID %s from %s to %s", + nbr_event_name(event), + nbr_action_name(nbr_fsm_tbl[i].action), + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + } + + return (ret); +} + +void +nbr_init(u_int32_t hashsize) +{ + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head)); + if (nbrtable.hashtbl == NULL) + fatal("nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&nbrtable.hashtbl[i]); + + nbrtable.hashmask = hs - 1; +} + +struct nbr * +nbr_new(u_int32_t nbr_id, struct iface *iface, int self) +{ + struct nbr_head *head; + struct nbr *nbr = NULL; + struct rde_nbr rn; + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_new"); + + nbr->state = NBR_STA_DOWN; + nbr->master = true; + 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; + nbr->addr.s_addr = iface->addr.s_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->adj_timer, nbr_adj_timer, nbr); + + log_debug("nbr_new: neighbor ID %s, peerid %lu", + inet_ntoa(nbr->id), nbr->peerid); + + 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); +} + +int +nbr_del(struct nbr *nbr) +{ + log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + if (nbr == nbr->iface->self) + return (0); + + ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); + + /* 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); + + return (0); +} + +struct nbr * +nbr_find_peerid(u_int32_t peerid) +{ + struct nbr_head *head; + struct nbr *nbr; + + head = NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct nbr * +nbr_find_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 */ +void +nbr_itimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_itimer: %s", inet_ntoa(nbr->id)); + + nbr_fsm(nbr, NBR_EVT_ITIMER); +} + +int +nbr_start_itimer(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id)); + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +int +nbr_stop_itimer(struct nbr *nbr) +{ + return (evtimer_del(&nbr->inactivity_timer)); +} + +int +nbr_reset_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +void +nbr_adj_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + if (nbr->state != NBR_STA_FULL) { + log_debug("nbr_adj_timer: failed to form adjacency"); + nbr_fsm(nbr, NBR_EVT_ADJTMOUT); + } +} + +int +nbr_start_adj_timer(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_start_adj_timer: %s", inet_ntoa(nbr->id)); + + timerclear(&tv); + tv.tv_sec = DEFAULT_ADJ_TMOUT; + + return (evtimer_add(&nbr->adj_timer, &tv)); +} + +/* actions */ +int +nbr_act_start(struct nbr *nbr) +{ + log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id)); + + return (-1); +} + +int +nbr_act_reset_itimer(struct nbr *nbr) +{ + if (nbr_reset_itimer(nbr)) { + log_warnx("nbr_act_reset_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + return (0); +} + +int +nbr_act_start_itimer(struct nbr *nbr) +{ + if (nbr_start_itimer(nbr)) { + log_warnx("nbr_act_start_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", + inet_ntoa(nbr->id)); + return (-1); + } + + 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: + break; + case IF_TYPE_BROADCAST: + case IF_TYPE_NBMA: + /* + * if neighbor is dr, bdr or router self is dr or bdr + * start forming adjacancy + */ + 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) +{ + log_debug("nbr_act_eval: neighbor ID %s", inet_ntoa(nbr->id)); + + if (!nbr_adj_ok(nbr)) { + nbr->state = NBR_STA_2_WAY; + return (0); + } + + nbr->state = NBR_STA_XSTRT; + nbr->master = true; + nbr->dd_seq_num++; /* as per RFC */ + /* initial db negotiation */ + start_db_tx_timer(nbr); + + return (0); +} + +int +nbr_act_snapshot(struct nbr *nbr) +{ + log_debug("nbr_act_snapshot: neighbor ID %s", inet_ntoa(nbr->id)); + + stop_db_tx_timer(nbr); + nbr_start_adj_timer(nbr); + + ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0); + + return (0); +} + +int +nbr_act_exchange_done(struct nbr *nbr) +{ + log_debug("nbr_act_exchange_done: neighbor ID %s", inet_ntoa(nbr->id)); + + if (nbr->master) + stop_db_tx_timer(nbr); + + if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG) { + nbr->state = NBR_STA_FULL; + return (0); + } + + nbr->state = NBR_STA_LOAD; + start_ls_req_tx_timer(nbr); + + return (0); +} + +int +nbr_act_adj_ok(struct nbr *nbr) +{ + log_debug("nbr_act_adj_ok: neighbor ID %s", inet_ntoa(nbr->id)); + + 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) +{ + log_debug("nbr_act_restart_dd: neighbor ID %s", inet_ntoa(nbr->id)); + + nbr->master = true; + nbr->dd_seq_num += arc4random() & 0xffff; + + return (nbr_act_clear_lists(nbr)); +} + +int +nbr_act_delete(struct nbr *nbr) +{ + log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id)); + + /* stop timers */ + if (nbr_stop_itimer(nbr)) { + log_warnx("nbr_act_delete: error removing inactivity timer, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + /* clear dr and bdr */ + nbr->dr.s_addr = 0; + nbr->bdr.s_addr = 0; + + return (nbr_act_clear_lists(nbr)); +} + +int +nbr_act_clear_lists(struct nbr *nbr) +{ + log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id)); + + if (stop_db_tx_timer(nbr)) { + log_warnx("nbr_act_delete: error removing db_tx_timer, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + if (stop_ls_req_tx_timer(nbr)) { + log_warnx("nbr_act_delete: error removing lsreq_tx_timer, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + /* 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->iface->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; + + /* XXX */ + nctl.ls_retrans_lst_cnt = 0; + TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) + nctl.ls_retrans_lst_cnt++; + + nctl.nbr_state = nbr->state; + 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); + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; + + return (&nctl); +} + +/* names */ +const char * +nbr_state_name(int state) +{ + switch (state) { + case NBR_STA_DOWN: + return ("DOWN"); + case NBR_STA_ATTEMPT: + return ("ATTEMPT"); + case NBR_STA_INIT: + return ("INIT"); + case NBR_STA_2_WAY: + return ("2-WAY"); + case NBR_STA_XSTRT: + return ("EXSTART"); + case NBR_STA_SNAP: + return ("SNAPSHOT"); + case NBR_STA_XCHNG: + return ("EXCHANGE"); + case NBR_STA_LOAD: + return ("LOADING"); + case NBR_STA_FULL: + return ("FULL"); + default: + return ("UNKNOWN"); + } +} + +const char * +nbr_event_name(int event) +{ + return (nbr_event_names[event]); +} + +const char * +nbr_action_name(int action) +{ + return (nbr_action_names[action]); +} + +struct 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/ospfd/ospf.h b/usr.sbin/ospfd/ospf.h new file mode 100644 index 00000000000..7886ffeee23 --- /dev/null +++ b/usr.sbin/ospfd/ospf.h @@ -0,0 +1,203 @@ +/* $OpenBSD: ospf.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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> +#include <stdbool.h> + +/* misc */ +#define OSPF_VERSION 2 +#define IPPROTO_OSPF 89 +#define AllSPFRouters "224.0.0.5" +#define AllDRouters "224.0.0.6" + +#define PACKET_HDR 100 /* XXX used to calculate the IP payload */ + +#define DEFAULT_METRIC 1 +#define MIN_METRIC 1 +#define MAX_METRIC 65535 + +#define DEFAULT_PRIORITY 0 /* XXX force to 0 for now */ +#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_TRANSIT_DELAY 1 +#define MIN_TRANSIT_DELAY 1 +#define MAX_TRANSIT_DELAY 3600 + +#define DEFAULT_ADJ_TMOUT 60 /* XXX sane value? */ + +/* OSPF compatibility flags */ +#define OSPF_OPTION_E 0x02 +#define OSPF_OPTION_MC 0x04 +#define OSPF_OPTION_NP 0x08 +#define OSPF_OPTION_EA 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 + +/* OSPF auth types */ +#define AUTH_TYPE_NONE 0 +#define AUTH_TYPE_SIMPLE 1 +#define AUTH_TYPE_CRYPT 2 + +#define MIN_AUTHTYPE 0 +#define MAX_AUTHTYPE 2 + +/* 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 INIT_SEQ_NUM 0x80000001 +#define MAX_SEQ_NUM 0x7fffffff + +/* OSPF header */ +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_int16_t auth_type; + union { + char simple[8]; + u_int64_t crypt; + } auth_key; +}; + +/* Hello header (type 1) */ +struct hello_hdr { + u_int32_t mask; + u_int16_t hello_interval; + u_int8_t opts; + u_int8_t rtr_priority; + u_int32_t rtr_dead_interval; + u_int32_t d_rtr; + u_int32_t bd_rtr; +}; + +/* Database Description header (type 2) */ +struct db_dscrp_hdr { + u_int16_t iface_mtu; + u_int8_t opts; + 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_int32_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 */ +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_sum { + u_int32_t mask; + u_int32_t metric; /* only lower 24 bit */ +}; + +struct lsa_hdr { + u_int16_t age; + u_int8_t opts; + u_int8_t type; + u_int32_t ls_id; + u_int32_t adv_rtr; + 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; + } data; +}; + +#endif /* !_OSPF_H_ */ diff --git a/usr.sbin/ospfd/ospfd.8 b/usr.sbin/ospfd/ospfd.8 new file mode 100644 index 00000000000..5e94a860ba4 --- /dev/null +++ b/usr.sbin/ospfd/ospfd.8 @@ -0,0 +1,69 @@ +.\" $OpenBSD: ospfd.8,v 1.1 2005/01/28 14:05:40 claudio Exp $ +.\" +.\" Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> +.\" +.\" 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 November 4, 2004 +.Dt OSPFD 8 +.Os +.Sh NAME +.Nm ospfd +.Nd "Open Shortest Path First daemon" +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl f Ar file +.Sh DESCRIPTION +.Nm +is an Open Shortest Path First +.Pq OSPF +daemon which manages routing tables. +Its main purpose is to keep AS internal routing tables in sync. +.Pp +A running +.Nm +can be controlled with the +.Xr ospfctl 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. +.El +.Sh FILES +.Bl -tag -width "/etc/ospfd.confXXXX" -compact +.It /etc/ospfd.conf +.Nm +configuration file. +.El +.Sh SEE ALSO +.Xr ospfctl 8 +.Rs +.%R RFC 2328 +.%T "OSPF Version 2" +.%D April 1998 +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 3.7 . diff --git a/usr.sbin/ospfd/ospfd.c b/usr.sbin/ospfd/ospfd.c new file mode 100644 index 00000000000..8367ad569b1 --- /dev/null +++ b/usr.sbin/ospfd/ospfd.c @@ -0,0 +1,456 @@ +/* $OpenBSD: ospfd.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * 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 <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 <signal.h> +#include <unistd.h> +#include <util.h> + +#include "ospfd.h" +#include "ospf.h" +#include "ospfe.h" +#include "control.h" +#include "log.h" +#include "rde.h" + +void main_sig_handler(int, short, void *); +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 *); +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 check_file_secrecy(int, const char *); + +int pipe_parent2ospfe[2]; +int pipe_parent2rde[2]; +int pipe_ospfe2rde[2]; + +volatile sig_atomic_t main_quit = 0; + +struct ospfd_conf *conf = NULL; +struct imsgbuf *ibuf_ospfe; +struct imsgbuf *ibuf_rde; + +pid_t ospfe_pid; +pid_t rde_pid; + +void +main_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + switch (sig) { + case SIGTERM: + case SIGINT: + ospfd_shutdown(); + /* NOTREACHED */ + case SIGCHLD: + if (check_child(ospfe_pid, "ospfe engine")) { + ospfe_pid = 0; + ospfd_shutdown(); + } + if (check_child(rde_pid, "route decision engine")) { + rde_pid = 0; + ospfd_shutdown(); + } + break; + case SIGHUP: + /* reconfigure */ + /* ... */ + break; + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-d] [-f file]\n", __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + char *conffile; + int ch; + int debug = 0; + + conffile = CONF_FILE; + ospfd_process = PROC_MAIN; + + /* start logging */ + log_init(1); + + while ((ch = getopt(argc, argv, "dhf:")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'f': + conffile = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + log_init(debug); + + /* parse config file */ + if ((conf = parse_config(conffile, OSPFD_OPT_VERBOSE)) == NULL ) + exit(1); + + /* check for root priviledges */ + if (geteuid()) + errx(1, "need root privileges"); + + /* check for ospfd user */ + if (getpwnam(OSPFD_USER) == NULL) + errx(1, "unknown user %s", OSPFD_USER); + + endpwent(); + + 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); + + event_init(); + + if (if_init(conf)) + log_info("error initializing interfaces"); + else + log_debug("interfaces initialized"); + + /* start children */ + rde_pid = rde(conf, pipe_parent2rde, pipe_ospfe2rde, pipe_parent2ospfe); + ospfe_pid = ospfe(conf, pipe_parent2ospfe, pipe_ospfe2rde, + pipe_parent2rde); + + /* show who we are */ + setproctitle("parent"); + + /* setup signal handler */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sigchld, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGTERM, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sigchld, NULL); + signal_add(&ev_sighup, NULL); + + /* setup pipes to children */ + close(pipe_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(!(conf->flags & OSPFD_FLAG_NO_FIB_UPDATE)) == -1) + ospfd_shutdown(); + + show_config(conf); + + 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(); + if_shutdown(conf); + + 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); + + log_info("terminating"); + exit(0); +} + +int +check_child(pid_t pid, const char *pname) +{ + int status; + + if (waitpid(pid, &status, WNOHANG) > 0) { + if (WIFEXITED(status)) { + log_warnx("lost child: %s exited", pname); + return (1); + } + if (WIFSIGNALED(status)) { + log_warnx("lost child: %s terminated; signal %d", + pname, WTERMSIG(status)); + return (1); + } + } + + return (0); +} + +/* imsg handling */ +void +main_dispatch_ospfe(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + int n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_RELOAD: + log_debug("main_dispatch_ospfe: IMSG_CTL_RELOAD"); + /* reconfig */ + break; + case IMSG_CTL_SHOW_INTERFACE: + kr_show_route(&imsg); + break; + case IMSG_CTL_FIB_COUPLE: + kr_fib_couple(); + break; + case IMSG_CTL_FIB_DECOUPLE: + kr_fib_decouple(); + break; + default: + log_debug("main_dispatch_ospfe: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +main_dispatch_rde(int fd, short event, void *bula) +{ + struct imsgbuf *ibuf = bula; + struct imsg imsg; + int n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + + if (n == 0) + break; + + switch (imsg.hdr.type) { + default: + log_debug("main_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +main_imsg_compose_ospfe(int type, pid_t pid, void *data, u_int16_t datalen) +{ + + imsg_compose(ibuf_ospfe, type, 0, pid, -1, 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, -1, data, datalen); +} + + +void +send_nexthop_update(struct kroute_nexthop *msg) +{ + char *gw = NULL; + + if (msg->gateway.s_addr) + if (asprintf(&gw, ": via %s", + inet_ntoa(msg->gateway)) == -1) { + log_warn("send_nexthop_update"); + main_quit = 1; + } + + log_info("nexthop %s now %s%s%s", inet_ntoa(msg->nexthop), + msg->valid ? "valid" : "invalid", + msg->connected ? ": directly connected" : "", + msg->gateway.s_addr ? gw : ""); + + free(gw); + + if (imsg_compose(ibuf_rde, IMSG_NEXTHOP_UPDATE, 0, 0, -1, + msg, sizeof(struct kroute_nexthop)) == -1) + main_quit = 1; +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + log_warn("cannot stat %s", fname); + return (-1); + } + + if (st.st_uid != 0 && st.st_uid != getuid()) { + log_warnx("%s: owner not root or current user", fname); + return (-1); + } + + if (st.st_mode & (S_IRWXG | S_IRWXO)) { + log_warnx("%s: group/world readable/writeable", fname); + return (-1); + } + + return (0); +} + +/* this needs to be added here so that 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); +} diff --git a/usr.sbin/ospfd/ospfd.h b/usr.sbin/ospfd/ospfd.h new file mode 100644 index 00000000000..b4ccb312825 --- /dev/null +++ b/usr.sbin/ospfd/ospfd.h @@ -0,0 +1,418 @@ +/* $OpenBSD: ospfd.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * 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 _OSPFD_H_ +#define _OSPFD_H_ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/tree.h> +#include <net/if.h> +#include <netinet/in.h> +#include <event.h> +#include <stdbool.h> + +#define CONF_FILE "/etc/ospfd.conf" +#define OSPFD_SOCKET "/var/run/ospfd.sock" +#define OSPFD_USER "_ospfd" + +#define NBR_HASHSIZE 128 +#define LSA_HASHSIZE 512 + +#define READ_BUF_SIZE 65535 +#define RT_BUF_SIZE 16384 + +#define OSPFD_FLAG_NO_FIB_UPDATE 0x0001 + +#define F_OSPFD_INSERTED 0x0001 +#define F_KERNEL 0x0002 +#define F_CONNECTED 0x0004 +#define F_NEXTHOP 0x0008 +#define F_DOWN 0x0010 +#define F_STATIC 0x0020 +#define F_LONGER 0x0040 +#define F_REJECT 0x0080 +#define F_BLACKHOLE 0x0100 + +/* buffer */ +struct buf { + TAILQ_ENTRY(buf) entry; + u_char *buf; + ssize_t size; + ssize_t wpos; + ssize_t rpos; + int fd; +}; + +struct msgbuf { + u_int32_t queued; + int fd; + TAILQ_HEAD(, buf) bufs; +}; + +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 8192 + +struct buf_read { + u_char buf[READ_BUF_SIZE]; + u_char *rptr; + ssize_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_NBR, + IMSG_CTL_FIB_COUPLE, + IMSG_CTL_FIB_DECOUPLE, + IMSG_CTL_AREA, + IMSG_CTL_END, + IMSG_NEXTHOP_ADD, + IMSG_NEXTHOP_REMOVE, + IMSG_NEXTHOP_UPDATE, + IMSG_IFINFO, + IMSG_NEIGHBOR_UP, + IMSG_NEIGHBOR_DOWN, + IMSG_NEIGHBOR_CHANGE, + IMSG_DD, + IMSG_DB_SNAPSHOT, + IMSG_DB_END, + IMSG_LS_REQ, + IMSG_LS_UPD, + IMSG_LS_ACK, + IMSG_LS_FLOOD, + IMSG_LS_BADREQ, + IMSG_LS_MAXAGE +}; + +struct imsg_hdr { + enum imsg_type type; + u_int16_t len; + u_int32_t peerid; + pid_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + void *data; +}; + +struct imsg_fd { + TAILQ_ENTRY(imsg_fd) entry; + int fd; +}; + +/* 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; */ + u_int32_t stub_default_cost; + + u_int32_t dead_interval; + u_int16_t transfer_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + u_int8_t priority; + + bool transit; + bool stub; +}; + +/* interface states */ +#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_UP, + IF_EVT_WTIMER, + IF_EVT_BACKUP_SEEN, + IF_EVT_NBR_CHNG, + IF_EVT_LOOP, + IF_EVT_UNLOOP, + IF_EVT_DOWN +}; + +static const char * const if_event_names[] = { + "UP", + "WAITTIMER", + "BACKUPSEEN", + "NEIGHBORCHANGE", + "LOOP", + "UNLOOP", + "DOWN" +}; + +/* interface actions */ +enum iface_action { + 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 +}; + +/* auth types */ +enum auth_type { + AUTH_NONE, + AUTH_SIMPLE, + AUTH_CRYPT +}; + +/* 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]; + struct in_addr addr; + struct in_addr mask; + struct in_addr rtr_id; + char *auth_key; + struct nbr *dr; /* designated router */ + struct nbr *bdr; /* backup designated router */ + struct nbr *self; + struct area *area; + + u_int32_t baudrate; + u_int32_t dead_interval; + u_int32_t ls_ack_cnt; + unsigned int ifindex; + int fd; + int state; + int mtu; + u_int16_t flags; + u_int16_t transfer_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + enum iface_type type; + enum auth_type auth_type; + u_int8_t linkstate; + u_int8_t priority; + + bool passive; +}; + +/* ospf_conf */ +enum { + PROC_MAIN, + PROC_OSPF_ENGINE, + PROC_RDE_ENGINE +} ospfd_process; + +struct ospfd_conf { + struct event ev; + struct in_addr rtr_id; + u_int32_t opts; +#define OSPFD_OPT_VERBOSE 0x00000001 +#define OSPFD_OPT_VERBOSE2 0x00000002 +#define OSPFD_OPT_NOACTION 0x00000004 + int maxdepth; + LIST_HEAD(, area) area_list; + + struct lsa_tree lsa_tree; + int ospf_socket; + int flags; + int options; /* OSPF options */ +}; + +/* kroute */ +struct kroute { + struct in_addr prefix; + u_int8_t prefixlen; + struct in_addr nexthop; + u_int16_t flags; + u_short ifindex; +}; + +struct kroute_nexthop { + struct in_addr nexthop; + u_int8_t valid; + u_int8_t connected; + struct in_addr gateway; + struct kroute kr; +}; + +struct kif { + u_short ifindex; + int flags; + char ifname[IF_NAMESIZE]; + u_int8_t media_type; + u_int8_t link_state; + u_long baudrate; + u_int8_t nh_reachable; /* for nexthop verification */ +}; + +/* control data structures */ +struct ctl_iface { + char name[IF_NAMESIZE]; + struct in_addr addr; + struct in_addr mask; + struct in_addr area; + struct in_addr rtr_id; + struct in_addr dr_id; + struct in_addr dr_addr; + struct in_addr bdr_id; + struct in_addr bdr_addr; + time_t hello_timer; + 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 transfer_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 priority; + bool passive; +}; + +struct ctl_nbr { + char name[IF_NAMESIZE]; + struct in_addr id; + struct in_addr addr; + struct in_addr dr; + struct in_addr bdr; + struct in_addr area; + time_t dead_timer; + 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; +}; + +void show_config(struct ospfd_conf *xconf); + +/* area.c */ +struct area *area_new(void); +int area_del(struct area *); +struct area *area_find(struct ospfd_conf *, struct in_addr); + +/* buffer.c */ +struct buf *buf_open(ssize_t); +int buf_add(struct buf *, void *, ssize_t); +void *buf_reserve(struct buf *, ssize_t); +int buf_close(struct msgbuf *, struct buf *); +int buf_write(int, struct buf *); +void buf_free(struct buf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); +int msgbuf_writebound(struct msgbuf *); +int msgbuf_unbounded(struct msgbuf *msgbuf); + +/* 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 *)); +int imsg_read(struct imsgbuf *); +int imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t, int, + 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 *); +int imsg_get_fd(struct imsgbuf *); +void imsg_event_add(struct imsgbuf *); /* needs to be provided externaly */ + +/* in_cksum.c */ +int in_cksum(void *, int); + +/* iso_cksum.c */ +u_int16_t iso_cksum(void *, u_int16_t, u_int16_t); + +/* kroute.c */ +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 *); +int kr_nexthop_add(struct in_addr); +void kr_nexthop_delete(struct in_addr); +void kr_show_route(struct imsg *); +void kr_ifinfo(char *); +void send_nexthop_update(struct kroute_nexthop *); + +/* ospfd.c */ +void send_nexthop_update(struct kroute_nexthop *); + +#endif /* _OSPFD_H_ */ diff --git a/usr.sbin/ospfd/ospfe.c b/usr.sbin/ospfd/ospfe.c new file mode 100644 index 00000000000..debad28f6d6 --- /dev/null +++ b/usr.sbin/ospfd/ospfe.c @@ -0,0 +1,725 @@ +/* $OpenBSD: ospfe.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <pwd.h> +#include <unistd.h> +#include <event.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "ospf.h" +#include "ospfd.h" +#include "ospfe.h" +#include "control.h" +#include "log.h" + +void ospfe_sig_handler(int, short, void *); +void ospfe_shutdown(void); +void orig_lsa(void); +void orig_rtr_lsa(struct area *); +void orig_net_lsa(struct iface *); + +volatile sig_atomic_t ospfe_quit = 0; +struct ospfd_conf *oeconf = NULL; +struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_rde; +struct ctl_conn *ctl_conn; + +void +ospfe_sig_handler(int sig, short event, void *bula) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + ospfe_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +/* ospf engine */ +pid_t +ospfe(struct ospfd_conf *xconf, int pipe_parent2ospfe[2], int pipe_ospfe2rde[2], + int pipe_parent2rde[2]) +{ + struct area *area = NULL; + struct iface *iface = NULL; + 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"); + + oeconf = xconf; + + if ((pw = getpwnam(OSPFD_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) || + setegid(pw->pw_gid) || setgid(pw->pw_gid) || + seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { + fatal("can't drop privileges"); + } + + endpwent(); + + 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); + + /* 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); + + /* listen on ospfd control socket */ + TAILQ_INIT(&ctl_conns); + control_listen(); + + /* start interfaces */ + LIST_FOREACH(area, &oeconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (if_fsm(iface, IF_EVT_UP)) { + log_debug("error starting interface %s", + iface->name); + } + } + } + + orig_lsa(); + + event_dispatch(); + + ospfe_shutdown(); + /* NOTREACHED */ + return (0); +} + +void +ospfe_shutdown(void) +{ + struct area *area; + struct iface *iface; + + /* stop all interfaces and remove all areas */ + LIST_FOREACH(area, &oeconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (if_fsm(iface, IF_EVT_DOWN)) { + log_debug("error stopping interface %s", + iface->name); + } + } + area_del(area); + } + + /* 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); + + 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, -1, 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, -1, data, datalen)); +} + +void +ospfe_dispatch_main(int fd, short event, void *bula) +{ + struct imsg imsg; + struct imsgbuf *ibuf = bula; + struct area *area = NULL; + struct iface *iface = NULL; + struct kif *kif; + int n; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("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; + + log_debug("ospfe_dispatch_main: ifindex %d changed", + kif->ifindex); + + LIST_FOREACH(area, &oeconf->area_list, entry) { + LIST_FOREACH(iface, &area->iface_list, entry) { + if (kif->ifindex == iface->ifindex) { + if (kif->flags & IFF_UP) { + if_fsm(iface, + IF_EVT_UP); + } else { + if_fsm(iface, + IF_EVT_DOWN); + } + } + } + } + + break; + case IMSG_CTL_END: + log_debug("ospfe_dispatch_main: 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); + } + imsg_event_add(ibuf); +} + +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; + int n, noack = 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 */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("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) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_DD, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + /* put these on my ls_req_list for retrival */ + lhp = lsa_hdr_new(); + memcpy(lhp, imsg.data, sizeof(*lhp)); + ls_req_list_add(nbr, lhp); + break; + case IMSG_DB_SNAPSHOT: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_DB_SNAPSHOT, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + /* 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) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_DB_END, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + /* 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) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_LS_FLOOD, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + 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, l); + } + } + } 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, l); + } + /* 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 */ + noack = 1; + } + + if (!noack && 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) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_LS_UPD, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + if (nbr->iface->self == nbr) + break; + + memcpy(&age, imsg.data, sizeof(age)); + if (ntohs(age) >= MAX_AGE) { + /* add to retransmit list */ + ref = lsa_cache_add(imsg.data, l); + ls_retrans_list_add(nbr, imsg.data); /* XXX */ + lsa_cache_put(ref, nbr); + } + + /* send direct don't add to retransmit list */ + send_ls_update(nbr->iface, nbr->addr, imsg.data, l); + break; + case IMSG_LS_ACK: + /* + * IMSG_LS_ACK is used in two cases: + * 1. LSA was a duplicate + * 2. LSA's age is MaxAge and there is no current + * instance in the DB plus no neighbor is state + * Exchange or Loading + */ + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_LS_ACK, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + if (nbr->iface->self == nbr) + break; + + /* TODO for case one check for implied acks */ + + /* 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) + fatalx("ospfe_dispatch_rde: " + "neighbor not found"); + + log_debug("ospfe_dispatch_rde: IMSG_LS_BADREQ, " + "neighbor id %s, len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + if (nbr->iface->self == nbr) + fatalx("ospfe_dispatch_rde: " + "dummy neighbor got BADREQ"); + + nbr_fsm(nbr, NBR_EVT_BAD_LS_REQ); + break; + case IMSG_CTL_SHOW_DATABASE: + case IMSG_CTL_AREA: + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("ospfe_dispatch_rde: error handling imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void +orig_lsa(void) +{ + struct area *area; + struct iface *iface; + + LIST_FOREACH(area, &oeconf->area_list, entry) { + orig_rtr_lsa(area); + LIST_FOREACH(iface, &area->iface_list, entry) + orig_net_lsa(iface); + } +} + +void +orig_rtr_lsa(struct area *area) +{ + struct lsa *lsa; + struct iface *iface; + char *buf; + struct lsa_rtr_link *rtr_link; + int num_links = 0; + u_int16_t len; + + log_debug("orig_rtr_lsa: area %s", inet_ntoa(area->id)); + + if ((buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("orig_rtr_lsa"); + + /* LSA header */ + lsa = (struct lsa *)buf; + lsa->hdr.age = htons(DEFAULT_AGE); /* XXX */ + lsa->hdr.opts = oeconf->options; /* XXX */ + 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.ls_chksum = 0; /* updated later */ + lsa->hdr.len = 0; /* updated later */ + len = sizeof(struct lsa_hdr) + sizeof(struct lsa_rtr); + + /* links */ + LIST_FOREACH(iface, &area->iface_list, entry) { + log_debug("orig_rtr_lsa: interface %s", iface->name); + + rtr_link = (struct lsa_rtr_link *)(buf + len); + switch (iface->type) { + case IF_TYPE_POINTOPOINT: + case IF_TYPE_VIRTUALLINK: + log_debug("orig_rtr_lsa: not supported, interface %s", + iface->name); + break; + case IF_TYPE_BROADCAST: + if (iface->state == IF_STA_DOWN) + break; + + if ((iface->state == IF_STA_WAITING) || + iface->dr == NULL) { /* XXX */ + log_debug("orig_rtr_lsa: stub net, " + "interface %s", iface->name); + + rtr_link->id = + iface->addr.s_addr & iface->mask.s_addr; + rtr_link->data = iface->mask.s_addr; + rtr_link->type = LINK_TYPE_STUB_NET; + } else { + log_debug("orig_rtr_lsa: transit net, " + "interface %s", iface->name); + + rtr_link->id = iface->dr->addr.s_addr; + rtr_link->data = iface->addr.s_addr; + rtr_link->type = LINK_TYPE_TRANSIT_NET; + } + + rtr_link->num_tos = 0; + rtr_link->metric = htons(iface->metric); + num_links++; + len += sizeof(struct lsa_rtr_link); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + log_debug("orig_rtr_lsa: not supported, interface %s", + iface->name); + break; + default: + fatalx("orig_rtr_lsa: unknown interface type"); + } + } + + lsa->data.rtr.flags = 0; /* XXX */ + lsa->data.rtr.dummy = 0; + lsa->data.rtr.nlinks = htons(num_links); + + lsa->hdr.len = htons(len); + + lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET)); + + imsg_compose(ibuf_rde, IMSG_LS_UPD, + LIST_FIRST(&area->iface_list)->self->peerid, 0, -1, buf, + ntohs(lsa->hdr.len)); +} + +void +orig_net_lsa(struct iface *iface) +{ + struct lsa *lsa; + struct nbr *nbr; + char *buf; + int num_rtr = 0; + u_int16_t len; + + log_debug("orig_net_lsa: iface %s", iface->name); + + /* XXX if not dr quit */ + /* ... */ + + if ((buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("orig_net_lsa"); + + /* LSA header */ + lsa = (struct lsa *)buf; + lsa->hdr.age = htons(DEFAULT_AGE); /* XXX */ + lsa->hdr.opts = oeconf->options; /* XXX */ + lsa->hdr.type = LSA_TYPE_NETWORK; + 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.ls_chksum = 0; /* updated later */ + lsa->hdr.len = 0; /* updated later */ + len = sizeof(lsa->hdr); + + lsa->data.net.mask = iface->mask.s_addr; + len += sizeof(lsa->data.net.mask); + + /* attached routers */ + /* fully adjacent neighbors + self */ + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->state != NBR_STA_FULL) { /* XXX */ + lsa->data.net.att_rtr[num_rtr] = nbr->id.s_addr; + num_rtr++; + len += sizeof(nbr->id); + } + } + + lsa->hdr.len = htons(len); + + lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET)); +/* + imsg_compose(ibuf_rde, IMSG_LS_UPD, iface->self->peerid, 0, -1, buf, + ntohs(lsa->hdr.len)); +*/ +} + +u_int32_t +ospfe_router_id(void) +{ + return (oeconf->rtr_id.s_addr); +} + +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, -1, + 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, -1, nctl, + sizeof(struct ctl_nbr)); + } + } + + imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0); +} diff --git a/usr.sbin/ospfd/ospfe.h b/usr.sbin/ospfd/ospfe.h new file mode 100644 index 00000000000..0e0281cbfbd --- /dev/null +++ b/usr.sbin/ospfd/ospfe.h @@ -0,0 +1,286 @@ +/* $OpenBSD: ospfe.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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; + +/* 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 0xfffc +#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_STRT, + 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_STRT, + 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 +}; + +struct lsa_entry { + TAILQ_ENTRY(lsa_entry) entry; + union { + struct lsa_hdr *lu_lsa; + struct lsa_ref *lu_ref; + } le_data; +}; +#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 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 in_addr addr; + struct in_addr 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 peerid; /* unique ID in DB */ + + u_int32_t ls_req_cnt; + + int state; + u_int8_t priority; + u_int8_t options; + u_int8_t last_rx_options; + u_int8_t last_rx_bits; + + bool master; +}; + +/* auth.c */ +int auth_validate(struct ospf_hdr *, const struct iface *); +int auth_gen(void *, u_int16_t, const struct iface *); + +/* 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 *); +int start_db_tx_timer(struct nbr *); +int stop_db_tx_timer(struct nbr *); + +/* hello.c */ +int send_hello(struct iface *); +void recv_hello(struct iface *, struct in_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_iface_ctl(struct ctl_conn *, unsigned int); +void ospfe_nbr_ctl(struct ctl_conn *); + +/* interface.c */ +int if_fsm(struct iface *, enum iface_event); + +struct iface *if_new(char *, unsigned int); +int if_del(struct iface *); +int if_init(struct ospfd_conf *); +int if_shutdown(struct ospfd_conf *); + +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 *); + +const char *if_state_name(int); +const char *if_event_name(int); +const char *if_action_name(int); +const char *if_type_name(int); +const char *if_auth_name(int); + +int if_join_group(struct iface *, struct in_addr *); +int if_leave_group(struct iface *, struct in_addr *); +int if_set_mcast(struct iface *); + +/* lsack.c */ +int delay_lsa_ack(struct iface *, struct lsa_hdr *); +int send_ls_ack(struct iface *, struct in_addr, void *data, int len); +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 *); +bool ls_ack_list_empty(struct iface *); +void ls_ack_tx_timer(int, short, void *); +int start_ls_ack_tx_timer(struct iface *); +int 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 *); +bool ls_req_list_empty(struct nbr *); +void ls_req_tx_timer(int, short, void *); +int start_ls_req_tx_timer(struct nbr *); +int stop_ls_req_tx_timer(struct nbr *); + +/* lsupdate.c */ +int lsa_flood(struct iface *, struct nbr *, struct lsa_hdr *, + void *, u_int16_t); +int send_ls_update(struct iface *, struct in_addr, void *, int); +void recv_ls_update(struct nbr *, char *, u_int16_t); + +int ls_retrans_list_add(struct nbr *, struct lsa_hdr *); +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 *); +bool ls_retrans_list_empty(struct nbr *); + +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); +int 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 *); +int nbr_start_itimer(struct nbr *); +int nbr_stop_itimer(struct nbr *); +int nbr_reset_itimer(struct nbr *); + +void nbr_adj_timer(int, short, void *); +int nbr_start_adj_timer(struct nbr *); + +int nbr_act_start(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 *); + +const char *nbr_state_name(int); +const char *nbr_event_name(int); +const char *nbr_action_name(int); + +struct lsa_hdr *lsa_hdr_new(void); + +/* packet.c */ +void gen_ospf_hdr(void *, struct iface *, u_int8_t); +int send_packet(struct iface *, char *, int, struct sockaddr_in *); +void recv_packet(int, short, void *); + +#endif /* _OSPFE_H_ */ diff --git a/usr.sbin/ospfd/packet.c b/usr.sbin/ospfd/packet.c new file mode 100644 index 00000000000..b76d9c22db4 --- /dev/null +++ b/usr.sbin/ospfd/packet.c @@ -0,0 +1,288 @@ +/* $OpenBSD: packet.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <event.h> +#include <stdlib.h> +#include <strings.h> + +#include "ospfd.h" +#include "ospf.h" +#include "log.h" +#include "ospfe.h" + +int ip_hdr_sanity_check(const struct ip *, u_int16_t); +int ospf_hdr_sanity_check(const struct ip *, + struct ospf_hdr *, u_int16_t, const struct iface *); +struct iface *find_iface(struct ospfd_conf *, struct in_addr); + +void +gen_ospf_hdr(void *buf, struct iface *iface, u_int8_t type) +{ + struct ospf_hdr *ospf_hdr = buf; + + ospf_hdr->version = OSPF_VERSION; + ospf_hdr->type = type; + ospf_hdr->len = 0; /* updated later */ + ospf_hdr->rtr_id = iface->rtr_id.s_addr; + ospf_hdr->area_id = iface->area->id.s_addr; + ospf_hdr->chksum = 0; /* updated later */ + ospf_hdr->auth_type = htons(iface->auth_type); +} + +/* send and receive packets */ +int +send_packet(struct iface *iface, char *pkt, int len, struct sockaddr_in *dst) +{ + if (iface->passive) { + log_warnx("send_packet: cannot send packet on passive " + "interface %s", iface->name); + return (-1); + } + + /* set outgoing interface for multicast traffic */ + if (IN_MULTICAST(ntohl(dst->sin_addr.s_addr))) + if (if_set_mcast(iface) == -1) { + log_debug("send_packet: error setting multicast " + "interface, %s", iface->name); + return (-1); + } + + if (sendto(iface->fd, pkt, len, 0, + (struct sockaddr *)dst, sizeof(*dst)) == -1 ) { + log_warnx("send_packet: error sending packet on interface %s", + iface->name); + return (-1); + } + + return (0); +} + +void +recv_packet(int fd, short event, void *bula) +{ + struct ospfd_conf *xconf = bula; + struct ip ip_hdr; + struct ospf_hdr *ospf_hdr; + struct iface *iface; + struct nbr *nbr = NULL; + struct in_addr addr; + char *buf, *ptr; + ssize_t r; + u_int16_t len; + int l; + + if (event != EV_READ) + return; + + /* + * XXX I don't like to allocate a buffer for each packet received + * and freeing that buffer at the end of the function. It would be + * enough to allocate the buffer on startup. + */ + if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL) + fatal("recv_packet"); + + if ((r = recvfrom(fd, buf, READ_BUF_SIZE, 0, NULL, NULL)) == -1) { + if (errno != EAGAIN) { + log_debug("recv_packet: error receiving packet"); + } + goto done; + } + + len = (u_int16_t)r; + + /* IP header sanity checks */ + if (len < sizeof(ip_hdr)) { + log_warnx("recv_packet: bad packet size"); + goto done; + } + memcpy(&ip_hdr, buf, sizeof(ip_hdr)); + if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1) + goto done; + buf += l; + len -= l; + + /* find a matching interface */ + if ((iface = find_iface(xconf, ip_hdr.ip_src)) == NULL) { + log_debug("recv_packet: cannot find valid interface"); + goto done; + } + + /* + * 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_aton(AllSPFRouters, &addr); + if (ip_hdr.ip_dst.s_addr != addr.s_addr) { + inet_aton(AllDRouters, &addr); + if (ip_hdr.ip_dst.s_addr != addr.s_addr) { + if (ip_hdr.ip_dst.s_addr != iface->addr.s_addr) { + log_debug("recv_packet: packet sent to wrong " + "address %s, interface %s", + inet_ntoa(ip_hdr.ip_dst), iface->name); + goto done; + } + } + } + + /* OSPF header sanity checks */ + if (len < sizeof(*ospf_hdr)) { + log_warnx("recv_packet: bad packet size"); + goto done; + } + ospf_hdr = (struct ospf_hdr *)buf; + + if ((l = ospf_hdr_sanity_check(&ip_hdr, ospf_hdr, len, iface)) == -1) + goto done; + + buf += sizeof(*ospf_hdr); + len = l - sizeof(*ospf_hdr); + + if (ospf_hdr->type != PACKET_TYPE_HELLO) + /* find neighbor */ + if ((nbr = nbr_find_id(iface, ospf_hdr->rtr_id)) == NULL) { + log_debug("recv_packet: unknown neighbor ID"); + goto done; + } + + /* switch OSPF packet type */ + switch (ospf_hdr->type) { + case PACKET_TYPE_HELLO: + inet_aton(AllDRouters, &addr); + if (ip_hdr.ip_dst.s_addr == addr.s_addr) { + log_debug("recv_packet: invalid destination IP " + "address"); + break; + } + + recv_hello(iface, ip_hdr.ip_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); + } +done: + free(ptr); + return; +} + +int +ip_hdr_sanity_check(const struct ip *ip_hdr, u_int16_t len) +{ + if (ntohs(ip_hdr->ip_len) != len) { + log_debug("recv_packet: invalid IP packet length %s", + ntohs(ip_hdr->ip_len)); + return (-1); + } + + if (ip_hdr->ip_p != IPPROTO_OSPF) + /* this is enforced by the socket itself */ + fatalx("recv_packet: invalid IP proto"); + + return (ip_hdr->ip_hl << 2); +} + +int +ospf_hdr_sanity_check(const struct ip *ip_hdr, struct ospf_hdr *ospf_hdr, + u_int16_t len, const struct iface *iface) +{ + struct in_addr addr; + + if (ospf_hdr->version != OSPF_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 (ospf_hdr->area_id != iface->area->id.s_addr) { + /* TODO backbone area is allowed for virtual links */ + addr.s_addr = ospf_hdr->area_id; + log_debug("recv_packet: invalid area ID %s, interface %s", + inet_ntoa(addr), iface->name); + return (-1); + } + + if (iface->type == IF_TYPE_BROADCAST || iface->type == IF_TYPE_NBMA) { + if (inet_aton(AllDRouters, &addr) == 0) + fatalx("recv_packet: inet_aton"); + if ((ip_hdr->ip_dst.s_addr == addr.s_addr) && + (iface->state != (IF_STA_DR | IF_STA_BACKUP))) { + log_debug("recv_packet: invalid destination IP in " + "state %s, interface %s", + if_state_name(iface->state), iface->name); + return (-1); + } + } + + if (auth_validate(ospf_hdr, iface)) { + log_warnx("recv_packet: authentication error, interface %s", + iface->name); + return (-1); + } + + return (ntohs(ospf_hdr->len)); +} + +struct iface * +find_iface(struct ospfd_conf *xconf, struct in_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) { + if (iface->fd > 0 && (iface->addr.s_addr & + iface->mask.s_addr) == (src.s_addr & + iface->mask.s_addr) && !iface->passive) + return (iface); + } + } + + return (NULL); +} diff --git a/usr.sbin/ospfd/parse.y b/usr.sbin/ospfd/parse.y new file mode 100644 index 00000000000..5cfad9b860c --- /dev/null +++ b/usr.sbin/ospfd/parse.y @@ -0,0 +1,878 @@ +/* $OpenBSD: parse.y,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include "ospf.h" +#include "ospfd.h" +#include "ospfe.h" +#include "log.h" + +static struct ospfd_conf *conf; +static FILE *fin = NULL; +static int lineno = 1; +static int errors = 0; +static int pdebug = 1; +char *infile; +char *start_state; + +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); + +static struct { + u_int32_t dead_interval; + u_int16_t transfer_delay; + u_int16_t hello_interval; + u_int16_t rxmt_interval; + u_int16_t metric; + u_int8_t priority; +} defaults; + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entries; + int used; + int persist; + char *nam; + char *val; +}; + +int symset(const char *, const char *, int); +char *symget(const char *); +int atoul(char *, u_long *); +struct area *conf_get_area(struct in_addr); +struct iface *conf_get_if(char *, unsigned int); + +typedef struct { + union { + u_int32_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token AREA INTERFACE ROUTERID FIBUPDATE +%token AUTHKEY AUTHTYPE +%token METRIC PASSIVE +%token HELLOINTERVAL TRANSMITDELAY +%token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY +%token ERROR +%token <v.string> STRING +%type <v.number> number yesno +%type <v.string> string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar conf_main '\n' + | grammar varset '\n' + | grammar area '\n' + | grammar error '\n' { errors++; } + ; + +number : STRING { + u_long ulval; + + if (atoul($1, &ulval) == -1) { + yyerror("%s is not a number", $1); + free($1); + YYERROR; + } else + $$ = ulval; + free($1); + } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +yesno : STRING { + if (!strcmp($1, "yes")) + $$ = 1; + else if (!strcmp($1, "no")) + $$ = 0; + else { + free($1); + YYERROR; + } + free($1); + } + ; + +varset : STRING '=' string { + if (conf->opts & 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 : METRIC number { + if ($2 < MIN_METRIC || $2 > MAX_METRIC) { + yyerror("metric out of range (%d-%d)", + MIN_METRIC, MAX_METRIC); + YYERROR; + } + defaults.metric = $2; + } + | ROUTERPRIORITY number { + if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) { + yyerror("router-priority out of range (%d-%d)", + MIN_PRIORITY, MAX_PRIORITY); + YYERROR; + } + defaults.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; + } + defaults.dead_interval = $2; + } + | TRANSMITDELAY number { + if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) { + yyerror("transfer-delay out of range (%d-%d)", + MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY); + YYERROR; + } + defaults.transfer_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; + } + defaults.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; + } + defaults.rxmt_interval = $2; + } + | ROUTERID string { + if (!inet_aton($2, &conf->rtr_id)) { + yyerror("error parsing router-id"); + free($2); + YYERROR; + } + } + | FIBUPDATE yesno { + if ($2 == 0) + conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE; + else + conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE; + } + ; + +authtype : AUTHTYPE STRING { + enum auth_type type; + + if (!strcmp($2, "none")) + type = AUTH_NONE; + else if (!strcmp($2, "simple")) + type = AUTH_SIMPLE; + else if (!strcmp($2, "crypt")) + type = AUTH_SIMPLE; + else { + yyerror("unknown auth-type"); + free($2); + YYERROR; + } + free($2); + iface->auth_type = type; + } + ; + +authkey : AUTHKEY STRING { + if (iface != NULL) { + iface->auth_key = $2; + /* XXX truncate and warn! */ + } + } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl /* one newline or more */ + ; + +area : AREA string { + struct in_addr id; + if (inet_aton($2, &id) == 0) { + yyerror("error parsing area-id"); + free($2); + YYERROR; + } + free($2); + area = conf_get_area(id); + } optnl '{' optnl { + + } areaopts_l '}' { + area = NULL; + } + ; + +areaopts_l : areaopts_l areaoptsl + | areaoptsl + ; + +areaoptsl : interface nl + | METRIC number nl { + if ($2 < MIN_METRIC || $2 > MAX_METRIC) { + yyerror("metric out of range (%d-%d)", + MIN_METRIC, MAX_METRIC); + YYERROR; + } + area->metric = $2; + } + | ROUTERPRIORITY number nl { + if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) { + yyerror("router-priority out of range (%d-%d)", + MIN_PRIORITY, MAX_PRIORITY); + YYERROR; + } + area->priority = $2; + } + | ROUTERDEADTIME number nl { + 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; + } + area->dead_interval = $2; + } + | TRANSMITDELAY number nl { + if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) { + yyerror("transfer-delay out of range (%d-%d)", + MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY); + YYERROR; + } + area->transfer_delay = $2; + } + | HELLOINTERVAL number nl { + if ($2 < MIN_HELLO_INTERVAL || + $2 > MAX_HELLO_INTERVAL) { + yyerror("hello-interval out of range (%d-%d)", + MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); + YYERROR; + } + area->hello_interval = $2; + } + | RETRANSMITINTERVAL number nl { + if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) { + yyerror("retransmit-interval out of range " + "(%d-%d)", MIN_RXMT_INTERVAL, + MAX_RXMT_INTERVAL); + YYERROR; + } + area->rxmt_interval = $2; + } + ; + +interface : INTERFACE STRING { + unsigned int idx; + + if ((idx = if_nametoindex($2)) == 0 ) { + yyerror("unknown interface %s", $2); + free($2); + YYERROR; + } + iface = conf_get_if($2, idx); + free($2); + if (iface == NULL) + YYERROR; + iface->area = area; + LIST_INSERT_HEAD(&area->iface_list, + iface, entry); + iface->rtr_id = conf->rtr_id; + iface->passive = false; + } optnl '{' optnl { + + } interfaceopts_l '}' { + iface = NULL; + } + ; + +interfaceopts_l : interfaceopts_l interfaceoptsl + | interfaceoptsl + ; + +interfaceoptsl : authkey nl + | authtype nl + | PASSIVE nl { iface->passive = true; } + | METRIC number nl { + if ($2 < MIN_METRIC || $2 > MAX_METRIC) { + yyerror("metric out of range (%d-%d)", + MIN_METRIC, MAX_METRIC); + YYERROR; + } + iface->metric = $2; + } + | ROUTERPRIORITY number nl { + if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) { + yyerror("router-priority out of range (%d-%d)", + MIN_PRIORITY, MAX_PRIORITY); + YYERROR; + } + iface->priority = $2; + } + | ROUTERDEADTIME number nl { + 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; + } + iface->dead_interval = $2; + } + | TRANSMITDELAY number nl { + if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) { + yyerror("transfer-delay out of range (%d-%d)", + MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY); + YYERROR; + } + iface->transfer_delay = $2; + } + | HELLOINTERVAL number nl { + if ($2 < MIN_HELLO_INTERVAL || + $2 > MAX_HELLO_INTERVAL) { + yyerror("hello-interval out of range (%d-%d)", + MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); + YYERROR; + } + iface->hello_interval = $2; + } + | RETRANSMITINTERVAL number nl { + if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) { + yyerror("retransmit-interval out of range " + "(%d-%d)", MIN_RXMT_INTERVAL, + MAX_RXMT_INTERVAL); + YYERROR; + } + iface->rxmt_interval = $2; + } + ; + +%% + +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}, + {"auth-key", AUTHKEY}, + {"auth-type", AUTHTYPE}, + {"fibupdate", FIBUPDATE}, + {"hello-interval", HELLOINTERVAL}, + {"interface", INTERFACE}, + {"metric", METRIC}, + {"passive", PASSIVE}, + {"retransmit-interval", RETRANSMITINTERVAL}, + {"router-dead-time", ROUTERDEADTIME}, + {"router-id", ROUTERID}, + {"router-priority", ROUTERPRIORITY}, + {"transmit-delay", TRANSMITDELAY}, + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) { + if (pdebug > 1) + fprintf(stderr, "%s: %d\n", s, p->k_val); + return (p->k_val); + } else { + if (pdebug > 1) + fprintf(stderr, "string: %s\n", s); + return (STRING); + } +} + +#define MAXPUSHBACK 128 + +char *parsebuf; +int parseindex; +char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(FILE *f) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + while ((c = getc(f)) == '\\') { + next = getc(f); + if (next != '\n') { + if (isspace(next)) + yyerror("whitespace after \\"); + ungetc(next, f); + break; + } + yylval.lineno = lineno; + lineno++; + } + if (c == '\t' || c == ' ') { + /* Compress blanks to a single space. */ + do { + c = getc(f); + } while (c == '\t' || c == ' '); + ungetc(c, f); + c = ' '; + } + + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + pushback_index = 0; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(fin); + if (c == '\n') { + lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int endc, c; + int token; + +top: + p = buf; + while ((c = lgetc(fin)) == ' ') + ; /* nothing */ + + yylval.lineno = lineno; + if (c == '#') + while ((c = lgetc(fin)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = (char)c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + endc = c; + while (1) { + if ((c = lgetc(fin)) == EOF) + return (0); + if (c == endc) { + *p = '\0'; + break; + } + if (c == '\n') { + lineno++; + continue; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = (char)c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + errx(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = lineno; + lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct ospfd_conf * +parse_config(char *filename, int opts) +{ + struct sym *sym, *next; + + if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL) { + errx(1, "parse_config calloc"); + return (NULL); + } + + defaults.dead_interval = DEFAULT_RTR_DEAD_TIME; + defaults.transfer_delay = DEFAULT_TRANSIT_DELAY; + defaults.hello_interval = DEFAULT_HELLO_INTERVAL; + defaults.rxmt_interval = DEFAULT_RXMT_INTERVAL; + defaults.metric = DEFAULT_METRIC; + defaults.priority = DEFAULT_PRIORITY; + + conf->options = OSPF_OPTION_E; + + if ((fin = fopen(filename, "r")) == NULL) { + warn("%s", filename); + free(conf); + return (NULL); + } + infile = filename; + + conf->opts = opts; + LIST_INIT(&conf->area_list); + + 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); + } + } + + if (errors) { + clear_config(conf); + return (NULL); + } + + return (conf); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entries)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entries); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entries); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "cmdline_symset: malloc"); + + strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entries) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +int +atoul(char *s, u_long *ulvalp) +{ + u_long ulval; + char *ep; + + errno = 0; + ulval = strtoul(s, &ep, 0); + if (s[0] == '\0' || *ep != '\0') + return (-1); + if (errno == ERANGE && ulval == ULONG_MAX) + return (-1); + *ulvalp = ulval; + return (0); +} + +struct 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->dead_interval = defaults.dead_interval; + a->transfer_delay = defaults.transfer_delay; + a->hello_interval = defaults.hello_interval; + a->rxmt_interval = defaults.rxmt_interval; + a->metric = defaults.metric; + a->priority = defaults.priority; + + a->id.s_addr = id.s_addr; + + return (a); +} + +struct iface * +conf_get_if(char *name, unsigned int idx) +{ + struct area *a; + struct iface *i; + + LIST_FOREACH(a, &conf->area_list, entry) + LIST_FOREACH(i, &a->iface_list, entry) + if (i->ifindex == idx) { + yyerror("interface %s already configured", + name); + return (NULL); + } + + i = if_new(name, idx); + i->dead_interval = area->dead_interval; + i->transfer_delay = area->transfer_delay; + i->hello_interval = area->hello_interval; + i->rxmt_interval = area->rxmt_interval; + i->metric = area->metric; + i->priority = area->priority; + + return (i); +} + +void +clear_config(struct ospfd_conf *xconf) +{ + /* XXX clear conf */ + /* ... */ +} diff --git a/usr.sbin/ospfd/rde.c b/usr.sbin/ospfd/rde.c new file mode 100644 index 00000000000..c968bc89cdc --- /dev/null +++ b/usr.sbin/ospfd/rde.c @@ -0,0 +1,555 @@ +/* $OpenBSD: rde.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com> + * 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 "ospf.h" +#include "ospfd.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_nbr_init(u_int32_t); +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 *); + +volatile sig_atomic_t rde_quit = 0; +struct ospfd_conf *rdeconf = NULL; +struct imsgbuf *ibuf_ospfe; +struct imsgbuf *ibuf_main; + +void +rde_sig_handler(int sig, short event, void *arg) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + rde_shutdown(); + /* NOTREACHED */ + default: + fatalx("unexpected signal"); + /* NOTREACHED */ + } +} + +/* route decision engine */ +pid_t +rde(struct ospfd_conf *xconf, int pipe_parent2rde[2], int pipe_ospfe2rde[2], + int pipe_parent2ospfe[2]) +{ + struct passwd *pw; + struct event ev_sigint, ev_sigterm; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + return (pid); + } + + rdeconf = xconf; + + if ((pw = getpwnam(OSPFD_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) || + setegid(pw->pw_gid) || setgid(pw->pw_gid) || + seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { + fatal("can't drop privileges"); + } + + endpwent(); + + event_init(); + rde_nbr_init(NBR_HASHSIZE); + lsa_init(&rdeconf->lsa_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); + + /* 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_imsg); + + /* 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); + + event_dispatch(); + + rde_shutdown(); + /* NOTREACHED */ + + return (0); +} + +void +rde_shutdown(void) +{ + + /* ... */ + + msgbuf_write(&ibuf_ospfe->w); + msgbuf_clear(&ibuf_ospfe->w); + free(ibuf_ospfe); + msgbuf_write(&ibuf_main->w); + msgbuf_clear(&ibuf_main->w); + free(ibuf_main); + + log_info("route decision engine exiting"); + _exit(0); +} + +/* imesg */ +int +rde_imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen) +{ + return (imsg_compose(ibuf_main, type, 0, pid, -1, data, datalen)); +} + +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, -1, data, datalen)); +} + +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 lsa *lsa; + struct area *area; + struct vertex *v; + char *buf; + time_t now; + int r, n, state, self; + u_int16_t l; + + switch (event) { + case EV_READ: + if ((n = imsg_read(ibuf)) == -1) + fatal("imsg_read error"); + if (n == 0) /* connection closed */ + fatalx("pipe closed"); + break; + case EV_WRITE: + if (msgbuf_write(&ibuf->w) == -1) + fatal("msgbuf_write"); + imsg_event_add(ibuf); + return; + default: + fatalx("unknown event"); + } + + now = time(NULL); + + 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) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + nbr->state = state; + break; + case IMSG_DB_SNAPSHOT: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + log_debug("rde_dispatch_imsg: IMSG_DB_SNAPSHOT, " + "neighbor %s", inet_ntoa(nbr->id)); + + lsa_snap(nbr->area, imsg.hdr.peerid); + + imsg_compose(ibuf_ospfe, IMSG_DB_END, imsg.hdr.peerid, + 0, -1, NULL, 0); + break; + case IMSG_DD: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + log_debug("rde_dispatch_imsg: IMSG_DD, " + "neighbor %s", inet_ntoa(nbr->id)); + + 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 LSA's that are + * newer or missing + */ + /* XXX add to local REQ list */ + imsg_compose(ibuf_ospfe, IMSG_DD, + imsg.hdr.peerid, 0, -1, + &lsa_hdr, sizeof(lsa_hdr)); + } + if (l != 0) + log_warnx("rde_dispatch_imsg: peerid %lu, " + "trailing garbage in Database Description " + "packet", imsg.hdr.peerid); + break; + case IMSG_LS_REQ: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + log_debug("rde_dispatch_imsg: IMSG_LS_REQ, " + "neighbor %s len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + 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, -1, NULL, 0); + continue; + } + imsg_compose(ibuf_ospfe, IMSG_LS_UPD, + imsg.hdr.peerid, 0, -1, + 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) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + log_debug("rde_dispatch_imsg: IMSG_LS_UPD, " + "neighbor %s len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + 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)) + 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; + + 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) + break; + if (!(self = lsa_self(nbr, lsa, v))) + lsa_add(nbr, lsa); + + /* flood and perhaps ack LSA */ + imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD, + imsg.hdr.peerid, 0, -1, + lsa, ntohs(lsa->hdr.len)); + + /* reflood self originated LSA */ + if (self && v) + imsg_compose(ibuf_ospfe, IMSG_LS_FLOOD, + v->nbr->peerid, 0, -1, + v->lsa, ntohs(v->lsa->hdr.len)); + /* TODO LSA on req list -> BadLSReq */ + } else if (r < 0) { + /* 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, -1, + v->lsa, ntohs(v->lsa->hdr.len)); + } else { + /* LSA equal send direct ack */ + imsg_compose(ibuf_ospfe, IMSG_LS_ACK, + imsg.hdr.peerid, 0, -1, &lsa->hdr, + sizeof(lsa->hdr)); + } + break; + case IMSG_LS_MAXAGE: + nbr = rde_nbr_find(imsg.hdr.peerid); + if (nbr == NULL) + fatalx("rde_dispatch_imsg: " + "neighbor does not exist"); + log_debug("rde_dispatch_imsg: IMSG_LS_MAXAGE, " + "neighbor %s len %d", inet_ntoa(nbr->id), + imsg.hdr.len - IMSG_HEADER_SIZE); + + 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; + + lsa_del(nbr, &lsa_hdr); + break; + case IMSG_CTL_SHOW_DATABASE: + 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, -1, area, + sizeof(*area)); + lsa_dump(&area->lsa_tree, imsg.hdr.pid); + } + lsa_dump(&rdeconf->lsa_tree, 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, -1, area, + sizeof(*area)); + lsa_dump(&area->lsa_tree, imsg.hdr.pid); + if (!area->stub) + lsa_dump(&rdeconf->lsa_tree, + imsg.hdr.pid); + } + } + imsg_compose(ibuf_ospfe, IMSG_CTL_END, 0, imsg.hdr.pid, + -1, NULL, 0); + break; + default: + log_debug("rde_dispatch_msg: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +u_int32_t +rde_router_id(void) +{ + return (rdeconf->rtr_id.s_addr); +} + + + +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) +{ + 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; +} + +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; + + 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; + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); +} + +int +rde_nbr_loading(struct area *area) +{ + struct rde_nbr *nbr; + + LIST_FOREACH(nbr, &area->nbr_list, entry) { + if (nbr->self) + continue; + if (nbr->state & NBR_STA_XCHNG || + nbr->state & NBR_STA_LOAD) + return (1); + } + return (0); +} + diff --git a/usr.sbin/ospfd/rde.h b/usr.sbin/ospfd/rde.h new file mode 100644 index 00000000000..25be7f683c4 --- /dev/null +++ b/usr.sbin/ospfd/rde.h @@ -0,0 +1,81 @@ +/* $OpenBSD: rde.h,v 1.1 2005/01/28 14:05:40 claudio Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com> + * + * 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 vertex { + RB_ENTRY(vertex) entry; + struct event ev; + struct in_addr nexthop; + struct vertex *prev; + struct rde_nbr *nbr; + struct lsa *lsa; + time_t changed; + time_t stamp; + u_int32_t cost; + u_int32_t ls_id; + u_int32_t adv_rtr; + u_int8_t type; + u_int8_t flooded; +}; + +/* just the info RDE needs */ +struct rde_nbr { + LIST_ENTRY(rde_nbr) entry, hash; + struct in_addr id; + struct in_addr area_id; + struct lsa_head ls_req_list; + struct area *area; + u_int32_t peerid; /* unique ID in DB */ + int state; + int self; +}; + +/* rde.c */ +pid_t rde(struct ospfd_conf *, int [2], int [2], int [2]); +int rde_imsg_compose_parent(int, pid_t, void *, u_int16_t); +int rde_imsg_compose_ospfe(int, u_int32_t, pid_t, void *, + u_int16_t); +u_int32_t rde_router_id(void); +void rde_nbr_del(struct rde_nbr *); +int rde_nbr_loading(struct area *); + +/* rde_spf.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 *); +void lsa_add(struct rde_nbr *, struct lsa *); +void lsa_del(struct rde_nbr *, struct lsa_hdr *); +struct vertex *lsa_find(struct area *, u_int8_t, u_int32_t, u_int32_t); +void lsa_snap(struct area *, u_int32_t); +void lsa_dump(struct lsa_tree *, pid_t); + +RB_PROTOTYPE(lsa_tree, vertex, entry, lsa_compare) + +#endif /* _RDE_H_ */ diff --git a/usr.sbin/ospfd/rde_lsdb.c b/usr.sbin/ospfd/rde_lsdb.c new file mode 100644 index 00000000000..d9ab39b85df --- /dev/null +++ b/usr.sbin/ospfd/rde_lsdb.c @@ -0,0 +1,490 @@ +/* $OpenBSD: rde_lsdb.c,v 1.1 2005/01/28 14:05:40 claudio 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 "ospf.h" +#include "ospfd.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); +int lsa_asext_check(struct area *, struct lsa *, u_int16_t); +void lsa_age(struct vertex *); +void lsa_timeout(int, short, void *); +void lsa_refresh(struct vertex *); + +struct lsa_tree *global_lsa_tree; + +RB_GENERATE(lsa_tree, vertex, entry, lsa_compare) + +void +lsa_init(struct lsa_tree *t) +{ + global_lsa_tree = t; + RB_INIT(global_lsa_tree); +} + +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; + + if ((v = calloc(1, sizeof(struct vertex))) == NULL) + fatal(NULL); + v->nbr = nbr; + v->lsa = lsa; + v->changed = v->stamp = time(NULL); + 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 */ + + evtimer_set(&v->ev, lsa_timeout, v); + + return (v); +} + +void +vertex_free(struct vertex *v) +{ + if (v == NULL) + return; + + 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); + + 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 (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; + + if (len < sizeof(lsa->hdr)) { + log_warnx("lsa_check: invalid packet size"); + return (0); + } + if (ntohs(lsa->hdr.len) != len) { + log_warnx("lsa_check: invalid packet size"); + return (0); + } + + if (iso_cksum(lsa, len, 0)) { + log_warnx("lsa_check: invalid packet checksum"); + return (0); + } + + /* invalid ages */ + if ((ntohs(lsa->hdr.age) < 1 && !nbr->self) || + ntohs(lsa->hdr.age) > MAX_AGE) { + log_debug("lsa_check: invalid age"); + return (0); + } + + /* invalid sequence number */ + if ((ntohl(lsa->hdr.seq_num) == 0x80000000)) { + log_debug("ls_check: invalid 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 & 0x03) || + len < sizeof(lsa->hdr) + sizeof(u_int32_t)) { + log_warnx("lsa_check: invalid LSA network packet"); + return (0); + } + break; + case LSA_TYPE_SUM_NETWORK: + case LSA_TYPE_SUM_ROUTER: + if (len != sizeof(struct lsa_hdr) + sizeof(struct lsa_sum)) { + log_warnx("lsa_check: invalid LSA summary packet"); + return (0); + } + break; + case LSA_TYPE_EXTERNAL: + if (!lsa_asext_check(area, lsa, len)) + return (0); + break; + default: + log_warnx("lsa_check: unknown type %u", lsa->hdr.type); + return (0); + } + + /* MaxAge handling */ + if (ntohs(lsa->hdr.age) == MAX_AGE && 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_asext_check(struct area *area, struct lsa *lsa, u_int16_t len) +{ + if (area->stub) + /* AS-external-LSA are discarded in stub areas */ + return (0); + + /* XXX check size */ + return (1); +} + +int +lsa_self(struct rde_nbr *nbr, struct lsa *new, struct vertex *v) +{ + struct iface *iface; + struct lsa *dummy; + + 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: + 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. + */ + lsa_add(nbr, 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); +} + +void +lsa_add(struct rde_nbr *nbr, struct lsa *lsa) +{ + struct lsa_tree *tree; + struct vertex *new, *old; + struct timeval tv; + + if (lsa->hdr.type == LSA_TYPE_EXTERNAL) + tree = global_lsa_tree; + else + tree = &nbr->area->lsa_tree; + + new = vertex_get(lsa, nbr); + old = RB_INSERT(lsa_tree, tree, new); + if (old != NULL) { + RB_REMOVE(lsa_tree, tree, old); + vertex_free(old); + RB_INSERT(lsa_tree, tree, new); + } + + /* timeout handling either MAX_AGE or LS_REFRESH_TIME */ + timerclear(&tv); + + if (nbr->self) + 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"); +} + +void +lsa_del(struct rde_nbr *nbr, struct lsa_hdr *lsa) +{ + struct lsa_tree *tree; + struct vertex *v; + + v = lsa_find(nbr->area, lsa->type, lsa->ls_id, lsa->adv_rtr); + if (v == NULL) { + log_warnx("lsa_del: LSA no longer in table"); + return; + } + + if (lsa->type == LSA_TYPE_EXTERNAL) + tree = global_lsa_tree; + else + tree = &nbr->area->lsa_tree; + + RB_REMOVE(lsa_tree, tree, v); + vertex_free(v); +} + +void +lsa_age(struct vertex *v) +{ + time_t now; + int d; + u_int16_t age; + + now = time(NULL); + d = now - v->stamp; + 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); + v->stamp = now; +} + +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 = global_lsa_tree; + else + tree = &area->lsa_tree; + + v = RB_FIND(lsa_tree, tree, &key); + if (v) + lsa_age(v); + + return (v); +} + +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) { + 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 = global_lsa_tree; + } while (1); +} + +void +lsa_dump(struct lsa_tree *tree, pid_t pid) +{ + struct vertex *v; + + RB_FOREACH(v, lsa_tree, tree) { + lsa_age(v); + rde_imsg_compose_ospfe(IMSG_CTL_SHOW_DATABASE, 0, pid, + &v->lsa->hdr, ntohs(v->lsa->hdr.len)); + } +} + +void +lsa_timeout(int fd, short event, void *bula) +{ + struct vertex *v = bula; + + lsa_age(v); + + log_debug("lsa_timeout: REFLOOD"); + + if (v->nbr->self) + lsa_refresh(v); + + rde_imsg_compose_ospfe(IMSG_LS_FLOOD, v->nbr->peerid, 0, + v->lsa, ntohs(v->lsa->hdr.len)); +} + +void +lsa_refresh(struct vertex *v) +{ + struct timeval tv; + u_int32_t seqnum; + u_int16_t len; + + /* refresh LSA by increasing sequnce number by one */ + v->lsa->hdr.age = ntohs(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)); + + v->changed = time(NULL); + timerclear(&tv); + tv.tv_sec = LS_REFRESH_TIME; + evtimer_add(&v->ev, &tv); +} |