mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-08 12:18:42 +00:00
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
@ -1,4 +1,4 @@
|
||||
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
|
||||
|
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"
|
||||
|
||||
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);
|
||||
handle_sigusr(0);
|
||||
|
||||
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);
|
||||
}
|
||||
debug("Entering I/O loop.\n");
|
||||
|
||||
io_loop();
|
||||
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 New Issue
Block a user