API to issue direct GET requests to the kernel

Provide nl_pickup() to pick up an answer from a netlink request and parse
it using the supplied parser.

Add rtnl_link_get_kernel() which sends an RTM_GETLINK to the kernel to
fetch a single link directly from the kernel. This can be faster than
dumping the whole table, especially if lots of links are configured.
This commit is contained in:
Thomas Graf 2011-04-11 12:34:01 +02:00
parent 41fb241b7b
commit 48d543cfdf
4 changed files with 174 additions and 1 deletions

View file

@ -31,6 +31,8 @@
#include <netlink/types.h>
#include <netlink/handlers.h>
#include <netlink/socket.h>
#include <netlink/object.h>
#include <netlink/cache-api.h>
#ifdef __cplusplus
extern "C" {
@ -74,6 +76,12 @@ extern int nl_recvmsgs_default(struct nl_sock *);
extern int nl_wait_for_ack(struct nl_sock *);
extern int nl_pickup(struct nl_sock *,
int (*parser)(struct nl_cache_ops *,
struct sockaddr_nl *,
struct nlmsghdr *,
struct nl_parser_param *),
struct nl_object **);
/* Netlink Family Translations */
extern char * nl_nlfamily2str(int, char *, size_t);
extern int nl_str2nlfamily(const char *);

View file

@ -105,6 +105,10 @@ extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *,
extern int rtnl_link_build_delete_request(const struct rtnl_link *,
struct nl_msg **);
extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *);
extern int rtnl_link_build_get_request(int, const char *,
struct nl_msg **);
extern int rtnl_link_get_kernel(struct nl_sock *, int, const char *,
struct rtnl_link **);
/* Name <-> Index Translations */
extern char * rtnl_link_i2name(struct nl_cache *, int, char *, size_t);

View file

@ -799,6 +799,76 @@ int nl_wait_for_ack(struct nl_sock *sk)
return err;
}
/** @cond SKIP */
struct pickup_param
{
int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
struct nlmsghdr *, struct nl_parser_param *);
struct nl_object *result;
};
static int __store_answer(struct nl_object *obj, struct nl_parser_param *p)
{
struct pickup_param *pp = p->pp_arg;
/*
* the parser will put() the object at the end, expecting the cache
* to take the reference.
*/
nl_object_get(obj);
pp->result = obj;
return 0;
}
static int __pickup_answer(struct nl_msg *msg, void *arg)
{
struct pickup_param *pp = arg;
struct nl_parser_param parse_arg = {
.pp_cb = __store_answer,
.pp_arg = pp,
};
return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg);
}
/** @endcond */
/**
* Pickup netlink answer, parse is and return object
* @arg sk Netlink socket
* @arg parser Parser function to parse answer
* @arg result Result pointer to return parsed object
*
* @return 0 on success or a negative error code.
*/
int nl_pickup(struct nl_sock *sk,
int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
struct nlmsghdr *, struct nl_parser_param *),
struct nl_object **result)
{
struct nl_cb *cb;
int err;
struct pickup_param pp = {
.parser = parser,
};
cb = nl_cb_clone(sk->s_cb);
if (cb == NULL)
return -NLE_NOMEM;
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp);
err = nl_recvmsgs(sk, cb);
if (err < 0)
goto errout;
*result = pp.result;
errout:
nl_cb_put(cb);
return err;
}
/** @} */
/** @} */

View file

@ -655,7 +655,6 @@ static int link_request_update(struct nl_cache *cache, struct nl_sock *sk)
static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
{
char buf[128];
struct nl_cache *cache = dp_cache(obj);
struct rtnl_link *link = (struct rtnl_link *) obj;
nl_dump_line(p, "%s %s ", link->l_name,
@ -1244,6 +1243,98 @@ int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link)
return nl_send_sync(sk, msg);
}
/**
* Build a netlink message requesting a link
* @arg ifindex Interface index
* @arg name Name of link
* @arg result Pointer to store resulting netlink message
*
* The behaviour of this function is identical to rtnl_link_get_kernel()
* with the exception that it will not send the message but return it in
* the provided return pointer instead.
*
* @see rtnl_link_get_kernel()
*
* @return 0 on success or a negative error code.
*/
int rtnl_link_build_get_request(int ifindex, const char *name,
struct nl_msg **result)
{
struct ifinfomsg ifi;
struct nl_msg *msg;
if (ifindex <= 0 && !name) {
APPBUG("ifindex or name must be specified");
return -NLE_MISSING_ATTR;
}
memset(&ifi, 0, sizeof(ifi));
if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0)))
return -NLE_NOMEM;
if (ifindex > 0)
ifi.ifi_index = ifindex;
if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
goto nla_put_failure;
if (name)
NLA_PUT_STRING(msg, IFLA_IFNAME, name);
*result = msg;
return 0;
nla_put_failure:
nlmsg_free(msg);
return -NLE_MSGSIZE;
}
/**
* Get a link object directly from the kernel
* @arg sk Netlink socket
* @arg ifindex Interface index
* @arg name name of link
* @arg result result pointer to return link object
*
* This function builds a \c RTM_GETLINK netlink message to request
* a specific link directly from the kernel. The returned answer is
* parsed into a struct rtnl_link object and returned via the result
* pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was
* found.
*
* @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
* this function to return immediately after sending. In this case,
* it is the responsibility of the caller to handle any error
* messages returned.
*
* @return 0 on success or a negative error code.
*/
int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name,
struct rtnl_link **result)
{
struct nl_msg *msg = NULL;
struct nl_object *obj;
int err;
if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0)
return err;
err = nl_send_auto(sk, msg);
nlmsg_free(msg);
if (err < 0)
return err;
if ((err = nl_pickup(sk, link_msg_parser, &obj)) < 0)
return err;
/* We have used link_msg_parser(), object is definitely a link */
*result = (struct rtnl_link *) obj;
return 0;
}
/** @} */
/**