Poll netlink socket for interface changes on linux systems

This commit is contained in:
Jeremy Lakeman 2015-10-12 16:42:19 +10:30
parent e770998372
commit 3049252c8a
5 changed files with 409 additions and 219 deletions

View File

@ -16,6 +16,7 @@ SQLITE3_INC := $(LOCAL_PATH)/sqlite-amalgamation-3070900
SERVALD_LOCAL_CFLAGS = \
-g \
-Wall -Wno-unused-value -Werror \
-DSERVALD_VERSION="\"Android\"" -DSERVALD_COPYRIGHT="\"Android\"" \
-DINSTANCE_PATH="\"/data/data/org.servalproject/var/serval-node\"" \
-DSHELL -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" \
@ -29,7 +30,7 @@ SERVALD_LOCAL_CFLAGS = \
-DHAVE_JNI_H=1 -DHAVE_STRUCT_UCRED=1 -DHAVE_CRYPTO_SIGN_NACL_GE25519_H=1 \
-DBYTE_ORDER=_BYTE_ORDER -DHAVE_LINUX_STRUCT_UCRED -DUSE_ABSTRACT_NAMESPACE \
-DHAVE_BCOPY -DHAVE_BZERO -DHAVE_NETINET_IN_H -DHAVE_LSEEK64 -DSIZEOF_OFF_T=4 \
-DHAVE_LINUX_IF_H -DHAVE_SYS_STAT_H -DHAVE_SYS_VFS_H \
-DHAVE_LINUX_IF_H -DHAVE_SYS_STAT_H -DHAVE_SYS_VFS_H -DHAVE_LINUX_NETLINK_H -DHAVE_LINUX_RTNETLINK_H \
-DSQLITE_OMIT_DATETIME_FUNCS -DSQLITE_OMIT_COMPILEOPTION_DIAGS -DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_VIRTUALTABLE -DSQLITE_OMIT_AUTHORIZATION \
-I$(NACL_INC) \

62
lsif.c
View File

