Commit fa4c481e authored by Julien Cristau's avatar Julien Cristau

Listen to address and link change events

On linux, listen to address and link change events using the netlink socket,
and react to them by dumping all local addresses or all local interfaces,
respectively, to keep babel's view up to date.
This breaks MacOS, as kernel_socket.c needs to grow a kernel_addresses()
function.
parent 3b4944b2
...@@ -81,10 +81,12 @@ unsigned char protocol_group[16]; ...@@ -81,10 +81,12 @@ unsigned char protocol_group[16];
int protocol_socket = -1; int protocol_socket = -1;
int kernel_socket = -1; int kernel_socket = -1;
static int kernel_routes_changed = 0; static int kernel_routes_changed = 0;
static int kernel_link_changed = 0;
static int kernel_addr_changed = 0;
static volatile sig_atomic_t exiting = 0, dumping = 0, changed = 0; static volatile sig_atomic_t exiting = 0, dumping = 0, changed = 0;
static int kernel_routes_callback(void *closure); static int kernel_routes_callback(int changed, void *closure);
static void init_signals(void); static void init_signals(void);
static void dump_tables(FILE *out); static void dump_tables(FILE *out);
...@@ -352,6 +354,8 @@ main(int argc, char **argv) ...@@ -352,6 +354,8 @@ main(int argc, char **argv)
} }
check_xroutes(); check_xroutes();
kernel_routes_changed = 0; kernel_routes_changed = 0;
kernel_link_changed = 0;
kernel_addr_changed = 0;
kernel_dump_time = now.tv_sec + 20 + random() % 20; kernel_dump_time = now.tv_sec + 20 + random() % 20;
timeval_plus_msec(&check_neighbours_time, &now, 5000 + random() % 5000); timeval_plus_msec(&check_neighbours_time, &now, 5000 + random() % 5000);
expiry_time = now.tv_sec + 20 + random() % 20; expiry_time = now.tv_sec + 20 + random() % 20;
...@@ -453,6 +457,16 @@ main(int argc, char **argv) ...@@ -453,6 +457,16 @@ main(int argc, char **argv)
changed = 0; changed = 0;
} }
if (kernel_link_changed) {
check_networks();
kernel_link_changed = 0;
}
if (kernel_addr_changed) {
check_addresses();
kernel_addr_changed = 0;
}
if(kernel_routes_changed || now.tv_sec >= kernel_dump_time) { if(kernel_routes_changed || now.tv_sec >= kernel_dump_time) {
rc = check_xroutes(); rc = check_xroutes();
if(rc > 0) if(rc > 0)
...@@ -744,8 +758,13 @@ dump_tables(FILE *out) ...@@ -744,8 +758,13 @@ dump_tables(FILE *out)
} }
static int static int
kernel_routes_callback(void *closure) kernel_routes_callback(int changed, void *closure)
{ {
kernel_routes_changed = 1; if (changed & CHANGE_LINK)
kernel_link_changed = 1;
if (changed & CHANGE_ADDR)
kernel_addr_changed = 1;
if (changed & CHANGE_ROUTE)
kernel_routes_changed = 1;
return 1; return 1;
} }
...@@ -20,8 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ...@@ -20,8 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include <netinet/in.h>
#define KERNEL_INFINITY 0xFFFF #define KERNEL_INFINITY 0xFFFF
#define RTPROTO_BABEL_LOCAL 257
struct kernel_route { struct kernel_route {
unsigned char prefix[16]; unsigned char prefix[16];
int plen; int plen;
...@@ -35,6 +39,10 @@ struct kernel_route { ...@@ -35,6 +39,10 @@ struct kernel_route {
#define ROUTE_ADD 1 #define ROUTE_ADD 1
#define ROUTE_MODIFY 2 #define ROUTE_MODIFY 2
#define CHANGE_LINK (1 << 0)
#define CHANGE_ROUTE (1 << 1)
#define CHANGE_ADDR (1 << 2)
extern int export_table, import_table; extern int export_table, import_table;
int kernel_setup(int setup); int kernel_setup(int setup);
...@@ -50,4 +58,5 @@ int kernel_route(int operation, const unsigned char *dest, unsigned short plen, ...@@ -50,4 +58,5 @@ int kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *newgate, int newifindex, const unsigned char *newgate, int newifindex,
unsigned int newmetric); unsigned int newmetric);
int kernel_routes(struct kernel_route *routes, int maxroutes); int kernel_routes(struct kernel_route *routes, int maxroutes);
int kernel_callback(int (*fn)(void*), void *closure); int kernel_callback(int (*fn)(int, void*), void *closure);
int kernel_addresses(int ifindex, struct in6_addr *addresses, int maxaddr);
...@@ -47,6 +47,7 @@ THE SOFTWARE. ...@@ -47,6 +47,7 @@ THE SOFTWARE.
#include "babel.h" #include "babel.h"
#include "kernel.h" #include "kernel.h"
#include "util.h" #include "util.h"
#include "network.h"
int export_table = -1, import_table = -1; int export_table = -1, import_table = -1;
...@@ -518,9 +519,11 @@ kernel_setup_socket(int setup) ...@@ -518,9 +519,11 @@ kernel_setup_socket(int setup)
int rc; int rc;
if(setup) { if(setup) {
rc = netlink_socket(&nl_listen, RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE); rc = netlink_socket(&nl_listen,
RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE
| RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR);
if(rc < 0) { if(rc < 0) {
perror("netlink_socket(RTMGRP_ROUTE)"); perror("netlink_socket(RTMGRP_ROUTE | RTMGRP_LINK | RTMGRP_IFADDR)");
kernel_socket = -1; kernel_socket = -1;
return -1; return -1;
} }
...@@ -963,10 +966,225 @@ kernel_routes(struct kernel_route *routes, int maxroutes) ...@@ -963,10 +966,225 @@ kernel_routes(struct kernel_route *routes, int maxroutes)
return found; return found;
} }
char *
parse_ifname_rta(struct ifinfomsg *info, int len)
{
struct rtattr *rta = IFLA_RTA(info);
char *ifname = NULL;
len -= NLMSG_ALIGN(sizeof(*info));
while(RTA_OK(rta, len)) {
switch (rta->rta_type) {
case IFLA_IFNAME:
ifname = RTA_DATA(rta);
break;
default:
break;
}
rta = RTA_NEXT(rta, len);
}
return ifname;
}
int
parse_addr_rta(struct ifaddrmsg *addr, int len, struct in6_addr *res)
{
struct rtattr *rta;
len -= NLMSG_ALIGN(sizeof(*addr));
rta = IFA_RTA(addr);
while (RTA_OK(rta, len)) {
switch(rta->rta_type) {
case IFA_LOCAL:
case IFA_ADDRESS:
switch (addr->ifa_family) {
case AF_INET:
if (res)
v4tov6(res->s6_addr, RTA_DATA(rta));
break;
case AF_INET6:
if (res)
memcpy(res->s6_addr, RTA_DATA(rta), 16);
break;
default:
kdebugf("ifaddr: unexpected address family %d\n", addr->ifa_family);
return -1;
break;
}
break;
default:
break;
}
rta = RTA_NEXT(rta, len);
}
return 0;
}
int
filter_link(struct nlmsghdr *nh, void *data)
{
struct ifinfomsg *info;
int len;
int ifindex;
char *ifname;
unsigned int ifflags;
int i;
len = nh->nlmsg_len;
if(nh->nlmsg_type != RTM_NEWLINK && nh->nlmsg_type != RTM_DELLINK)
return 0;
info = (struct ifinfomsg*)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
ifindex = info->ifi_index;
ifflags = info->ifi_flags;
ifname = parse_ifname_rta(info, len);
kdebugf("filter_interfaces: link change on if %s(%d): %s\n",
ifname, ifindex, parse_ifflags(ifflags));
for (i = 0; i < numnets; i++) {
if (!strcmp(nets[i].ifname, ifname)) {
return 1;
}
}
return 0;
}
int
filter_addresses(struct nlmsghdr *nh, void *data)
{
int rc;
int maxaddr = 0;
struct in6_addr *addrs = NULL;
struct in6_addr addr;
struct in6_addr *current_addr;
int *found = NULL;
int len;
struct ifaddrmsg *ifa;
int index;
char ifname[IFNAMSIZ];
if (data) {
void **args = (void **)data;
maxaddr = *(int *)args[0];
addrs = (struct in6_addr *)args[1];
found = (int *)args[2];
index = (int)args[3];
}
len = nh->nlmsg_len;
if (data && *found >= maxaddr)
return 0;
if (nh->nlmsg_type != RTM_NEWADDR &&
(data || nh->nlmsg_type != RTM_DELADDR))
return 0;
ifa = (struct ifaddrmsg *)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
if (ifa->ifa_index != index)
return 0;
if (data)
current_addr = &addrs[*found];
else
current_addr = &addr;
rc = parse_addr_rta(ifa, len, current_addr);
if (rc < 0)
return 0;
if (IN6_IS_ADDR_LINKLOCAL(current_addr))
return 0;
if (data) *found = (*found)+1;
kdebugf("found address on interface %s(%d): %s\n",
if_indextoname(index, ifname), index,
format_address(current_addr->s6_addr));
return 1;
}
int
filter_netlink(struct nlmsghdr *nh, void *data)
{
int rc;
int *changed = data;
switch (nh->nlmsg_type) {
case RTM_NEWROUTE:
case RTM_DELROUTE:
rc = filter_kernel_routes(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_ROUTE;
return rc;
case RTM_NEWLINK:
case RTM_DELLINK:
rc = filter_link(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_LINK;
return rc;
case RTM_NEWADDR:
case RTM_DELADDR:
rc = filter_addresses(nh, NULL);
if (changed && rc > 0)
*changed |= CHANGE_ADDR;
return rc;
default:
kdebugf("filter_netlink: unexpected message type %d\n",
nh->nlmsg_type);
break;
}
return 0;
}
int
kernel_addresses(int ifindex, struct in6_addr *addr, int maxaddr)
{
int maxa = maxaddr;
int found = 0;
void *data[] = { &maxa, addr, &found, (void *)ifindex };
struct rtgenmsg g;
int rc;
if (!nl_setup) {
fprintf(stderr, "kernel_addresses: netlink not initialized.\n");
errno = ENOSYS;
return -1;
}
if (nl_command.sock < 0) {
rc = netlink_socket(&nl_command, 0);
if (rc < 0) {
perror("kernel_addresses: netlink_socket()");
return -1;
}
}
memset(&g, 0, sizeof(g));
g.rtgen_family = AF_UNSPEC;
rc = netlink_send_dump(RTM_GETADDR, &g, sizeof(g));
if (rc < 0)
return -1;
rc = netlink_read(&nl_command, NULL, 1, filter_addresses, (void*)data);
if (rc < 0)
return -1;
return found;
}
int int
kernel_callback(int (*fn)(void*), void *closure) kernel_callback(int (*fn)(int, void*), void *closure)
{ {
int rc; int rc;
int changed;
kdebugf("\nReceived changes in kernel tables.\n"); kdebugf("\nReceived changes in kernel tables.\n");
...@@ -977,7 +1195,7 @@ kernel_callback(int (*fn)(void*), void *closure) ...@@ -977,7 +1195,7 @@ kernel_callback(int (*fn)(void*), void *closure)
return -1; return -1;
} }
} }
rc = netlink_read(&nl_listen, &nl_command, 0, filter_kernel_routes, NULL); rc = netlink_read(&nl_listen, &nl_command, 0, filter_netlink, &changed);
if(rc < 0 && nl_listen.sock < 0) if(rc < 0 && nl_listen.sock < 0)
kernel_setup_socket(1); kernel_setup_socket(1);
...@@ -985,7 +1203,7 @@ kernel_callback(int (*fn)(void*), void *closure) ...@@ -985,7 +1203,7 @@ kernel_callback(int (*fn)(void*), void *closure)
/* if netlink return 0 (found something interesting) */ /* if netlink return 0 (found something interesting) */
/* or -1 (i.e. IO error), we call... back ! */ /* or -1 (i.e. IO error), we call... back ! */
if(rc) if(rc)
return fn(closure); return fn(changed, closure);
return 0; return 0;
} }
...@@ -595,7 +595,7 @@ socket_read(int sock) ...@@ -595,7 +595,7 @@ socket_read(int sock)
} }
int int
kernel_callback(int (*fn)(void*), void *closure) kernel_callback(int (*fn)(int, void*), void *closure)
{ {
int rc; int rc;
...@@ -604,7 +604,7 @@ kernel_callback(int (*fn)(void*), void *closure) ...@@ -604,7 +604,7 @@ kernel_callback(int (*fn)(void*), void *closure)
kdebugf("Reading kernel table modification."); kdebugf("Reading kernel table modification.");
rc = socket_read(kernel_socket); rc = socket_read(kernel_socket);
if(rc) if(rc)
return fn(closure); return fn(~0, closure);
return 0; return 0;
......
...@@ -336,3 +336,19 @@ v4tov6(unsigned char *dst, const unsigned char *src) ...@@ -336,3 +336,19 @@ v4tov6(unsigned char *dst, const unsigned char *src)
memcpy(dst, v4prefix, 12); memcpy(dst, v4prefix, 12);
memcpy(dst + 12, src, 4); memcpy(dst + 12, src, 4);
} }
char *
parse_ifflags(unsigned int flags)
{
static char buf[512];
buf[0] = '\0';
if (flags & IFF_UP)
strcat(buf, "UP ");
if (flags & IFF_BROADCAST)
strcat(buf, "BROADCAST ");
if (flags & IFF_RUNNING)
strcat(buf, "RUNNING ");
if (flags & IFF_MULTICAST)
strcat(buf, "MULTICAST ");
return buf;
}
...@@ -53,6 +53,7 @@ int wait_for_fd(int direction, int fd, int msecs); ...@@ -53,6 +53,7 @@ int wait_for_fd(int direction, int fd, int msecs);
int martian_prefix(const unsigned char *prefix, int plen); int martian_prefix(const unsigned char *prefix, int plen);
int v4mapped(const unsigned char *address); int v4mapped(const unsigned char *address);
void v4tov6(unsigned char *dst, const unsigned char *src); void v4tov6(unsigned char *dst, const unsigned char *src);
char *parse_ifflags(unsigned int flags);
/* If debugging is disabled, we want to avoid calling format_address /* If debugging is disabled, we want to avoid calling format_address
for every omitted debugging message. So debug is a macro. But for every omitted debugging message. So debug is a macro. But
......
...@@ -25,6 +25,7 @@ THE SOFTWARE. ...@@ -25,6 +25,7 @@ THE SOFTWARE.
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
#include <netinet/in.h>
#include "babel.h" #include "babel.h"
#include "kernel.h" #include "kernel.h"
...@@ -34,6 +35,7 @@ THE SOFTWARE. ...@@ -34,6 +35,7 @@ THE SOFTWARE.
#include "xroute.h" #include "xroute.h"
#include "util.h" #include "util.h"
#include "filter.h" #include "filter.h"
#include "network.h"
struct xroute xroutes[MAXXROUTES]; struct xroute xroutes[MAXXROUTES];
int numxroutes = 0; int numxroutes = 0;
...@@ -152,3 +154,26 @@ check_xroutes() ...@@ -152,3 +154,26 @@ check_xroutes()
} }
return change; return change;
} }
int
check_addresses()
{
int maxaddr = MAXXROUTES;
struct in6_addr addresses[MAXXROUTES];
int found = 0;
int i;
int rc;
for (i = 0; i < numnets; i++) {
rc = kernel_addresses(nets[i].ifindex, addresses + found, maxaddr);
if (rc < 0)
break;
for (i = 0; i < rc && numxroutes < MAXXROUTES; i++) {
rc = add_xroute(addresses[i].s6_addr, 128, 0, nets[i].ifindex,
RTPROTO_BABEL_LOCAL);
if (rc < 0)
break;
}
}
return 0;
}
...@@ -21,6 +21,7 @@ THE SOFTWARE. ...@@ -21,6 +21,7 @@ THE SOFTWARE.
*/ */
int check_xroutes(void); int check_xroutes(void);
int check_addresses(void);
struct xroute { struct xroute {
unsigned char prefix[16]; unsigned char prefix[16];
......
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