summaryrefslogtreecommitdiffstats
path: root/lib/genl/mngt.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/genl/mngt.c')
-rw-r--r--lib/genl/mngt.c316
1 files changed, 206 insertions, 110 deletions
diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c
index 0ebe74d..a3faaf2 100644
--- a/lib/genl/mngt.c
+++ b/lib/genl/mngt.c
@@ -6,81 +6,19 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup genl
- * @defgroup genl_mngt Management
+ * @defgroup genl_mngt Family and Command Registration
*
- * @par 1) Registering a generic netlink module
- * @code
- * #include <netlink/genl/mngt.h>
+ * Registering Generic Netlink Families and Commands
*
- * // First step is to define all the commands being used in
- * // particular generic netlink family. The ID and name are
- * // mandatory to be filled out. A callback function and
- * // most the attribute policy that comes with it must be
- * // defined for commands expected to be issued towards
- * // userspace.
- * static struct genl_cmd foo_cmds[] = {
- * {
- * .c_id = FOO_CMD_NEW,
- * .c_name = "NEWFOO" ,
- * .c_maxattr = FOO_ATTR_MAX,
- * .c_attr_policy = foo_policy,
- * .c_msg_parser = foo_msg_parser,
- * },
- * {
- * .c_id = FOO_CMD_DEL,
- * .c_name = "DELFOO" ,
- * },
- * };
- *
- * // The list of commands must then be integrated into a
- * // struct genl_ops serving as handle for this particular
- * // family.
- * static struct genl_ops my_genl_ops = {
- * .o_cmds = foo_cmds,
- * .o_ncmds = ARRAY_SIZE(foo_cmds),
- * };
- *
- * // Using the above struct genl_ops an arbitary number of
- * // cache handles can be associated to it.
- * //
- * // The macro GENL_HDRSIZE() must be used to specify the
- * // length of the header to automatically take headers on
- * // generic layers into account.
- * //
- * // The macro GENL_FAMILY() is used to represent the generic
- * // netlink family id.
- * static struct nl_cache_ops genl_foo_ops = {
- * .co_name = "genl/foo",
- * .co_hdrsize = GENL_HDRSIZE(sizeof(struct my_hdr)),
- * .co_msgtypes = GENL_FAMILY(GENL_ID_GENERATE, "foo"),
- * .co_genl = &my_genl_ops,
- * .co_protocol = NETLINK_GENERIC,
- * .co_request_update = foo_request_update,
- * .co_obj_ops = &genl_foo_ops,
- * };
- *
- * // Finally each cache handle for a generic netlink family
- * // must be registered using genl_register().
- * static void __init foo_init(void)
- * {
- * genl_register(&genl_foo_ops);
- * }
- *
- * // ... respectively unregsted again.
- * static void __exit foo_exit(void)
- * {
- * genl_unregister(&genl_foo_ops);
- * }
- * @endcode
* @{
*/
-#include <netlink-generic.h>
+#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/mngt.h>
@@ -88,30 +26,38 @@
#include <netlink/genl/ctrl.h>
#include <netlink/utils.h>
+/** @cond SKIP */
+
static NL_LIST_HEAD(genl_ops_list);
-static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
- struct nlmsghdr *nlh, struct nl_parser_param *pp)
+static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id)
{
- int i, err;
- struct genlmsghdr *ghdr;
struct genl_cmd *cmd;
+ int i;
- ghdr = nlmsg_data(nlh);
+ for (i = 0; i < ops->o_ncmds; i++) {
+ cmd = &ops->o_cmds[i];
+ if (cmd->c_id == cmd_id)
+ return cmd;
+ }
- if (ops->co_genl == NULL)
- BUG();
+ return NULL;
+}
- for (i = 0; i < ops->co_genl->o_ncmds; i++) {
- cmd = &ops->co_genl->o_cmds[i];
- if (cmd->c_id == ghdr->cmd)
- goto found;
- }
+static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh,
+ struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg)
+{
+ int err;
+ struct genlmsghdr *ghdr;
+ struct genl_cmd *cmd;
- err = -NLE_MSGTYPE_NOSUPPORT;
- goto errout;
+ ghdr = genlmsg_hdr(nlh);
+
+ if (!(cmd = lookup_cmd(ops, ghdr->cmd))) {
+ err = -NLE_MSGTYPE_NOSUPPORT;
+ goto errout;
+ }
-found:
if (cmd->c_msg_parser == NULL)
err = -NLE_OPNOTSUPP;
else {
@@ -120,37 +66,68 @@ found:
.who = who,
.nlh = nlh,
.genlhdr = ghdr,
- .userhdr = genlmsg_data(ghdr),
+ .userhdr = genlmsg_user_hdr(ghdr),
.attrs = tb,
};
- err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr,
+ err = nlmsg_parse(nlh, GENL_HDRSIZE(ops->o_hdrsize), tb, cmd->c_maxattr,
cmd->c_attr_policy);
if (err < 0)
goto errout;
- err = cmd->c_msg_parser(ops, cmd, &info, pp);
+ err = cmd->c_msg_parser(cache_ops, cmd, &info, arg);
}
errout:
return err;
}
+static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ if (ops->co_genl == NULL)
+ BUG();
+
+ return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp);
+}
+
+static struct genl_ops *lookup_family(int family)
+{
+ struct genl_ops *ops;
+
+ nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+ if (ops->o_id == family)
+ return ops;
+ }
+
+ return NULL;
+}
+
+static struct genl_ops *lookup_family_by_name(const char *name)
+{
+ struct genl_ops *ops;
+
+ nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+ if (!strcmp(ops->o_name, name))
+ return ops;
+ }
+
+ return NULL;
+}
+
char *genl_op2name(int family, int op, char *buf, size_t len)
{
struct genl_ops *ops;
int i;
- nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
- if (ops->o_family == family) {
- for (i = 0; i < ops->o_ncmds; i++) {
- struct genl_cmd *cmd;
- cmd = &ops->o_cmds[i];
-
- if (cmd->c_id == op) {
- strncpy(buf, cmd->c_name, len - 1);
- return buf;
- }
+ if ((ops = lookup_family(family))) {
+ for (i = 0; i < ops->o_ncmds; i++) {
+ struct genl_cmd *cmd;
+ cmd = &ops->o_cmds[i];
+
+ if (cmd->c_id == op) {
+ strncpy(buf, cmd->c_name, len - 1);
+ return buf;
}
}
}
@@ -159,15 +136,107 @@ char *genl_op2name(int family, int op, char *buf, size_t len)
return NULL;
}
+/** @endcond */
+
+/**
+ * @name Registration
+ * @{
+ */
+
+/**
+ * Register Generic Netlink family and associated commands
+ * @arg ops Generic Netlink family definition
+ *
+ * Registers the specified Generic Netlink family definition together with
+ * all associated commands. After registration, received Generic Netlink
+ * messages can be passed to genl_handle_msg() which will validate the
+ * messages, look for a matching command and call the respective callback
+ * function automatically.
+ *
+ * @note Consider using genl_register() if the family is used to implement a
+ * cacheable type.
+ *
+ * @see genl_unregister_family();
+ * @see genl_register();
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_register_family(struct genl_ops *ops)
+{
+ if (!ops->o_name)
+ return -NLE_INVAL;
+
+ if (ops->o_cmds && ops->o_ncmds <= 0)
+ return -NLE_INVAL;
+
+ if (ops->o_id && lookup_family(ops->o_id))
+ return -NLE_EXIST;
+
+ if (lookup_family_by_name(ops->o_name))
+ return -NLE_EXIST;
+
+ nl_list_add_tail(&ops->o_list, &genl_ops_list);
+
+ return 0;
+}
+
+/**
+ * Unregister Generic Netlink family
+ * @arg ops Generic Netlink family definition
+ *
+ * Unregisters a family and all associated commands that were previously
+ * registered using genl_register_family().
+ *
+ * @see genl_register_family()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_unregister_family(struct genl_ops *ops)
+{
+ nl_list_del(&ops->o_list);
+
+ return 0;
+}
/**
- * @name Register/Unregister
+ * Run a received message through the demultiplexer
+ * @arg msg Generic Netlink message
+ * @arg arg Argument passed on to the message handler callback
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_handle_msg(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct genl_ops *ops;
+
+ if (!genlmsg_valid_hdr(nlh, 0))
+ return -NLE_INVAL;
+
+ if (!(ops = lookup_family(nlh->nlmsg_type)))
+ return -NLE_MSGTYPE_NOSUPPORT;
+
+ return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg);
+}
+
+/** @} */
+
+/**
+ * @name Registration of Cache Operations
* @{
*/
/**
- * Register generic netlink operations
- * @arg ops cache operations
+ * Register Generic Netlink family backed cache
+ * @arg ops Cache operations definition
+ *
+ * Same as genl_register_family() but additionally registers the specified
+ * cache operations using nl_cache_mngt_register() and associates it with
+ * the Generic Netlink family.
+ *
+ * @see genl_register_family()
+ *
+ * @return 0 on success or a negative error code.
*/
int genl_register(struct nl_cache_ops *ops)
{
@@ -189,13 +258,13 @@ int genl_register(struct nl_cache_ops *ops)
}
ops->co_genl->o_cache_ops = ops;
+ ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN;
ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
- ops->co_genl->o_family = ops->co_msgtypes[0].mt_id;
+ ops->co_genl->o_id = ops->co_msgtypes[0].mt_id;
ops->co_msg_parser = genl_msg_parser;
- /* FIXME: check for dup */
-
- nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list);
+ if ((err = genl_register_family(ops->co_genl)) < 0)
+ goto errout;
err = nl_cache_mngt_register(ops);
errout:
@@ -203,22 +272,22 @@ errout:
}
/**
- * Unregister generic netlink operations
- * @arg ops cache operations
+ * Unregister cache based Generic Netlink family
+ * @arg ops Cache operations definition
*/
void genl_unregister(struct nl_cache_ops *ops)
{
+ if (!ops)
+ return;
+
nl_cache_mngt_unregister(ops);
- nl_list_del(&ops->co_genl->o_list);
+
+ genl_unregister_family(ops->co_genl);
}
/** @} */
-/**
- * @name Resolving ID/Name
- * @{
- */
-
+/** @cond SKIP */
static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
{
struct genl_family *family;
@@ -233,7 +302,22 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
return -NLE_OBJ_NOTFOUND;
}
+/** @endcond */
+/**
+ * @name Resolving the name of registered families
+ * @{
+ */
+
+/**
+ * Resolve a single Generic Netlink family
+ * @arg sk Generic Netlink socket
+ * @arg ops Generic Netlink family definition
+ *
+ * Resolves the family name to its numeric identifier.
+ *
+ * @return 0 on success or a negative error code.
+ */
int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
{
struct nl_cache *ctrl;
@@ -249,6 +333,19 @@ errout:
return err;
}
+/**
+ * Resolve all registered Generic Netlink families
+ * @arg sk Generic Netlink socket
+ *
+ * Walks through all local Generic Netlink families that have been registered
+ * using genl_register() and resolves the name of each family to the
+ * corresponding numeric identifier.
+ *
+ * @see genl_register()
+ * @see genl_ops_resolve()
+ *
+ * @return 0 on success or a negative error code.
+ */
int genl_mngt_resolve(struct nl_sock *sk)
{
struct nl_cache *ctrl;
@@ -269,5 +366,4 @@ errout:
/** @} */
-
/** @} */