mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-23 16:48:06 +02:00
268 lines
7.0 KiB
C++
268 lines
7.0 KiB
C++
//
|
|
// Route_BSD.cpp
|
|
//
|
|
// $Id: //poco/1.4/Foundation/src/Route_BSD.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 <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/route.h>
|
|
#include <sys/sysctl.h>
|
|
#include <netinet/in.h>
|
|
#include <string>
|
|
|
|
|
|
#define RTMSG_LEN (sizeof(struct rt_msghdr) + 512)
|
|
#define ROUNDUP(a) ((a) > 0 ? (((a) + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1)) : sizeof(long))
|
|
#define NEXTADDR(w, u) \
|
|
if (rtm_addrs & (w)) {\
|
|
int l = ROUNDUP(u.sin_len); memmove(cp, &(u), l); cp += l;\
|
|
}
|
|
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
|
|
|
|
#define SIN_OFFSET (offsetof(sockaddr_in,sin_addr))
|
|
#define SIN_LENGTH (sizeof(struct in_addr))
|
|
#define SIN6_OFFSET (offsetof(sockaddr_in6,sin6_addr))
|
|
#define SIN6_LENGTH (sizeof(struct in6_addr))
|
|
|
|
|
|
|
|
namespace Poco {
|
|
namespace Net {
|
|
|
|
|
|
static int seq = rand();
|
|
|
|
|
|
static void get_rtaddrs(unsigned addrs, struct sockaddr *sa, struct sockaddr **rti_info)
|
|
{
|
|
for (unsigned i = 0; i < RTAX_MAX; i++) {
|
|
if (addrs & (1 << i)) {
|
|
rti_info[i] = sa;
|
|
sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
|
|
} else {
|
|
rti_info[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static IPAddress unpack_sockaddr_inX(struct sockaddr *sa, bool ipv4)
|
|
{
|
|
const unsigned offset = (ipv4 ? SIN_OFFSET : SIN6_OFFSET);
|
|
unsigned char *addr = (unsigned char*)sa + offset;
|
|
|
|
// return wildcard
|
|
if (sa->sa_len == 0)
|
|
return IPAddress(ipv4 ? IPAddress::IPv4 : IPAddress::IPv6);
|
|
|
|
struct in6_addr buf;
|
|
memset(&buf, 0, sizeof(buf));
|
|
memcpy(&buf, addr, sa->sa_len - offset);
|
|
|
|
IPAddress ip(&buf, (ipv4 ? SIN_LENGTH : SIN6_LENGTH));
|
|
|
|
return ip;
|
|
}
|
|
|
|
|
|
static Route createRoute(struct rt_msghdr2 *rtm, struct sockaddr **rti_info)
|
|
{
|
|
IPAddress dest, netmask, nexthop;
|
|
int ifIndex = rtm->rtm_index;
|
|
bool adjacent = false;
|
|
sa_family_t family = AF_MAX;
|
|
char macaddr[16];
|
|
|
|
for (unsigned i = 0; i < RTAX_MAX; i++)
|
|
{
|
|
sockaddr* sa = (struct sockaddr*)rti_info[i];
|
|
|
|
if (sa == 0) continue;
|
|
|
|
switch (i)
|
|
{
|
|
case RTAX_DST:
|
|
poco_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
|
|
family = sa->sa_family;
|
|
if (family == AF_INET)
|
|
{
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
|
dest = IPAddress(&sin->sin_addr, SIN_LENGTH);
|
|
}
|
|
else
|
|
{
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
|
dest = IPAddress(&sin6->sin6_addr, SIN6_LENGTH, rtm->rtm_index);
|
|
}
|
|
break;
|
|
|
|
case RTAX_GATEWAY:
|
|
poco_assert((sa->sa_family == family || sa->sa_family == AF_LINK));
|
|
switch (sa->sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
|
nexthop = IPAddress(&sin->sin_addr, SIN_LENGTH);
|
|
break;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
|
nexthop = IPAddress(&sin6->sin6_addr, SIN6_LENGTH, rtm->rtm_index);
|
|
break;
|
|
}
|
|
case AF_LINK:
|
|
{
|
|
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
|
|
adjacent = true;
|
|
ifIndex = sdl->sdl_index;
|
|
memcpy(macaddr, sdl->sdl_data, sdl->sdl_alen);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case RTAX_NETMASK:
|
|
netmask = unpack_sockaddr_inX(sa, (family == AF_INET));
|
|
break;
|
|
|
|
case RTAX_GENMASK:
|
|
// IPAddress genmask = unpack_sockaddr_inX(sa, (family == AF_INET));
|
|
break;
|
|
|
|
default:
|
|
// do something?
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rtm->rtm_flags & RTF_HOST)
|
|
{
|
|
poco_assert(!(rtm->rtm_addrs & RTA_NETMASK));
|
|
if (family == AF_INET)
|
|
netmask = IPAddress(32, IPAddress::IPv4);
|
|
else
|
|
netmask = IPAddress(128, IPAddress::IPv6);
|
|
}
|
|
|
|
if (adjacent)
|
|
{
|
|
Route route(dest, netmask, ifIndex, Route::ROUTE_DIRECT);
|
|
route.setMTU(rtm->rtm_rmx.rmx_mtu);
|
|
route.setHops(rtm->rtm_rmx.rmx_hopcount);
|
|
route.setUsage(rtm->rtm_use);
|
|
// OSX uses expire, not metric; we reuse metric member here
|
|
if (rtm->rtm_rmx.rmx_expire)
|
|
{
|
|
time_t expTime;
|
|
if ((expTime = rtm->rtm_rmx.rmx_expire - ::time(0)) > 0)
|
|
route.setMetric(expTime);
|
|
else route.setMetric(Route::ROUTE_METRIC_UNKNOWN);
|
|
}
|
|
else route.setMetric(Route::ROUTE_METRIC_UNKNOWN);
|
|
return route;
|
|
}
|
|
else
|
|
{
|
|
Route route(dest, netmask, nexthop, ifIndex, Route::ROUTE_INDIRECT);
|
|
route.setMTU(rtm->rtm_rmx.rmx_mtu);
|
|
route.setHops(rtm->rtm_rmx.rmx_hopcount);
|
|
route.setUsage(rtm->rtm_use);
|
|
// OSX uses expire, not metric; we reuse metric member here
|
|
if (rtm->rtm_rmx.rmx_expire)
|
|
{
|
|
time_t expTime;
|
|
if ((expTime = rtm->rtm_rmx.rmx_expire - ::time(0)) > 0)
|
|
route.setMetric(expTime);
|
|
else route.setMetric(Route::ROUTE_METRIC_UNKNOWN);
|
|
}
|
|
else route.setMetric(Route::ROUTE_METRIC_UNKNOWN);
|
|
return route;
|
|
}
|
|
}
|
|
|
|
|
|
Route::RouteList Route::list(IPAddress::Family family)
|
|
{
|
|
Route::RouteList routes;
|
|
int mib[6];
|
|
|
|
mib[0] = CTL_NET;
|
|
mib[1] = PF_ROUTE;
|
|
mib[2] = mib[3] = 0;
|
|
mib[4] = NET_RT_DUMP2;
|
|
mib[5] = 0;
|
|
|
|
size_t needed = 0;
|
|
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
|
throw std::runtime_error("sysctl failed to return routing table size");;
|
|
|
|
char *buf = new char[needed];
|
|
|
|
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
|
|
throw std::runtime_error("sysctl failed to populate routing table");
|
|
|
|
struct rt_msghdr2 *rtm = NULL;
|
|
for (char *next = buf, *lim = &buf[needed]; next < lim; next += rtm->rtm_msglen)
|
|
{
|
|
rtm = (struct rt_msghdr2 *)next;
|
|
|
|
struct sockaddr_in *sin = (struct sockaddr_in*)(rtm + 1);
|
|
|
|
if (sin->sin_family != ((family == IPAddress::IPv4) ? AF_INET : AF_INET6)) continue;
|
|
|
|
// skip cloned routes
|
|
if (rtm->rtm_parentflags & RTF_PRCLONING) continue;
|
|
|
|
struct sockaddr* rti_info[RTAX_MAX];
|
|
get_rtaddrs(rtm->rtm_addrs, (struct sockaddr *)sin, rti_info);
|
|
|
|
// RTF_IFSCOPE?
|
|
|
|
routes.push_back(createRoute(rtm, rti_info));
|
|
}
|
|
|
|
delete[] buf;
|
|
|
|
return routes;
|
|
}
|
|
|
|
|
|
}} // namespace Poco::Net
|