// // 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 #include #include #include #include #include #include #include #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