diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-01-28 14:05:41 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-01-28 14:05:41 +0000 |
commit | ccc5f735cc0a287cd117c41e690e7bab855d35df (patch) | |
tree | 73ce6ba377048b4358b66dcd68c700d22eea33eb /usr.sbin/ospfd | |
parent | 0dcb4b3a6285e3cc2e96ba56db5f830a086696f7 (diff) |
Welcome ospfd
started by Esben Norby some time ago by using the imsg/three process framework
of bgpd. He implemented the basic concept plus the ospf finite state machines.
Later I joined and helped him cleanup, debug and extend his work.
Right now it is not particularly useful, major parts are still missing but is
imported to allow more people to work on it.
status:
The basic protocol works for broadcast networks and the LS database is
synchronized and updated. It is not possible to be DR or BDR on a network
and other interface types like point-to-point are not yet supported.
The shortest path tree is not calculated and so no routing information is
exchanged with the kernel FIB.
Not yet connected to the builds.
OK henning@
Diffstat (limited to 'usr.sbin/ospfd')
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); +} |