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