Files
poco/Net/src/Route_BSD.cpp
2012-11-08 22:56:25 -06:00

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