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-2012 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup genl
14 * @defgroup genl_mngt Family and Command Registration
15 *
16 * Registering Generic Netlink Families and Commands
17 *
18 * @{
19 */
20
21#include <netlink-private/genl.h>
22#include <netlink/netlink.h>
23#include <netlink/genl/genl.h>
24#include <netlink/genl/mngt.h>
25#include <netlink/genl/family.h>
26#include <netlink/genl/ctrl.h>
27#include <netlink/utils.h>
28
29/** @cond SKIP */
30
31static NL_LIST_HEAD(genl_ops_list);
32
33static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id)
34{
35	struct genl_cmd *cmd;
36	int i;
37
38	for (i = 0; i < ops->o_ncmds; i++) {
39		cmd = &ops->o_cmds[i];
40		if (cmd->c_id == cmd_id)
41			return cmd;
42	}
43
44	return NULL;
45}
46
47static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh,
48			  struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg)
49{
50	int err;
51	struct genlmsghdr *ghdr;
52	struct genl_cmd *cmd;
53
54	ghdr = genlmsg_hdr(nlh);
55
56	if (!(cmd = lookup_cmd(ops, ghdr->cmd))) {
57		err = -NLE_MSGTYPE_NOSUPPORT;
58		goto errout;
59	}
60
61	if (cmd->c_msg_parser == NULL)
62		err = -NLE_OPNOTSUPP;
63	else {
64		struct nlattr *tb[cmd->c_maxattr + 1];
65		struct genl_info info = {
66			.who = who,
67			.nlh = nlh,
68			.genlhdr = ghdr,
69			.userhdr = genlmsg_user_hdr(ghdr),
70			.attrs = tb,
71		};
72
73		err = nlmsg_parse(nlh, GENL_HDRSIZE(ops->o_hdrsize), tb, cmd->c_maxattr,
74				  cmd->c_attr_policy);
75		if (err < 0)
76			goto errout;
77
78		err = cmd->c_msg_parser(cache_ops, cmd, &info, arg);
79	}
80errout:
81	return err;
82
83}
84
85static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
86			   struct nlmsghdr *nlh, struct nl_parser_param *pp)
87{
88	if (ops->co_genl == NULL)
89		BUG();
90
91	return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp);
92}
93
94static struct genl_ops *lookup_family(int family)
95{
96	struct genl_ops *ops;
97
98	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
99		if (ops->o_id == family)
100			return ops;
101	}
102
103	return NULL;
104}
105
106static struct genl_ops *lookup_family_by_name(const char *name)
107{
108	struct genl_ops *ops;
109
110	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
111		if (!strcmp(ops->o_name, name))
112			return ops;
113	}
114
115	return NULL;
116}
117
118char *genl_op2name(int family, int op, char *buf, size_t len)
119{
120	struct genl_ops *ops;
121	int i;
122
123	if ((ops = lookup_family(family))) {
124		for (i = 0; i < ops->o_ncmds; i++) {
125			struct genl_cmd *cmd;
126			cmd = &ops->o_cmds[i];
127
128			if (cmd->c_id == op) {
129				strncpy(buf, cmd->c_name, len - 1);
130				return buf;
131			}
132		}
133	}
134
135	strncpy(buf, "unknown", len - 1);
136	return NULL;
137}
138
139/** @endcond */
140
141/**
142 * @name Registration
143 * @{
144 */
145
146/**
147 * Register Generic Netlink family and associated commands
148 * @arg ops		Generic Netlink family definition
149 *
150 * Registers the specified Generic Netlink family definition together with
151 * all associated commands. After registration, received Generic Netlink
152 * messages can be passed to genl_handle_msg() which will validate the
153 * messages, look for a matching command and call the respective callback
154 * function automatically.
155 *
156 * @note Consider using genl_register() if the family is used to implement a
157 *       cacheable type.
158 *
159 * @see genl_unregister_family();
160 * @see genl_register();
161 *
162 * @return 0 on success or a negative error code.
163 */
164int genl_register_family(struct genl_ops *ops)
165{
166	if (!ops->o_name)
167		return -NLE_INVAL;
168
169	if (ops->o_cmds && ops->o_ncmds <= 0)
170		return -NLE_INVAL;
171
172	if (ops->o_id && lookup_family(ops->o_id))
173		return -NLE_EXIST;
174
175	if (lookup_family_by_name(ops->o_name))
176		return -NLE_EXIST;
177
178	nl_list_add_tail(&ops->o_list, &genl_ops_list);
179
180	return 0;
181}
182
183/**
184 * Unregister Generic Netlink family
185 * @arg ops		Generic Netlink family definition
186 *
187 * Unregisters a family and all associated commands that were previously
188 * registered using genl_register_family().
189 *
190 * @see genl_register_family()
191 *
192 * @return 0 on success or a negative error code.
193 */
194int genl_unregister_family(struct genl_ops *ops)
195{
196	nl_list_del(&ops->o_list);
197
198	return 0;
199}
200
201/**
202 * Run a received message through the demultiplexer
203 * @arg msg		Generic Netlink message
204 * @arg arg		Argument passed on to the message handler callback
205 *
206 * @return 0 on success or a negative error code.
207 */
208int genl_handle_msg(struct nl_msg *msg, void *arg)
209{
210	struct nlmsghdr *nlh = nlmsg_hdr(msg);
211	struct genl_ops *ops;
212
213	if (!genlmsg_valid_hdr(nlh, 0))
214		return -NLE_INVAL;
215
216	if (!(ops = lookup_family(nlh->nlmsg_type)))
217		return -NLE_MSGTYPE_NOSUPPORT;
218
219	return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg);
220}
221
222/** @} */
223
224/**
225 * @name Registration of Cache Operations
226 * @{
227 */
228
229/**
230 * Register Generic Netlink family backed cache
231 * @arg ops		Cache operations definition
232 *
233 * Same as genl_register_family() but additionally registers the specified
234 * cache operations using nl_cache_mngt_register() and associates it with
235 * the Generic Netlink family.
236 *
237 * @see genl_register_family()
238 *
239 * @return 0 on success or a negative error code.
240 */
241int genl_register(struct nl_cache_ops *ops)
242{
243	int err;
244
245	if (ops->co_protocol != NETLINK_GENERIC) {
246		err = -NLE_PROTO_MISMATCH;
247		goto errout;
248	}
249
250	if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
251		err = -NLE_INVAL;
252		goto errout;
253	}
254
255	if (ops->co_genl == NULL) {
256		err = -NLE_INVAL;
257		goto errout;
258	}
259
260	ops->co_genl->o_cache_ops = ops;
261	ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN;
262	ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
263	ops->co_genl->o_id = ops->co_msgtypes[0].mt_id;
264	ops->co_msg_parser = genl_msg_parser;
265
266	if ((err = genl_register_family(ops->co_genl)) < 0)
267		goto errout;
268
269	err = nl_cache_mngt_register(ops);
270errout:
271	return err;
272}
273
274/**
275 * Unregister cache based Generic Netlink family
276 * @arg ops		Cache operations definition
277 */
278void genl_unregister(struct nl_cache_ops *ops)
279{
280	if (!ops)
281		return;
282
283	nl_cache_mngt_unregister(ops);
284
285	genl_unregister_family(ops->co_genl);
286}
287
288/** @} */
289
290/** @cond SKIP */
291static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
292{
293	struct genl_family *family;
294
295	family = genl_ctrl_search_by_name(ctrl, ops->o_name);
296	if (family != NULL) {
297		ops->o_id = genl_family_get_id(family);
298
299		if (ops->o_cache_ops)
300			ops->o_cache_ops->co_msgtypes[0].mt_id = ops->o_id;
301
302		genl_family_put(family);
303
304		return 0;
305	}
306
307	return -NLE_OBJ_NOTFOUND;
308}
309
310int genl_resolve_id(struct genl_ops *ops)
311{
312	struct nl_sock *sk;
313	int err = 0;
314
315	/* Check if resolved already */
316	if (ops->o_id != GENL_ID_GENERATE)
317		return 0;
318
319	if (!ops->o_name)
320		return -NLE_INVAL;
321
322	if (!(sk = nl_socket_alloc()))
323		return -NLE_NOMEM;
324
325	if ((err = genl_connect(sk)) < 0)
326		goto errout_free;
327
328	err = genl_ops_resolve(sk, ops);
329
330errout_free:
331	nl_socket_free(sk);
332
333	return err;
334}
335/** @endcond */
336
337/**
338 * @name Resolving the name of registered families
339 * @{
340 */
341
342/**
343 * Resolve a single Generic Netlink family
344 * @arg sk		Generic Netlink socket
345 * @arg ops		Generic Netlink family definition
346 *
347 * Resolves the family name to its numeric identifier.
348 *
349 * @return 0 on success or a negative error code.
350 */
351int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
352{
353	struct nl_cache *ctrl;
354	int err;
355
356	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
357		goto errout;
358
359	err = __genl_ops_resolve(ctrl, ops);
360
361	nl_cache_free(ctrl);
362errout:
363	return err;
364}
365
366/**
367 * Resolve all registered Generic Netlink families
368 * @arg sk		Generic Netlink socket
369 *
370 * Walks through all local Generic Netlink families that have been registered
371 * using genl_register() and resolves the name of each family to the
372 * corresponding numeric identifier.
373 *
374 * @see genl_register()
375 * @see genl_ops_resolve()
376 *
377 * @return 0 on success or a negative error code.
378 */
379int genl_mngt_resolve(struct nl_sock *sk)
380{
381	struct nl_cache *ctrl;
382	struct genl_ops *ops;
383	int err = 0;
384
385	if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
386		goto errout;
387
388	nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
389		err = __genl_ops_resolve(ctrl, ops);
390	}
391
392	nl_cache_free(ctrl);
393errout:
394	return err;
395}
396
397/** @} */
398
399/** @} */
400