rtmon_linux.c revision 23bcfa32fddbe29a8c4c40d3bcfa4693a555c177
/* $Id$ */
/** @file
* NAT Network - IPv6 default route monitor for Linux netlink.
*/
/*
* Copyright (C) 2013-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_NAT_SERVICE
#include "proxy.h"
#include <linux/rtnetlink.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
/**
* Read IPv6 routing table - Linux rtnetlink version.
*
* XXX: TODO: To avoid re-reading the table we should subscribe to
* updates by binding a monitoring NETLINK_ROUTE socket to
* sockaddr_nl::nl_groups = RTMGRP_IPV6_ROUTE.
*
* But that will provide updates only. Documentation is scarce, but
* from what I've seen it seems that to get accurate routing info the
* monitoring socket needs to be created first, then full routing
* table requested (easier to do via spearate socket), then monitoring
* socket polled for input. The first update(s) of the monitoring
* socket may happen before full table is returned, so we can't just
* count the defaults, we need to keep track of their { oif, gw } to
* correctly ignore updates that are reported via monitoring socket,
* but that are already reflected in the full routing table returned
* in response to our request.
*/
int
rtmon_get_defaults(void)
{
int rtsock;
int ndefrts;
struct {
char attrbuf[512];
} rtreq;
bufsize = 1024;
for (;;) {
char *newbuf;
int recverr;
DPRINTF0(("rtmon: failed to %sallocate buffer\n",
return -1;
}
/* it's easier to reopen than to flush */
if (rtsock < 0) {
return -1;
}
if (nsent < 0) {
return -1;
}
if (ssize < 0) {
DPRINTF(("rtmon: failed to read RTM_GETROUTE response: %s",
return -1;
}
DPRINTF2(("rtmon: RTM_GETROUTE: %lu bytes\n",
(unsigned long)ssize));
break;
}
DPRINTF2(("rtmon: RTM_GETROUTE: truncated %lu to %lu bytes, retrying\n",
/* try again with larger buffer */
}
if (ndefrts == 0) {
DPRINTF(("rtmon: no IPv6 default routes found\n"));
}
else {
DPRINTF(("rtmon: %d IPv6 default route%s found\n",
}
return ndefrts;
}
/**
* Scan netlink message in the buffer for IPv6 default route changes.
*/
static int
{
int dfltdiff = 0;
{
int attrlen;
int delta = 0;
const void *gwbuf;
int oif;
DPRINTF2(("nlmsg type %d flags 0x%x\n",
break;
}
break;
}
/* shouldn't happen */
DPRINTF2(("> not an RTM message!\n"));
continue;
}
delta = +1;
}
delta = -1;
}
else {
/* shouldn't happen */
continue;
}
/*
* Is this an IPv6 default route in the main table? (Local
* table always has ::/0 reject route, hence the last check).
*/
&& rtm->rtm_dst_len == 0
{
}
else {
/* some other route change */
continue;
}
gwlen = 0;
oif = -1;
{
}
/* assert RTA_PAYLOAD(rta) == 4 */
}
}
}
return dfltdiff;
}