You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
7.9 KiB
352 lines
7.9 KiB
/* SPDX-License-Identifier: LGPL-2.1-only */
|
|
/*
|
|
* 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-private/utils.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)),
|
|
nl_strerror_l(-res->fr_error), res->fr_error);
|
|
}
|
|
|
|
static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
|
|
{
|
|
result_dump_line(obj, p);
|
|
}
|
|
|
|
static uint64_t result_compare(struct nl_object *_a, struct nl_object *_b,
|
|
uint64_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);
|
|
}
|
|
|
|
/** @} */
|