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