libnl/lib/route/route.c
Thomas Graf 3040a1d625 Export interface to define caches
This interface was internal so far which required all code defining
caches to be compiled with the sources available.

In order to simplify the interface, the co_msg_parser prototype was
changed to take the struct nl_parser_param directly instead of a
void *. It used to be void * because the co_msg_parser was directly
passed as the NL_CB_VALID callback function.
2007-09-17 13:36:16 +02:00

443 lines
10 KiB
C

/*
* 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 <tgraf@suug.ch>
*/
/**
* @ingroup rtnl
* @defgroup route Routing
* @brief
* @{
*/
#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>
static struct nl_cache_ops rtnl_route_ops;
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_cacheinfo_into_route(struct rta_cacheinfo *ci,
struct rtnl_route *route)
{
struct rtnl_rtcacheinfo nci = {
.rtci_clntref = ci->rta_clntref,
.rtci_last_use = ci->rta_lastuse,
.rtci_expires = ci->rta_expires,
.rtci_error = ci->rta_error,
.rtci_used = ci->rta_used,
.rtci_id = ci->rta_id,
.rtci_ts = ci->rta_ts,
.rtci_tsage = ci->rta_tsage,
};
rtnl_route_set_cacheinfo(route, &nci);
}
static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *nlh, struct nl_parser_param *pp)
{
struct rtmsg *rtm;
struct rtnl_route *route;
struct nlattr *tb[RTA_MAX + 1];
struct nl_addr *src = NULL, *dst = NULL, *addr;
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;
rtm = nlmsg_data(nlh);
rtnl_route_set_family(route, rtm->rtm_family);
rtnl_route_set_tos(route, rtm->rtm_tos);
rtnl_route_set_table(route, rtm->rtm_table);
rtnl_route_set_type(route, rtm->rtm_type);
rtnl_route_set_scope(route, rtm->rtm_scope);
rtnl_route_set_protocol(route, rtm->rtm_protocol);
rtnl_route_set_flags(route, rtm->rtm_flags);
if (tb[RTA_DST]) {
dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family);
if (dst == NULL)
goto errout_errno;
} else if (rtm->rtm_dst_len)
dst = nl_addr_alloc(0);
if (dst) {
nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
rtnl_route_set_dst(route, dst);
nl_addr_put(dst);
}
if (tb[RTA_SRC]) {
src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family);
if (src == NULL)
goto errout_errno;
} else if (rtm->rtm_src_len)
src = nl_addr_alloc(0);
if (src) {
nl_addr_set_prefixlen(src, rtm->rtm_src_len);
rtnl_route_set_src(route, src);
nl_addr_put(src);
}
if (tb[RTA_IIF])
rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF]));
if (tb[RTA_OIF])
rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF]));
if (tb[RTA_GATEWAY]) {
addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family);
if (addr == NULL)
goto errout_errno;
rtnl_route_set_gateway(route, addr);
nl_addr_put(addr);
}
if (tb[RTA_PRIORITY])
rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY]));
if (tb[RTA_PREFSRC]) {
addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family);
if (addr == NULL)
goto errout_errno;
rtnl_route_set_pref_src(route, addr);
nl_addr_put(addr);
}
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]);
if (rtnl_route_set_metric(route, i, m) < 0)
goto errout_errno;
}
}
}
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])
rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW]));
if (tb[RTA_CACHEINFO])
copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route);
if (tb[RTA_MP_ALGO])
rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_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);
}
/**
* @name 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;
cache = nl_cache_alloc(&rtnl_route_ops);
if (!cache)
return NULL;
if (handle && nl_cache_refill(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)
{
struct nl_msg *msg;
struct nl_addr *addr;
int scope, i, oif, nmetrics = 0;
struct nlattr *metrics;
struct rtmsg rtmsg = {
.rtm_family = rtnl_route_get_family(tmpl),
.rtm_dst_len = rtnl_route_get_dst_len(tmpl),
.rtm_src_len = rtnl_route_get_src_len(tmpl),
.rtm_tos = rtnl_route_get_tos(tmpl),
.rtm_table = rtnl_route_get_table(tmpl),
.rtm_type = rtnl_route_get_type(tmpl),
.rtm_protocol = rtnl_route_get_protocol(tmpl),
.rtm_flags = rtnl_route_get_flags(tmpl),
};
if (rtmsg.rtm_family == AF_UNSPEC) {
nl_error(EINVAL, "Cannot build route message, address " \
"family is unknown.");
return NULL;
}
scope = rtnl_route_get_scope(tmpl);
if (scope == RT_SCOPE_NOWHERE) {
if (rtmsg.rtm_type == RTN_LOCAL)
scope = RT_SCOPE_HOST;
else {
/* XXX Change to UNIVERSE if gw || nexthops */
scope = RT_SCOPE_LINK;
}
}
rtmsg.rtm_scope = scope;
msg = nlmsg_alloc_simple(cmd, flags);
if (msg == NULL)
return NULL;
if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
goto nla_put_failure;
addr = rtnl_route_get_dst(tmpl);
if (addr)
NLA_PUT_ADDR(msg, RTA_DST, addr);
addr = rtnl_route_get_src(tmpl);
if (addr)
NLA_PUT_ADDR(msg, RTA_SRC, addr);
addr = rtnl_route_get_gateway(tmpl);
if (addr)
NLA_PUT_ADDR(msg, RTA_GATEWAY, addr);
addr = rtnl_route_get_pref_src(tmpl);
if (addr)
NLA_PUT_ADDR(msg, RTA_PREFSRC, addr);
NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl));
oif = rtnl_route_get_oif(tmpl);
if (oif != RTNL_LINK_NOT_FOUND)
NLA_PUT_U32(msg, RTA_OIF, oif);
for (i = 1; i <= RTAX_MAX; i++)
if (rtnl_route_get_metric(tmpl, i) != UINT_MAX)
nmetrics++;
if (nmetrics > 0) {
unsigned int val;
metrics = nla_nest_start(msg, RTA_METRICS);
if (metrics == NULL)
goto nla_put_failure;
for (i = 1; i <= RTAX_MAX; i++) {
val = rtnl_route_get_metric(tmpl, i);
if (val != UINT_MAX)
NLA_PUT_U32(msg, i, val);
}
nla_nest_end(msg, metrics);
}
#if 0
RTA_IIF,
RTA_MULTIPATH,
RTA_PROTOINFO,
RTA_FLOW,
RTA_CACHEINFO,
RTA_SESSION,
RTA_MP_ALGO,
#endif
return msg;
nla_put_failure:
nlmsg_free(msg);
return NULL;
}
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);
}
int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route,
int flags)
{
struct nl_msg *msg;
int err;
msg = rtnl_route_build_add_request(route, flags);
if (!msg)
return nl_get_errno();
err = nl_send_auto_complete(handle, msg);
nlmsg_free(msg);
if (err < 0)
return err;
return nl_wait_for_ack(handle);
}
struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags)
{
return build_route_msg(tmpl, RTM_DELROUTE, flags);
}
int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route,
int flags)
{
struct nl_msg *msg;
int err;
msg = rtnl_route_build_del_request(route, flags);
if (!msg)
return nl_get_errno();
err = nl_send_auto_complete(handle, msg);
nlmsg_free(msg);
if (err < 0)
return err;
return nl_wait_for_ack(handle);
}
/** @} */
static struct nl_af_group route_groups[] = {
{ AF_INET, RTNLGRP_IPV4_ROUTE },
{ AF_INET6, RTNLGRP_IPV6_ROUTE },
{ AF_DECnet, RTNLGRP_DECnet_ROUTE },
{ END_OF_GROUP_LIST },
};
static struct nl_cache_ops rtnl_route_ops = {
.co_name = "route/route",
.co_hdrsize = sizeof(struct rtmsg),
.co_msgtypes = {
{ RTM_NEWROUTE, NL_ACT_NEW, "new" },
{ RTM_DELROUTE, NL_ACT_DEL, "del" },
{ RTM_GETROUTE, NL_ACT_GET, "get" },
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_ROUTE,
.co_groups = route_groups,
.co_request_update = route_request_update,
.co_msg_parser = route_msg_parser,
.co_obj_ops = &route_obj_ops,
};
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);
}
/** @} */