mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-23 16:48:06 +02:00
333 lines
9.9 KiB
C++
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;
|
|
}
|