
The cache pickup does a check for duplicates for some time now.
However, the pickup in nl_cache_refill() explicitely clears the
cache, so the pickup doesn't need to actually do the dupe check,
as uniqueness is already guaranteed be Netlink subsystem.
And avoiding the dup check is beneficitial for performance
reasons, as the current algorithm is O(n^2).
http://lists.infradead.org/pipermail/libnl/2014-October/001680.html
Fixes: 96bb7c9a4c
Signed-off-by: Holger Eitzenberger <holger@eitzenberger.org>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
349 lines
7.8 KiB
C
349 lines
7.8 KiB
C
/*
|
|
* lib/fib_lookup/lookup.c FIB Lookup
|
|
*
|
|
* 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-2012 Thomas Graf <tgraf@suug.ch>
|
|
*/
|
|
|
|
/**
|
|
* @ingroup rtnl
|
|
* @defgroup fib_lookup FIB Lookup
|
|
* @brief
|
|
* @{
|
|
*/
|
|
|
|
#include <netlink-private/netlink.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/attr.h>
|
|
#include <netlink/utils.h>
|
|
#include <netlink/object.h>
|
|
#include <netlink/route/rtnl.h>
|
|
#include <netlink/route/route.h>
|
|
#include <netlink/fib_lookup/request.h>
|
|
#include <netlink/fib_lookup/lookup.h>
|
|
|
|
/** @cond SKIP */
|
|
static struct nl_cache_ops fib_lookup_ops;
|
|
static struct nl_object_ops result_obj_ops;
|
|
|
|
/* not exported so far */
|
|
struct fib_result_nl {
|
|
uint32_t fl_addr; /* To be looked up*/
|
|
uint32_t fl_fwmark;
|
|
unsigned char fl_tos;
|
|
unsigned char fl_scope;
|
|
unsigned char tb_id_in;
|
|
|
|
unsigned char tb_id; /* Results */
|
|
unsigned char prefixlen;
|
|
unsigned char nh_sel;
|
|
unsigned char type;
|
|
unsigned char scope;
|
|
int err;
|
|
};
|
|
/** @endcond */
|
|
|
|
static void result_free_data(struct nl_object *obj)
|
|
{
|
|
struct flnl_result *res = nl_object_priv(obj);
|
|
|
|
if (res && res->fr_req)
|
|
nl_object_put(OBJ_CAST(res->fr_req));
|
|
}
|
|
|
|
static int result_clone(struct nl_object *_dst, struct nl_object *_src)
|
|
{
|
|
struct flnl_result *dst = nl_object_priv(_dst);
|
|
struct flnl_result *src = nl_object_priv(_src);
|
|
|
|
if (src->fr_req)
|
|
if (!(dst->fr_req = (struct flnl_request *)
|
|
nl_object_clone(OBJ_CAST(src->fr_req))))
|
|
return -NLE_NOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
|
struct nlmsghdr *n, struct nl_parser_param *pp)
|
|
{
|
|
struct flnl_result *res;
|
|
struct fib_result_nl *fr;
|
|
struct nl_addr *addr;
|
|
int err = -NLE_INVAL;
|
|
|
|
res = flnl_result_alloc();
|
|
if (!res)
|
|
goto errout;
|
|
|
|
res->ce_msgtype = n->nlmsg_type;
|
|
|
|
res->fr_req = flnl_request_alloc();
|
|
if (!res->fr_req)
|
|
goto errout;
|
|
|
|
fr = nlmsg_data(n);
|
|
addr = nl_addr_build(AF_INET, &fr->fl_addr, 4);
|
|
if (!addr)
|
|
goto errout;
|
|
err = flnl_request_set_addr(res->fr_req, addr);
|
|
nl_addr_put(addr);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark);
|
|
flnl_request_set_tos(res->fr_req, fr->fl_tos);
|
|
flnl_request_set_scope(res->fr_req, fr->fl_scope);
|
|
flnl_request_set_table(res->fr_req, fr->tb_id_in);
|
|
|
|
res->fr_table_id = fr->tb_id;
|
|
res->fr_prefixlen = fr->prefixlen;
|
|
res->fr_nh_sel = fr->nh_sel;
|
|
res->fr_type = fr->type;
|
|
res->fr_scope = fr->scope;
|
|
res->fr_error = fr->err;
|
|
|
|
err = pp->pp_cb((struct nl_object *) res, pp);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
/* REAL HACK, fib_lookup doesn't support ACK nor does it
|
|
* send a DONE message, enforce end of message stream
|
|
* after just the first message */
|
|
err = NL_STOP;
|
|
|
|
errout:
|
|
flnl_result_put(res);
|
|
return err;
|
|
}
|
|
|
|
static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
struct flnl_result *res = (struct flnl_result *) obj;
|
|
char buf[256];
|
|
|
|
nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n",
|
|
rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)),
|
|
res->fr_prefixlen, res->fr_nh_sel);
|
|
nl_dump_line(p, "type %s ",
|
|
nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
|
|
nl_dump(p, "scope %s error %s (%d)\n",
|
|
rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
|
|
strerror_r(-res->fr_error, buf, sizeof(buf)), res->fr_error);
|
|
}
|
|
|
|
static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
result_dump_line(obj, p);
|
|
}
|
|
|
|
static int result_compare(struct nl_object *_a, struct nl_object *_b,
|
|
uint32_t attrs, int flags)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @name Allocation/Freeing
|
|
* @{
|
|
*/
|
|
|
|
struct flnl_result *flnl_result_alloc(void)
|
|
{
|
|
return (struct flnl_result *) nl_object_alloc(&result_obj_ops);
|
|
}
|
|
|
|
void flnl_result_put(struct flnl_result *res)
|
|
{
|
|
nl_object_put((struct nl_object *) res);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Cache Management
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Allocate lookup result cache.
|
|
*
|
|
* Allocates a new lookup result cache and initializes it properly.
|
|
*
|
|
* @note Free the memory after usage using nl_cache_destroy_and_free().
|
|
* @return Newly allocated cache or NULL if an error occured.
|
|
*/
|
|
struct nl_cache *flnl_result_alloc_cache(void)
|
|
{
|
|
return nl_cache_alloc(&fib_lookup_ops);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Lookup
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Builds a netlink request message to do a lookup
|
|
* @arg req Requested match.
|
|
* @arg flags additional netlink message flags
|
|
* @arg result Result pointer
|
|
*
|
|
* Builds a new netlink message requesting a change of link attributes.
|
|
* The netlink message header isn't fully equipped with all relevant
|
|
* fields and must be sent out via nl_send_auto_complete() or
|
|
* supplemented as needed.
|
|
* \a old must point to a link currently configured in the kernel
|
|
* and \a tmpl must contain the attributes to be changed set via
|
|
* \c rtnl_link_set_* functions.
|
|
*
|
|
* @return 0 on success or a negative error code.
|
|
*/
|
|
int flnl_lookup_build_request(struct flnl_request *req, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
struct nl_msg *msg;
|
|
struct nl_addr *addr;
|
|
uint64_t fwmark;
|
|
int tos, scope, table;
|
|
struct fib_result_nl fr = {0};
|
|
|
|
fwmark = flnl_request_get_fwmark(req);
|
|
tos = flnl_request_get_tos(req);
|
|
scope = flnl_request_get_scope(req);
|
|
table = flnl_request_get_table(req);
|
|
|
|
fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0;
|
|
fr.fl_tos = tos >= 0 ? tos : 0;
|
|
fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE;
|
|
fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC;
|
|
|
|
addr = flnl_request_get_addr(req);
|
|
if (!addr)
|
|
return -NLE_MISSING_ATTR;
|
|
|
|
fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr);
|
|
|
|
msg = nlmsg_alloc_simple(0, flags);
|
|
if (!msg)
|
|
return -NLE_NOMEM;
|
|
|
|
if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0)
|
|
goto errout;
|
|
|
|
*result = msg;
|
|
return 0;
|
|
|
|
errout:
|
|
nlmsg_free(msg);
|
|
return -NLE_MSGSIZE;
|
|
}
|
|
|
|
/**
|
|
* Perform FIB Lookup
|
|
* @arg sk Netlink socket.
|
|
* @arg req Lookup request object.
|
|
* @arg cache Cache for result.
|
|
*
|
|
* Builds a netlink message to request a FIB lookup, waits for the
|
|
* reply and adds the result to the specified cache.
|
|
*
|
|
* @return 0 on success or a negative error code.
|
|
*/
|
|
int flnl_lookup(struct nl_sock *sk, struct flnl_request *req,
|
|
struct nl_cache *cache)
|
|
{
|
|
struct nl_msg *msg;
|
|
int err;
|
|
|
|
if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto_complete(sk, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return nl_cache_pickup_checkdup(sk, cache);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Attribute Access
|
|
* @{
|
|
*/
|
|
|
|
int flnl_result_get_table_id(struct flnl_result *res)
|
|
{
|
|
return res->fr_table_id;
|
|
}
|
|
|
|
int flnl_result_get_prefixlen(struct flnl_result *res)
|
|
{
|
|
return res->fr_prefixlen;
|
|
}
|
|
|
|
int flnl_result_get_nexthop_sel(struct flnl_result *res)
|
|
{
|
|
return res->fr_nh_sel;
|
|
}
|
|
|
|
int flnl_result_get_type(struct flnl_result *res)
|
|
{
|
|
return res->fr_type;
|
|
}
|
|
|
|
int flnl_result_get_scope(struct flnl_result *res)
|
|
{
|
|
return res->fr_scope;
|
|
}
|
|
|
|
int flnl_result_get_error(struct flnl_result *res)
|
|
{
|
|
return res->fr_error;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
static struct nl_object_ops result_obj_ops = {
|
|
.oo_name = "fib_lookup/result",
|
|
.oo_size = sizeof(struct flnl_result),
|
|
.oo_free_data = result_free_data,
|
|
.oo_clone = result_clone,
|
|
.oo_dump = {
|
|
[NL_DUMP_LINE] = result_dump_line,
|
|
[NL_DUMP_DETAILS] = result_dump_details,
|
|
},
|
|
.oo_compare = result_compare,
|
|
};
|
|
|
|
static struct nl_cache_ops fib_lookup_ops = {
|
|
.co_name = "fib_lookup/fib_lookup",
|
|
.co_hdrsize = sizeof(struct fib_result_nl),
|
|
.co_msgtypes = {
|
|
{ 0, NL_ACT_UNSPEC, "any" },
|
|
END_OF_MSGTYPES_LIST,
|
|
},
|
|
.co_protocol = NETLINK_FIB_LOOKUP,
|
|
.co_msg_parser = result_msg_parser,
|
|
.co_obj_ops = &result_obj_ops,
|
|
};
|
|
|
|
static void __init fib_lookup_init(void)
|
|
{
|
|
nl_cache_mngt_register(&fib_lookup_ops);
|
|
}
|
|
|
|
static void __exit fib_lookup_exit(void)
|
|
{
|
|
nl_cache_mngt_unregister(&fib_lookup_ops);
|
|
}
|
|
|
|
/** @} */
|