X-Git-Url: http://git.onelab.eu/?p=libnl.git;a=blobdiff_plain;f=lib%2Ffib_lookup%2Flookup.c;fp=lib%2Ffib_lookup%2Flookup.c;h=6306fb4fcb03a9a27ecf9ea6485e870db4350490;hp=0000000000000000000000000000000000000000;hb=4cee2ecb3b8afa0637e6f5fe4c57985a4bc740ff;hpb=2df2fbe518d5a221ce6e3ee88a3fb23fb1b94b27 diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c new file mode 100644 index 0000000..6306fb4 --- /dev/null +++ b/lib/fib_lookup/lookup.c @@ -0,0 +1,355 @@ +/* + * 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-2006 Thomas Graf + */ + +/** + * @defgroup fib_lookup FIB Lookup + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +static struct nl_cache_ops fib_lookup_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) + flnl_request_put(res->fr_req); +} + +static int result_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct flnl_result *res; + struct fib_result_nl *fr; + struct nl_parser_param *pp = arg; + struct nl_addr *addr; + int err = -EINVAL; + + 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 */ + return NL_EXIT; + +errout: + flnl_result_put(res); + return err; +} + +static int result_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct flnl_result *res = (struct flnl_result *) obj; + char buf[128]; + int line = 1; + + dp_dump(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); + dp_dump_line(p, line++, "type %s ", + nl_rtntype2str(res->fr_type, buf, sizeof(buf))); + dp_dump(p, "scope %s error %s (%d)\n", + rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), + strerror(-res->fr_error), res->fr_error); + + return line; +} + +static int result_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + return result_dump_brief(obj, p); +} + +static int result_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct flnl_result *o = (struct flnl_result *) obj; + struct flnl_result *f = (struct flnl_result *) filter; + + if (o->fr_req && f->fr_req) + return flnl_request_cmp(o->fr_req, f->fr_req); + + return 1; +} + +/** + * @name FIB Result Allocation/Freeage + * @{ + */ + +/** + * Allocate and initialize new lookup result object. + * @note Free the memory after usage using flnl_result_put() or + * flnl_result_free(). + * @return Newly allocated lookup result object or NULL if an error occured. + */ +struct flnl_result *flnl_result_alloc(void) +{ + return (struct flnl_result *) nl_object_alloc_from_ops(&fib_lookup_ops); +} + +/** + * 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_from_ops(&fib_lookup_ops); +} + +/** + * Give back reference on lookup result object. + * @arg res lookup result object to be given back. + * + * Decrements the reference counter and frees the object if the + * last reference has been released. + */ +void flnl_result_put(struct flnl_result *res) +{ + nl_object_put((struct nl_object *) res); +} + +/** + * Free lookup result object. + * @arg res lookup result object to be freed. + * + * @note Always use flnl_result_put() unless you're absolutely sure + * that no other user may have a reference on this object. + */ +void flnl_result_free(struct flnl_result *res) +{ + nl_object_free((struct nl_object *) res); +} + +/** @} */ + +/** + * @name Lookup + * @{ + */ + +/** + * Builds a netlink request message to do a lookup + * @arg req Requested match. + * @arg flags additional netlink message flags + * + * 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 New netlink message + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags) +{ + 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) { + nl_error(EINVAL, "Request must specify the address"); + return NULL; + } + + fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); + + msg = nlmsg_build_simple(0, flags); + if (!msg) + goto errout; + + if (nlmsg_append(msg, &fr, sizeof(fr), 1) < 0) + goto errout; + + return msg; + +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * Perform FIB Lookup + * @arg handle Netlink handle. + * @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_handle *handle, struct flnl_request *req, + struct nl_cache *cache) +{ + struct nl_msg *msg; + int err; + + msg = flnl_lookup_build_request(req, 0); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_cache_pickup(handle, 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_cache_ops fib_lookup_ops = { + .co_name = "fib_lookup/fib_lookup", + .co_size = sizeof(struct flnl_result), + .co_hdrsize = sizeof(struct fib_result_nl), + .co_msgtypes = { + { 0, "any" }, + { -1, NULL }, + }, + .co_protocol = NETLINK_FIB_LOOKUP, + .co_msg_parser = result_msg_parser, + .co_free_data = result_free_data, + .co_dump[NL_DUMP_BRIEF] = result_dump_brief, + .co_dump[NL_DUMP_FULL] = result_dump_full, + .co_filter = result_filter, +}; + +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); +} + +/** @} */