libnl/lib/route/route_obj.c
Thomas Graf 4649886288 Represent default route with destination address length zero
So far the destination address for default routes was NULL
which complicated the handling of routes in general. By
assigning a address of length zero they can be compared
to each other.

This allows the cache manager to properly handle default
routes.
2007-12-19 19:41:01 +01:00

909 lines
23 KiB
C

/*
* lib/route/route_obj.c Route Object
*
* 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 <tgraf@suug.ch>
*/
/**
* @ingroup route
* @defgroup route_obj Route Object
*
* @par Attributes
* @code
* Name Default
* -------------------------------------------------------------
* routing table RT_TABLE_MAIN
* scope RT_SCOPE_NOWHERE
* tos 0
* realms 0
* protocol RTPROT_STATIC
* prio 0
* family AF_UNSPEC
* type RTN_UNICAST
* oif RTNL_LINK_NOT_FOUND
* iif NULL
* mpalgo IP_MP_ALG_NONE
* @endcode
*
* @{
*/
#include <netlink-local.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
#include <netlink/data.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/route.h>
#include <netlink/route/link.h>
/** @cond SKIP */
#define ROUTE_ATTR_FAMILY 0x000001
#define ROUTE_ATTR_TOS 0x000002
#define ROUTE_ATTR_TABLE 0x000004
#define ROUTE_ATTR_PROTOCOL 0x000008
#define ROUTE_ATTR_SCOPE 0x000010
#define ROUTE_ATTR_TYPE 0x000020
#define ROUTE_ATTR_FLAGS 0x000040
#define ROUTE_ATTR_DST 0x000080
#define ROUTE_ATTR_SRC 0x000100
#define ROUTE_ATTR_IIF 0x000200
#define ROUTE_ATTR_OIF 0x000400
#define ROUTE_ATTR_GATEWAY 0x000800
#define ROUTE_ATTR_PRIO 0x001000
#define ROUTE_ATTR_PREF_SRC 0x002000
#define ROUTE_ATTR_METRICS 0x004000
#define ROUTE_ATTR_MULTIPATH 0x008000
#define ROUTE_ATTR_REALMS 0x010000
#define ROUTE_ATTR_CACHEINFO 0x020000
#define ROUTE_ATTR_MP_ALGO 0x040000
/** @endcond */
static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p);
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 int route_clone(struct nl_object *_dst, struct nl_object *_src)
{
struct rtnl_route *dst = (struct rtnl_route *) _dst;
struct rtnl_route *src = (struct rtnl_route *) _src;
struct rtnl_nexthop *nh, *new;
if (src->rt_dst)
if (!(dst->rt_dst = nl_addr_clone(src->rt_dst)))
goto errout;
if (src->rt_src)
if (!(dst->rt_src = nl_addr_clone(src->rt_src)))
goto errout;
if (src->rt_gateway)
if (!(dst->rt_gateway = nl_addr_clone(src->rt_gateway)))
goto errout;
if (src->rt_pref_src)
if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
goto errout;
nl_init_list_head(&dst->rt_nexthops);
nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) {
new = rtnl_route_nh_clone(nh);
if (!new)
goto errout;
rtnl_route_add_nexthop(dst, new);
}
return 0;
errout:
return nl_get_errno();
}
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->ce_mask & ROUTE_ATTR_DST) ||
nl_addr_get_len(r->rt_dst) == 0)
dp_dump(p, "default ");
else
dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
if (r->ce_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->ce_mask & ROUTE_ATTR_GATEWAY)
dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf,
sizeof(buf)));
else if (r->ce_mask & ROUTE_ATTR_MULTIPATH)
dp_dump(p, "via nexthops ");
if (r->ce_mask & ROUTE_ATTR_SCOPE)
dp_dump(p, "scope %s ",
rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
if (r->ce_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->ce_mask & ROUTE_ATTR_MULTIPATH) {
struct rtnl_nexthop *nh;
nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
dp_dump_line(p, line++, " via ");
if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY)
dp_dump(p, "%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>\n", nh->rtnh_weight,
rtnl_route_nh_flags2str(nh->rtnh_flags,
buf, sizeof(buf)));
}
}
dp_dump_line(p, line++, " ");
if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
dp_dump(p, "preferred-src %s ",
nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
if (r->ce_mask & ROUTE_ATTR_TABLE)
dp_dump(p, "table %s ",
rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
if (r->ce_mask & ROUTE_ATTR_TYPE)
dp_dump(p, "type %s ",
nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
if (r->ce_mask & ROUTE_ATTR_PRIO)
dp_dump(p, "metric %#x ", r->rt_prio);
if (r->ce_mask & ROUTE_ATTR_FAMILY)
dp_dump(p, "family %s ",
nl_af2str(r->rt_family, buf, sizeof(buf)));
if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
dp_dump(p, "protocol %s ",
rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
dp_dump(p, "\n");
if ((r->ce_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS |
ROUTE_ATTR_REALMS)) ||
((r->ce_mask & ROUTE_ATTR_CACHEINFO) &&
r->rt_cacheinfo.rtci_error)) {
dp_dump_line(p, line++, " ");
if (r->ce_mask & ROUTE_ATTR_IIF)
dp_dump(p, "iif %s ", r->rt_iif);
if (r->ce_mask & ROUTE_ATTR_SRC)
dp_dump(p, "src %s ",
nl_addr2str(r->rt_src, buf, sizeof(buf)));
if (r->ce_mask & ROUTE_ATTR_TOS)
dp_dump(p, "tos %#x ", r->rt_tos);
if (r->ce_mask & ROUTE_ATTR_REALMS)
dp_dump(p, "realm %04x:%04x ",
RTNL_REALM_FROM(r->rt_realms),
RTNL_REALM_TO(r->rt_realms));
if ((r->ce_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->ce_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->ce_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++, "<route>\n");
dp_dump_line(p, line++, " <family>%s</family>\n",
nl_af2str(route->rt_family, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_DST)
dp_dump_line(p, line++, " <dst>%s</dst>\n",
nl_addr2str(route->rt_dst, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_SRC)
dp_dump_line(p, line++, " <src>%s</src>\n",
nl_addr2str(route->rt_src, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_GATEWAY)
dp_dump_line(p, line++, " <gateway>%s</gateway>\n",
nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
dp_dump_line(p, line++, " <prefsrc>%s</prefsrc>\n",
nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_IIF)
dp_dump_line(p, line++, " <iif>%s</iif>\n", route->rt_iif);
if (route->ce_mask & ROUTE_ATTR_REALMS)
dp_dump_line(p, line++, " <realms>%u</realms>\n",
route->rt_realms);
if (route->ce_mask & ROUTE_ATTR_TOS)
dp_dump_line(p, line++, " <tos>%u</tos>\n", route->rt_tos);
if (route->ce_mask & ROUTE_ATTR_TABLE)
dp_dump_line(p, line++, " <table>%u</table>\n",
route->rt_table);
if (route->ce_mask & ROUTE_ATTR_SCOPE)
dp_dump_line(p, line++, " <scope>%s</scope>\n",
rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_PRIO)
dp_dump_line(p, line++, " <metric>%u</metric>\n",
route->rt_prio);
if (route->ce_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++, " <oif>%s</oif>\n",
rtnl_link_i2name(link_cache,
route->rt_oif,
buf, sizeof(buf)));
else
dp_dump_line(p, line++, " <oif>%u</oif>\n",
route->rt_oif);
}
if (route->ce_mask & ROUTE_ATTR_TYPE)
dp_dump_line(p, line++, " <type>%s</type>\n",
nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
dp_dump_line(p, line++, "</route>\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->ce_mask & ROUTE_ATTR_DST)
dp_dump_line(p, line++, "ROUTE_DST=%s\n",
nl_addr2str(route->rt_dst, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_SRC)
dp_dump_line(p, line++, "ROUTE_SRC=%s\n",
nl_addr2str(route->rt_src, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_GATEWAY)
dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n",
nl_addr2str(route->rt_gateway, buf, sizeof(buf)));
if (route->ce_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->ce_mask & ROUTE_ATTR_IIF)
dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif);
if (route->ce_mask & ROUTE_ATTR_REALMS)
dp_dump_line(p, line++, "ROUTE_REALM=%u\n",
route->rt_realms);
if (route->ce_mask & ROUTE_ATTR_TOS)
dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos);
if (route->ce_mask & ROUTE_ATTR_TABLE)
dp_dump_line(p, line++, "ROUTE_TABLE=%u\n",
route->rt_table);
if (route->ce_mask & ROUTE_ATTR_SCOPE)
dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n",
rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
if (route->ce_mask & ROUTE_ATTR_PRIO)
dp_dump_line(p, line++, "ROUTE_METRIC=%u\n",
route->rt_prio);
if (route->ce_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->ce_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_compare(struct nl_object *_a, struct nl_object *_b,
uint32_t attrs, int flags)
{
struct rtnl_route *a = (struct rtnl_route *) _a;
struct rtnl_route *b = (struct rtnl_route *) _b;
int diff = 0;
#define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
diff |= ROUTE_DIFF(FAMILY, a->rt_family != b->rt_family);
diff |= ROUTE_DIFF(TOS, a->rt_tos != b->rt_tos);
diff |= ROUTE_DIFF(TABLE, a->rt_table != b->rt_table);
diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol);
diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope);
diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type);
diff |= ROUTE_DIFF(OIF, a->rt_oif != b->rt_oif);
diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio);
diff |= ROUTE_DIFF(REALMS, a->rt_realms != b->rt_realms);
diff |= ROUTE_DIFF(MP_ALGO, a->rt_mp_algo != b->rt_mp_algo);
diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst));
diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src));
diff |= ROUTE_DIFF(IIF, strcmp(a->rt_iif, b->rt_iif));
diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src,
b->rt_pref_src));
diff |= ROUTE_DIFF(GATEWAY, nl_addr_cmp(a->rt_gateway,
b->rt_gateway));
/* FIXME: Compare metrics, multipath config */
if (flags & LOOSE_FLAG_COMPARISON)
diff |= ROUTE_DIFF(FLAGS,
(a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
else
diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
#undef ROUTE_DIFF
return diff;
}
static struct trans_tbl route_attrs[] = {
__ADD(ROUTE_ATTR_FAMILY, family)
__ADD(ROUTE_ATTR_TOS, tos)
__ADD(ROUTE_ATTR_TABLE, table)
__ADD(ROUTE_ATTR_PROTOCOL, protocol)
__ADD(ROUTE_ATTR_SCOPE, scope)
__ADD(ROUTE_ATTR_TYPE, type)
__ADD(ROUTE_ATTR_FLAGS, flags)
__ADD(ROUTE_ATTR_DST, dst)
__ADD(ROUTE_ATTR_SRC, src)
__ADD(ROUTE_ATTR_IIF, iif)
__ADD(ROUTE_ATTR_OIF, oif)
__ADD(ROUTE_ATTR_GATEWAY, gateway)
__ADD(ROUTE_ATTR_PRIO, prio)
__ADD(ROUTE_ATTR_PREF_SRC, pref_src)
__ADD(ROUTE_ATTR_METRICS, metrics)
__ADD(ROUTE_ATTR_MULTIPATH, multipath)
__ADD(ROUTE_ATTR_REALMS, realms)
__ADD(ROUTE_ATTR_CACHEINFO, cacheinfo)
__ADD(ROUTE_ATTR_MP_ALGO, mp_algo)
};
static char *route_attrs2str(int attrs, char *buf, size_t len)
{
return __flags2str(attrs, buf, len, route_attrs,
ARRAY_SIZE(route_attrs));
}
/**
* @name Allocation/Freeing
* @{
*/
struct rtnl_route *rtnl_route_alloc(void)
{
return (struct rtnl_route *) nl_object_alloc(&route_obj_ops);
}
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 Attributes
* @{
*/
void rtnl_route_set_table(struct rtnl_route *route, int table)
{
route->rt_table = table;
route->ce_mask |= ROUTE_ATTR_TABLE;
}
int rtnl_route_get_table(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_TABLE)
return route->rt_table;
else
return RT_TABLE_MAIN;
}
void rtnl_route_set_scope(struct rtnl_route *route, int scope)
{
route->rt_scope = scope;
route->ce_mask |= ROUTE_ATTR_SCOPE;
}
int rtnl_route_get_scope(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_SCOPE)
return route->rt_scope;
else
return RT_SCOPE_NOWHERE;
}
void rtnl_route_set_tos(struct rtnl_route *route, int tos)
{
route->rt_tos = tos;
route->ce_mask |= ROUTE_ATTR_TOS;
}
int rtnl_route_get_tos(struct rtnl_route *route)
{
return route->rt_tos;
}
void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms)
{
route->rt_realms = realms;
route->ce_mask |= ROUTE_ATTR_REALMS;
}
realm_t rtnl_route_get_realms(struct rtnl_route *route)
{
return route->rt_realms;
}
void rtnl_route_set_protocol(struct rtnl_route *route, int proto)
{
route->rt_protocol = proto;
route->ce_mask |= ROUTE_ATTR_PROTOCOL;
}
int rtnl_route_get_protocol(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_PROTOCOL)
return route->rt_protocol;
else
return RTPROT_STATIC;
}
void rtnl_route_set_prio(struct rtnl_route *route, int prio)
{
route->rt_prio = prio;
route->ce_mask |= ROUTE_ATTR_PRIO;
}
int rtnl_route_get_prio(struct rtnl_route *route)
{
return route->rt_prio;
}
void rtnl_route_set_family(struct rtnl_route *route, int family)
{
route->rt_family = family;
route->ce_mask |= ROUTE_ATTR_FAMILY;
}
int rtnl_route_get_family(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_FAMILY)
return route->rt_family;
else
return AF_UNSPEC;
}
int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr)
{
if (route->ce_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->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY);
return 0;
}
struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route)
{
return route->rt_dst;
}
int rtnl_route_get_dst_len(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_DST)
return nl_addr_get_prefixlen(route->rt_dst);
else
return 0;
}
int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr)
{
if (route->ce_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->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY);
return 0;
}
struct nl_addr *rtnl_route_get_src(struct rtnl_route *route)
{
return route->rt_src;
}
int rtnl_route_get_src_len(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_SRC)
return nl_addr_get_prefixlen(route->rt_src);
else
return 0;
}
int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr)
{
if (route->ce_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->ce_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY);
return 0;
}
struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route)
{
return route->rt_gateway;
}
void rtnl_route_set_type(struct rtnl_route *route, int type)
{
route->rt_type = type;
route->ce_mask |= ROUTE_ATTR_TYPE;
}
int rtnl_route_get_type(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_TYPE)
return route->rt_type;
else
return RTN_UNICAST;
}
void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags)
{
route->rt_flag_mask |= flags;
route->rt_flags |= flags;
route->ce_mask |= ROUTE_ATTR_FLAGS;
}
void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags)
{
route->rt_flag_mask |= flags;
route->rt_flags &= ~flags;
route->ce_mask |= ROUTE_ATTR_FLAGS;
}
unsigned int rtnl_route_get_flags(struct rtnl_route *route)
{
return route->rt_flags;
}
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;
}
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;
}
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];
}
int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr)
{
if (route->ce_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->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY);
return 0;
}
struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route)
{
return route->rt_pref_src;
}
void rtnl_route_set_oif(struct rtnl_route *route, int ifindex)
{
route->rt_oif = ifindex;
route->ce_mask |= ROUTE_ATTR_OIF;
}
int rtnl_route_get_oif(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_OIF)
return route->rt_oif;
else
return RTNL_LINK_NOT_FOUND;
}
void rtnl_route_set_iif(struct rtnl_route *route, const char *name)
{
strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1);
route->ce_mask |= ROUTE_ATTR_IIF;
}
char *rtnl_route_get_iif(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_IIF)
return route->rt_iif;
else
return NULL;
}
void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
{
nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops);
route->ce_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;
}
void rtnl_route_set_cacheinfo(struct rtnl_route *route,
struct rtnl_rtcacheinfo *ci)
{
memcpy(&route->rt_cacheinfo, ci, sizeof(*ci));
route->ce_mask |= ROUTE_ATTR_CACHEINFO;
}
uint32_t rtnl_route_get_mp_algo(struct rtnl_route *route)
{
if (route->ce_mask & ROUTE_ATTR_MP_ALGO)
return route->rt_mp_algo;
else
return IP_MP_ALG_NONE;
}
void rtnl_route_set_mp_algo(struct rtnl_route *route, uint32_t algo)
{
route->rt_mp_algo = algo;
route->ce_mask |= ROUTE_ATTR_MP_ALGO;
}
/** @} */
struct nl_object_ops route_obj_ops = {
.oo_name = "route/route",
.oo_size = sizeof(struct rtnl_route),
.oo_constructor = route_constructor,
.oo_free_data = route_free_data,
.oo_clone = route_clone,
.oo_dump[NL_DUMP_BRIEF] = route_dump_brief,
.oo_dump[NL_DUMP_FULL] = route_dump_full,
.oo_dump[NL_DUMP_STATS] = route_dump_stats,
.oo_dump[NL_DUMP_XML] = route_dump_xml,
.oo_dump[NL_DUMP_ENV] = route_dump_env,
.oo_compare = route_compare,
.oo_attrs2str = route_attrs2str,
.oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
ROUTE_ATTR_TABLE | ROUTE_ATTR_DST),
};
/** @} */