Implemented scanning of network interfaces. Mostly very ugly code due to
terrible kernel interface (SIOGIFCONF and friends).
This commit is contained in:
parent
b1487ee909
commit
8a48ecb8b1
6 changed files with 386 additions and 21 deletions
|
@ -1,4 +1,4 @@
|
||||||
THISDIR=nest
|
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
|
include ../Rules
|
||||||
|
|
177
nest/iface.c
Normal file
177
nest/iface.c
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
22
nest/iface.h
22
nest/iface.h
|
@ -11,9 +11,11 @@
|
||||||
|
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
|
|
||||||
|
extern list iface_list;
|
||||||
|
|
||||||
struct iface {
|
struct iface {
|
||||||
node n;
|
node n;
|
||||||
char *name;
|
char name[16];
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
unsigned mtu;
|
unsigned mtu;
|
||||||
struct ifa *ifa; /* First address is primary */
|
struct ifa *ifa; /* First address is primary */
|
||||||
|
@ -26,6 +28,10 @@ struct iface {
|
||||||
#define IF_BROADCAST 8
|
#define IF_BROADCAST 8
|
||||||
#define IF_MULTICAST 16
|
#define IF_MULTICAST 16
|
||||||
#define IF_TUNNEL 32
|
#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 */
|
/* Interface address */
|
||||||
|
|
||||||
|
@ -39,4 +45,18 @@ struct ifa {
|
||||||
struct neighbor *neigh; /* List of neighbors on this interface */
|
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
|
#endif
|
||||||
|
|
|
@ -3,3 +3,4 @@ main.c
|
||||||
timer.h
|
timer.h
|
||||||
io.c
|
io.c
|
||||||
unix.h
|
unix.h
|
||||||
|
sync-if.c
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "lib/socket.h"
|
#include "lib/socket.h"
|
||||||
#include "nest/route.h"
|
#include "nest/route.h"
|
||||||
#include "nest/protocol.h"
|
#include "nest/protocol.h"
|
||||||
|
#include "nest/iface.h"
|
||||||
|
|
||||||
#include "unix.h"
|
#include "unix.h"
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ handle_sigusr(int sig)
|
||||||
|
|
||||||
sk_dump_all();
|
sk_dump_all();
|
||||||
tm_dump_all();
|
tm_dump_all();
|
||||||
|
if_dump_all();
|
||||||
rta_dump_all();
|
rta_dump_all();
|
||||||
rt_dump_all();
|
rt_dump_all();
|
||||||
|
|
||||||
|
@ -83,29 +85,16 @@ main(void)
|
||||||
resource_init();
|
resource_init();
|
||||||
io_init();
|
io_init();
|
||||||
rt_init();
|
rt_init();
|
||||||
|
if_init();
|
||||||
protos_init();
|
protos_init();
|
||||||
|
|
||||||
|
scan_if_init();
|
||||||
|
|
||||||
signal_init();
|
signal_init();
|
||||||
|
|
||||||
{
|
handle_sigusr(0);
|
||||||
sock *s = sk_new(&root_pool);
|
|
||||||
|
|
||||||
if (!s)
|
debug("Entering I/O loop.\n");
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
io_loop();
|
io_loop();
|
||||||
die("I/O loop died");
|
die("I/O loop died");
|
||||||
|
|
178
sysdep/unix/sync-if.c
Normal file
178
sysdep/unix/sync-if.c
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue