Commit 8a48ecb8 authored by Martin Mareš's avatar Martin Mareš

Implemented scanning of network interfaces. Mostly very ugly code due to

terrible kernel interface (SIOGIFCONF and friends).
parent b1487ee9
THISDIR=nest
OBJS=rt-table.o rt-fib.o rt-attr.o proto.o
OBJS=rt-table.o rt-fib.o rt-attr.o proto.o iface.o
include ../Rules
/*
* BIRD -- Management of Interfaces
*
* (c) 1998 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "nest/bird.h"
#include "nest/iface.h"
#include "lib/resource.h"
list iface_list;
static pool *if_pool;
void
if_dump(struct iface *i)
{
struct ifa *a;
debug("IF%d: %s", i->index, i->name);
if (i->flags & IF_ADMIN_DOWN)
debug(" ADMIN-DOWN");
if (i->flags & IF_UP)
debug(" UP");
if (i->flags & IF_MULTIACCESS)
debug(" MA");
if (i->flags & IF_UNNUMBERED)
debug(" UNNUM");
if (i->flags & IF_BROADCAST)
debug(" BC");
if (i->flags & IF_MULTICAST)
debug(" MC");
if (i->flags & IF_TUNNEL)
debug(" TUNL");
if (i->flags & IF_LOOPBACK)
debug(" LOOP");
if (i->flags & IF_IGNORE)
debug(" IGN");
debug(" MTU=%d\n", i->mtu);
for(a=i->ifa; a; a=a->next)
debug("\t%08x, net %08x/%-2d bc %08x -> %08x\n", _I(a->ip), _I(a->prefix), a->pxlen, _I(a->brd), _I(a->opposite));
}
void
if_dump_all(void)
{
struct iface *i;
debug("Known network interfaces:\n\n");
WALK_LIST(i, iface_list)
if_dump(i);
debug("\n");
}
struct if_with_a {
struct iface i;
struct ifa a[0];
};
static struct iface *
if_copy(struct iface *j)
{
int len;
struct if_with_a *w;
struct iface *i;
struct ifa *a, **b, *c;
len = 0;
for(a=j->ifa; a; a=a->next)
len++;
w = mb_alloc(if_pool, sizeof(struct if_with_a) + len*sizeof(struct ifa));
i = &w->i;
c = w->a;
memcpy(i, j, sizeof(struct iface));
b = &i->ifa;
a = j->ifa;
while (a)
{
*b = c;
memcpy(c, a, sizeof(struct ifa));
b = &c->next;
a = a->next;
c++;
}
*b = NULL;
return i;
}
static inline void
if_free(struct iface *i)
{
mb_free(i);
}
static unsigned
if_changed(struct iface *i, struct iface *j)
{
unsigned f = 0;
struct ifa *x, *y;
x = i->ifa;
y = j->ifa;
while (x && y)
{
x = x->next;
y = y->next;
}
if (x || y)
f |= IF_CHANGE_ADDR;
if (i->mtu != j->mtu)
f |= IF_CHANGE_MTU;
if (i->flags != j->flags)
{
f |= IF_CHANGE_FLAGS;
if ((i->flags ^ j->flags) & IF_UP)
if (i->flags & IF_UP)
f |= IF_CHANGE_DOWN;
else
f |= IF_CHANGE_UP;
}
return f;
}
static void
if_notify_change(unsigned c, struct iface *old, struct iface *new)
{
debug("Interface change notification (%x) for %s\n", c, new->name);
}
void
if_update(struct iface *new)
{
struct iface *i;
WALK_LIST(i, iface_list)
if (!strcmp(new->name, i->name))
{
unsigned c = if_changed(i, new);
if (c)
{
struct iface *j = if_copy(new);
if_notify_change(c, i, j);
insert_node(&j->n, &i->n);
rem_node(&i->n);
if_free(i);
}
return;
}
i = if_copy(new);
add_tail(&iface_list, &i->n);
if_notify_change(IF_CHANGE_UP | IF_CHANGE_FLAGS | IF_CHANGE_MTU | IF_CHANGE_ADDR, NULL, i);
}
void
if_end_update(void)
{
struct iface *i, j;
WALK_LIST(i, iface_list)
if (i->flags & IF_UPDATED)
i->flags &= ~IF_UPDATED;
else
{
memcpy(&j, i, sizeof(struct iface));
i->flags = (i->flags & ~IF_UP) | IF_ADMIN_DOWN;
if_notify_change(IF_CHANGE_DOWN | IF_CHANGE_FLAGS, &j, i);
}
}
void
if_init(void)
{
if_pool = rp_new(&root_pool, "Interfaces");
init_list(&iface_list);
}
......@@ -11,9 +11,11 @@
#include "lib/lists.h"
extern list iface_list;
struct iface {
node n;
char *name;
char name[16];
unsigned flags;
unsigned mtu;
struct ifa *ifa; /* First address is primary */
......@@ -26,6 +28,10 @@ struct iface {
#define IF_BROADCAST 8
#define IF_MULTICAST 16
#define IF_TUNNEL 32
#define IF_ADMIN_DOWN 64
#define IF_LOOPBACK 128
#define IF_IGNORE 256
#define IF_UPDATED 0x1000 /* Touched in last scan */
/* Interface address */
......@@ -39,4 +45,18 @@ struct ifa {
struct neighbor *neigh; /* List of neighbors on this interface */
};
/* Interface change events */
#define IF_CHANGE_UP 1
#define IF_CHANGE_DOWN 2
#define IF_CHANGE_FLAGS 4
#define IF_CHANGE_MTU 8
#define IF_CHANGE_ADDR 16
void if_init(void);
void if_dump(struct iface *);
void if_dump_all(void);
void if_update(struct iface *);
void if_end_update(void);
#endif
......@@ -3,3 +3,4 @@ main.c
timer.h
io.c
unix.h
sync-if.c
......@@ -15,6 +15,7 @@
#include "lib/socket.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "unix.h"
......@@ -29,6 +30,7 @@ handle_sigusr(int sig)
sk_dump_all();
tm_dump_all();
if_dump_all();
rta_dump_all();
rt_dump_all();
......@@ -83,29 +85,16 @@ main(void)
resource_init();
io_init();
rt_init();
if_init();
protos_init();
scan_if_init();
signal_init();
{
sock *s = sk_new(&root_pool);
if (!s)
die("no socket");
s->type = SK_UDP_MC;
s->sport = 7899;
s->saddr = _MI(0x3ea80015);
s->daddr = _MI(0xe0000001);
s->dport = 7890;
s->rx_hook = xxx;
s->tx_hook = bla;
s->err_hook = erro;
s->rbsize = 1024;
s->tbsize = 1024;
s->ttl = 1;
if (sk_open(s))
die("open failed");
bla(s);
}
handle_sigusr(0);
debug("Entering I/O loop.\n");
io_loop();
die("I/O loop died");
......
/*
* BIRD -- Unix Interface Scanning and Syncing
*
* (c) 1998 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#define LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/iface.h"
#include "lib/timer.h"
#include "unix.h"
int if_scan_sock;
int if_scan_period = 60;
static timer *if_scan_timer;
static void
scan_ifs(struct ifreq *r, int cnt)
{
struct iface i;
struct ifa a;
char *err;
unsigned fl;
ip_addr netmask;
int l;
for (cnt /= sizeof(struct ifreq); cnt; cnt--, r++)
{
bzero(&i, sizeof(i));
bzero(&a, sizeof(a));
debug("%s\n", r->ifr_ifrn.ifrn_name);
strncpy(i.name, r->ifr_ifrn.ifrn_name, sizeof(i.name) - 1);
i.name[sizeof(i.name) - 1] = 0;
i.ifa = &a;
get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &a.ip, NULL);
l = ipa_classify(a.ip);
if (l < 0 || !(l & IADDR_HOST))
{
log(L_ERR "%s: Invalid interface address", i.name);
goto bad;
}
if ((l & IADDR_SCOPE_MASK) == SCOPE_HOST)
i.flags |= IF_LOOPBACK | IF_IGNORE;
if (ioctl(if_scan_sock, SIOCGIFFLAGS, r) < 0)
{
err = "SIOCGIFFLAGS";
faulty:
log(L_ERR "%s(%s): %m", err, i.name);
bad:
i.flags = (i.flags & ~IF_UP) | IF_ADMIN_DOWN;
continue;
}
fl = r->ifr_flags;
if (fl & IFF_UP)
i.flags |= IF_UP;
if (ioctl(if_scan_sock, SIOCGIFNETMASK, r) < 0)
{ err = "SIOCGIFNETMASK"; goto faulty; }
get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &netmask, NULL);
l = ipa_mklen(netmask);
if (l < 0 || l == 31)
{
log(L_ERR "%s: Invalid netmask", i.name);
goto bad;
}
a.pxlen = l;
if (fl & IFF_POINTOPOINT)
{
i.flags |= IF_UNNUMBERED;
a.pxlen = BITS_PER_IP_ADDRESS;
if (ioctl(if_scan_sock, SIOCGIFDSTADDR, r) < 0)
{ err = "SIOCGIFDSTADDR"; goto faulty; }
get_sockaddr((struct sockaddr_in *) &r->ifr_addr, &a.opposite, NULL);
}
if (fl & IFF_LOOPBACK)
i.flags |= IF_LOOPBACK | IF_IGNORE;
#ifndef CONFIG_ALL_MULTICAST
if (fl & IFF_MULTICAST)
#endif
i.flags |= IF_MULTICAST;
a.prefix = ipa_and(a.ip, ipa_mkmask(a.pxlen));
if (a.pxlen < 32)
{
a.brd = ipa_or(a.prefix, ipa_not(ipa_mkmask(a.pxlen)));
if (ipa_equal(a.ip, a.prefix) || ipa_equal(a.ip, a.brd))
{
log(L_ERR "%s: Using network or broadcast address for interface", i.name);
goto bad;
}
if (fl & IFF_BROADCAST)
i.flags |= IF_BROADCAST;
if (a.pxlen < 30)
i.flags |= IF_MULTIACCESS;
else
a.opposite = ipa_opposite(a.ip);
}
else
a.brd = a.opposite;
if (ioctl(if_scan_sock, SIOCGIFMTU, r) < 0)
{ err = "SIOCGIFMTU"; goto faulty; }
i.mtu = r->ifr_mtu;
#ifdef SIOCGIFINDEX
if (ioctl(if_scan_sock, SIOCGIFINDEX, r) < 0)
{ err = "SIOCGIFINDEX"; goto faulty; }
i.index = r->ifr_ifindex;
#endif
if_update(&i);
}
}
static void
scan_if(timer *t)
{
struct ifconf ic;
static int last_ifbuf_size;
int res;
DBG("Scanning interfaces...\n");
for(;;)
{
if (last_ifbuf_size)
{
struct ifreq *r = alloca(last_ifbuf_size);
ic.ifc_ifcu.ifcu_req = r;
ic.ifc_len = last_ifbuf_size;
res = ioctl(if_scan_sock, SIOCGIFCONF, &ic);
if (res < 0 && errno != EFAULT)
die("SIOCCGIFCONF: %m");
if (res < last_ifbuf_size)
{
scan_ifs(r, ic.ifc_len);
break;
}
}
ic.ifc_ifcu.ifcu_req = NULL;
if (ioctl(if_scan_sock, SIOCGIFCONF, &ic) < 0)
die("SIOCIFCONF: %m");
ic.ifc_len += sizeof(struct ifreq);
if (last_ifbuf_size < ic.ifc_len)
{
last_ifbuf_size = ic.ifc_len;
DBG("Increased ifconf buffer size to %d\n", last_ifbuf_size);
}
}
}
void
scan_if_init(void)
{
if_scan_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
DBG("Using socket %d for interface and route scanning\n", if_scan_sock);
if (if_scan_sock < 0)
die("Cannot create scanning socket: %m");
scan_if(NULL);
if_scan_timer = tm_new(&root_pool);
if_scan_timer->hook = scan_if;
if_scan_timer->recurrent = if_scan_period;
tm_start(if_scan_timer, if_scan_period);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment