
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.
447 lines
10 KiB
C
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);
|
|
}
|
|
|
|
/** @} */
|