Commit fa4c481e by 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.
1 parent 3b4944b2
......@@ -81,10 +81,12 @@ unsigned char protocol_group[16];
int protocol_socket = -1;
int kernel_socket = -1;
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 int kernel_routes_callback(void *closure);
static int kernel_routes_callback(int changed, void *closure);
static void init_signals(void);
static void dump_tables(FILE *out);
......@@ -352,6 +354,8 @@ main(int argc, char **argv)
}
check_xroutes();
kernel_routes_changed = 0;
kernel_link_changed = 0;
kernel_addr_changed = 0;
kernel_dump_time = now.tv_sec + 20 + random() % 20;
timeval_plus_msec(&check_neighbours_time, &now, 5000 + random() % 5000);
expiry_time = now.tv_sec + 20 + random() % 20;
......@@ -453,6 +457,16 @@ main(int argc, char **argv)
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) {
rc = check_xroutes();
if(rc > 0)
......@@ -744,8 +758,13 @@ dump_tables(FILE *out)
}
static int
kernel_routes_callback(void *closure)
kernel_routes_callback(int changed, void *closure)
{
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;
}
......@@ -20,8 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <netinet/in.h>
#define KERNEL_INFINITY 0xFFFF
#define RTPROTO_BABEL_LOCAL 257
struct kernel_route {
unsigned char prefix[16];
int plen;
......@@ -35,6 +39,10 @@ struct kernel_route {
#define ROUTE_ADD 1
#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;
int kernel_setup(int setup);
......@@ -50,4 +58,5 @@ int kernel_route(int operation, const unsigned char *dest, unsigned short plen,
const unsigned char *newgate, int newifindex,
unsigned int newmetric);
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.
#include "babel.h"
#include "kernel.h"
#include "util.h"
#include "network.h"
int export_table = -1, import_table = -1;
......@@ -518,9 +519,11 @@ kernel_setup_socket(int setup)
int rc;
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) {
perror("netlink_socket(RTMGRP_ROUTE)");
perror("netlink_socket(RTMGRP_ROUTE | RTMGRP_LINK | RTMGRP_IFADDR)");
kernel_socket = -1;
return -1;
}
......@@ -963,10 +966,225 @@ kernel_routes(struct kernel_route *routes, int maxroutes)
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
kernel_callback(int (*fn)(void*), void *closure)
kernel_callback(int (*fn)(int, void*), void *closure)
{
int rc;
int changed;
kdebugf("\nReceived changes in kernel tables.\n");
......@@ -977,7 +1195,7 @@ kernel_callback(int (*fn)(void*), void *closure)
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)
kernel_setup_socket(1);
......@@ -985,7 +1203,7 @@ kernel_callback(int (*fn)(void*), void *closure)
/* if netlink return 0 (found something interesting) */
/* or -1 (i.e. IO error), we call... back ! */
if(rc)
return fn(closure);
return fn(changed, closure);
return 0;
}
......@@ -595,7 +595,7 @@ socket_read(int sock)
}
int
kernel_callback(int (*fn)(void*), void *closure)
kernel_callback(int (*fn)(int, void*), void *closure)
{
int rc;
......@@ -604,7 +604,7 @@ kernel_callback(int (*fn)(void*), void *closure)
kdebugf("Reading kernel table modification.");
rc = socket_read(kernel_socket);
if(rc)
return fn(closure);
return fn(~0, closure);
return 0;
......
......@@ -336,3 +336,19 @@ v4tov6(unsigned char *dst, const unsigned char *src)
memcpy(dst, v4prefix, 12);
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);
int martian_prefix(const unsigned char *prefix, int plen);
int v4mapped(const unsigned char *address);
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
for every omitted debugging message. So debug is a macro. But
......
......@@ -25,6 +25,7 @@ THE SOFTWARE.
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <netinet/in.h>
#include "babel.h"
#include "kernel.h"
......@@ -34,6 +35,7 @@ THE SOFTWARE.
#include "xroute.h"
#include "util.h"
#include "filter.h"
#include "network.h"
struct xroute xroutes[MAXXROUTES];
int numxroutes = 0;
......@@ -152,3 +154,26 @@ check_xroutes()
}
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.
*/
int check_xroutes(void);
int check_addresses(void);
struct xroute {
unsigned char prefix[16];
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!