summaryrefslogtreecommitdiff
path: root/usr.sbin/ospfd/kroute.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2005-01-28 14:05:41 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2005-01-28 14:05:41 +0000
commitccc5f735cc0a287cd117c41e690e7bab855d35df (patch)
tree73ce6ba377048b4358b66dcd68c700d22eea33eb /usr.sbin/ospfd/kroute.c
parent0dcb4b3a6285e3cc2e96ba56db5f830a086696f7 (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/kroute.c')
-rw-r--r--usr.sbin/ospfd/kroute.c1270
1 files changed, 1270 insertions, 0 deletions
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);
+}