summaryrefslogtreecommitdiff
path: root/sysdep/bsd/krt-sock.c
diff options
context:
space:
mode:
authorOndrej Filip <feela@network.cz>2004-05-31 13:25:00 +0000
committerOndrej Filip <feela@network.cz>2004-05-31 13:25:00 +0000
commitb1a1fabac70201e9b05aeb9fd6af703f0fbffdb4 (patch)
treec03705e3b77b1fd2d39e2179eea9f85932f3af61 /sysdep/bsd/krt-sock.c
parent781aa475aaa7503d4a86f0d4b8771cd027d30c04 (diff)
*BSD port added. (Tested on FreeBSD and NetBSD)
Diffstat (limited to 'sysdep/bsd/krt-sock.c')
-rw-r--r--sysdep/bsd/krt-sock.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
new file mode 100644
index 00000000..e1d4ff6a
--- /dev/null
+++ b/sysdep/bsd/krt-sock.c
@@ -0,0 +1,703 @@
+/*
+ * BIRD -- Unix Routing Table Syncing
+ *
+ * (c) 2004 Ondrej Filip <feela@network.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#undef LOCAL_DEBUG
+
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "lib/timer.h"
+#include "lib/unix.h"
+#include "lib/krt.h"
+#include "lib/string.h"
+#include "lib/socket.h"
+
+#ifdef IPV6
+#define HOST_MASK 128
+#else
+#define HOST_MASK 32
+#endif
+
+int rt_sock = 0;
+
+#define CHECK_FAMILY(sa) \
+ ((((struct sockaddr *)sa)->sa_family) == BIRD_AF)
+
+struct iface *
+krt_temp_iface_index(struct krt_proto *p, unsigned index)
+{
+ struct iface *i, *j;
+
+ WALK_LIST(i, p->scan.temp_ifs)
+ if (i->index == index)
+ return i;
+ i = mb_allocz(p->p.pool, sizeof(struct iface));
+ if (j = if_find_by_index(index))
+ {
+ strcpy(i->name, j->name);
+ i->addr = j->addr;
+ }
+ else
+ strcpy(i->name, "?");
+ i->index = index;
+ add_tail(&p->scan.temp_ifs, &i->n);
+ return i;
+}
+
+
+int
+krt_capable(rte *e)
+{
+ rta *a = e->attrs;
+
+#ifdef CONFIG_AUTO_ROUTES
+ if (a->source == RTS_DEVICE)
+ return 0;
+#endif
+ return
+ a->cast == RTC_UNICAST &&
+ (a->dest == RTD_ROUTER
+ || a->dest == RTD_DEVICE
+#ifdef RTF_REJECT
+ || a->dest == RTD_UNREACHABLE
+#endif
+#ifdef RTF_BLACKHOLE
+ || a->dest == RTD_BLACKHOLE /* FIXME Prohibited? */
+#endif
+ );
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#define NEXTADDR(w, u) \
+ if (msg.rtm.rtm_addrs & (w)) {\
+ l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\
+ memmove(body, &(u), l); body += l;}
+
+static void
+krt_sock_send(int cmd, rte *e, char *name)
+{
+ net *net = e->net;
+ rta *a = e->attrs;
+ static int msg_seq;
+ struct iface *j, *i = a->iface;
+ int l;
+ struct ks_msg msg;
+ char *body = (char *)msg.buf;
+ sockaddr gate, mask, dst;
+
+ log("KRT sock send: %I/%d via %I", net->n.prefix, net->n.pxlen, a->gw);
+
+ fill_in_sockaddr(&dst, net->n.prefix, 0);
+ fill_in_sockaddr(&mask, ipa_mkmask(net->n.pxlen), 0);
+ fill_in_sockaddr(&gate, a->gw, 0);
+
+ memset (&msg, 0, sizeof (struct rt_msghdr));
+ msg.rtm.rtm_version = RTM_VERSION;
+ msg.rtm.rtm_type = cmd;
+ msg.rtm.rtm_seq = msg_seq++;
+ msg.rtm.rtm_addrs = RTA_DST;
+ msg.rtm.rtm_flags = RTF_UP;
+
+ if (net->n.pxlen == HOST_MASK)
+ {
+ msg.rtm.rtm_flags |= RTF_HOST;
+ }
+ else
+ {
+ msg.rtm.rtm_addrs |= RTA_NETMASK;
+ }
+
+#ifdef RTF_REJECT
+ if(a->dest == RTD_UNREACHABLE)
+ msg.rtm.rtm_flags |= RTF_REJECT;
+#endif
+#ifdef RTF_BLACKHOLE
+ if(a->dest == RTD_BLACKHOLE)
+ msg.rtm.rtm_flags |= RTF_BLACKHOLE;
+#endif
+
+ /* This is really very nasty, but I'm not able
+ * to add "(reject|blackhole)" route without
+ * gateway set
+ */
+ if(!i)
+ {
+ i = HEAD(iface_list);
+
+ WALK_LIST(j, iface_list)
+ {
+ if (j->flags & IF_LOOPBACK)
+ {
+ i = j;
+ break;
+ }
+ }
+ }
+
+ switch (a->dest)
+ {
+ case RTD_ROUTER:
+ msg.rtm.rtm_flags |= RTF_GATEWAY;
+ msg.rtm.rtm_addrs |= RTA_GATEWAY;
+ break;
+#ifdef RTF_REJECT
+ case RTD_UNREACHABLE:
+#endif
+#ifdef RTF_BLACKHOLE
+ case RTD_BLACKHOLE:
+#endif
+ case RTD_DEVICE:
+ if(i)
+ {
+ if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */
+ msg.rtm.rtm_flags |= RTF_CLONING;
+
+ if(!i->addr) {
+ log(L_ERR "KIF: interface \"%s\" has no IP addess", i->name);
+ return;
+ }
+
+ fill_in_sockaddr(&gate, i->addr->ip, 0);
+ msg.rtm.rtm_addrs |= RTA_GATEWAY;
+ }
+ else
+ {
+ bug("krt-sock: interface route %I/%d without interface", net->n.prefix, net->n.pxlen);
+ }
+ break;
+ default:
+ bug("krt-sock: unknown flags, but not filtered");
+ }
+
+ if(i) msg.rtm.rtm_index = i->index;
+
+ NEXTADDR(RTA_DST, dst);
+ NEXTADDR(RTA_GATEWAY, gate);
+ NEXTADDR(RTA_NETMASK, mask);
+
+ l = body - (char *)&msg;
+ msg.rtm.rtm_msglen = l;
+
+ if ((l = write(rt_sock, (char *)&msg, l)) < 0) {
+ log(L_ERR "KIF: error writting route to socket (%I/%d)", net->n.prefix, net->n.pxlen);
+ }
+}
+
+void
+krt_set_notify(struct krt_proto *p, net *net, rte *new, rte *old)
+{
+ if (old)
+ {
+ DBG("krt_remove_route(%I/%d)\n", net->n.prefix, net->n.pxlen);
+ krt_sock_send(RTM_DELETE, old, "RTM_DELETE");
+
+ }
+ if (new)
+ {
+ DBG("krt_add_route(%I/%d)\n", net->n.prefix, net->n.pxlen);
+ krt_sock_send(RTM_ADD, new, "RTM_ADD");
+ }
+}
+
+void
+krt_set_start(struct krt_proto *x, int first)
+{
+ sock *sk_rt;
+ static int ks_open_tried = 0;
+
+ if (ks_open_tried)
+ return;
+
+ ks_open_tried = 1;
+
+ DBG("KRT: Opening kernel socket\n");
+
+ if( (rt_sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0)
+ die("Cannot open kernel socket for routes");
+
+ sk_rt = sk_new(krt_pool);
+ sk_rt->type = SK_MAGIC;
+ sk_rt->rx_hook = krt_set_hook;
+ sk_rt->fd = rt_sock;
+ sk_rt->data = x;
+ if (sk_open(sk_rt))
+ bug("Krt_sock: sk_rt_open failed");
+}
+
+static int
+krt_set_hook(sock *sk, int size)
+{
+ int l;
+ struct ks_msg msg;
+
+ l = read(sk->fd, (char *)&msg, sizeof(msg));
+
+ krt_read_msg((struct proto *)sk->data, &msg, 0);
+ return 0;
+}
+
+void
+krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
+{
+ sockaddr gate, mask, dst;
+ rta a;
+ rte *e;
+ net *net;
+ u32 oif;
+ ip_addr idst, igate, imask;
+ void *body = (char *)msg->buf;
+ int new = (msg->rtm.rtm_type == RTM_ADD);
+ int src;
+ int flags = msg->rtm.rtm_flags;
+ int addrs = msg->rtm.rtm_addrs;
+ int masklen = -1;
+
+ if (!(flags & RTF_UP))
+ {
+ DBG("Down.\n");
+ return;
+ }
+
+ if (flags & RTF_HOST)
+ masklen = HOST_MASK;
+
+ if(!CHECK_FAMILY(body)) return;
+
+ if(msg->rtm.rtm_flags & RTF_LLINFO) return; /* ARPs etc. */
+
+#define GETADDR(p, F) \
+ memset(p, 0, sizeof(*p));\
+ if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\
+ unsigned int l = ROUNDUP(((struct sockaddr *)body)->sa_len);\
+ memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\
+ body += l;}
+
+ GETADDR (&dst, RTA_DST);
+ GETADDR (&gate, RTA_GATEWAY);
+ GETADDR (&mask, RTA_NETMASK);
+
+ idst = IPA_NONE;
+ igate = IPA_NONE;
+ imask = IPA_NONE;
+
+ get_sockaddr(&dst, &idst, NULL, 0);
+ if(CHECK_FAMILY(&gate)) get_sockaddr(&gate, &igate, NULL, 0);
+ get_sockaddr(&mask, &imask, NULL, 0);
+
+ if (masklen < 0) masklen = ipa_mklen(imask);
+
+ if (flags & (RTF_DYNAMIC | RTF_MODIFIED))
+ {
+ log(L_WARN "krt: Ignoring redirect to %I/%d via %I", idst, masklen, igate);
+ return;
+ }
+
+ if (masklen < 0)
+ {
+ log(L_WARN "krt: Got invalid route from kernel!");
+ return;
+ }
+
+ net = net_get(p->p.table, idst, masklen);
+
+ memset(&a, 0, sizeof(a));
+
+ a.proto = &p->p;
+ a.source = RTS_INHERIT;
+ a.scope = SCOPE_UNIVERSE;
+ a.cast = RTC_UNICAST;
+ a.flags = a.aflags = 0;
+ a.from = IPA_NONE;
+ a.gw = IPA_NONE;
+ a.iface = NULL;
+ a.eattrs = NULL;
+
+ a.dest = RTD_NONE;
+
+
+ if (flags & RTF_GATEWAY)
+ {
+ neighbor *ng = neigh_find(&p->p, &igate, 0);
+ if (ng && ng->scope)
+ a.iface = ng->iface;
+ else
+ log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", igate, net->n.prefix, net->n.pxlen);
+
+ a.dest = RTD_ROUTER;
+ a.gw = igate;
+ }
+ else
+ {
+ a.dest = RTD_DEVICE;
+ a.gw = IPA_NONE;
+ a.iface = krt_temp_iface_index(p, msg->rtm.rtm_index);
+ }
+
+#ifdef RTF_REJECT
+ if(flags & RTF_REJECT) {
+ a.dest = RTD_UNREACHABLE;
+ a.gw = IPA_NONE;
+ }
+#endif
+
+#ifdef RTF_BLACKHOLE
+ if(flags & RTF_BLACKHOLE) {
+ a.dest = RTD_BLACKHOLE;
+ a.gw = IPA_NONE;
+ }
+#endif
+
+ if (a.dest == RTD_NONE)
+ {
+ log(L_WARN "Kernel reporting unknown route type to %I/%d", net->n.prefix, net->n.pxlen);
+ return;
+ }
+
+ src = KRT_SRC_UNKNOWN; /* FIXME */
+
+
+
+ e = rte_get_temp(&a);
+ e->net = net;
+ e->u.krt.src = src;
+ //e->u.krt.proto = i->rtm_protocol;
+ //e->u.krt.type = i->rtm_type;
+ e->u.krt.metric = 0;
+
+ if (scan)
+ krt_got_route(p, e);
+ else
+ krt_got_route_async(p, e, new);
+}
+
+void
+krt_read_ifinfo(struct ks_msg *msg)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm;
+ void *body = (void *)(ifm + 1);
+ struct sockaddr_dl *dl = NULL;
+ unsigned int i;
+ struct iface *iface = NULL, f;
+ char *ifname = "(none)";
+ int fl = ifm->ifm_flags;
+
+ for(i = 1; i!=0; i <<= 1)
+ {
+ if((i & ifm->ifm_addrs) && (i == RTA_IFP))
+ {
+ if( i == RTA_IFP)
+ {
+ dl = (struct sockaddr_dl *)body;
+ break;
+ }
+ body += ROUNDUP(((struct sockaddr *)&(body))->sa_len);\
+ }
+ }
+
+ if(dl && (dl->sdl_family != AF_LINK))
+ {
+ log("Ignoring strange IFINFO");
+ return;
+ }
+
+ if(dl) ifname = dl->sdl_data;
+
+ iface = if_find_by_index(ifm->ifm_index);
+
+ if(!iface)
+ {
+ /* New interface */
+ if(!dl) return; /* No interface name, ignoring */
+ DBG("New interface \"%s\" found", ifname);
+ bzero(&f, sizeof(f));
+ f.index = ifm->ifm_index;
+ strncpy(f.name, ifname, sizeof(f.name) -1);
+ }
+ else
+ {
+ memcpy(&f, iface, sizeof(struct iface));
+ }
+
+ f.mtu = ifm->ifm_data.ifi_mtu;
+ f.flags = 0;
+
+ if (fl & IFF_UP)
+ {
+ //f.flags |= IF_UP; /* FIXME */
+ f.flags |= IF_LINK_UP;
+ }
+ if (fl & IFF_LOOPBACK) /* Loopback */
+ f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE;
+ else if (fl & IFF_POINTOPOINT) /* PtP */
+ f.flags |= IF_MULTICAST;
+ else if (fl & IFF_BROADCAST) /* Broadcast */
+ f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST;
+ else
+ f.flags |= IF_MULTIACCESS; /* NBMA */
+
+ if((!iface) || memcmp(&f, iface, sizeof(struct iface)))
+ if_update(&f); /* Just if something happens */
+}
+
+void
+krt_read_addr(struct ks_msg *msg)
+{
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *)&msg->rtm;
+ void *body = (void *)(ifam + 1);
+ sockaddr addr, mask, brd;
+ unsigned int i;
+ struct iface *iface = NULL;
+ struct ifa ifa;
+ struct sockaddr null;
+ ip_addr iaddr, imask, ibrd;
+ int addrs = ifam->ifam_addrs;
+ int scope, masklen = -1;
+ int new = (ifam->ifam_type == RTM_NEWADDR);
+ int fl = ifam->ifam_flags;
+
+ if(!(iface = if_find_by_index(ifam->ifam_index)))
+ {
+ log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index);
+ return;
+ }
+
+ GETADDR (&null, RTA_DST);
+ GETADDR (&null, RTA_GATEWAY);
+ GETADDR (&mask, RTA_NETMASK);
+ GETADDR (&null, RTA_GENMASK);
+ GETADDR (&null, RTA_IFP);
+ GETADDR (&addr, RTA_IFA);
+ GETADDR (&null, RTA_AUTHOR);
+ GETADDR (&brd, RTA_BRD);
+
+ if(!CHECK_FAMILY(&addr)) return; /* Some other family address */
+
+ get_sockaddr(&addr, &iaddr, NULL, 0);
+ get_sockaddr(&mask, &imask, NULL, 0);
+ get_sockaddr(&brd, &ibrd, NULL, 0);
+
+ if ((masklen = ipa_mklen(imask)) < 0)
+ {
+ log("Invalid masklen");
+ return;
+ }
+
+ memset(&ifa, 0, sizeof(ifa));
+
+ ifa.iface = iface;
+
+ memcpy(&ifa.ip, &iaddr, sizeof(ip_addr));
+ ifa.pxlen = masklen;
+ memcpy(&ifa.brd, &ibrd, sizeof(ip_addr));
+
+ scope = ipa_classify(ifa.ip);
+
+ ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen));
+
+ if (scope < 0)
+ {
+ log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, iface->name);
+ return;
+ }
+ ifa.scope = scope & IADDR_SCOPE_MASK;
+
+ if (new)
+ ifa_update(&ifa);
+ else
+ ifa_delete(&ifa);
+}
+
+
+void
+krt_read_msg(struct proto *p, struct ks_msg *msg, int scan)
+{
+ switch (msg->rtm.rtm_type)
+ {
+ case RTM_GET:
+ if(!scan) return;
+ case RTM_ADD:
+ case RTM_DELETE:
+ //log("KRT_ADD/DELETE");
+ krt_read_rt(msg, (struct krt_proto *)p, scan);
+ break;
+ case RTM_IFINFO:
+ //log("KRT_IFINFO");
+ krt_read_ifinfo(msg);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ //log("KRT_NEWADDR/DELADDR");
+ krt_read_addr(msg);
+ break;
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ log("KRT_IFANNOUNCE");
+ //ifan_read (&buf.ian.ifan);
+ break;
+#endif /* RTM_IFANNOUNCE */
+ default:
+ log("Unprocessed RTM_type: %d", msg->rtm.rtm_type);
+ break;
+ }
+}
+
+
+struct iface *
+krt_temp_iface(struct krt_proto *p, char *name)
+{
+ struct iface *i;
+
+ WALK_LIST(i, p->scan.temp_ifs)
+ if (!strcmp(i->name, name))
+ return i;
+ i = mb_allocz(p->p.pool, sizeof(struct iface));
+ strcpy(i->name, name);
+ add_tail(&p->scan.temp_ifs, &i->n);
+ return i;
+}
+
+
+void
+krt_scan_construct(struct krt_config *c)
+{
+}
+
+void
+krt_scan_preconfig(struct config *c)
+{
+}
+
+void
+krt_scan_postconfig(struct krt_config *c)
+{
+}
+
+void
+krt_scan_start(struct krt_proto *x, int first)
+{
+ init_list(&x->scan.temp_ifs);
+}
+
+void
+krt_scan_shutdown(struct krt_proto *x, int last)
+{
+}
+
+void
+krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, int *bl, int cmd)
+{
+ byte *next;
+ int obl, needed, mib[6], on;
+ struct ks_msg *m;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = BIRD_PF;
+ mib[4] = cmd;
+ mib[5] = 0;
+
+ if( sysctl(mib, 6 , NULL , &needed, NULL, 0) < 0)
+ {
+ die("RT scan...");
+ }
+
+ obl = *bl;
+
+ while(needed > *bl) *bl *= 2;
+ while(needed < (*bl/2)) *bl /= 2;
+
+ if( (obl!=*bl) || !*buf)
+ {
+ if(*buf) mb_free(*buf);
+ if( (*buf = mb_alloc(pool, *bl)) == NULL ) die("RT scan buf alloc");
+ }
+
+ on = needed;
+
+ if( sysctl(mib, 6 , *buf, &needed, NULL, 0) < 0)
+ {
+ if(on != needed) return; /* The buffer size changed since last sysctl */
+ die("RT scan 2");
+ }
+
+ for (next = *buf; next < (*buf + needed); next += m->rtm.rtm_msglen)
+ {
+ m = (struct ks_msg *)next;
+ krt_read_msg(p, m, 1);
+ }
+}
+
+void
+krt_scan_fire(struct krt_proto *p)
+{
+ static byte *buf = NULL;
+ static int bl = 32768;
+ krt_sysctl_scan((struct proto *)p , p->krt_pool, &buf, &bl, NET_RT_DUMP);
+}
+
+void
+krt_if_scan(struct kif_proto *p)
+{
+ static byte *buf = NULL;
+ static int bl = 4096;
+ struct proto *P = (struct proto *)p;
+ if_start_update();
+ krt_sysctl_scan(P, P->pool, &buf, &bl, NET_RT_IFLIST);
+ if_end_update();
+}
+
+
+void
+krt_set_construct(struct krt_config *c)
+{
+}
+
+void
+krt_set_shutdown(struct krt_proto *x, int last)
+{
+}
+
+void
+krt_if_io_init(void)
+{
+}
+
+void
+krt_if_construct(struct kif_config *c)
+{
+}
+
+void
+krt_if_start(struct kif_proto *p)
+{
+}
+
+void
+krt_if_shutdown(struct kif_proto *p)
+{
+}
+