X-Git-Url: http://git.onelab.eu/?p=libnl.git;a=blobdiff_plain;f=lib%2Froute%2Froute.c;fp=lib%2Froute%2Froute.c;h=7602853485f8e42d4fc8d257fdabb6f01c4dc344;hp=0000000000000000000000000000000000000000;hb=4cee2ecb3b8afa0637e6f5fe4c57985a4bc740ff;hpb=2df2fbe518d5a221ce6e3ee88a3fb23fb1b94b27 diff --git a/lib/route/route.c b/lib/route/route.c new file mode 100644 index 0000000..7602853 --- /dev/null +++ b/lib/route/route.c @@ -0,0 +1,1772 @@ +/* + * lib/route/route.c Routes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup route Routing + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define ROUTE_ATTR_FAMILY 0x000001 +#define ROUTE_ATTR_DST_LEN 0x000002 +#define ROUTE_ATTR_SRC_LEN 0x000004 +#define ROUTE_ATTR_TOS 0x000008 +#define ROUTE_ATTR_TABLE 0x000010 +#define ROUTE_ATTR_PROTOCOL 0x000020 +#define ROUTE_ATTR_SCOPE 0x000040 +#define ROUTE_ATTR_TYPE 0x000080 +#define ROUTE_ATTR_FLAGS 0x000100 +#define ROUTE_ATTR_DST 0x000200 +#define ROUTE_ATTR_SRC 0x000400 +#define ROUTE_ATTR_IIF 0x000800 +#define ROUTE_ATTR_OIF 0x001000 +#define ROUTE_ATTR_GATEWAY 0x002000 +#define ROUTE_ATTR_PRIO 0x004000 +#define ROUTE_ATTR_PREF_SRC 0x008000 +#define ROUTE_ATTR_METRICS 0x010000 +#define ROUTE_ATTR_MULTIPATH 0x020000 +#define ROUTE_ATTR_REALMS 0x040000 +#define ROUTE_ATTR_CACHEINFO 0x080000 +#define ROUTE_ATTR_MP_ALGO 0x100000 + +#define NEXTHOP_HAS_FLAGS 0x000001 +#define NEXTHOP_HAS_WEIGHT 0x000002 +#define NEXTHOP_HAS_IFINDEX 0x000004 +#define NEXTHOP_HAS_GATEWAY 0x000008 + +static struct nl_cache_ops rtnl_route_ops; +/** @endcond */ + +static void route_constructor(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + + nl_init_list_head(&r->rt_nexthops); +} + +static void route_free_data(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + struct rtnl_nexthop *nh, *tmp; + + if (r == NULL) + return; + + nl_addr_put(r->rt_dst); + nl_addr_put(r->rt_src); + nl_addr_put(r->rt_gateway); + nl_addr_put(r->rt_pref_src); + + nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { + rtnl_route_remove_nexthop(nh); + rtnl_route_nh_free(nh); + } +} + +static struct nla_policy route_policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_MP_ALGO] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, +}; + +static void copy_rtmsg_into_route(struct rtmsg *rtmsg, struct rtnl_route *route) +{ + route->rt_family = rtmsg->rtm_family; + route->rt_dst_len = rtmsg->rtm_dst_len; + route->rt_src_len = rtmsg->rtm_src_len; + route->rt_tos = rtmsg->rtm_tos; + route->rt_table = rtmsg->rtm_table; + route->rt_type = rtmsg->rtm_type; + route->rt_scope = rtmsg->rtm_scope; + route->rt_protocol = rtmsg->rtm_protocol; + route->rt_flags = rtmsg->rtm_flags; + + route->rt_mask = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_DST_LEN | + ROUTE_ATTR_SRC_LEN | ROUTE_ATTR_TABLE | + ROUTE_ATTR_PROTOCOL| ROUTE_ATTR_SCOPE | + ROUTE_ATTR_TYPE | ROUTE_ATTR_FLAGS); + + if (route->rt_tos) + route->rt_mask |= ROUTE_ATTR_TOS; +} + +static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci, + struct rtnl_route *route) +{ + route->rt_cacheinfo.rtci_clntref = ci->rta_clntref; + route->rt_cacheinfo.rtci_last_use = ci->rta_lastuse; + route->rt_cacheinfo.rtci_expires = ci->rta_expires; + route->rt_cacheinfo.rtci_error = ci->rta_error; + route->rt_cacheinfo.rtci_used = ci->rta_used; + route->rt_cacheinfo.rtci_id = ci->rta_id; + route->rt_cacheinfo.rtci_ts = ci->rta_ts; + route->rt_cacheinfo.rtci_tsage = ci->rta_tsage; + + route->rt_mask |= ROUTE_ATTR_CACHEINFO; +} + +static int route_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh, + void *arg) +{ + struct nl_parser_param *pp = arg; + struct rtnl_route *route; + struct nlattr *tb[RTA_MAX + 1]; + int err; + + route = rtnl_route_alloc(); + if (!route) { + err = nl_errno(ENOMEM); + goto errout; + } + + route->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, + route_policy); + if (err < 0) + goto errout; + + copy_rtmsg_into_route((struct rtmsg *) nlmsg_data(nlh), route); + + if (tb[RTA_DST]) { + route->rt_dst = nla_get_addr(tb[RTA_DST], route->rt_family); + if (!route->rt_dst) + goto errout_errno; + nl_addr_set_prefixlen(route->rt_dst, route->rt_dst_len); + route->rt_mask |= ROUTE_ATTR_DST; + } + + if (tb[RTA_SRC]) { + route->rt_src = nla_get_addr(tb[RTA_SRC], route->rt_family); + if (!route->rt_src) + goto errout_errno; + nl_addr_set_prefixlen(route->rt_src, route->rt_src_len); + route->rt_mask |= ROUTE_ATTR_SRC; + } + + if (tb[RTA_IIF]) { + nla_strlcpy(route->rt_iif, tb[RTA_IIF], IFNAMSIZ); + route->rt_mask |= ROUTE_ATTR_IIF; + } + + if (tb[RTA_OIF]) { + route->rt_oif = nla_get_u32(tb[RTA_OIF]); + route->rt_mask |= ROUTE_ATTR_OIF; + } + + if (tb[RTA_GATEWAY]) { + route->rt_gateway = nla_get_addr(tb[RTA_GATEWAY], + route->rt_family); + if (!route->rt_gateway) + goto errout_errno; + route->rt_mask |= ROUTE_ATTR_GATEWAY; + } + + if (tb[RTA_PRIORITY]) { + route->rt_prio = nla_get_u32(tb[RTA_PRIORITY]); + route->rt_mask |= ROUTE_ATTR_PRIO; + } + + if (tb[RTA_PREFSRC]) { + route->rt_pref_src = nla_get_addr(tb[RTA_PREFSRC], + route->rt_family); + if (!route->rt_pref_src) + goto errout_errno; + route->rt_mask |= ROUTE_ATTR_PREF_SRC; + } + + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + goto errout; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { + uint32_t m = nla_get_u32(mtb[i]); + route->rt_metrics[i-1] = m; + route->rt_metrics_mask |= (1 << (i - 1)); + } + } + + route->rt_mask |= ROUTE_ATTR_METRICS; + } + + if (tb[RTA_MULTIPATH]) { + struct rtnl_nexthop *nh; + struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]); + size_t tlen = nla_len(tb[RTA_MULTIPATH]); + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + nh = rtnl_route_nh_alloc(); + if (!nh) + goto errout; + + rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); + rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); + rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + nla_parse(ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + route_policy); + + if (ntb[RTA_GATEWAY]) { + nh->rtnh_gateway = nla_get_addr( + ntb[RTA_GATEWAY], + route->rt_family); + nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; + } + } + + rtnl_route_add_nexthop(route, nh); + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + } + + if (tb[RTA_FLOW]) { + route->rt_realms = nla_get_u32(tb[RTA_FLOW]); + route->rt_mask |= ROUTE_ATTR_REALMS; + } + + if (tb[RTA_CACHEINFO]) + copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route); + + if (tb[RTA_MP_ALGO]) { + route->rt_mp_algo = nla_get_u32(tb[RTA_MP_ALGO]); + route->rt_mask |= ROUTE_ATTR_MP_ALGO; + } + + err = pp->pp_cb((struct nl_object *) route, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout_errno: + err = nl_get_errno(); +errout: + rtnl_route_put(route); + return err; + +} + +static int route_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP); +} + +static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + if (r->rt_mask & ROUTE_ATTR_DST) + dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); + else if (r->rt_dst_len) + dp_dump(p, "0/%u ", r->rt_dst_len); + else + dp_dump(p, "default "); + + if (r->rt_mask & ROUTE_ATTR_OIF) { + if (link_cache) + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, r->rt_oif, + buf, sizeof(buf))); + else + dp_dump(p, "dev %d ", r->rt_oif); + } + + if (r->rt_mask & ROUTE_ATTR_GATEWAY) + dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf, + sizeof(buf))); + else if (r->rt_mask & ROUTE_ATTR_MULTIPATH) + dp_dump(p, "via nexthops "); + + if (r->rt_mask & ROUTE_ATTR_TABLE) + dp_dump(p, "table %s ", + rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); + + if (r->rt_mask & ROUTE_ATTR_SCOPE) + dp_dump(p, "scope %s ", + rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); + + if (r->rt_mask & ROUTE_ATTR_FLAGS && r->rt_flags) { + int flags = r->rt_flags; + + dp_dump(p, "<"); + +#define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ + flags &= ~RTNH_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(DEAD); + PRINT_FLAG(ONLINK); + PRINT_FLAG(PERVASIVE); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ + flags &= ~RTM_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(CLONED); + PRINT_FLAG(EQUALIZE); + PRINT_FLAG(PREFIX); +#undef PRINT_FLAG + + dp_dump(p, ">"); + } + + dp_dump(p, "\n"); + + return 1; +} + +static int route_dump_full(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + struct nl_cache *link_cache; + char buf[128]; + int i, line; + + link_cache = nl_cache_mngt_require("route/link"); + line = route_dump_brief(a, p); + + if (r->rt_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + dp_dump_line(p, line++, " "); + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + dp_dump(p, "nh "); + + if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY) + dp_dump(p, "via %s ", + nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + if (link_cache) + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + else + dp_dump(p, "dev %d ", nh->rtnh_ifindex); + + dp_dump(p, "weight %u <%s> ", nh->rtnh_weight, + rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); + } + + dp_dump(p, "\n"); + } + + dp_dump_line(p, line++, " "); + + if (r->rt_mask & ROUTE_ATTR_PREF_SRC) + dp_dump(p, "preferred-src %s ", + nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); + + if (r->rt_mask & ROUTE_ATTR_TYPE) + dp_dump(p, "type %s ", + nl_rtntype2str(r->rt_type, buf, sizeof(buf))); + + if (r->rt_mask & ROUTE_ATTR_PRIO) + dp_dump(p, "metric %#x ", r->rt_prio); + + if (r->rt_mask & ROUTE_ATTR_FAMILY) + dp_dump(p, "family %s ", + nl_af2str(r->rt_family, buf, sizeof(buf))); + + if (r->rt_mask & ROUTE_ATTR_PROTOCOL) + dp_dump(p, "protocol %s ", + rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); + + dp_dump(p, "\n"); + + if ((r->rt_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS | + ROUTE_ATTR_REALMS)) || r->rt_src_len || + ((r->rt_mask & ROUTE_ATTR_CACHEINFO) && + r->rt_cacheinfo.rtci_error)) { + dp_dump_line(p, line++, " "); + + if (r->rt_mask & ROUTE_ATTR_IIF) + dp_dump(p, "iif %s ", r->rt_iif); + + if (r->rt_mask & ROUTE_ATTR_SRC) + dp_dump(p, "src %s ", + nl_addr2str(r->rt_src, buf, sizeof(buf))); + else if (r->rt_src_len) + dp_dump(p, "src 0/%u ", r->rt_src_len); + + if (r->rt_mask & ROUTE_ATTR_TOS) + dp_dump(p, "tos %#x ", r->rt_tos); + + if (r->rt_mask & ROUTE_ATTR_REALMS) + dp_dump(p, "realm %04x:%04x ", + RTNL_REALM_FROM(r->rt_realms), + RTNL_REALM_TO(r->rt_realms)); + + if ((r->rt_mask & ROUTE_ATTR_CACHEINFO) && + r->rt_cacheinfo.rtci_error) + dp_dump(p, "error %d (%s) ", r->rt_cacheinfo.rtci_error, + strerror(-r->rt_cacheinfo.rtci_error)); + + dp_dump(p, "\n"); + } + + if (r->rt_mask & ROUTE_ATTR_METRICS) { + dp_dump_line(p, line++, " "); + for (i = 0; i < RTAX_MAX; i++) + if (r->rt_metrics_mask & (1 << i)) + dp_dump(p, "%s %u ", + rtnl_route_metric2str(i+1, + buf, sizeof(buf)), + r->rt_metrics[i]); + dp_dump(p, "\n"); + } + + return line; +} + +static int route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + int line; + + line = route_dump_full(obj, p); + + if (route->rt_mask & ROUTE_ATTR_CACHEINFO) { + struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; + dp_dump_line(p, line++, " used %u refcnt %u ", + ci->rtci_used, ci->rtci_clntref); + dp_dump_line(p, line++, "last-use %us expires %us\n", + ci->rtci_last_use / nl_get_hz(), + ci->rtci_expires / nl_get_hz()); + } + + return line; +} + +static int route_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "\n"); + dp_dump_line(p, line++, " %s\n", + nl_af2str(route->rt_family, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_DST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_dst, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_DST_LEN) + dp_dump_line(p, line++, " %u\n", + route->rt_dst_len); + + if (route->rt_mask & ROUTE_ATTR_SRC) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_src, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_SRC_LEN) + dp_dump_line(p, line++, " %u\n", + route->rt_src_len); + + if (route->rt_mask & ROUTE_ATTR_GATEWAY) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_gateway, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_PREF_SRC) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_IIF) + dp_dump_line(p, line++, " %s\n", route->rt_iif); + + if (route->rt_mask & ROUTE_ATTR_REALMS) + dp_dump_line(p, line++, " %u\n", + route->rt_realms); + + if (route->rt_mask & ROUTE_ATTR_TOS) + dp_dump_line(p, line++, " %u\n", route->rt_tos); + + if (route->rt_mask & ROUTE_ATTR_TABLE) + dp_dump_line(p, line++, " %u
\n", + route->rt_table); + + if (route->rt_mask & ROUTE_ATTR_SCOPE) + dp_dump_line(p, line++, " %s\n", + rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_PRIO) + dp_dump_line(p, line++, " %u\n", + route->rt_prio); + + if (route->rt_mask & ROUTE_ATTR_OIF) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require("route/link"); + if (link_cache) + dp_dump_line(p, line++, " %s\n", + rtnl_link_i2name(link_cache, + route->rt_oif, + buf, sizeof(buf))); + else + dp_dump_line(p, line++, " %u\n", + route->rt_oif); + } + + if (route->rt_mask & ROUTE_ATTR_TYPE) + dp_dump_line(p, line++, " %s\n", + nl_rtntype2str(route->rt_type, buf, sizeof(buf))); + + dp_dump_line(p, line++, "
\n"); + +#if 0 + uint8_t rt_protocol; + uint32_t rt_flags; + uint32_t rt_metrics[RTAX_MAX]; + uint32_t rt_metrics_mask; + struct rtnl_nexthop * rt_nexthops; + struct rtnl_rtcacheinfo rt_cacheinfo; + uint32_t rt_mp_algo; + +#endif + + return line; +} + +static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "ROUTE_FAMILY=%s\n", + nl_af2str(route->rt_family, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_DST) + dp_dump_line(p, line++, "ROUTE_DST=%s\n", + nl_addr2str(route->rt_dst, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_DST_LEN) + dp_dump_line(p, line++, "ROUTE_DSTLEN=%u\n", + route->rt_dst_len); + + if (route->rt_mask & ROUTE_ATTR_SRC) + dp_dump_line(p, line++, "ROUTE_SRC=%s\n", + nl_addr2str(route->rt_src, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_SRC_LEN) + dp_dump_line(p, line++, "ROUTE_SRCLEN=%u\n", + route->rt_src_len); + + if (route->rt_mask & ROUTE_ATTR_GATEWAY) + dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n", + nl_addr2str(route->rt_gateway, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_PREF_SRC) + dp_dump_line(p, line++, "ROUTE_PREFSRC=%s\n", + nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_IIF) + dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif); + + if (route->rt_mask & ROUTE_ATTR_REALMS) + dp_dump_line(p, line++, "ROUTE_REALM=%u\n", + route->rt_realms); + + if (route->rt_mask & ROUTE_ATTR_TOS) + dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos); + + if (route->rt_mask & ROUTE_ATTR_TABLE) + dp_dump_line(p, line++, "ROUTE_TABLE=%u\n", + route->rt_table); + + if (route->rt_mask & ROUTE_ATTR_SCOPE) + dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n", + rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); + + if (route->rt_mask & ROUTE_ATTR_PRIO) + dp_dump_line(p, line++, "ROUTE_METRIC=%u\n", + route->rt_prio); + + if (route->rt_mask & ROUTE_ATTR_OIF) { + struct nl_cache *link_cache; + + dp_dump_line(p, line++, "ROUTE_OIF_IFINDEX=%u\n", + route->rt_oif); + + link_cache = nl_cache_mngt_require("route/link"); + if (link_cache) + dp_dump_line(p, line++, "ROUTE_OIF_IFNAME=%s\n", + rtnl_link_i2name(link_cache, + route->rt_oif, + buf, sizeof(buf))); + } + + if (route->rt_mask & ROUTE_ATTR_TYPE) + dp_dump_line(p, line++, "ROUTE_TYPE=%s\n", + nl_rtntype2str(route->rt_type, buf, sizeof(buf))); + + return line; +} + +static int route_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct rtnl_route *o = (struct rtnl_route *) obj; + struct rtnl_route *f = (struct rtnl_route *) filter; + +#define REQ(F) (f->rt_mask & ROUTE_ATTR_##F) +#define AVAIL(F) (o->rt_mask & ROUTE_ATTR_##F) +#define _O(F, EXPR) (REQ(F) && (!AVAIL(F) || (EXPR))) +#define _C(F, N) (REQ(F) && (!AVAIL(F) || (o->N != f->N))) + if (_C(FAMILY, rt_family) || + _C(DST_LEN, rt_dst_len) || + _C(SRC_LEN, rt_src_len) || + _C(TOS, rt_tos) || + _C(TABLE, rt_table) || + _C(PROTOCOL, rt_protocol) || + _C(SCOPE, rt_scope) || + _C(TYPE, rt_type) || + _C(OIF, rt_oif) || + _C(PRIO, rt_prio) || + _C(REALMS, rt_realms) || + _C(MP_ALGO, rt_mp_algo) || + _O(DST, nl_addr_cmp(o->rt_dst, f->rt_dst)) || + _O(SRC, nl_addr_cmp(o->rt_src, f->rt_src)) || + _O(IIF, strcmp(o->rt_iif, f->rt_iif)) || + _O(PREF_SRC, nl_addr_cmp(o->rt_pref_src, f->rt_pref_src)) || + _O(GATEWAY, nl_addr_cmp(o->rt_gateway, f->rt_gateway)) || + _O(FLAGS, f->rt_flags ^ (o->rt_flags & f->rt_flag_mask))) + return 0; + + if (REQ(METRICS)) { + int i; + + if (!AVAIL(METRICS)) + return 0; + + for (i = 0; i < RTAX_MAX; i++) { + if (f->rt_metrics_mask & (1 << i)) { + if (!(o->rt_metrics_mask & (1 << i)) || + f->rt_metrics[i+1] != o->rt_metrics[i+1]) + return 0; + } + } + } + + if (REQ(MULTIPATH)) { + /* FIXME */ + } + +#undef REQ +#undef AVAIL +#undef _O +#undef _C + + return 1; + +} + +/** + * @name Route Object Allocation/Freeage + * @{ + */ + +/** + * Allocate a new route object + * @return New route object + */ +struct rtnl_route *rtnl_route_alloc(void) +{ + return (struct rtnl_route *) nl_object_alloc_from_ops(&rtnl_route_ops); +} + +/** + * Free route object. + * @arg route Route object to be freed. + * + * @note Always use rtnl_route_put() unless you're absolutely sure + * that no other user may have a reference on this object. + */ +void rtnl_route_free(struct rtnl_route *route) +{ + nl_object_free((struct nl_object *) route); +} + +/** @} */ + +/** + * @name Route Object Reference Counting + * @{ + */ + +void rtnl_route_get(struct rtnl_route *route) +{ + nl_object_get((struct nl_object *) route); +} + +void rtnl_route_put(struct rtnl_route *route) +{ + nl_object_put((struct nl_object *) route); +} + +/** @} */ + +/** + * @name Route Cache Management + * @{ + */ + +/** + * Build a route cache holding all routes currently configured in the kernel + * @arg handle netlink handle + * + * Allocates a new cache, initializes it properly and updates it to + * contain all routes currently configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The cache or NULL if an error has occured. + */ +struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache *cache = nl_cache_alloc_from_ops(&rtnl_route_ops); + + if (!cache) + return NULL; + + if (nl_cache_update(handle, cache) < 0) { + free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +/** + * @name Route Addition + * @{ + */ + +static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd, + int flags) +{ +#if 0 + struct nl_msg *msg; + struct rtmsg rtmsg = { + .rtm_family = tmpl->rt_family, + }; + route->rt_dst_len = rtmsg->rtm_dst_len; + route->rt_src_len = rtmsg->rtm_src_len; + route->rt_tos = rtmsg->rtm_tos; + route->rt_table = rtmsg->rtm_table; + route->rt_type = rtmsg->rtm_type; + route->rt_scope = rtmsg->rtm_scope; + route->rt_protocol = rtmsg->rtm_protocol; + route->rt_flags = rtmsg->rtm_flags; + + route->rt_mask = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_DST_LEN | + ROUTE_ATTR_SRC_LEN | ROUTE_ATTR_TABLE | + ROUTE_ATTR_PROTOCOL| ROUTE_ATTR_SCOPE | + ROUTE_ATTR_TYPE | ROUTE_ATTR_FLAGS); + + msg = nlmsg_build_simple(cmd, flags); + if (!msg) + return NULL; + + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), 1) < 0) + goto nla_put_failure; + + NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); + + if (tmpl->n_mask & NEIGH_ATTR_LLADDR) + NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +#endif + + return NULL; +} + +/** + * Build netlink request message to add a new route + * @arg tmpl template with data of new route + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a addition of a new route. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() or + * supplemented as needed. \a tmpl must contain the attributes of the + * new route set via \c rtnl_route_set_* functions. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return The netlink message + */ +struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags) +{ + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags); +} + +/** @} */ +/** + * @name Attribute: Routing Table + * @{ + */ + +/** + * Set the table of a route to the specified value + * @arg route route to be changed + * @arg table new table value + */ +void rtnl_route_set_table(struct rtnl_route *route, int table) +{ + route->rt_table = table; + route->rt_mask |= ROUTE_ATTR_TABLE; +} + +/** + * Get the table of a route + * @arg route route handle + * @return Table id or -1 if not set + */ +int rtnl_route_get_table(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_TABLE) + return route->rt_table; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Scope + * @{ + */ + +/** + * Set the scope of a route to the specified value + * @arg route route to be changed + * @arg scope new scope + */ +void rtnl_route_set_scope(struct rtnl_route *route, int scope) +{ + route->rt_scope = scope; + route->rt_mask |= ROUTE_ATTR_SCOPE; +} + +/** + * Get the scope of a route + * @arg route route handle + * @return Scope or -1 if not set + */ +int rtnl_route_get_scope(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_SCOPE) + return route->rt_scope; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Type Of Service + * @{ + */ + +/** + * Set the TOS of a route to the specified value + * @arg route route to be changed + * @arg tos new TOS value + */ +void rtnl_route_set_tos(struct rtnl_route *route, int tos) +{ + route->rt_tos = tos; + route->rt_mask |= ROUTE_ATTR_TOS; +} + +/** + * Get the TOS of a route + * @arg route route handle + * @return TOS value or -1 if not set + */ +int rtnl_route_get_tos(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_TOS) + return route->rt_tos; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Realm + * @{ + */ + +/** + * Set the realms of a route to the specified value + * @arg route route to be changed + * @arg realms New realms value. + */ +void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms) +{ + route->rt_realms = realms; + route->rt_mask |= ROUTE_ATTR_REALMS; +} + +/** + * Get realms of route object. + * @arg route Route object. + * @return Realms value or 0 if not set. + */ +realm_t rtnl_route_get_realms(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_REALMS) + return route->rt_realms; + else + return 0; +} + +/** @} */ +/** + * @name Attribute: Routing Protocol + * @{ + */ + +/** + * Set the protocol of a route to the specified value + * @arg route route to be changed + * @arg proto new protocol + */ +void rtnl_route_set_protocol(struct rtnl_route *route, int proto) +{ + route->rt_protocol = proto; + route->rt_mask |= ROUTE_ATTR_PROTOCOL; +} + +/** + * Get the protocol of a route + * @arg route route handle + * @return Protocol number or -1 if not set + */ +int rtnl_route_get_protocol(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_PROTOCOL) + return route->rt_protocol; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Priority/Metric + * @{ + */ + +/** + * Set the priority of a route to the specified value + * @arg route route to be changed + * @arg prio new priority + */ +void rtnl_route_set_prio(struct rtnl_route *route, int prio) +{ + route->rt_prio = prio; + route->rt_mask |= ROUTE_ATTR_PRIO; +} + +/** + * Get the priority of a route + * @arg route route handle + * @return Priority or -1 if not set + */ +int rtnl_route_get_prio(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_PRIO) + return route->rt_prio; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Address Family + * @{ + */ + +/** + * Set the address family of a route to the specified value + * @arg route route to be changed + * @arg family new address family + */ +void rtnl_route_set_family(struct rtnl_route *route, int family) +{ + route->rt_family = family; + route->rt_mask |= ROUTE_ATTR_FAMILY; +} + +/** + * Get the address family of a route + * @arg route route handle + * @return Address family or AF_UNSPEC if not set + */ +int rtnl_route_get_family(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_FAMILY) + return route->rt_family; + else + return AF_UNSPEC; +} + +/** @} */ +/** + * @name Attribute: Destination Address + * @{ + */ + +/** + * Set the destination address of a route to the specified address + * @arg route route to be changed + * @arg addr new destination address + * + * Assigns the new destination address to the specified \a route, + * overwrites the destination address length (rtnl_route::rt_dst_len), + * and sets the route's address family to the new address's family if + * it is not set already. + * + * If a address family has been specified already via either calling + * rtnl_route_set_family() or by setting one of the other addresses, + * the specified \a addr is automatically validated against this family + * and the assignment fails in case of a mismatch. + * + * @return 0 on success or a negative error code. + */ +int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->rt_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_dst) + nl_addr_put(route->rt_dst); + + nl_addr_get(addr); + route->rt_dst = addr; + + route->rt_mask |= (ROUTE_ATTR_DST|ROUTE_ATTR_FAMILY|ROUTE_ATTR_DST_LEN); + + return 0; +} + +/** + * Get the destination address of a route + * @arg route route handle + * @return Destination address or NULL if not set + */ +struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_DST) + return route->rt_dst; + else + return NULL; +} + +/** + * Set the destination address prefix length of a route to the specified value + * @arg route route to be changed + * @arg prefix new destination address prefix + * @attention The destination address prefix gets overwritten by calls + * to rtnl_route_set_dst() rtnl_route_set_dst_str(). + */ +void rtnl_route_set_dst_len(struct rtnl_route *route, int prefix) +{ + route->rt_dst_len = prefix; + route->rt_mask |= ROUTE_ATTR_DST_LEN; +} + +/** + * Get the destination address prefix length of a route + * @arg route route handle + * @return Prefix length or -1 if not set + */ +int rtnl_route_get_dst_len(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_DST_LEN) + return route->rt_dst_len; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Source Address + * @{ + */ + +/** + * Set the source address of a route to the specified address + * @arg route route to be changed + * @arg addr new source address + * + * Assigns the new source address to the specified \a route, + * overwrites the source address length (rtnl_route::rt_src_len), + * and sets the route's address family to the new address's family if + * it is not set already. + * + * If a address family has been specified already via either calling + * rtnl_route_set_family() or by setting one of the other addresses, + * the specified \a addr is automatically validated against this family + * and the assignment fails in case of a mismatch. + * + * @return 0 on success or a negative error code. + */ +int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->rt_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_src) + nl_addr_put(route->rt_src); + + nl_addr_get(addr); + route->rt_src = addr; + route->rt_mask |= (ROUTE_ATTR_SRC|ROUTE_ATTR_FAMILY|ROUTE_ATTR_SRC_LEN); + + return 0; +} + +/** + * Get the source address of a route + * @arg route route handle + * @return Source address or NULL if not set + */ +struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_SRC) + return route->rt_src; + else + return NULL; +} + +/** + * Set the source address prefix length of a route to the specified value + * @arg route route to be changed + * @arg prefix new source address prefix + * @attention The source address prefix gets overwritten by calls + * to rtnl_route_src_dst() rtnl_route_set_src_str(). + */ +void rtnl_route_set_src_len(struct rtnl_route *route, int prefix) +{ + route->rt_dst_len = prefix; + route->rt_mask |= ROUTE_ATTR_SRC_LEN; +} + +/** + * Get the source address prefix length of a route + * @arg route route handle + * @return Prefix length or -1 if not set + */ +int rtnl_route_get_src_len(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_SRC_LEN) + return route->rt_src_len; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Gateway Address + * @{ + */ + +/** + * Set the gateway address of a route to the specified address + * @arg route route to be changed + * @arg addr new gateway address + * + * Assigns the new gateway address to the specified \a route, + * and sets the route's address family to the new address's family if + * it is not set already. + * + * If a address family has been specified already via either calling + * rtnl_route_set_family() or by setting one of the other addresses, + * the specified \a addr is automatically validated against this family + * and the assignment fails in case of a mismatch. + * + * @return 0 on success or a negative error code. + */ +int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->rt_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_gateway) + nl_addr_put(route->rt_gateway); + + nl_addr_get(addr); + route->rt_gateway = addr; + route->rt_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY); + + return 0; +} + +/** + * Get the gateway address of a route + * @arg route route handle + * @return Gateway address or NULL if not set + */ +struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_GATEWAY) + return route->rt_gateway; + else + return NULL; +} + +/** @} */ +/** + * @name Attribute: Type + * @{ + */ + +/** + * Set the type of a route to the specified value + * @arg route route to be changed + * @arg type new route type + */ +void rtnl_route_set_type(struct rtnl_route *route, int type) +{ + route->rt_type = type; + route->rt_mask |= ROUTE_ATTR_TYPE; +} + +/** + * Get the type of a route + * @arg route route handle + * @return Type of route or -1 if not set + */ +int rtnl_route_get_type(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_TYPE) + return route->rt_type; + else + return -1; +} + +/** @} */ +/** + * @name Attribute: Flags + * @{ + */ + +/** + * Add flags to a route + * @arg route route to be changed + * @arg flags flags to set + */ +void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags |= flags; + route->rt_mask |= ROUTE_ATTR_FLAGS; +} + +/** + * Remove flags from a route + * @arg route route to be changed + * @arg flags flags to unset + */ +void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags &= ~flags; + route->rt_mask |= ROUTE_ATTR_FLAGS; +} + +/** + * Get flags of a route + * @arg route route handle + */ +unsigned int rtnl_route_get_flags(struct rtnl_route *route) +{ + return route->rt_flags; +} + +/** @} */ +/** + * @name Attribute: Routing Metrics + * @{ + */ + +/** + * Set a metric of a route to the specified value + * @arg route route to be changed + * @arg metric metric to be changed (see XXX) + * @arg value new metric value + * @return 0 on sucess or a negative error code + */ +int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) +{ + if (metric <= RTAX_MAX || metric < 1) + return nl_error(EINVAL, "Metric out of range (1..%d)", + RTAX_MAX); + + route->rt_metrics[metric - 1] = value; + route->rt_metrics_mask |= (1 << (metric - 1)); + + return 0; +} + +/** + * Unset a metric of a route + * @arg route route to be changed + * @arg metric metric to be unset (see XXX) + * @return 0 on sucess or a negative error code + */ +int rtnl_route_unset_metric(struct rtnl_route *route, int metric) +{ + if (metric <= RTAX_MAX || metric < 1) + return nl_error(EINVAL, "Metric out of range (1..%d)", + RTAX_MAX); + + route->rt_metrics_mask &= ~(1 << (metric - 1)); + + return 0; +} + +/** + * Get a metric for a route + * @arg route route handle + * @arg metric metric to get + * @return The value for the specified metric or UINT_MAX if not set + */ +unsigned int rtnl_route_get_metric(struct rtnl_route *route, int metric) +{ + if (metric <= RTAX_MAX || metric < 1) + return UINT_MAX; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) + return UINT_MAX; + + return route->rt_metrics[metric - 1]; +} + +/** @} */ +/** + * @name Attribute: Preferred Source Address + * @{ + */ + +/** + * Set the preferred source address of a route to the specified address + * @arg route route to be changed + * @arg addr new preferred source address + * + * Assigns the new preferred source address to the specified \a route, + * and sets the route's address family to the new address's family if + * it is not set already. + * + * If a address family has been specified already via either calling + * rtnl_route_set_family() or by setting one of the other addresses, + * the specified \a addr is automatically validated against this family + * and the assignment fails in case of a mismatch. + * + * @return 0 on success or a negative error code. + */ +int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->rt_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_pref_src) + nl_addr_put(route->rt_pref_src); + + nl_addr_get(addr); + route->rt_pref_src = addr; + route->rt_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +/** + * Get the preferred source address of a route + * @arg route route handle + * @return Preferred source address or NULL if not set + */ +struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_PREF_SRC) + return route->rt_pref_src; + else + return NULL; +} + +/** @} */ +/** + * @name Attribute: Outgoing Interface Index + * @{ + */ + +/** + * Set the outgoing interface of a route to the specified value + * @arg route route to be changed + * @arg ifindex interface index of new outoing interface + */ +void rtnl_route_set_oif(struct rtnl_route *route, int ifindex) +{ + route->rt_oif = ifindex; + route->rt_mask |= ROUTE_ATTR_OIF; +} + +/** + * Get the outgoing interface index of a route + * @arg route route handle + * @return interface index or RTNL_LINK_NOT_FOUND if not set + */ +int rtnl_route_get_oif(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_OIF) + return route->rt_oif; + else + return RTNL_LINK_NOT_FOUND; +} + +/** @} */ +/** + * @name Attribute: Incoming Interface + * @{ + */ + +/** + * Set the incoming interface of a route to the specified value + * @arg route route to be changed + * @arg name interface name of the new incoming interface + */ +void rtnl_route_set_iif(struct rtnl_route *route, const char *name) +{ + strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1); + route->rt_mask |= ROUTE_ATTR_IIF; +} + +/** + * Get the incomming interface name of a route + * @arg route route handle + * @return interface name or NULL if not set + */ +char *rtnl_route_get_iif(struct rtnl_route *route) +{ + if (route->rt_mask & ROUTE_ATTR_IIF) + return route->rt_iif; + else + return NULL; +} + +#if 0 + struct rtnl_rtcacheinfo rt_cacheinfo; +#endif + +/** @} */ +/** + * @name Attribute: Nexthop + * @{ + */ + +void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); + route->rt_mask |= ROUTE_ATTR_MULTIPATH; +} + +void rtnl_route_remove_nexthop(struct rtnl_nexthop *nh) +{ + nl_list_del(&nh->rtnh_list); +} + +struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) +{ + return &route->rt_nexthops; +} + +/** @} */ + +/** + * @name Routing Table Identifier Translations + * @{ + */ + +static struct trans_tbl route_tables[] = { + __ADD(RT_TABLE_UNSPEC, unspec) + __ADD(RT_TABLE_DEFAULT, default) + __ADD(RT_TABLE_MAIN, main) + __ADD(RT_TABLE_LOCAL, local) +}; + +/** + * Convert routing table identifier to character string. + * @arg table Routing table identifier. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a routing table identifier to a character string and stores + * it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if the routing table identifier is unknown. + */ +char *rtnl_route_table2str(int table, char *buf, size_t size) +{ + return __type2str(table, buf, size, route_tables, + ARRAY_SIZE(route_tables)); +} + +/** + * Convert character string to routing table identifier. + * @arg name Name of routing table. + * + * Converts the provided character string specifying a routing table + * identifier to the corresponding numeric value. + * + * @return Routing table identifier or a negative value if no match was found. + */ +int rtnl_route_str2table(const char *name) +{ + return __str2type(name, route_tables, ARRAY_SIZE(route_tables)); +} + + +/** @} */ + +/** + * @name Routing Protocol Translations + * @{ + */ + +static struct trans_tbl route_protos[] = { + __ADD(RTPROT_UNSPEC, unspec) + __ADD(RTPROT_REDIRECT, redirect) + __ADD(RTPROT_KERNEL, kernel) + __ADD(RTPROT_BOOT, boot) + __ADD(RTPROT_STATIC, static) +}; + +/** + * Convert routing protocol identifier to character string. + * @arg proto Routing protocol identifier. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a routing protocol identifier to a character string and stores + * it in the specified destination buffer. + * + * @return The destination buffer or the protocol encoded in hexidecimal + * form if the routing protocol is unknown. + */ +char *rtnl_route_proto2str(int proto, char *buf, size_t size) +{ + return __type2str(proto, buf, size, route_protos, + ARRAY_SIZE(route_protos)); +} + +/** + * Convert character string to routing protocol identifier. + * @arg name Name of routing protocol. + * + * Converts the provided character string specifying a routing protocl + * identifier to the corresponding numeric value. + * + * @return Routing protocol dentifier or a negative value if no match was found. + */ +int rtnl_route_str2proto(const char *name) +{ + return __str2type(name, route_protos, ARRAY_SIZE(route_protos)); +} + +/** @} */ + +/** + * @name Routing Metrices Translations + * @{ + */ + +static struct trans_tbl route_metrices[] = { + __ADD(RTAX_UNSPEC, unspec) + __ADD(RTAX_LOCK, lock) + __ADD(RTAX_MTU, mtu) + __ADD(RTAX_WINDOW, window) + __ADD(RTAX_RTT, rtt) + __ADD(RTAX_RTTVAR, rttvar) + __ADD(RTAX_SSTHRESH, ssthresh) + __ADD(RTAX_CWND, cwnd) + __ADD(RTAX_ADVMSS, advmss) + __ADD(RTAX_REORDERING, reordering) + __ADD(RTAX_HOPLIMIT, hoplimit) + __ADD(RTAX_INITCWND, initcwnd) + __ADD(RTAX_FEATURES, features) +}; + +/** + * Convert routing metric identifier to character string. + * @arg metric Routing metric identifier. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a routing metric identifier to a character string and stores + * it in the specified destination buffer. + * + * @return The destination buffer or the metric encoded in hexidecimal + * form if the routing metric identifier is unknown. + */ +char *rtnl_route_metric2str(int metric, char *buf, size_t size) +{ + return __type2str(metric, buf, size, route_metrices, + ARRAY_SIZE(route_metrices)); +} + +/** + * Convert character string to routing metric identifier. + * @arg name Name of routing metric. + * + * Converts the provided character string specifying a routing metric + * identifier to the corresponding numeric value. + * + * @return Routing metric dentifier or a negative value if no match was found. + */ +int rtnl_route_str2metric(const char *name) +{ + return __str2type(name, route_metrices, ARRAY_SIZE(route_metrices)); +} + +/** @} */ + +/** + * @name Nexthop Flags Translations + * @{ + */ + +static struct trans_tbl nh_flags[] = { + __ADD(RTNH_F_DEAD, dead) + __ADD(RTNH_F_PERVASIVE, pervasive) + __ADD(RTNH_F_ONLINK, onlink) +}; + +/** + * Convert nexthop flags to a character string. + * @arg flags Nexthop flags. + * @arg buf Destination buffer. + * @arg len Length of destination buffer. + * + * Converts nexthop flags to a character string separated by + * commas and stores it in the specified destination buffer. + * + * \return The destination buffer + */ +char * rtnl_route_nh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); +} + +/** + * Convert a character string to a nexthop flag + * @arg name Name of nexthop flag. + * + * Converts the provided character string specifying a nexthop + * flag to the corresponding numeric value. + * + * \return Nexthop flag or a negative value if none was found. + */ +int rtnl_route_nh_str2flags(const char *name) +{ + return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); +} + +/** @} */ + +static struct nl_cache_ops rtnl_route_ops = { + .co_name = "route/route", + .co_size = sizeof(struct rtnl_route), + .co_hdrsize = sizeof(struct rtmsg), + .co_msgtypes = { + { RTM_NEWROUTE, "new" }, + { RTM_DELROUTE, "delete" }, + { RTM_GETROUTE, "get" }, + { -1, NULL }, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = route_request_update, + .co_msg_parser = route_msg_parser, + .co_constructor = route_constructor, + .co_free_data = route_free_data, + .co_dump[NL_DUMP_BRIEF] = route_dump_brief, + .co_dump[NL_DUMP_FULL] = route_dump_full, + .co_dump[NL_DUMP_STATS] = route_dump_stats, + .co_dump[NL_DUMP_XML] = route_dump_xml, + .co_dump[NL_DUMP_ENV] = route_dump_env, + .co_filter = route_filter, +}; + +static void __init route_init(void) +{ + nl_cache_mngt_register(&rtnl_route_ops); +} + +static void __exit route_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_route_ops); +} + +/** @} */