diff options
Diffstat (limited to 'lib/genl/mngt.c')
-rw-r--r-- | lib/genl/mngt.c | 316 |
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: /** @} */ - /** @} */ |