From 48d543cfdf49aef75b9898ebea379f2303d3935d Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Mon, 11 Apr 2011 12:34:01 +0200 Subject: [PATCH] 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. --- include/netlink/netlink.h | 8 ++++ include/netlink/route/link.h | 4 ++ lib/nl.c | 70 +++++++++++++++++++++++++++ lib/route/link.c | 93 +++++++++++++++++++++++++++++++++++- 4 files changed, 174 insertions(+), 1 deletion(-) diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h index a13c48f..0768708 100644 --- a/include/netlink/netlink.h +++ b/include/netlink/netlink.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include #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 *); diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h index b9951d6..3790c09 100644 --- a/include/netlink/route/link.h +++ b/include/netlink/route/link.h @@ -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); diff --git a/lib/nl.c b/lib/nl.c index b70242c..f5f94e3 100644 --- a/lib/nl.c +++ b/lib/nl.c @@ -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; +} + /** @} */ /** @} */ diff --git a/lib/route/link.c b/lib/route/link.c index 6ecde1d..38b9d82 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -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; +} + /** @} */ /**