From faef2fa45f67da810415f4dd4f0ce62d70965ad4 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 1 Jun 2012 11:48:08 +0200 Subject: genl: Support registration of families without depending on caches Introduces the functions genl_register_family() and genl_unregister_family() to register a Generic Netlink family which does not implement a cachable type. API users can direct received messages into genl_handle_msg() which will validate the messages and call the callback functions defined in the commands definition. See test/test-genl.c for an example on how to use it. --- lib/genl/mngt.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 34 deletions(-) (limited to 'lib') diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c index f53aa8a..ad25172 100644 --- a/lib/genl/mngt.c +++ b/lib/genl/mngt.c @@ -11,7 +11,7 @@ /** * @ingroup genl - * @defgroup genl_mngt Family and Operations Management + * @defgroup genl_mngt Family and Command Registration * * Registering Generic Netlink Families and Commands * @@ -30,28 +30,34 @@ 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; + + ghdr = genlmsg_hdr(nlh); - err = -NLE_MSGTYPE_NOSUPPORT; - goto errout; + if (!(cmd = lookup_cmd(ops, ghdr->cmd))) { + err = -NLE_MSGTYPE_NOSUPPORT; + goto errout; + } -found: if (cmd->c_msg_parser == NULL) err = -NLE_OPNOTSUPP; else { @@ -64,33 +70,64 @@ found: .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; } } } @@ -102,7 +139,90 @@ char *genl_op2name(int family, int op, char *buf, size_t len) /** @endcond */ /** - * @name Registration (Cache Based) + * @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; +} + +/** + * 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 * @{ */ @@ -110,6 +230,12 @@ char *genl_op2name(int family, int op, char *buf, size_t len) * 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) @@ -132,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: @@ -155,7 +281,8 @@ void genl_unregister(struct nl_cache_ops *ops) return; nl_cache_mngt_unregister(ops); - nl_list_del(&ops->co_genl->o_list); + + genl_unregister_family(ops->co_genl); } /** @} */ -- cgit v1.2.1