diff --git a/nest/Makefile b/nest/Makefile index 0a9d5fe8..aaa0c2a0 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -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 diff --git a/nest/iface.c b/nest/iface.c new file mode 100644 index 00000000..79a7b7d9 --- /dev/null +++ b/nest/iface.c @@ -0,0 +1,177 @@ +/* + * BIRD -- Management of Interfaces + * + * (c) 1998 Martin Mares + * + * 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); +} diff --git a/nest/iface.h b/nest/iface.h index 454c0501..4df98739 100644 --- a/nest/iface.h +++ b/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 diff --git a/sysdep/unix/Modules b/sysdep/unix/Modules index 95b80b38..0b5117a5 100644 --- a/sysdep/unix/Modules +++ b/sysdep/unix/Modules @@ -3,3 +3,4 @@ main.c timer.h io.c unix.h +sync-if.c diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index b6b783b2..293ee63f 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.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"); diff --git a/sysdep/unix/sync-if.c b/sysdep/unix/sync-if.c new file mode 100644 index 00000000..742421f1 --- /dev/null +++ b/sysdep/unix/sync-if.c @@ -0,0 +1,178 @@ +/* + * BIRD -- Unix Interface Scanning and Syncing + * + * (c) 1998 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include +#include +#include + +#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); +} +