Files
poco/Net/src/Route_Linux.cpp
aleks-f 325a98a91f Net::Route (windows compile)
Net::Route (windows compile only)
2012-11-04 23:25:57 -06:00

333 lines
9.9 KiB
C++

//
// Route_Linux.cpp
//
// $Id: //poco/1.4/Foundation/src/Route_Linux.cpp#2 $
//
// Library: Net
// Package: NetCore
// Module: Route
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <linux/if.h>
#include <libmnl/libmnl.h>
#include <linux/rtnetlink.h>
class RouteHelper
{
public:
static void createRouteIPv4(Route::RouteList *routes, struct rt_container *rt_stuff);
static void createRouteIPv6(Route::RouteList *routes, struct rt_container *rt_stuff);
private:
RouteHelper();
~RouteHelper();
};
typedef union
{
struct in_addr u_in;
struct in6_addr u_in6;
} in_addrX;
#define in4 u_in
#define in6 u_in6
struct rt_container
{
const struct nlattr *tb[RTA_MAX+1];
in_addrX gw;
in_addrX dest;
in_addrX src;
in_addrX prefsrc;
uint32_t oif, flow, priority;
unsigned char family;
unsigned char prefix;
unsigned char table;
unsigned char protocol;
unsigned char scope;
unsigned char type;
uint32_t metrics[RTA_MAX+1];
};
static inline bool in6zero(struct in6_addr in6)
{
return ((in6.s6_addr32[0] | in6.s6_addr32[1] | in6.s6_addr32[2] | in6.s6_addr32[3]) == 0);
}
static Route::RouteProto xlateProto(unsigned prot)
{
switch (prot) {
default:
// fallthru
case RTPROT_UNSPEC:
return Route::ROUTE_PROTO_OTHER;
case RTPROT_REDIRECT:
return Route::ROUTE_PROTO_ICMP;
case RTPROT_KERNEL:
case RTPROT_BOOT:
case RTPROT_STATIC:
return Route::ROUTE_PROTO_LOCAL;
// others?
}
}
void RouteHelper::createRouteIPv4(Route::RouteList *routes, struct rt_container *rt_stuff)
{
if (rt_stuff->table == RT_TABLE_MAIN && rt_stuff->type <= RTN_MULTICAST) {
Route *route;
if (rt_stuff->gw.in4.s_addr != 0)
route = new Route(IPAddress(&rt_stuff->dest.in4, sizeof(rt_stuff->dest.in4)), IPAddress(rt_stuff->prefix, IPAddress::IPv4), IPAddress(&rt_stuff->gw.in4, sizeof(rt_stuff->gw.in4)), rt_stuff->oif, Route::ROUTE_INDIRECT);
else
route = new Route(IPAddress(&rt_stuff->dest.in4, sizeof(rt_stuff->dest.in4)), IPAddress(rt_stuff->prefix, IPAddress::IPv4), rt_stuff->oif, Route::ROUTE_DIRECT);
route->setProto(xlateProto(rt_stuff->protocol));
route->setMetric(rt_stuff->metrics[0]);
// route->setPriority(rt_stuff->priority);
// no hops, usage, mtu, or age...
routes->push_back(route);
}
}
void RouteHelper::createRouteIPv6(Route::RouteList *routes, struct rt_container *rt_stuff)
{
if (rt_stuff->table == RT_TABLE_MAIN && rt_stuff->type <= RTN_MULTICAST) {
Route *route;
if (!in6zero(rt_stuff->gw.in6))
route = new Route(IPAddress(&rt_stuff->dest.in6, sizeof(rt_stuff->dest.in6)), IPAddress(rt_stuff->prefix, IPAddress::IPv6), IPAddress(&rt_stuff->gw.in6, sizeof(rt_stuff->gw.in6), rt_stuff->oif), rt_stuff->oif, Route::ROUTE_INDIRECT);
else
route = new Route(IPAddress(&rt_stuff->dest.in6, sizeof(rt_stuff->dest.in6)), IPAddress(rt_stuff->prefix, IPAddress::IPv6), rt_stuff->oif, Route::ROUTE_DIRECT);
route->setProto(xlateProto(rt_stuff->protocol));
route->setMetric(rt_stuff->metrics[0]);
// route->setPriority(rt_stuff->priority);
// no hops, usage, mtu, or age...
routes->push_back(route);
}
}
static int data_attr_cb2(const struct nlattr *attr, void *)
{
// skip unsupported attribute in user-space
if (mnl_attr_type_valid(attr, RTAX_MAX) < 0)
return MNL_CB_OK;
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
return MNL_CB_OK;
}
static void parse_attrs_ipv4(struct rt_container *rt_stuff)
{
if (rt_stuff->tb[RTA_TABLE])
rt_stuff->table = mnl_attr_get_u32(rt_stuff->tb[RTA_TABLE]);
if (rt_stuff->tb[RTA_DST])
rt_stuff->dest.in4 = *(struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_DST]);
if (rt_stuff->tb[RTA_SRC])
rt_stuff->src.in4 = *(struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_SRC]);
if (rt_stuff->tb[RTA_OIF])
rt_stuff->oif = mnl_attr_get_u32(rt_stuff->tb[RTA_OIF]);
if (rt_stuff->tb[RTA_FLOW])
rt_stuff->flow = mnl_attr_get_u32(rt_stuff->tb[RTA_FLOW]);
if (rt_stuff->tb[RTA_PRIORITY])
rt_stuff->priority = mnl_attr_get_u32(rt_stuff->tb[RTA_PRIORITY]);
if (rt_stuff->tb[RTA_PREFSRC])
rt_stuff->prefsrc.in4 = *(struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_PREFSRC]);
if (rt_stuff->tb[RTA_GATEWAY])
rt_stuff->gw.in4 = *(struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_GATEWAY]);
if (rt_stuff->tb[RTA_METRICS]) {
struct nlattr *tbx[RTAX_MAX+1] = {};
mnl_attr_parse_nested(rt_stuff->tb[RTA_METRICS], data_attr_cb2, tbx);
for (unsigned i = 0; i < RTAX_MAX; ++i)
if (tbx[i])
rt_stuff->metrics[i] = mnl_attr_get_u32(tbx[i]);
}
}
static void parse_attrs_ipv6(struct rt_container *rt_stuff)
{
if (rt_stuff->tb[RTA_TABLE])
rt_stuff->table = mnl_attr_get_u32(rt_stuff->tb[RTA_TABLE]);
if (rt_stuff->tb[RTA_DST])
memcpy(&rt_stuff->dest.in6, (struct in6_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_DST]), sizeof(struct in6_addr));
if (rt_stuff->tb[RTA_SRC])
memcpy(&rt_stuff->src.in6, (struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_SRC]), sizeof(struct in6_addr));
if (rt_stuff->tb[RTA_OIF])
rt_stuff->oif = mnl_attr_get_u32(rt_stuff->tb[RTA_OIF]);
if (rt_stuff->tb[RTA_FLOW])
rt_stuff->flow = mnl_attr_get_u32(rt_stuff->tb[RTA_FLOW]);
if (rt_stuff->tb[RTA_PRIORITY])
rt_stuff->priority = mnl_attr_get_u32(rt_stuff->tb[RTA_PRIORITY]);
if (rt_stuff->tb[RTA_PREFSRC])
memcpy(&rt_stuff->prefsrc.in6, (struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_PREFSRC]), sizeof(struct in6_addr));
if (rt_stuff->tb[RTA_GATEWAY])
memcpy(&rt_stuff->gw.in6, (struct in_addr *)mnl_attr_get_payload(rt_stuff->tb[RTA_GATEWAY]), sizeof(struct in6_addr));
if (rt_stuff->tb[RTA_METRICS]) {
struct nlattr *tbx[RTAX_MAX+1] = {};
mnl_attr_parse_nested(rt_stuff->tb[RTA_METRICS], data_attr_cb2, tbx);
for (unsigned i = 0; i < RTAX_MAX; ++i)
if (tbx[i])
rt_stuff->metrics[i] = mnl_attr_get_u32(tbx[i]);
}
}
static int data_attr_cb(const struct nlattr *attr, void *data)
{
struct rt_container *rt_stuff = (struct rt_container*)data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
return MNL_CB_OK;
switch (type) {
case RTA_DST:
case RTA_SRC:
case RTA_PREFSRC:
case RTA_GATEWAY:
if (rt_stuff->family == AF_INET) {
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
} else {
if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, sizeof(struct in6_addr)) < 0)
return MNL_CB_ERROR;
}
break;
case RTA_TABLE:
case RTA_OIF:
case RTA_FLOW:
case RTA_PRIORITY:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
break;
case RTA_METRICS:
if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
return MNL_CB_ERROR;
break;
}
rt_stuff->tb[type] = attr;
return MNL_CB_OK;
}
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
const struct rtmsg *rm = (const struct rtmsg *)mnl_nlmsg_get_payload(nlh);
Route::RouteList *routes = (Route::RouteList *)data;
struct rt_container rt_stuff;
memset(&rt_stuff, 0, sizeof(rt_stuff));
rt_stuff.family = rm->rtm_family;
rt_stuff.prefix = rm->rtm_dst_len;
rt_stuff.protocol = rm->rtm_protocol;
rt_stuff.scope = rm->rtm_scope;
rt_stuff.type = rm->rtm_type;
mnl_attr_parse(nlh, sizeof(*rm), data_attr_cb, &rt_stuff);
switch (rm->rtm_family) {
case AF_INET:
parse_attrs_ipv4(&rt_stuff);
RouteHelper::createRouteIPv4(routes, &rt_stuff);
break;
case AF_INET6:
parse_attrs_ipv6(&rt_stuff);
RouteHelper::createRouteIPv6(routes, &rt_stuff);
break;
}
return MNL_CB_OK;
}
Route::RouteList Route::list(IPAddress::Family family)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct rtmsg *rtm;
int ret;
unsigned seq, portid;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_GETROUTE;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlh->nlmsg_seq = seq = time(NULL);
rtm = (struct rtmsg *)mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
rtm->rtm_family = ((family == IPAddress::IPv4) ? AF_INET : AF_INET6);
Route::RouteList routes;
do {
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
break;
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
break;
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
break;
while (1) {
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret <= 0)
break;
ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &routes);
if (ret == MNL_CB_ERROR)
throw std::runtime_error("Couldn't parse routing table");
else if (ret == MNL_CB_STOP)
break;
}
if (ret == -1)
throw std::runtime_error("Couldn't talk to kernel");
} while (false);
if (nl)
mnl_socket_close(nl);
return routes;
}