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@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 
genl_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)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 
genl_op2name(int family,int op,char * buf,size_t len)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  */
genl_register(struct nl_cache_ops * ops)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  */
genl_unregister(struct nl_cache_ops * ops)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 
__genl_ops_resolve(struct nl_cache * ctrl,struct genl_ops * ops)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 
genl_ops_resolve(struct nl_sock * sk,struct genl_ops * ops)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 
genl_mngt_resolve(struct nl_sock * sk)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