bird/sysdep/bsd/krt-sock.c
2010-02-10 14:57:16 +01:00

676 lines
14 KiB
C

/*
* 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)
static 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)
{
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;
DBG("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);
bzero(&msg,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)
{
#ifdef RTF_CLONING
if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */
msg.rtm.rtm_flags |= RTF_CLONING;
#endif
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;
}
break;
default:
bug("krt-sock: unknown flags, but not filtered");
}
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 UNUSED, net *net UNUSED, 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);
}
if (new)
{
DBG("krt_add_route(%I/%d)\n", net->n.prefix, net->n.pxlen);
krt_sock_send(RTM_ADD, new);
}
}
static int
krt_set_hook(sock *sk, int size UNUSED)
{
struct ks_msg msg;
int l = read(sk->fd, (char *)&msg, sizeof(msg));
if(l <= 0)
log(L_ERR "krt-sock: read failed");
else
krt_read_msg((struct proto *)sk->data, &msg, 0);
return 0;
}
void
krt_set_start(struct krt_proto *x, int first UNUSED)
{
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_open failed");
}
static void
krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
{
sockaddr gate, mask, dst;
rta a;
rte *e;
net *net;
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) \
bzero(p, 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);
bzero(&a, 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);
return;
}
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);
}
static 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_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 */
}
static 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;
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);
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;
}
bzero(&ifa, 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:
krt_read_rt(msg, (struct krt_proto *)p, scan);
break;
case RTM_IFINFO:
krt_read_ifinfo(msg);
break;
case RTM_NEWADDR:
case RTM_DELADDR:
krt_read_addr(msg);
break;
#ifdef RTM_IFANNOUNCE
case RTM_IFANNOUNCE: /* FIXME: We should handle it */
break;
#endif /* RTM_IFANNOUNCE */
default:
log(L_ERR "Unprocessed RTM_type: %d", msg->rtm.rtm_type);
break;
}
}
void
krt_scan_construct(struct krt_config *c UNUSED)
{
}
void
krt_scan_preconfig(struct config *c UNUSED)
{
}
void
krt_scan_postconfig(struct krt_config *c UNUSED)
{
}
void
krt_scan_start(struct krt_proto *x, int first UNUSED)
{
init_list(&x->scan.temp_ifs);
}
void
krt_scan_shutdown(struct krt_proto *x UNUSED, int last UNUSED)
{
}
static void
krt_sysctl_scan(struct proto *p, pool *pool, byte **buf, size_t *bl, int cmd)
{
byte *next;
int mib[6], on;
size_t obl, needed;
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 size_t 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 size_t 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 UNUSED)
{
}
void
krt_set_shutdown(struct krt_proto *x UNUSED, int last UNUSED)
{
}
void
krt_if_io_init(void)
{
}
void
krt_if_construct(struct kif_config *c UNUSED)
{
}
void
krt_if_start(struct kif_proto *p UNUSED)
{
}
void
krt_if_shutdown(struct kif_proto *p UNUSED)
{
}