Home | History | Annotate | Download | only in genl
      1 /*
      2  * lib/genl/mngt.c		Generic Netlink Management
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup genl
     14  * @defgroup genl_mngt Management
     15  *
     16  * @par 1) Registering a generic netlink module
     17  * @code
     18  * #include <netlink/genl/mngt.h>
     19  *
     20  * // First step is to define all the commands being used in
     21  * // particular generic netlink family. The ID and name are
     22  * // mandatory to be filled out. A callback function and
     23  * // most the attribute policy that comes with it must be
     24  * // defined for commands expected to be issued towards
     25  * // userspace.
     26  * static struct genl_cmd foo_cmds[] = {
     27  * 	{
     28  * 		.c_id		= FOO_CMD_NEW,
     29  * 		.c_name		= "NEWFOO" ,
     30  * 		.c_maxattr	= FOO_ATTR_MAX,
     31  * 		.c_attr_policy	= foo_policy,
     32  * 		.c_msg_parser	= foo_msg_parser,
     33  * 	},
     34  * 	{
     35  * 		.c_id		= FOO_CMD_DEL,
     36  * 		.c_name		= "DELFOO" ,
     37  * 	},
     38  * };
     39  *
     40  * // The list of commands must then be integrated into a
     41  * // struct genl_ops serving as handle for this particular
     42  * // family.
     43  * static struct genl_ops my_genl_ops = {
     44  * 	.o_cmds			= foo_cmds,
     45  * 	.o_ncmds		= ARRAY_SIZE(foo_cmds),
     46  * };
     47  *
     48  * // Using the above struct genl_ops an arbitary number of
     49  * // cache handles can be associated to it.
     50  * //
     51  * // The macro GENL_HDRSIZE() must be used to specify the
     52  * // length of the header to automatically take headers on
     53  * // generic layers into account.
     54  * //
     55  * // The macro GENL_FAMILY() is used to represent the generic
     56  * // netlink family id.
     57  * static struct nl_cache_ops genl_foo_ops = {
     58  * 	.co_name		= "genl/foo",
     59  * 	.co_hdrsize		= GENL_HDRSIZE(sizeof(struct my_hdr)),
     60  * 	.co_msgtypes		= GENL_FAMILY(GENL_ID_GENERATE, "foo"),
     61  * 	.co_genl		= &my_genl_ops,
     62  * 	.co_protocol		= NETLINK_GENERIC,
     63  * 	.co_request_update      = foo_request_update,
     64  * 	.co_obj_ops		= &genl_foo_ops,
     65  * };
     66  *
     67  * // Finally each cache handle for a generic netlink family
     68  * // must be registered using genl_register().
     69  * static void __init foo_init(void)
     70  * {
     71  * 	genl_register(&genl_foo_ops);
     72  * }
     73  *
     74  * // ... respectively unregsted again.
     75  * static void __exit foo_exit(void)
     76  * {
     77  * 	genl_unregister(&genl_foo_ops);
     78  * }
     79  * @endcode
     80  * @{
     81  */
     82 
     83 #include <netlink-generic.h>
     84 #include <netlink/netlink.h>
     85 #include <netlink/genl/genl.h>
     86 #include <netlink/genl/mngt.h>
     87 #include <netlink/genl/family.h>
     88 #include <netlink/genl/ctrl.h>
     89 #include <netlink/utils.h>
     90 
     91 static NL_LIST_HEAD(genl_ops_list);
     92 
     93 static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
     94 			   struct nlmsghdr *nlh, struct nl_parser_param *pp)
     95 {
     96 	int i, err;
     97 	struct genlmsghdr *ghdr;
     98 	struct genl_cmd *cmd;
     99 
    100 	ghdr = nlmsg_data(nlh);
    101 
    102 	if (ops->co_genl == NULL)
    103 		BUG();
    104 
    105 	for (i = 0; i < ops->co_genl->o_ncmds; i++) {
    106 		cmd = &ops->co_genl->o_cmds[i];
    107 		if (cmd->c_id == ghdr->cmd)
    108 			goto found;
    109 	}
    110 
    111 	err = -NLE_MSGTYPE_NOSUPPORT;
    112 	goto errout;
    113 
    114 found:
    115 	if (cmd->c_msg_parser == NULL)
    116 		err = -NLE_OPNOTSUPP;
    117 	else {
    118 		struct nlattr *tb[cmd->c_maxattr + 1];
    119 		struct genl_info info = {
    120 			.who = who,
    121 			.nlh = nlh,
    122 			.genlhdr = ghdr,
    123 			.userhdr = genlmsg_data(ghdr),
    124 			.attrs = tb,
    125 		};
    126 
    127 		err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr,
    128 				  cmd->c_attr_policy);
    129 		if (err < 0)
    130 			goto errout;
    131 
    132 		err = cmd->c_msg_parser(ops, cmd, &info, pp);
    133 	}
    134 errout:
    135 	return err;
    136 
    137 }
    138 
    139 char *genl_op2name(int family, int op, char *buf, size_t len)
    140 {
    141 	struct genl_ops *ops;
    142 	int i;
    143 
    144 	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
    145 		if (ops->o_family == family) {
    146 			for (i = 0; i < ops->o_ncmds; i++) {
    147 				struct genl_cmd *cmd;
    148 				cmd = &ops->o_cmds[i];
    149 
    150 				if (cmd->c_id == op) {
    151 					strncpy(buf, cmd->c_name, len - 1);
    152 					return buf;
    153 				}
    154 			}
    155 		}
    156 	}
    157 
    158 	strncpy(buf, "unknown", len - 1);
    159 	return NULL;
    160 }
    161 
    162 
    163 /**
    164  * @name Register/Unregister
    165  * @{
    166  */
    167 
    168 /**
    169  * Register generic netlink operations
    170  * @arg ops		cache operations
    171  */
    172 int genl_register(struct nl_cache_ops *ops)
    173 {
    174 	int err;
    175 
    176 	if (ops->co_protocol != NETLINK_GENERIC) {
    177 		err = -NLE_PROTO_MISMATCH;
    178 		goto errout;
    179 	}
    180 
    181 	if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
    182 		err = -NLE_INVAL;
    183 		goto errout;
    184 	}
    185 
    186 	if (ops->co_genl == NULL) {
    187 		err = -NLE_INVAL;
    188 		goto errout;
    189 	}
    190 
    191 	ops->co_genl->o_cache_ops = ops;
    192 	ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
    193 	ops->co_genl->o_family = ops->co_msgtypes[0].mt_id;
    194 	ops->co_msg_parser = genl_msg_parser;
    195 
    196 	/* FIXME: check for dup */
    197 
    198 	nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list);
    199 
    200 	err = nl_cache_mngt_register(ops);
    201 errout:
    202 	return err;
    203 }
    204 
    205 /**
    206  * Unregister generic netlink operations
    207  * @arg ops		cache operations
    208  */
    209 void genl_unregister(struct nl_cache_ops *ops)
    210 {
    211 	nl_cache_mngt_unregister(ops);
    212 	nl_list_del(&ops->co_genl->o_list);
    213 }
    214 
    215 /** @} */
    216 
    217 /**
    218  * @name Resolving ID/Name
    219  * @{
    220  */
    221 
    222 static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
    223 {
    224 	struct genl_family *family;
    225 
    226 	family = genl_ctrl_search_by_name(ctrl, ops->o_name);
    227 	if (family != NULL) {
    228 		ops->o_id = genl_family_get_id(family);
    229 		genl_family_put(family);
    230 
    231 		return 0;
    232 	}
    233 
    234 	return -NLE_OBJ_NOTFOUND;
    235 }
    236 
    237 int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
    238 {
    239 	struct nl_cache *ctrl;
    240 	int err;
    241 
    242 	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
    243 		goto errout;
    244 
    245 	err = __genl_ops_resolve(ctrl, ops);
    246 
    247 	nl_cache_free(ctrl);
    248 errout:
    249 	return err;
    250 }
    251 
    252 int genl_mngt_resolve(struct nl_sock *sk)
    253 {
    254 	struct nl_cache *ctrl;
    255 	struct genl_ops *ops;
    256 	int err = 0;
    257 
    258 	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
    259 		goto errout;
    260 
    261 	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
    262 		err = __genl_ops_resolve(ctrl, ops);
    263 	}
    264 
    265 	nl_cache_free(ctrl);
    266 errout:
    267 	return err;
    268 }
    269 
    270 /** @} */
    271 
    272 
    273 /** @} */
    274