@ -74,9 +74,8 @@
#ifdef linux
/* for when all other options fail, as can happen on Android,
if the permissions for the socket-based method are broken.
Down side is that it while it gets the interface name and
broadcast, it doesn't get the local address for that
interface.
While it gets the interface name and broadcast, it doesn't
get the local address for that interface.
*/
int scrapeProcNetRoute()
{
@ -97,27 +96,26 @@ int scrapeProcNetRoute()
if (fgets(line,1024,f) == NULL)
return WHYF_perror("fgets(%p,1024,\"/proc/net/route\")", line);
struct socket_address addr, broadcast;
struct socket_address addr, broadcast, netmask;
bzero(&addr, sizeof(addr));
bzero(&broadcast, sizeof(broadcast));
bzero(&netmask, sizeof(netmask));
addr.addrlen = sizeof(addr.inet);
addr.inet.sin_family = AF_INET;
broadcast.addrlen = sizeof(addr.inet);
broadcast.inet.sin_family = AF_INET;
netmask.addrlen = sizeof(netmask.inet);
netmask.inet.sin_family=AF_INET;
while(line[0]) {
int r;
if ((r=sscanf(line,"%s %s %*s %*s %*s %*s %*s %s",name,dest,mask))==3) {
addr.inet.sin_addr.s_addr=strtol(dest,NULL,16);
struct in_addr netmask = {.s_addr=strtol(mask,NULL,16)};
broadcast.inet.sin_addr.s_addr=addr.inet.sin_addr.s_addr | ~netmask.s_addr;
netmask.inet.sin_addr.s_addr=strtol(mask,NULL,16);
broadcast.inet.sin_addr.s_addr=addr.inet.sin_addr.s_addr | ~netmask.inet.sin_addr.s_addr;
struct socket_address netmask_addr;
netmask_addr.addrlen = sizeof(netmask_addr.inet);
netmask_addr.inet.sin_family=AF_INET;
netmask_addr.inet.sin_addr.s_addr=netmask.s_addr;
overlay_interface_register(name,&addr,&netmask_addr,&broadcast);
overlay_interface_register(name,&addr,&netmask,&broadcast);
}
line[0] = '\0';
if (fgets(line,1024,f) == NULL)
@ -141,10 +139,10 @@ lsif(void) {
struct ifconf ifc;
int sck;
struct ifreq *ifr;
struct in_addr netmask;
struct socket_address addr, broadcast;
struct socket_address addr, broadcast, netmask;
bzero(&addr, sizeof(addr));
bzero(&broadcast, sizeof(broadcast));
bzero(&netmask, sizeof(netmask));
DEBUG(overlayinterfaces, "called");
@ -198,16 +196,13 @@ lsif(void) {
WHY_perror("ioctl(SIOCGIFNETMASK)");
continue;
}
netmask = ((struct sockaddr_in *)&ifr->ifr_ifru.ifru_addr)->sin_addr;
broadcast.inet.sin_addr.s_addr=addr.inet.sin_addr.s_addr | ~netmask.s_addr;
netmask.addrlen = sizeof(netmask.inet);
bcopy(&ifr->ifr_ifru.ifru_addr, &netmask.inet, sizeof(netmask.inet));
struct socket_address netmask_addr;
netmask_addr.addrlen = sizeof(netmask_addr.inet);
netmask_addr.inet.sin_family=AF_INET;
netmask_addr.inet.sin_addr.s_addr=netmask.s_addr;
broadcast.inet.sin_addr.s_addr=addr.inet.sin_addr.s_addr | ~netmask.inet.sin_addr.s_addr;
overlay_interface_register(ifr->ifr_name, &addr, &netmask_addr, &broadcast);
overlay_interface_register(ifr->ifr_name, &addr, &netmask, &broadcast);
nInterfaces++;
}
@ -224,21 +219,18 @@ int
doifaddrs(void) {
struct ifaddrs *ifaddr, *ifa;
char *name;
struct socket_address addr, broadcast;
struct in_addr netmask;
struct socket_address addr, broadcast, netmask;
bzero(&addr, sizeof(addr));
bzero(&broadcast, sizeof(broadcast));
bzero(&netmask, sizeof(netmask));
DEBUGF(overlayinterfaces, "called");
if (getifaddrs(&ifaddr) == -1)
return WHY_perror("getifaddr()");
broadcast.addrlen = sizeof(addr.inet);
broadcast.inet.sin_family = AF_INET;
for (ifa = ifaddr; ifa != NULL ; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr || !ifa->ifa_netmask)
if (!ifa->ifa_addr || !ifa->ifa_netmask || !ifa->ifa_broadaddr)
continue;
/* We're only interested in IPv4 addresses */
@ -254,22 +246,16 @@ doifaddrs(void) {
}
name = ifa->ifa_name;
addr.addrlen = sizeof(addr.inet);
bcopy(ifa->ifa_addr, &addr.addr, addr.addrlen);
broadcast.addrlen = netmask.addrlen = addr.addrlen = sizeof(addr.inet);
netmask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
bcopy(ifa->ifa_addr, &addr.inet, addr.addrlen);
bcopy(ifa->ifa_netmask, &netmask.inet, netmask.addrlen);
bcopy(ifa->ifa_broadaddr, &broadcast.inet, broadcast.addrlen);
struct socket_address netmask_addr;
netmask_addr.inet.sin_family=AF_INET;
netmask_addr.inet.sin_addr.s_addr=netmask.s_addr;
broadcast.inet.sin_addr.s_addr=addr.inet.sin_addr.s_addr | ~netmask.s_addr;
overlay_interface_register(name, &addr, &netmask_addr, &broadcast);
overlay_interface_register(name, &addr, &netmask, &broadcast);
}
freeifaddrs(ifaddr);
return 0;
}
#endif

View File

@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <assert.h>
#include <time.h>
#include <fnmatch.h>
#include "serval.h"
#include "conf.h"
#include "net.h"
@ -40,9 +41,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "server.h"
#include "route_link.h"
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
// The size of the receive buffer. This effectively sets the MRU for packet radio interfaces where
// we have to buffer packets on the receive side.
@ -59,6 +58,7 @@ struct socket_address sock_any_addr;
struct profile_total sock_any_stats;
static void overlay_interface_poll(struct sched_ent *alarm);
static int inet_up_count=0;
void overlay_interface_close(overlay_interface *interface)
{
@ -67,6 +67,17 @@ void overlay_interface_close(overlay_interface *interface)
unlink(interface->address.local.sun_path);
if (is_watching(&interface->alarm))
unwatch(&interface->alarm);
if (interface->address.addr.sa_family == AF_INET &&
inet_up_count>0 &&
--inet_up_count==0 &&
sock_any.poll.fd>0){
unwatch(&sock_any);
close(sock_any.poll.fd);
sock_any.poll.fd=-1;
}
close(interface->alarm.poll.fd);
interface->alarm.poll.fd=-1;
}
@ -293,6 +304,21 @@ overlay_interface * overlay_interface_find_name_addr(const char *name, struct so
return NULL;
}
// find an interface by name and socket type
overlay_interface * overlay_interface_find_name_type(const char *name, int socket_type){
int i;
for(i = 0; i < OVERLAY_MAX_INTERFACES; i++){
if (overlay_interfaces[i].state==INTERFACE_STATE_DOWN)
continue;
if (strcasecmp(overlay_interfaces[i].name, name)==0 &&
overlay_interfaces[i].ifconfig.socket_type == socket_type)
return &overlay_interfaces[i];
}
return NULL;
}
static int interface_type_priority(int type)
{
switch(type){
@ -365,8 +391,20 @@ overlay_interface_read_any(struct sched_ent *alarm)
}
}
// bind a socket to INADDR_ANY:port
// for now, we don't have a graceful close for this interface but it should go away when the process dies
/*
bind a socket to INADDR_ANY:port
On linux you can bind to the broadcast address to receive broadcast packets per interface [or subnet],
but then you can't receive unicast packets on the same socket.
On osx, you can only receive broadcast packets if you bind to INADDR_ANY.
So the most portable way to do this is to bind to each interface's IP address for sending broadcasts
and receiving unicasts, and bind a separate socket to INADDR_ANY just for receiving broadcast packets.
Sending packets from INADDR_ANY would probably work, but gives us less control over which interfaces are sending packets.
But there may be some platforms that need some other combination for everything to work.
*/
static int overlay_interface_init_any(int port)
{
if (sock_any.poll.fd>0){
@ -423,20 +461,6 @@ static void calc_next_tick(struct overlay_interface *interface)
static int
overlay_interface_init_socket(overlay_interface *interface)
{
/*
On linux you can bind to the broadcast address to receive broadcast packets per interface [or subnet],
but then you can't receive unicast packets on the same socket.
On osx, you can only receive broadcast packets if you bind to INADDR_ANY.
So the most portable way to do this is to bind to each interface's IP address for sending broadcasts
and receiving unicasts, and bind a separate socket to INADDR_ANY just for receiving broadcast packets.
Sending packets from INADDR_ANY would probably work, but gives us less control over which interfaces are sending packets.
But there may be some platforms that need some other combination for everything to work.
*/
overlay_interface_init_any(interface->ifconfig.port);
interface->alarm.poll.fd = overlay_bind_socket(&interface->address);
@ -531,9 +555,10 @@ int overlay_interface_configure(struct overlay_interface *interface, const struc
* Returns -1 in case of error (misconfiguration or system error).
*/
int
overlay_interface_init(const char *name, struct socket_address *addr,
struct socket_address *netmask,
struct socket_address *broadcast,
overlay_interface_init(const char *name,
const struct socket_address *addr,
const struct socket_address *netmask,
const struct socket_address *broadcast,
const struct config_network_interface *ifconfig)
{
int cleanup_ret = -1;
@ -578,10 +603,7 @@ overlay_interface_init(const char *name, struct socket_address *addr,
case SOCK_DGRAM:
if (ifconfig->broadcast.drop || ifconfig->unicast.drop || ifconfig->drop_packets)
FATALF("Invalid interface definition. We only support dropping packets on dummy file interfaces");
if (netmask)
interface->netmask = netmask->inet.sin_addr;
else
interface->netmask = ifconfig->dummy_netmask;
interface->local_echo = 1;
if (overlay_interface_init_socket(interface))
@ -1056,30 +1078,52 @@ int overlay_broadcast_ensemble(struct network_destination *destination, struct o
}
}
/* Register the real interface, or update the existing interface registration. */
int
overlay_interface_register(char *name,
struct socket_address *addr,
struct socket_address *netmask,
struct socket_address *broadcast)
static const struct config_network_interface *find_interface_config(const char *name, int socket_type)
{
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = NULL;
// Find a matching non-dummy interface rule.
unsigned i;
for (i = 0; i < config.interfaces.ac; ++i, ifconfig = NULL) {
ifconfig = &config.interfaces.av[i].value;
if (ifconfig->socket_type==SOCK_DGRAM) {
for (i = 0; i < config.interfaces.ac; ++i) {
const struct config_network_interface *ifconfig = &config.interfaces.av[i].value;
if (ifconfig->socket_type==socket_type) {
unsigned j;
for (j = 0; j < ifconfig->match.patc; ++j){
if (fnmatch(ifconfig->match.patv[j], name, 0) == 0)
break;
return ifconfig;
}
}
}
return NULL;
}
if (j < ifconfig->match.patc)
break;
static int interface_unregister(const char *name,
struct socket_address *addr
)
{
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = find_interface_config(name, SOCK_DGRAM);
if (!ifconfig)
return 0;
if (addr->addr.sa_family==AF_INET)
addr->inet.sin_port = htons(ifconfig->port);
struct overlay_interface *interface = overlay_interface_find_name_addr(name, addr);
if (interface)
overlay_interface_close(interface);
return 0;
}
}
if (ifconfig == NULL) {
/* Register the real interface, or update the existing interface registration. */
int
overlay_interface_register(const char *name,
struct socket_address *addr,
const struct socket_address *netmask,
struct socket_address *broadcast)
{
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = find_interface_config(name, SOCK_DGRAM);
if (!ifconfig) {
DEBUGF(overlayinterfaces, "Interface %s does not match any rule", name);
return 0;
}
@ -1093,119 +1137,187 @@ overlay_interface_register(char *name,
if (broadcast->addr.sa_family==AF_INET)
broadcast->inet.sin_port = htons(ifconfig->port);
// note, inet_ntop doesn't seem to behave on android
DEBUGF(overlayinterfaces, "%s address: %s", name, alloca_socket_address(addr));
DEBUGF(overlayinterfaces, "%s netmask: %s", name, alloca_socket_address(netmask));
DEBUGF(overlayinterfaces, "%s broadcast address: %s", name, alloca_socket_address(broadcast));
struct overlay_interface *interface = overlay_interface_find_name_addr(name, addr);
if (interface){
// mark this interface as still alive
if (interface->state == INTERFACE_STATE_DETECTING)
interface->state = INTERFACE_STATE_UP;
// nothing to do if a matching interface is already up
if (interface)
return 0;
}
/* New interface, so register it */
if (overlay_interface_init(name, addr, netmask, broadcast, ifconfig))
return WHYF("Could not initialise newly seen interface %s", name);
DEBUGF(overlayinterfaces, "Registered interface %s", name);
overlay_interface_init_any(ifconfig->port);
inet_up_count++;
return 0;
}
#ifdef HAVE_LINUX_NETLINK_H
DEFINE_ALARM(netlink_poll);
void netlink_poll(struct sched_ent *alarm)
{
uint8_t buff[4096];
ssize_t len = recv(alarm->poll.fd, buff, sizeof buff, 0);
if (len<=0)
return;
DEBUGF(overlayinterfaces, "recv(%d) len %u", alarm->poll.fd, len);
struct nlmsghdr *nlh = (struct nlmsghdr *)buff;
for (nlh = (struct nlmsghdr *)buff; (NLMSG_OK (nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)){
switch(nlh->nlmsg_type){
case RTM_NEWADDR:
case RTM_DELADDR:
{
struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA (nlh);
// ignore loopback addresses
if (ifa->ifa_scope == RT_SCOPE_HOST)
continue;
struct rtattr *rth = IFA_RTA (ifa);
int rtl = IFA_PAYLOAD (nlh);
// ifa->ifa_family;
// ifa->ifa_prefixlen;
const char *name=NULL;
struct socket_address addr, broadcast, netmask_addr;
bzero(&addr, sizeof(addr));
bzero(&broadcast, sizeof(broadcast));
bzero(&netmask_addr, sizeof(netmask_addr));
addr.addr.sa_family = broadcast.addr.sa_family = netmask_addr.addr.sa_family = ifa->ifa_family;
if (ifa->ifa_family == AF_INET){
addr.addrlen = broadcast.addrlen = netmask_addr.addrlen = sizeof(addr.inet);
}else{
DEBUGF(overlayinterfaces, "Ignoring family %d", ifa->ifa_family);
continue;
}
for (;rtl && RTA_OK (rth, rtl); rth = RTA_NEXT (rth,rtl)){
void *data = RTA_DATA(rth);
switch(rth->rta_type){
case IFA_LOCAL:
addr.inet.sin_addr.s_addr = *((uint32_t *)data);
break;
case IFA_LABEL:
name = RTA_DATA(rth);
break;
case IFA_BROADCAST:
broadcast.inet.sin_addr.s_addr = *((uint32_t *)data);
break;
}
}
if (!name){
WARNF_perror("Interface name not provided by IFA_LABEL");
continue;
}
{
//calculate netmask
unsigned prefix = ifa->ifa_prefixlen;
if (prefix>32)
prefix=32;
char *c = (char *)&netmask_addr.inet.sin_addr.s_addr;
unsigned i;
for (i=0;i<(prefix/8);i++)
*c++ = 0xFF;
if (prefix %8)
*c = 0xFF << (8 - (prefix %8));
}
if (nlh->nlmsg_type==RTM_NEWADDR){
DEBUGF(overlayinterfaces, "New addr %s, %s, %s, %s",
name,
alloca_socket_address(&addr),
alloca_socket_address(&broadcast),
alloca_socket_address(&netmask_addr)
);
overlay_interface_register(name, &addr, &netmask_addr, &broadcast);
}else if (nlh->nlmsg_type==RTM_DELADDR){
DEBUGF(overlayinterfaces, "Del addr %s, %s",
name,
alloca_socket_address(&addr)
);
interface_unregister(name, &addr);
}
break;
}
}
}
}
static int netlink_socket()
{
int sock = esocket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (sock<0)
return -1;
struct sockaddr_nl addr;
memset (&addr,0,sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
return WHYF_perror("bind(%d,AF_NETLINK,%lu)", sock, (unsigned long)sizeof(addr));
DEBUGF(overlayinterfaces, "bind(%d,AF_NETLINK,%lu)", sock, (unsigned long)sizeof(addr));
return sock;
}
static int netlink_send_get(int fd)
{
struct {
struct nlmsghdr n;
struct ifaddrmsg r;
} req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
req.n.nlmsg_type = RTM_GETADDR;
req.r.ifa_family = AF_INET;
struct rtattr *rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
rta->rta_len = RTA_LENGTH(4);
if (send(fd, &req, req.n.nlmsg_len, 0)<0)
return WHYF_perror("send(%d)", fd);
DEBUG(overlayinterfaces, "Sent RTM_GETADDR");
return 0;
}
// send a request to the kernel to get all interface addresses now
// eg on config change
static int netlink_init()
{
struct sched_ent *alarm=&ALARM_STRUCT(netlink_poll);
if (!is_watching(alarm)){
alarm->poll.fd = netlink_socket();
if (alarm->poll.fd<0)
return -1;
alarm->poll.events = POLLIN;
watch(alarm);
}
return netlink_send_get(alarm->poll.fd);
}
#else
// poll the OS's network interfaces
DEFINE_ALARM(overlay_interface_discover);
void overlay_interface_discover(struct sched_ent *alarm)
{
/* Mark all UP interfaces as DETECTING, so we can tell which interfaces are new, and which are dead */
unsigned i;
for (i = 0; i < OVERLAY_MAX_INTERFACES; i++)
if (overlay_interfaces[i].state==INTERFACE_STATE_UP)
overlay_interfaces[i].state=INTERFACE_STATE_DETECTING;
/* Register new file interfaces */
int detect_real_interfaces = 0;
const struct config_network_interface *ifconfig = NULL;
for (i = 0; i < config.interfaces.ac; ++i, ifconfig = NULL) {
ifconfig = &config.interfaces.av[i].value;
if (ifconfig->exclude)
continue;
if (!*ifconfig->file) {
detect_real_interfaces = 1;
continue;
}
unsigned j;
for (j = 0; j < OVERLAY_MAX_INTERFACES; j++){
if (overlay_interfaces[j].ifconfig.socket_type == ifconfig->socket_type &&
strcasecmp(overlay_interfaces[j].name, ifconfig->file) == 0 &&
overlay_interfaces[j].state==INTERFACE_STATE_DETECTING){
overlay_interfaces[j].state=INTERFACE_STATE_UP;
break;
}
}
if (j >= OVERLAY_MAX_INTERFACES) {
// New file interface, so register it.
struct socket_address addr, netmask, broadcast;
bzero(&addr, sizeof addr);
bzero(&netmask, sizeof addr);
bzero(&broadcast, sizeof broadcast);
switch(ifconfig->socket_type){
case SOCK_FILE:
// use a fake inet address
addr.addrlen=sizeof addr.inet;
addr.inet.sin_family=AF_INET;
addr.inet.sin_port=htons(ifconfig->port);
addr.inet.sin_addr=ifconfig->dummy_address;
netmask.addrlen=sizeof addr.inet;
netmask.inet.sin_family=AF_INET;
netmask.inet.sin_port=htons(ifconfig->port);
netmask.inet.sin_addr.s_addr=ifconfig->dummy_netmask.s_addr;
broadcast.addrlen=sizeof addr.inet;
broadcast.inet.sin_family=AF_INET;
broadcast.inet.sin_port=htons(ifconfig->port);
broadcast.inet.sin_addr.s_addr=ifconfig->dummy_address.s_addr | ~ifconfig->dummy_netmask.s_addr;
// Fallthrough
case SOCK_STREAM:
overlay_interface_init(ifconfig->file, &addr, NULL, &broadcast, ifconfig);
break;
case SOCK_EXT:
// FAIL?
break;
case SOCK_DGRAM:
{
// use a local dgram socket
// no abstract sockets for now
if (!FORMF_SERVAL_RUN_PATH(addr.local.sun_path, "%s/%s", config.server.interface_path, ifconfig->file)) {
// TODO set ifconfig->exclude to prevent spam??
break;
}
unlink(addr.local.sun_path);
addr.local.sun_family=AF_UNIX;
size_t len = strlen(addr.local.sun_path);
addr.addrlen=sizeof addr.local.sun_family + len + 1;
broadcast = addr;
while(len && broadcast.local.sun_path[len]!='/')
broadcast.local.sun_path[len--]='\0';
broadcast.addrlen = sizeof addr.local.sun_family + len + 2;
INFOF("Attempting to bind local socket w. addr %s, broadcast %s",
alloca_socket_address(&addr), alloca_socket_address(&broadcast));
overlay_interface_init(ifconfig->file, &addr, &netmask, &broadcast, ifconfig);
break;
}
}
}
}
// Register new real interfaces
if (detect_real_interfaces) {
int no_route = 1;
#ifdef HAVE_IFADDRS_H
if (no_route != 0)
@ -1222,31 +1334,124 @@ void overlay_interface_discover(struct sched_ent *alarm)
if (no_route != 0) {
FATAL("Unable to get any interface information");
}
}
// Close any interfaces that have gone away.
unsigned inet_up_count=0;
for(i = 0; i < OVERLAY_MAX_INTERFACES; i++){
if (overlay_interfaces[i].state==INTERFACE_STATE_DETECTING)
overlay_interface_close(&overlay_interfaces[i]);
if (overlay_interfaces[i].state==INTERFACE_STATE_UP &&
overlay_interfaces[i].address.addr.sa_family == AF_INET)
inet_up_count++;
}
if (inet_up_count==0 && sock_any.poll.fd>0){
unwatch(&sock_any);
close(sock_any.poll.fd);
sock_any.poll.fd=-1;
}
// if there are no real interfaces to detect, we can wait for the config file to change
if (detect_real_interfaces){
alarm->alarm = gettime_ms()+5000;
alarm->deadline = alarm->alarm + 10000;
schedule(alarm);
}
#endif
static void file_interface_init(const struct config_network_interface *ifconfig)
{
struct socket_address addr, netmask, broadcast;
bzero(&addr, sizeof addr);
bzero(&netmask, sizeof addr);
bzero(&broadcast, sizeof broadcast);
switch(ifconfig->socket_type){
case SOCK_FILE:
// use a fake inet address
addr.addrlen=sizeof addr.inet;
addr.inet.sin_family=AF_INET;
addr.inet.sin_port=htons(ifconfig->port);
addr.inet.sin_addr=ifconfig->dummy_address;
netmask.addrlen=sizeof addr.inet;
netmask.inet.sin_family=AF_INET;
netmask.inet.sin_port=htons(ifconfig->port);
netmask.inet.sin_addr=ifconfig->dummy_netmask;
broadcast.addrlen=sizeof addr.inet;
broadcast.inet.sin_family=AF_INET;
broadcast.inet.sin_port=htons(ifconfig->port);
broadcast.inet.sin_addr.s_addr=ifconfig->dummy_address.s_addr | ~ifconfig->dummy_netmask.s_addr;
break;
case SOCK_STREAM:
break;
case SOCK_DGRAM:
{
// use a local dgram socket
// no abstract sockets for now
if (!FORMF_SERVAL_RUN_PATH(addr.local.sun_path, "%s/%s", config.server.interface_path, ifconfig->file))
return;
unlink(addr.local.sun_path);
addr.local.sun_family=AF_UNIX;
size_t len = strlen(addr.local.sun_path);
addr.addrlen=sizeof addr.local.sun_family + len + 1;
broadcast = addr;
while(len && broadcast.local.sun_path[len]!='/')
broadcast.local.sun_path[len--]='\0';
broadcast.addrlen = sizeof addr.local.sun_family + len + 2;
break;
}
default:
// ignore
return;
}
overlay_interface_init(ifconfig->file, &addr, &netmask, &broadcast, ifconfig);
}
void overlay_interface_config_change()
{
unsigned i;
int real_interface = 0;
// bring down any interface that no longer matches configuration
for (i = 0; i < OVERLAY_MAX_INTERFACES; i++){
if (overlay_interfaces[i].state!=INTERFACE_STATE_UP ||
overlay_interfaces[i].ifconfig.socket_type == SOCK_EXT)
continue;
const struct config_network_interface *ifconfig = find_interface_config(
overlay_interfaces[i].name,
overlay_interfaces[i].ifconfig.socket_type
);
if (!ifconfig || ifconfig->exclude){
overlay_interface_close(&overlay_interfaces[i]);
continue;
}
}
// create dummy file or AF_UNIX interfaces
for (i = 0; i < config.interfaces.ac; ++i) {
const struct config_network_interface *ifconfig = &config.interfaces.av[i].value;
if (ifconfig->exclude)
continue;
// ignore real interfaces, we'll deal with them later
if (!*ifconfig->file) {
real_interface = 1;
continue;
}
overlay_interface *interface = overlay_interface_find_name_type(ifconfig->file, ifconfig->socket_type);
// ignore interfaces that are already up
if (interface)
continue;
// New file interface, so register it.
file_interface_init(ifconfig);
}
if (real_interface){
#ifdef HAVE_LINUX_NETLINK_H
// start listening for network changes & request current interface addresses
netlink_init();
#else
// re-check all interfaces periodically
time_ms_t now = gettime_ms();
RESCHEDULE(&ALARM_STRUCT(overlay_interface_discover), now, now, now);
#endif
}
}
void logServalPacket(int level, struct __sourceloc __whence, const char *message, const unsigned char *packet, size_t len) {

View File

@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define INTERFACE_STATE_DOWN 0
#define INTERFACE_STATE_UP 1
#define INTERFACE_STATE_DETECTING 2
struct overlay_interface;
@ -107,8 +106,7 @@ struct network_destination * create_unicast_destination(struct socket_address *a
struct network_destination * add_destination_ref(struct network_destination *ref);
void release_destination_ref(struct network_destination *ref);
int set_destination_ref(struct network_destination **ptr, struct network_destination *ref);
DECLARE_ALARM(overlay_interface_discover);
void overlay_interface_config_change();
struct config_mdp_iftype;
int overlay_destination_configure(struct network_destination *dest, const struct config_mdp_iftype *ifconfig);
@ -117,15 +115,16 @@ struct config_network_interface;
int overlay_interface_configure(struct overlay_interface *interface, const struct config_network_interface *ifconfig);
int
overlay_interface_init(const char *name, struct socket_address *addr,
struct socket_address *netmask,
struct socket_address *broadcast,
overlay_interface_init(const char *name,
const struct socket_address *addr,
const struct socket_address *netmask,
const struct socket_address *broadcast,
const struct config_network_interface *ifconfig);
void overlay_interface_close(overlay_interface *interface);
int overlay_interface_register(char *name,
int overlay_interface_register(const char *name,
struct socket_address *addr,
struct socket_address *netmask,
const struct socket_address *netmask,
struct socket_address *broadcast);
void overlay_interface_close_all();
overlay_interface * overlay_interface_get_default();

View File

@ -564,8 +564,7 @@ void cf_on_config_change()
dna_helper_start();
directory_service_init();
// check for interfaces at least once after config change
RESCHEDULE(&ALARM_STRUCT(overlay_interface_discover), now, now, now);
overlay_interface_config_change();
if (link_has_neighbours())
// send rhizome sync periodically