libnl/lib/route/route.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

447 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 {
dst = nl_addr_alloc(0);
nl_addr_set_family(dst, rtm->rtm_family);
}
nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
err = rtnl_route_set_dst(route, dst);
if (err < 0)
goto errout;
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;
err = P_ACCEPT;
errout:
rtnl_route_put(route);
return err;
errout_errno:
err = nl_get_errno();
goto errout;
}
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);
}
/** @} */