]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
482a8524 TG |
2 | /* |
3 | * NETLINK Generic Netlink Family | |
4 | * | |
5 | * Authors: Jamal Hadi Salim | |
6 | * Thomas Graf <tgraf@suug.ch> | |
2dbba6f7 | 7 | * Johannes Berg <johannes@sipsolutions.net> |
482a8524 TG |
8 | */ |
9 | ||
482a8524 TG |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
482a8524 TG |
13 | #include <linux/errno.h> |
14 | #include <linux/types.h> | |
15 | #include <linux/socket.h> | |
16 | #include <linux/string.h> | |
17 | #include <linux/skbuff.h> | |
14cc3e2b | 18 | #include <linux/mutex.h> |
2dbba6f7 | 19 | #include <linux/bitmap.h> |
def31174 | 20 | #include <linux/rwsem.h> |
2ae0f17d | 21 | #include <linux/idr.h> |
482a8524 TG |
22 | #include <net/sock.h> |
23 | #include <net/genetlink.h> | |
24 | ||
14cc3e2b | 25 | static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ |
def31174 | 26 | static DECLARE_RWSEM(cb_lock); |
482a8524 | 27 | |
ee1c2442 JB |
28 | atomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0); |
29 | DECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq); | |
30 | ||
f408e0ce | 31 | void genl_lock(void) |
482a8524 | 32 | { |
14cc3e2b | 33 | mutex_lock(&genl_mutex); |
482a8524 | 34 | } |
f408e0ce | 35 | EXPORT_SYMBOL(genl_lock); |
482a8524 | 36 | |
f408e0ce | 37 | void genl_unlock(void) |
482a8524 | 38 | { |
14cc3e2b | 39 | mutex_unlock(&genl_mutex); |
482a8524 | 40 | } |
f408e0ce | 41 | EXPORT_SYMBOL(genl_unlock); |
482a8524 | 42 | |
320f5ea0 | 43 | #ifdef CONFIG_LOCKDEP |
61d03535 | 44 | bool lockdep_genl_is_held(void) |
86b1309c PS |
45 | { |
46 | return lockdep_is_held(&genl_mutex); | |
47 | } | |
48 | EXPORT_SYMBOL(lockdep_genl_is_held); | |
49 | #endif | |
50 | ||
def31174 PS |
51 | static void genl_lock_all(void) |
52 | { | |
53 | down_write(&cb_lock); | |
54 | genl_lock(); | |
55 | } | |
56 | ||
57 | static void genl_unlock_all(void) | |
58 | { | |
59 | genl_unlock(); | |
60 | up_write(&cb_lock); | |
61 | } | |
62 | ||
2ae0f17d | 63 | static DEFINE_IDR(genl_fam_idr); |
482a8524 | 64 | |
2dbba6f7 JB |
65 | /* |
66 | * Bitmap of multicast groups that are currently in use. | |
67 | * | |
68 | * To avoid an allocation at boot of just one unsigned long, | |
69 | * declare it global instead. | |
70 | * Bit 0 is marked as already used since group 0 is invalid. | |
e5dcecba JB |
71 | * Bit 1 is marked as already used since the drop-monitor code |
72 | * abuses the API and thinks it can statically use group 1. | |
73 | * That group will typically conflict with other groups that | |
74 | * any proper users use. | |
2a94fe48 JB |
75 | * Bit 16 is marked as used since it's used for generic netlink |
76 | * and the code no longer marks pre-reserved IDs as used. | |
2ecf7536 JB |
77 | * Bit 17 is marked as already used since the VFS quota code |
78 | * also abused this API and relied on family == group ID, we | |
79 | * cater to that by giving it a static family and group ID. | |
5e53e689 JB |
80 | * Bit 18 is marked as already used since the PMCRAID driver |
81 | * did the same thing as the VFS quota code (maybe copied?) | |
2dbba6f7 | 82 | */ |
2a94fe48 | 83 | static unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | |
5e53e689 JB |
84 | BIT(GENL_ID_VFS_DQUOT) | |
85 | BIT(GENL_ID_PMCRAID); | |
2dbba6f7 JB |
86 | static unsigned long *mc_groups = &mc_group_start; |
87 | static unsigned long mc_groups_longs = 1; | |
482a8524 | 88 | |
2ae0f17d | 89 | static int genl_ctrl_event(int event, const struct genl_family *family, |
2a94fe48 JB |
90 | const struct genl_multicast_group *grp, |
91 | int grp_id); | |
482a8524 | 92 | |
2ae0f17d | 93 | static const struct genl_family *genl_family_find_byid(unsigned int id) |
482a8524 | 94 | { |
2ae0f17d | 95 | return idr_find(&genl_fam_idr, id); |
482a8524 TG |
96 | } |
97 | ||
2ae0f17d | 98 | static const struct genl_family *genl_family_find_byname(char *name) |
482a8524 | 99 | { |
2ae0f17d JB |
100 | const struct genl_family *family; |
101 | unsigned int id; | |
482a8524 | 102 | |
2ae0f17d JB |
103 | idr_for_each_entry(&genl_fam_idr, family, id) |
104 | if (strcmp(family->name, name) == 0) | |
105 | return family; | |
482a8524 TG |
106 | |
107 | return NULL; | |
108 | } | |
109 | ||
0b588afd JK |
110 | static int genl_get_cmd_cnt(const struct genl_family *family) |
111 | { | |
112 | return family->n_ops + family->n_small_ops; | |
113 | } | |
114 | ||
115 | static void genl_op_from_full(const struct genl_family *family, | |
116 | unsigned int i, struct genl_ops *op) | |
117 | { | |
118 | *op = family->ops[i]; | |
48526a0f JK |
119 | |
120 | if (!op->maxattr) | |
121 | op->maxattr = family->maxattr; | |
122 | if (!op->policy) | |
123 | op->policy = family->policy; | |
0b588afd JK |
124 | } |
125 | ||
e992a6ed | 126 | static int genl_get_cmd_full(u32 cmd, const struct genl_family *family, |
0b588afd | 127 | struct genl_ops *op) |
482a8524 | 128 | { |
d91824c0 | 129 | int i; |
482a8524 | 130 | |
d91824c0 | 131 | for (i = 0; i < family->n_ops; i++) |
0b588afd JK |
132 | if (family->ops[i].cmd == cmd) { |
133 | genl_op_from_full(family, i, op); | |
134 | return 0; | |
135 | } | |
482a8524 | 136 | |
0b588afd JK |
137 | return -ENOENT; |
138 | } | |
139 | ||
140 | static void genl_op_from_small(const struct genl_family *family, | |
141 | unsigned int i, struct genl_ops *op) | |
142 | { | |
143 | memset(op, 0, sizeof(*op)); | |
144 | op->doit = family->small_ops[i].doit; | |
145 | op->dumpit = family->small_ops[i].dumpit; | |
146 | op->cmd = family->small_ops[i].cmd; | |
147 | op->internal_flags = family->small_ops[i].internal_flags; | |
148 | op->flags = family->small_ops[i].flags; | |
149 | op->validate = family->small_ops[i].validate; | |
48526a0f JK |
150 | |
151 | op->maxattr = family->maxattr; | |
152 | op->policy = family->policy; | |
0b588afd JK |
153 | } |
154 | ||
e992a6ed | 155 | static int genl_get_cmd_small(u32 cmd, const struct genl_family *family, |
0b588afd JK |
156 | struct genl_ops *op) |
157 | { | |
158 | int i; | |
159 | ||
160 | for (i = 0; i < family->n_small_ops; i++) | |
161 | if (family->small_ops[i].cmd == cmd) { | |
162 | genl_op_from_small(family, i, op); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | return -ENOENT; | |
167 | } | |
168 | ||
e992a6ed | 169 | static int genl_get_cmd(u32 cmd, const struct genl_family *family, |
0b588afd JK |
170 | struct genl_ops *op) |
171 | { | |
172 | if (!genl_get_cmd_full(cmd, family, op)) | |
173 | return 0; | |
174 | return genl_get_cmd_small(cmd, family, op); | |
175 | } | |
176 | ||
177 | static void genl_get_cmd_by_index(unsigned int i, | |
178 | const struct genl_family *family, | |
179 | struct genl_ops *op) | |
180 | { | |
181 | if (i < family->n_ops) | |
182 | genl_op_from_full(family, i, op); | |
183 | else if (i < family->n_ops + family->n_small_ops) | |
184 | genl_op_from_small(family, i - family->n_ops, op); | |
185 | else | |
186 | WARN_ON_ONCE(1); | |
482a8524 TG |
187 | } |
188 | ||
2a94fe48 | 189 | static int genl_allocate_reserve_groups(int n_groups, int *first_id) |
2dbba6f7 | 190 | { |
2dbba6f7 | 191 | unsigned long *new_groups; |
2a94fe48 JB |
192 | int start = 0; |
193 | int i; | |
194 | int id; | |
195 | bool fits; | |
196 | ||
197 | do { | |
198 | if (start == 0) | |
199 | id = find_first_zero_bit(mc_groups, | |
200 | mc_groups_longs * | |
201 | BITS_PER_LONG); | |
202 | else | |
203 | id = find_next_zero_bit(mc_groups, | |
204 | mc_groups_longs * BITS_PER_LONG, | |
205 | start); | |
206 | ||
207 | fits = true; | |
208 | for (i = id; | |
209 | i < min_t(int, id + n_groups, | |
210 | mc_groups_longs * BITS_PER_LONG); | |
211 | i++) { | |
212 | if (test_bit(i, mc_groups)) { | |
213 | start = i; | |
214 | fits = false; | |
215 | break; | |
216 | } | |
217 | } | |
2dbba6f7 | 218 | |
b8e429a2 | 219 | if (id + n_groups > mc_groups_longs * BITS_PER_LONG) { |
2a94fe48 JB |
220 | unsigned long new_longs = mc_groups_longs + |
221 | BITS_TO_LONGS(n_groups); | |
222 | size_t nlen = new_longs * sizeof(unsigned long); | |
223 | ||
224 | if (mc_groups == &mc_group_start) { | |
225 | new_groups = kzalloc(nlen, GFP_KERNEL); | |
226 | if (!new_groups) | |
227 | return -ENOMEM; | |
228 | mc_groups = new_groups; | |
229 | *mc_groups = mc_group_start; | |
230 | } else { | |
231 | new_groups = krealloc(mc_groups, nlen, | |
232 | GFP_KERNEL); | |
233 | if (!new_groups) | |
234 | return -ENOMEM; | |
235 | mc_groups = new_groups; | |
236 | for (i = 0; i < BITS_TO_LONGS(n_groups); i++) | |
237 | mc_groups[mc_groups_longs + i] = 0; | |
238 | } | |
239 | mc_groups_longs = new_longs; | |
240 | } | |
241 | } while (!fits); | |
2dbba6f7 | 242 | |
2a94fe48 JB |
243 | for (i = id; i < id + n_groups; i++) |
244 | set_bit(i, mc_groups); | |
245 | *first_id = id; | |
246 | return 0; | |
247 | } | |
248 | ||
249 | static struct genl_family genl_ctrl; | |
250 | ||
251 | static int genl_validate_assign_mc_groups(struct genl_family *family) | |
252 | { | |
253 | int first_id; | |
254 | int n_groups = family->n_mcgrps; | |
0f0e2159 | 255 | int err = 0, i; |
2a94fe48 JB |
256 | bool groups_allocated = false; |
257 | ||
258 | if (!n_groups) | |
259 | return 0; | |
260 | ||
261 | for (i = 0; i < n_groups; i++) { | |
262 | const struct genl_multicast_group *grp = &family->mcgrps[i]; | |
263 | ||
264 | if (WARN_ON(grp->name[0] == '\0')) | |
265 | return -EINVAL; | |
266 | if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL)) | |
267 | return -EINVAL; | |
268 | } | |
2dbba6f7 | 269 | |
e5dcecba | 270 | /* special-case our own group and hacks */ |
2a94fe48 JB |
271 | if (family == &genl_ctrl) { |
272 | first_id = GENL_ID_CTRL; | |
273 | BUG_ON(n_groups != 1); | |
274 | } else if (strcmp(family->name, "NET_DM") == 0) { | |
275 | first_id = 1; | |
276 | BUG_ON(n_groups != 1); | |
5e53e689 | 277 | } else if (family->id == GENL_ID_VFS_DQUOT) { |
2a94fe48 JB |
278 | first_id = GENL_ID_VFS_DQUOT; |
279 | BUG_ON(n_groups != 1); | |
5e53e689 JB |
280 | } else if (family->id == GENL_ID_PMCRAID) { |
281 | first_id = GENL_ID_PMCRAID; | |
282 | BUG_ON(n_groups != 1); | |
2a94fe48 JB |
283 | } else { |
284 | groups_allocated = true; | |
285 | err = genl_allocate_reserve_groups(n_groups, &first_id); | |
286 | if (err) | |
287 | return err; | |
2dbba6f7 JB |
288 | } |
289 | ||
2a94fe48 JB |
290 | family->mcgrp_offset = first_id; |
291 | ||
85405918 | 292 | /* if still initializing, can't and don't need to realloc bitmaps */ |
2a94fe48 JB |
293 | if (!init_net.genl_sock) |
294 | return 0; | |
295 | ||
134e6375 JB |
296 | if (family->netnsok) { |
297 | struct net *net; | |
298 | ||
d136f1bd | 299 | netlink_table_grab(); |
134e6375 JB |
300 | rcu_read_lock(); |
301 | for_each_net_rcu(net) { | |
d136f1bd | 302 | err = __netlink_change_ngroups(net->genl_sock, |
134e6375 JB |
303 | mc_groups_longs * BITS_PER_LONG); |
304 | if (err) { | |
305 | /* | |
306 | * No need to roll back, can only fail if | |
307 | * memory allocation fails and then the | |
308 | * number of _possible_ groups has been | |
309 | * increased on some sockets which is ok. | |
310 | */ | |
2a94fe48 | 311 | break; |
134e6375 JB |
312 | } |
313 | } | |
314 | rcu_read_unlock(); | |
d136f1bd | 315 | netlink_table_ungrab(); |
134e6375 JB |
316 | } else { |
317 | err = netlink_change_ngroups(init_net.genl_sock, | |
318 | mc_groups_longs * BITS_PER_LONG); | |
134e6375 | 319 | } |
2dbba6f7 | 320 | |
2a94fe48 JB |
321 | if (groups_allocated && err) { |
322 | for (i = 0; i < family->n_mcgrps; i++) | |
323 | clear_bit(family->mcgrp_offset + i, mc_groups); | |
324 | } | |
2dbba6f7 | 325 | |
79d310d0 | 326 | return err; |
2dbba6f7 | 327 | } |
2dbba6f7 | 328 | |
2ae0f17d | 329 | static void genl_unregister_mc_groups(const struct genl_family *family) |
79dc4386 | 330 | { |
134e6375 | 331 | struct net *net; |
2a94fe48 | 332 | int i; |
134e6375 | 333 | |
b8273570 | 334 | netlink_table_grab(); |
134e6375 | 335 | rcu_read_lock(); |
2a94fe48 JB |
336 | for_each_net_rcu(net) { |
337 | for (i = 0; i < family->n_mcgrps; i++) | |
338 | __netlink_clear_multicast_users( | |
339 | net->genl_sock, family->mcgrp_offset + i); | |
340 | } | |
134e6375 | 341 | rcu_read_unlock(); |
b8273570 | 342 | netlink_table_ungrab(); |
134e6375 | 343 | |
2a94fe48 JB |
344 | for (i = 0; i < family->n_mcgrps; i++) { |
345 | int grp_id = family->mcgrp_offset + i; | |
2dbba6f7 | 346 | |
2a94fe48 JB |
347 | if (grp_id != 1) |
348 | clear_bit(grp_id, mc_groups); | |
349 | genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, | |
350 | &family->mcgrps[i], grp_id); | |
351 | } | |
2dbba6f7 JB |
352 | } |
353 | ||
2f91abd4 | 354 | static int genl_validate_ops(const struct genl_family *family) |
482a8524 | 355 | { |
d91824c0 JB |
356 | int i, j; |
357 | ||
0b588afd JK |
358 | if (WARN_ON(family->n_ops && !family->ops) || |
359 | WARN_ON(family->n_small_ops && !family->small_ops)) | |
568508aa JB |
360 | return -EINVAL; |
361 | ||
0b588afd JK |
362 | for (i = 0; i < genl_get_cmd_cnt(family); i++) { |
363 | struct genl_ops op; | |
568508aa | 364 | |
0b588afd JK |
365 | genl_get_cmd_by_index(i, family, &op); |
366 | if (op.dumpit == NULL && op.doit == NULL) | |
d91824c0 | 367 | return -EINVAL; |
0b588afd JK |
368 | for (j = i + 1; j < genl_get_cmd_cnt(family); j++) { |
369 | struct genl_ops op2; | |
370 | ||
371 | genl_get_cmd_by_index(j, family, &op2); | |
372 | if (op.cmd == op2.cmd) | |
d91824c0 | 373 | return -EINVAL; |
0b588afd | 374 | } |
482a8524 TG |
375 | } |
376 | ||
d91824c0 | 377 | return 0; |
482a8524 | 378 | } |
482a8524 TG |
379 | |
380 | /** | |
489111e5 | 381 | * genl_register_family - register a generic netlink family |
482a8524 TG |
382 | * @family: generic netlink family |
383 | * | |
384 | * Registers the specified family after validating it first. Only one | |
385 | * family may be registered with the same family name or identifier. | |
482a8524 | 386 | * |
489111e5 JB |
387 | * The family's ops, multicast groups and module pointer must already |
388 | * be assigned. | |
568508aa | 389 | * |
482a8524 TG |
390 | * Return 0 on success or a negative error code. |
391 | */ | |
489111e5 | 392 | int genl_register_family(struct genl_family *family) |
482a8524 | 393 | { |
a07ea4d9 | 394 | int err, i; |
2ae0f17d | 395 | int start = GENL_START_ALLOC, end = GENL_MAX_ID; |
482a8524 | 396 | |
568508aa JB |
397 | err = genl_validate_ops(family); |
398 | if (err) | |
399 | return err; | |
400 | ||
def31174 | 401 | genl_lock_all(); |
482a8524 TG |
402 | |
403 | if (genl_family_find_byname(family->name)) { | |
404 | err = -EEXIST; | |
405 | goto errout_locked; | |
406 | } | |
407 | ||
2ae0f17d JB |
408 | /* |
409 | * Sadly, a few cases need to be special-cased | |
410 | * due to them having previously abused the API | |
411 | * and having used their family ID also as their | |
412 | * multicast group ID, so we use reserved IDs | |
413 | * for both to be sure we can do that mapping. | |
414 | */ | |
a07ea4d9 | 415 | if (family == &genl_ctrl) { |
2ae0f17d JB |
416 | /* and this needs to be special for initial family lookups */ |
417 | start = end = GENL_ID_CTRL; | |
418 | } else if (strcmp(family->name, "pmcraid") == 0) { | |
419 | start = end = GENL_ID_PMCRAID; | |
420 | } else if (strcmp(family->name, "VFS_DQUOT") == 0) { | |
421 | start = end = GENL_ID_VFS_DQUOT; | |
482a8524 TG |
422 | } |
423 | ||
4e43df38 MH |
424 | family->id = idr_alloc_cyclic(&genl_fam_idr, family, |
425 | start, end + 1, GFP_KERNEL); | |
22ca904a WY |
426 | if (family->id < 0) { |
427 | err = family->id; | |
bf64ff4c | 428 | goto errout_locked; |
22ca904a | 429 | } |
2ae0f17d | 430 | |
2a94fe48 JB |
431 | err = genl_validate_assign_mc_groups(family); |
432 | if (err) | |
2ae0f17d | 433 | goto errout_remove; |
2a94fe48 | 434 | |
def31174 | 435 | genl_unlock_all(); |
482a8524 | 436 | |
2a94fe48 JB |
437 | /* send all events */ |
438 | genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0); | |
439 | for (i = 0; i < family->n_mcgrps; i++) | |
440 | genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, | |
441 | &family->mcgrps[i], family->mcgrp_offset + i); | |
482a8524 TG |
442 | |
443 | return 0; | |
444 | ||
2ae0f17d JB |
445 | errout_remove: |
446 | idr_remove(&genl_fam_idr, family->id); | |
482a8524 | 447 | errout_locked: |
def31174 | 448 | genl_unlock_all(); |
482a8524 TG |
449 | return err; |
450 | } | |
489111e5 | 451 | EXPORT_SYMBOL(genl_register_family); |
482a8524 TG |
452 | |
453 | /** | |
454 | * genl_unregister_family - unregister generic netlink family | |
455 | * @family: generic netlink family | |
456 | * | |
457 | * Unregisters the specified family. | |
458 | * | |
459 | * Returns 0 on success or a negative error code. | |
460 | */ | |
2ae0f17d | 461 | int genl_unregister_family(const struct genl_family *family) |
482a8524 | 462 | { |
def31174 | 463 | genl_lock_all(); |
482a8524 | 464 | |
0e82c763 | 465 | if (!genl_family_find_byid(family->id)) { |
2ae0f17d JB |
466 | genl_unlock_all(); |
467 | return -ENOENT; | |
468 | } | |
482a8524 | 469 | |
2ae0f17d | 470 | genl_unregister_mc_groups(family); |
ee1c2442 | 471 | |
2ae0f17d | 472 | idr_remove(&genl_fam_idr, family->id); |
482a8524 | 473 | |
2ae0f17d JB |
474 | up_write(&cb_lock); |
475 | wait_event(genl_sk_destructing_waitq, | |
476 | atomic_read(&genl_sk_destructing_cnt) == 0); | |
477 | genl_unlock(); | |
482a8524 | 478 | |
2ae0f17d JB |
479 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); |
480 | ||
481 | return 0; | |
482a8524 | 482 | } |
416c2f9c | 483 | EXPORT_SYMBOL(genl_unregister_family); |
482a8524 | 484 | |
a46621a3 DV |
485 | /** |
486 | * genlmsg_put - Add generic netlink header to netlink message | |
487 | * @skb: socket buffer holding the message | |
15e47304 | 488 | * @portid: netlink portid the message is addressed to |
a46621a3 DV |
489 | * @seq: sequence number (usually the one of the sender) |
490 | * @family: generic netlink family | |
2c53040f | 491 | * @flags: netlink message flags |
a46621a3 DV |
492 | * @cmd: generic netlink command |
493 | * | |
494 | * Returns pointer to user specific header | |
495 | */ | |
15e47304 | 496 | void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, |
2ae0f17d | 497 | const struct genl_family *family, int flags, u8 cmd) |
a46621a3 DV |
498 | { |
499 | struct nlmsghdr *nlh; | |
500 | struct genlmsghdr *hdr; | |
501 | ||
15e47304 | 502 | nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN + |
a46621a3 DV |
503 | family->hdrsize, flags); |
504 | if (nlh == NULL) | |
505 | return NULL; | |
506 | ||
507 | hdr = nlmsg_data(nlh); | |
508 | hdr->cmd = cmd; | |
509 | hdr->version = family->version; | |
510 | hdr->reserved = 0; | |
511 | ||
512 | return (char *) hdr + GENL_HDRLEN; | |
513 | } | |
514 | EXPORT_SYMBOL(genlmsg_put); | |
515 | ||
1927f41a JP |
516 | static struct genl_dumpit_info *genl_dumpit_info_alloc(void) |
517 | { | |
518 | return kmalloc(sizeof(struct genl_dumpit_info), GFP_KERNEL); | |
519 | } | |
520 | ||
521 | static void genl_dumpit_info_free(const struct genl_dumpit_info *info) | |
522 | { | |
523 | kfree(info); | |
524 | } | |
525 | ||
c10e6cf8 JP |
526 | static struct nlattr ** |
527 | genl_family_rcv_msg_attrs_parse(const struct genl_family *family, | |
528 | struct nlmsghdr *nlh, | |
529 | struct netlink_ext_ack *extack, | |
530 | const struct genl_ops *ops, | |
531 | int hdrlen, | |
b65ce380 | 532 | enum genl_validate_flags no_strict_flag) |
c10e6cf8 JP |
533 | { |
534 | enum netlink_validation validate = ops->validate & no_strict_flag ? | |
535 | NL_VALIDATE_LIBERAL : | |
536 | NL_VALIDATE_STRICT; | |
537 | struct nlattr **attrbuf; | |
538 | int err; | |
539 | ||
48526a0f | 540 | if (!ops->maxattr) |
cb0ce18a MK |
541 | return NULL; |
542 | ||
48526a0f | 543 | attrbuf = kmalloc_array(ops->maxattr + 1, |
bf64ff4c CW |
544 | sizeof(struct nlattr *), GFP_KERNEL); |
545 | if (!attrbuf) | |
546 | return ERR_PTR(-ENOMEM); | |
c10e6cf8 | 547 | |
48526a0f JK |
548 | err = __nlmsg_parse(nlh, hdrlen, attrbuf, ops->maxattr, ops->policy, |
549 | validate, extack); | |
39f3b41a | 550 | if (err) { |
bf64ff4c | 551 | kfree(attrbuf); |
c10e6cf8 JP |
552 | return ERR_PTR(err); |
553 | } | |
554 | return attrbuf; | |
555 | } | |
556 | ||
bf64ff4c | 557 | static void genl_family_rcv_msg_attrs_free(struct nlattr **attrbuf) |
c10e6cf8 | 558 | { |
bf64ff4c | 559 | kfree(attrbuf); |
c10e6cf8 JP |
560 | } |
561 | ||
c36f0555 CW |
562 | struct genl_start_context { |
563 | const struct genl_family *family; | |
564 | struct nlmsghdr *nlh; | |
565 | struct netlink_ext_ack *extack; | |
566 | const struct genl_ops *ops; | |
567 | int hdrlen; | |
568 | }; | |
569 | ||
570 | static int genl_start(struct netlink_callback *cb) | |
fc9e50f5 | 571 | { |
c36f0555 CW |
572 | struct genl_start_context *ctx = cb->data; |
573 | const struct genl_ops *ops = ctx->ops; | |
574 | struct genl_dumpit_info *info; | |
575 | struct nlattr **attrs = NULL; | |
fc9e50f5 TH |
576 | int rc = 0; |
577 | ||
c36f0555 CW |
578 | if (ops->validate & GENL_DONT_VALIDATE_DUMP) |
579 | goto no_attrs; | |
580 | ||
581 | if (ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) | |
582 | return -EINVAL; | |
583 | ||
584 | attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack, | |
585 | ops, ctx->hdrlen, | |
b65ce380 | 586 | GENL_DONT_VALIDATE_DUMP_STRICT); |
c36f0555 CW |
587 | if (IS_ERR(attrs)) |
588 | return PTR_ERR(attrs); | |
589 | ||
590 | no_attrs: | |
591 | info = genl_dumpit_info_alloc(); | |
592 | if (!info) { | |
bf64ff4c | 593 | genl_family_rcv_msg_attrs_free(attrs); |
c36f0555 CW |
594 | return -ENOMEM; |
595 | } | |
596 | info->family = ctx->family; | |
0b588afd | 597 | info->op = *ops; |
c36f0555 CW |
598 | info->attrs = attrs; |
599 | ||
600 | cb->data = info; | |
fc9e50f5 | 601 | if (ops->start) { |
c36f0555 CW |
602 | if (!ctx->family->parallel_ops) |
603 | genl_lock(); | |
fc9e50f5 | 604 | rc = ops->start(cb); |
c36f0555 CW |
605 | if (!ctx->family->parallel_ops) |
606 | genl_unlock(); | |
607 | } | |
608 | ||
609 | if (rc) { | |
bf64ff4c | 610 | genl_family_rcv_msg_attrs_free(info->attrs); |
c36f0555 CW |
611 | genl_dumpit_info_free(info); |
612 | cb->data = NULL; | |
fc9e50f5 TH |
613 | } |
614 | return rc; | |
615 | } | |
616 | ||
9b96309c PS |
617 | static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) |
618 | { | |
0b588afd | 619 | const struct genl_ops *ops = &genl_dumpit_info(cb)->op; |
9b96309c PS |
620 | int rc; |
621 | ||
622 | genl_lock(); | |
623 | rc = ops->dumpit(skb, cb); | |
624 | genl_unlock(); | |
625 | return rc; | |
626 | } | |
627 | ||
628 | static int genl_lock_done(struct netlink_callback *cb) | |
629 | { | |
1927f41a | 630 | const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
0b588afd | 631 | const struct genl_ops *ops = &info->op; |
9b96309c PS |
632 | int rc = 0; |
633 | ||
634 | if (ops->done) { | |
635 | genl_lock(); | |
636 | rc = ops->done(cb); | |
637 | genl_unlock(); | |
638 | } | |
bf64ff4c | 639 | genl_family_rcv_msg_attrs_free(info->attrs); |
1927f41a JP |
640 | genl_dumpit_info_free(info); |
641 | return rc; | |
642 | } | |
643 | ||
644 | static int genl_parallel_done(struct netlink_callback *cb) | |
645 | { | |
646 | const struct genl_dumpit_info *info = genl_dumpit_info(cb); | |
0b588afd | 647 | const struct genl_ops *ops = &info->op; |
1927f41a JP |
648 | int rc = 0; |
649 | ||
650 | if (ops->done) | |
651 | rc = ops->done(cb); | |
bf64ff4c | 652 | genl_family_rcv_msg_attrs_free(info->attrs); |
1927f41a | 653 | genl_dumpit_info_free(info); |
9b96309c PS |
654 | return rc; |
655 | } | |
656 | ||
be064def JP |
657 | static int genl_family_rcv_msg_dumpit(const struct genl_family *family, |
658 | struct sk_buff *skb, | |
659 | struct nlmsghdr *nlh, | |
660 | struct netlink_ext_ack *extack, | |
661 | const struct genl_ops *ops, | |
662 | int hdrlen, struct net *net) | |
482a8524 | 663 | { |
c36f0555 | 664 | struct genl_start_context ctx; |
be064def | 665 | int err; |
482a8524 | 666 | |
be064def | 667 | if (!ops->dumpit) |
1d00a4eb | 668 | return -EOPNOTSUPP; |
482a8524 | 669 | |
c36f0555 CW |
670 | ctx.family = family; |
671 | ctx.nlh = nlh; | |
672 | ctx.extack = extack; | |
673 | ctx.ops = ops; | |
674 | ctx.hdrlen = hdrlen; | |
1927f41a | 675 | |
be064def JP |
676 | if (!family->parallel_ops) { |
677 | struct netlink_dump_control c = { | |
678 | .module = family->module, | |
c36f0555 CW |
679 | .data = &ctx, |
680 | .start = genl_start, | |
be064def JP |
681 | .dump = genl_lock_dumpit, |
682 | .done = genl_lock_done, | |
683 | }; | |
ef6243ac | 684 | |
be064def JP |
685 | genl_unlock(); |
686 | err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); | |
687 | genl_lock(); | |
be064def JP |
688 | } else { |
689 | struct netlink_dump_control c = { | |
690 | .module = family->module, | |
c36f0555 CW |
691 | .data = &ctx, |
692 | .start = genl_start, | |
be064def | 693 | .dump = ops->dumpit, |
1927f41a | 694 | .done = genl_parallel_done, |
be064def JP |
695 | }; |
696 | ||
697 | err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); | |
698 | } | |
ef6243ac | 699 | |
be064def JP |
700 | return err; |
701 | } | |
9b96309c | 702 | |
be064def JP |
703 | static int genl_family_rcv_msg_doit(const struct genl_family *family, |
704 | struct sk_buff *skb, | |
705 | struct nlmsghdr *nlh, | |
706 | struct netlink_ext_ack *extack, | |
707 | const struct genl_ops *ops, | |
708 | int hdrlen, struct net *net) | |
709 | { | |
710 | struct nlattr **attrbuf; | |
711 | struct genl_info info; | |
712 | int err; | |
482a8524 | 713 | |
be064def | 714 | if (!ops->doit) |
1d00a4eb | 715 | return -EOPNOTSUPP; |
482a8524 | 716 | |
c10e6cf8 JP |
717 | attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack, |
718 | ops, hdrlen, | |
b65ce380 | 719 | GENL_DONT_VALIDATE_STRICT); |
c10e6cf8 JP |
720 | if (IS_ERR(attrbuf)) |
721 | return PTR_ERR(attrbuf); | |
482a8524 TG |
722 | |
723 | info.snd_seq = nlh->nlmsg_seq; | |
15e47304 | 724 | info.snd_portid = NETLINK_CB(skb).portid; |
482a8524 TG |
725 | info.nlhdr = nlh; |
726 | info.genlhdr = nlmsg_data(nlh); | |
727 | info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; | |
def31174 | 728 | info.attrs = attrbuf; |
7ab606d1 | 729 | info.extack = extack; |
134e6375 | 730 | genl_info_net_set(&info, net); |
ff4c92d8 | 731 | memset(&info.user_ptr, 0, sizeof(info.user_ptr)); |
482a8524 | 732 | |
ff4c92d8 JB |
733 | if (family->pre_doit) { |
734 | err = family->pre_doit(ops, skb, &info); | |
735 | if (err) | |
50754d21 | 736 | goto out; |
ff4c92d8 JB |
737 | } |
738 | ||
739 | err = ops->doit(skb, &info); | |
740 | ||
741 | if (family->post_doit) | |
742 | family->post_doit(ops, skb, &info); | |
743 | ||
50754d21 | 744 | out: |
bf64ff4c | 745 | genl_family_rcv_msg_attrs_free(attrbuf); |
def31174 PS |
746 | |
747 | return err; | |
748 | } | |
749 | ||
be064def JP |
750 | static int genl_family_rcv_msg(const struct genl_family *family, |
751 | struct sk_buff *skb, | |
752 | struct nlmsghdr *nlh, | |
753 | struct netlink_ext_ack *extack) | |
754 | { | |
be064def JP |
755 | struct net *net = sock_net(skb->sk); |
756 | struct genlmsghdr *hdr = nlmsg_data(nlh); | |
0b588afd | 757 | struct genl_ops op; |
be064def JP |
758 | int hdrlen; |
759 | ||
760 | /* this family doesn't exist in this netns */ | |
761 | if (!family->netnsok && !net_eq(net, &init_net)) | |
762 | return -ENOENT; | |
763 | ||
764 | hdrlen = GENL_HDRLEN + family->hdrsize; | |
765 | if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) | |
766 | return -EINVAL; | |
767 | ||
0b588afd | 768 | if (genl_get_cmd(hdr->cmd, family, &op)) |
be064def JP |
769 | return -EOPNOTSUPP; |
770 | ||
0b588afd | 771 | if ((op.flags & GENL_ADMIN_PERM) && |
be064def JP |
772 | !netlink_capable(skb, CAP_NET_ADMIN)) |
773 | return -EPERM; | |
774 | ||
0b588afd | 775 | if ((op.flags & GENL_UNS_ADMIN_PERM) && |
be064def JP |
776 | !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
777 | return -EPERM; | |
778 | ||
779 | if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) | |
780 | return genl_family_rcv_msg_dumpit(family, skb, nlh, extack, | |
0b588afd | 781 | &op, hdrlen, net); |
be064def JP |
782 | else |
783 | return genl_family_rcv_msg_doit(family, skb, nlh, extack, | |
0b588afd | 784 | &op, hdrlen, net); |
be064def JP |
785 | } |
786 | ||
2d4bc933 JB |
787 | static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, |
788 | struct netlink_ext_ack *extack) | |
def31174 | 789 | { |
2ae0f17d | 790 | const struct genl_family *family; |
def31174 PS |
791 | int err; |
792 | ||
793 | family = genl_family_find_byid(nlh->nlmsg_type); | |
794 | if (family == NULL) | |
795 | return -ENOENT; | |
796 | ||
797 | if (!family->parallel_ops) | |
798 | genl_lock(); | |
799 | ||
7ab606d1 | 800 | err = genl_family_rcv_msg(family, skb, nlh, extack); |
def31174 PS |
801 | |
802 | if (!family->parallel_ops) | |
803 | genl_unlock(); | |
804 | ||
ff4c92d8 | 805 | return err; |
482a8524 TG |
806 | } |
807 | ||
cd40b7d3 | 808 | static void genl_rcv(struct sk_buff *skb) |
482a8524 | 809 | { |
def31174 | 810 | down_read(&cb_lock); |
cd40b7d3 | 811 | netlink_rcv_skb(skb, &genl_rcv_msg); |
def31174 | 812 | up_read(&cb_lock); |
482a8524 TG |
813 | } |
814 | ||
815 | /************************************************************************** | |
816 | * Controller | |
817 | **************************************************************************/ | |
818 | ||
489111e5 | 819 | static struct genl_family genl_ctrl; |
17c157c8 | 820 | |
2ae0f17d | 821 | static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, |
482a8524 TG |
822 | u32 flags, struct sk_buff *skb, u8 cmd) |
823 | { | |
824 | void *hdr; | |
825 | ||
15e47304 | 826 | hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); |
482a8524 TG |
827 | if (hdr == NULL) |
828 | return -1; | |
829 | ||
444653f6 DM |
830 | if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || |
831 | nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) || | |
832 | nla_put_u32(skb, CTRL_ATTR_VERSION, family->version) || | |
833 | nla_put_u32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize) || | |
834 | nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) | |
835 | goto nla_put_failure; | |
eb328111 | 836 | |
0b588afd | 837 | if (genl_get_cmd_cnt(family)) { |
e94ef682 | 838 | struct nlattr *nla_ops; |
d91824c0 | 839 | int i; |
eb328111 | 840 | |
ae0be8de | 841 | nla_ops = nla_nest_start_noflag(skb, CTRL_ATTR_OPS); |
e94ef682 | 842 | if (nla_ops == NULL) |
eb328111 TG |
843 | goto nla_put_failure; |
844 | ||
0b588afd | 845 | for (i = 0; i < genl_get_cmd_cnt(family); i++) { |
e94ef682 | 846 | struct nlattr *nest; |
0b588afd JK |
847 | struct genl_ops op; |
848 | u32 op_flags; | |
f84f771d | 849 | |
0b588afd JK |
850 | genl_get_cmd_by_index(i, family, &op); |
851 | op_flags = op.flags; | |
852 | if (op.dumpit) | |
029b234f | 853 | op_flags |= GENL_CMD_CAP_DUMP; |
0b588afd | 854 | if (op.doit) |
029b234f | 855 | op_flags |= GENL_CMD_CAP_DO; |
48526a0f | 856 | if (op.policy) |
029b234f | 857 | op_flags |= GENL_CMD_CAP_HASPOL; |
eb328111 | 858 | |
ae0be8de | 859 | nest = nla_nest_start_noflag(skb, i + 1); |
e94ef682 TG |
860 | if (nest == NULL) |
861 | goto nla_put_failure; | |
eb328111 | 862 | |
0b588afd | 863 | if (nla_put_u32(skb, CTRL_ATTR_OP_ID, op.cmd) || |
029b234f | 864 | nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags)) |
444653f6 | 865 | goto nla_put_failure; |
eb328111 | 866 | |
e94ef682 TG |
867 | nla_nest_end(skb, nest); |
868 | } | |
869 | ||
870 | nla_nest_end(skb, nla_ops); | |
871 | } | |
482a8524 | 872 | |
2a94fe48 | 873 | if (family->n_mcgrps) { |
2dbba6f7 | 874 | struct nlattr *nla_grps; |
2a94fe48 | 875 | int i; |
2dbba6f7 | 876 | |
ae0be8de | 877 | nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); |
2dbba6f7 JB |
878 | if (nla_grps == NULL) |
879 | goto nla_put_failure; | |
880 | ||
2a94fe48 | 881 | for (i = 0; i < family->n_mcgrps; i++) { |
2dbba6f7 | 882 | struct nlattr *nest; |
2a94fe48 | 883 | const struct genl_multicast_group *grp; |
2dbba6f7 | 884 | |
2a94fe48 JB |
885 | grp = &family->mcgrps[i]; |
886 | ||
ae0be8de | 887 | nest = nla_nest_start_noflag(skb, i + 1); |
2dbba6f7 JB |
888 | if (nest == NULL) |
889 | goto nla_put_failure; | |
890 | ||
2a94fe48 JB |
891 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, |
892 | family->mcgrp_offset + i) || | |
444653f6 DM |
893 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, |
894 | grp->name)) | |
895 | goto nla_put_failure; | |
2dbba6f7 JB |
896 | |
897 | nla_nest_end(skb, nest); | |
898 | } | |
899 | nla_nest_end(skb, nla_grps); | |
900 | } | |
901 | ||
053c095a JB |
902 | genlmsg_end(skb, hdr); |
903 | return 0; | |
2dbba6f7 JB |
904 | |
905 | nla_put_failure: | |
bc3ed28c TG |
906 | genlmsg_cancel(skb, hdr); |
907 | return -EMSGSIZE; | |
2dbba6f7 JB |
908 | } |
909 | ||
2ae0f17d | 910 | static int ctrl_fill_mcgrp_info(const struct genl_family *family, |
2a94fe48 JB |
911 | const struct genl_multicast_group *grp, |
912 | int grp_id, u32 portid, u32 seq, u32 flags, | |
913 | struct sk_buff *skb, u8 cmd) | |
2dbba6f7 JB |
914 | { |
915 | void *hdr; | |
916 | struct nlattr *nla_grps; | |
917 | struct nlattr *nest; | |
918 | ||
15e47304 | 919 | hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); |
2dbba6f7 JB |
920 | if (hdr == NULL) |
921 | return -1; | |
922 | ||
c2ebb908 JB |
923 | if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || |
924 | nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id)) | |
444653f6 | 925 | goto nla_put_failure; |
2dbba6f7 | 926 | |
ae0be8de | 927 | nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); |
2dbba6f7 JB |
928 | if (nla_grps == NULL) |
929 | goto nla_put_failure; | |
930 | ||
ae0be8de | 931 | nest = nla_nest_start_noflag(skb, 1); |
2dbba6f7 JB |
932 | if (nest == NULL) |
933 | goto nla_put_failure; | |
934 | ||
2a94fe48 | 935 | if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) || |
444653f6 DM |
936 | nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, |
937 | grp->name)) | |
938 | goto nla_put_failure; | |
2dbba6f7 JB |
939 | |
940 | nla_nest_end(skb, nest); | |
941 | nla_nest_end(skb, nla_grps); | |
942 | ||
053c095a JB |
943 | genlmsg_end(skb, hdr); |
944 | return 0; | |
482a8524 TG |
945 | |
946 | nla_put_failure: | |
bc3ed28c TG |
947 | genlmsg_cancel(skb, hdr); |
948 | return -EMSGSIZE; | |
482a8524 TG |
949 | } |
950 | ||
951 | static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) | |
952 | { | |
2ae0f17d | 953 | int n = 0; |
482a8524 | 954 | struct genl_family *rt; |
134e6375 | 955 | struct net *net = sock_net(skb->sk); |
2ae0f17d JB |
956 | int fams_to_skip = cb->args[0]; |
957 | unsigned int id; | |
482a8524 | 958 | |
2ae0f17d JB |
959 | idr_for_each_entry(&genl_fam_idr, rt, id) { |
960 | if (!rt->netnsok && !net_eq(net, &init_net)) | |
961 | continue; | |
962 | ||
963 | if (n++ < fams_to_skip) | |
964 | continue; | |
482a8524 | 965 | |
2ae0f17d JB |
966 | if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, |
967 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | |
1d2a6a5e SG |
968 | skb, CTRL_CMD_NEWFAMILY) < 0) { |
969 | n--; | |
2ae0f17d | 970 | break; |
1d2a6a5e | 971 | } |
2ae0f17d | 972 | } |
482a8524 | 973 | |
2ae0f17d | 974 | cb->args[0] = n; |
482a8524 TG |
975 | return skb->len; |
976 | } | |
977 | ||
2ae0f17d | 978 | static struct sk_buff *ctrl_build_family_msg(const struct genl_family *family, |
15e47304 | 979 | u32 portid, int seq, u8 cmd) |
482a8524 TG |
980 | { |
981 | struct sk_buff *skb; | |
982 | int err; | |
983 | ||
339bf98f | 984 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
482a8524 TG |
985 | if (skb == NULL) |
986 | return ERR_PTR(-ENOBUFS); | |
987 | ||
15e47304 | 988 | err = ctrl_fill_info(family, portid, seq, 0, skb, cmd); |
482a8524 TG |
989 | if (err < 0) { |
990 | nlmsg_free(skb); | |
991 | return ERR_PTR(err); | |
992 | } | |
993 | ||
994 | return skb; | |
995 | } | |
996 | ||
2a94fe48 | 997 | static struct sk_buff * |
2ae0f17d | 998 | ctrl_build_mcgrp_msg(const struct genl_family *family, |
2a94fe48 JB |
999 | const struct genl_multicast_group *grp, |
1000 | int grp_id, u32 portid, int seq, u8 cmd) | |
2dbba6f7 JB |
1001 | { |
1002 | struct sk_buff *skb; | |
1003 | int err; | |
1004 | ||
1005 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1006 | if (skb == NULL) | |
1007 | return ERR_PTR(-ENOBUFS); | |
1008 | ||
2a94fe48 JB |
1009 | err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid, |
1010 | seq, 0, skb, cmd); | |
2dbba6f7 JB |
1011 | if (err < 0) { |
1012 | nlmsg_free(skb); | |
1013 | return ERR_PTR(err); | |
1014 | } | |
1015 | ||
1016 | return skb; | |
1017 | } | |
1018 | ||
a4bb4f5f | 1019 | static const struct nla_policy ctrl_policy_family[] = { |
482a8524 | 1020 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
5176f91e TG |
1021 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, |
1022 | .len = GENL_NAMSIZ - 1 }, | |
482a8524 TG |
1023 | }; |
1024 | ||
1025 | static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) | |
1026 | { | |
1027 | struct sk_buff *msg; | |
2ae0f17d | 1028 | const struct genl_family *res = NULL; |
482a8524 TG |
1029 | int err = -EINVAL; |
1030 | ||
1031 | if (info->attrs[CTRL_ATTR_FAMILY_ID]) { | |
1032 | u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); | |
1033 | res = genl_family_find_byid(id); | |
134e6375 | 1034 | err = -ENOENT; |
482a8524 TG |
1035 | } |
1036 | ||
1037 | if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { | |
5176f91e | 1038 | char *name; |
482a8524 | 1039 | |
5176f91e | 1040 | name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); |
482a8524 | 1041 | res = genl_family_find_byname(name); |
fa843095 SH |
1042 | #ifdef CONFIG_MODULES |
1043 | if (res == NULL) { | |
1044 | genl_unlock(); | |
c74f2b26 | 1045 | up_read(&cb_lock); |
e9412c37 | 1046 | request_module("net-pf-%d-proto-%d-family-%s", |
fa843095 | 1047 | PF_NETLINK, NETLINK_GENERIC, name); |
c74f2b26 | 1048 | down_read(&cb_lock); |
fa843095 SH |
1049 | genl_lock(); |
1050 | res = genl_family_find_byname(name); | |
1051 | } | |
1052 | #endif | |
134e6375 | 1053 | err = -ENOENT; |
482a8524 TG |
1054 | } |
1055 | ||
134e6375 JB |
1056 | if (res == NULL) |
1057 | return err; | |
1058 | ||
1059 | if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { | |
1060 | /* family doesn't exist here */ | |
1061 | return -ENOENT; | |
482a8524 TG |
1062 | } |
1063 | ||
15e47304 | 1064 | msg = ctrl_build_family_msg(res, info->snd_portid, info->snd_seq, |
2dbba6f7 | 1065 | CTRL_CMD_NEWFAMILY); |
134e6375 JB |
1066 | if (IS_ERR(msg)) |
1067 | return PTR_ERR(msg); | |
482a8524 | 1068 | |
134e6375 | 1069 | return genlmsg_reply(msg, info); |
482a8524 TG |
1070 | } |
1071 | ||
2ae0f17d | 1072 | static int genl_ctrl_event(int event, const struct genl_family *family, |
2a94fe48 JB |
1073 | const struct genl_multicast_group *grp, |
1074 | int grp_id) | |
482a8524 TG |
1075 | { |
1076 | struct sk_buff *msg; | |
1077 | ||
134e6375 JB |
1078 | /* genl is still initialising */ |
1079 | if (!init_net.genl_sock) | |
482a8524 TG |
1080 | return 0; |
1081 | ||
1082 | switch (event) { | |
1083 | case CTRL_CMD_NEWFAMILY: | |
1084 | case CTRL_CMD_DELFAMILY: | |
c2ebb908 | 1085 | WARN_ON(grp); |
134e6375 | 1086 | msg = ctrl_build_family_msg(family, 0, 0, event); |
2dbba6f7 JB |
1087 | break; |
1088 | case CTRL_CMD_NEWMCAST_GRP: | |
1089 | case CTRL_CMD_DELMCAST_GRP: | |
c2ebb908 | 1090 | BUG_ON(!grp); |
2a94fe48 | 1091 | msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event); |
482a8524 | 1092 | break; |
134e6375 JB |
1093 | default: |
1094 | return -EINVAL; | |
1095 | } | |
1096 | ||
1097 | if (IS_ERR(msg)) | |
1098 | return PTR_ERR(msg); | |
1099 | ||
1100 | if (!family->netnsok) { | |
68eb5503 | 1101 | genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, |
2a94fe48 | 1102 | 0, GFP_KERNEL); |
134e6375 JB |
1103 | } else { |
1104 | rcu_read_lock(); | |
68eb5503 | 1105 | genlmsg_multicast_allns(&genl_ctrl, msg, 0, |
2a94fe48 | 1106 | 0, GFP_ATOMIC); |
134e6375 | 1107 | rcu_read_unlock(); |
482a8524 TG |
1108 | } |
1109 | ||
1110 | return 0; | |
1111 | } | |
1112 | ||
adc84845 JK |
1113 | struct ctrl_dump_policy_ctx { |
1114 | struct netlink_policy_dump_state *state; | |
50a896cf JB |
1115 | const struct genl_family *rt; |
1116 | unsigned int opidx; | |
e992a6ed | 1117 | u32 op; |
adc84845 | 1118 | u16 fam_id; |
e992a6ed JK |
1119 | u8 policies:1, |
1120 | single_op:1; | |
adc84845 JK |
1121 | }; |
1122 | ||
a4bb4f5f JK |
1123 | static const struct nla_policy ctrl_policy_policy[] = { |
1124 | [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, | |
1125 | [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, | |
1126 | .len = GENL_NAMSIZ - 1 }, | |
e992a6ed | 1127 | [CTRL_ATTR_OP] = { .type = NLA_U32 }, |
a4bb4f5f JK |
1128 | }; |
1129 | ||
78ade619 | 1130 | static int ctrl_dumppolicy_start(struct netlink_callback *cb) |
d07dcf9a | 1131 | { |
8e1ed28f | 1132 | const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
adc84845 | 1133 | struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
8e1ed28f | 1134 | struct nlattr **tb = info->attrs; |
d07dcf9a | 1135 | const struct genl_family *rt; |
50a896cf JB |
1136 | struct genl_ops op; |
1137 | int err, i; | |
d07dcf9a | 1138 | |
adc84845 JK |
1139 | BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); |
1140 | ||
78ade619 JK |
1141 | if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME]) |
1142 | return -EINVAL; | |
d07dcf9a | 1143 | |
78ade619 JK |
1144 | if (tb[CTRL_ATTR_FAMILY_ID]) { |
1145 | ctx->fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); | |
1146 | } else { | |
1147 | rt = genl_family_find_byname( | |
1148 | nla_data(tb[CTRL_ATTR_FAMILY_NAME])); | |
1149 | if (!rt) | |
1150 | return -ENOENT; | |
1151 | ctx->fam_id = rt->id; | |
d07dcf9a JB |
1152 | } |
1153 | ||
adc84845 | 1154 | rt = genl_family_find_byid(ctx->fam_id); |
d07dcf9a JB |
1155 | if (!rt) |
1156 | return -ENOENT; | |
1157 | ||
50a896cf JB |
1158 | ctx->rt = rt; |
1159 | ||
e992a6ed JK |
1160 | if (tb[CTRL_ATTR_OP]) { |
1161 | ctx->single_op = true; | |
1162 | ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); | |
1163 | ||
1164 | err = genl_get_cmd(ctx->op, rt, &op); | |
1165 | if (err) { | |
1166 | NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); | |
1167 | return err; | |
1168 | } | |
1169 | ||
1170 | if (!op.policy) | |
1171 | return -ENODATA; | |
1172 | ||
1173 | return netlink_policy_dump_add_policy(&ctx->state, op.policy, | |
1174 | op.maxattr); | |
1175 | } | |
1176 | ||
50a896cf JB |
1177 | for (i = 0; i < genl_get_cmd_cnt(rt); i++) { |
1178 | genl_get_cmd_by_index(i, rt, &op); | |
1179 | ||
1180 | if (op.policy) { | |
1181 | err = netlink_policy_dump_add_policy(&ctx->state, | |
1182 | op.policy, | |
1183 | op.maxattr); | |
1184 | if (err) | |
1185 | return err; | |
1186 | } | |
1187 | } | |
d07dcf9a | 1188 | |
50a896cf JB |
1189 | if (!ctx->state) |
1190 | return -ENODATA; | |
1191 | return 0; | |
78ade619 JK |
1192 | } |
1193 | ||
aa85ee5f JB |
1194 | static void *ctrl_dumppolicy_prep(struct sk_buff *skb, |
1195 | struct netlink_callback *cb) | |
1196 | { | |
1197 | struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; | |
1198 | void *hdr; | |
1199 | ||
1200 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, | |
1201 | cb->nlh->nlmsg_seq, &genl_ctrl, | |
1202 | NLM_F_MULTI, CTRL_CMD_GETPOLICY); | |
1203 | if (!hdr) | |
1204 | return NULL; | |
1205 | ||
1206 | if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id)) | |
1207 | return NULL; | |
1208 | ||
1209 | return hdr; | |
1210 | } | |
1211 | ||
50a896cf JB |
1212 | static int ctrl_dumppolicy_put_op(struct sk_buff *skb, |
1213 | struct netlink_callback *cb, | |
1214 | struct genl_ops *op) | |
1215 | { | |
1216 | struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; | |
1217 | struct nlattr *nest_pol, *nest_op; | |
1218 | void *hdr; | |
1219 | int idx; | |
1220 | ||
1221 | /* skip if we have nothing to show */ | |
1222 | if (!op->policy) | |
1223 | return 0; | |
1224 | if (!op->doit && | |
1225 | (!op->dumpit || op->validate & GENL_DONT_VALIDATE_DUMP)) | |
1226 | return 0; | |
1227 | ||
1228 | hdr = ctrl_dumppolicy_prep(skb, cb); | |
1229 | if (!hdr) | |
1230 | return -ENOBUFS; | |
1231 | ||
1232 | nest_pol = nla_nest_start(skb, CTRL_ATTR_OP_POLICY); | |
1233 | if (!nest_pol) | |
1234 | goto err; | |
1235 | ||
1236 | nest_op = nla_nest_start(skb, op->cmd); | |
1237 | if (!nest_op) | |
1238 | goto err; | |
1239 | ||
1240 | /* for now both do/dump are always the same */ | |
1241 | idx = netlink_policy_dump_get_policy_idx(ctx->state, | |
1242 | op->policy, | |
1243 | op->maxattr); | |
1244 | ||
1245 | if (op->doit && nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) | |
1246 | goto err; | |
1247 | ||
1248 | if (op->dumpit && !(op->validate & GENL_DONT_VALIDATE_DUMP) && | |
1249 | nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) | |
1250 | goto err; | |
1251 | ||
1252 | nla_nest_end(skb, nest_op); | |
1253 | nla_nest_end(skb, nest_pol); | |
1254 | genlmsg_end(skb, hdr); | |
1255 | ||
1256 | return 0; | |
1257 | err: | |
1258 | genlmsg_cancel(skb, hdr); | |
1259 | return -ENOBUFS; | |
1260 | } | |
1261 | ||
78ade619 JK |
1262 | static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) |
1263 | { | |
1264 | struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; | |
50a896cf JB |
1265 | void *hdr; |
1266 | ||
1267 | if (!ctx->policies) { | |
1268 | while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { | |
1269 | struct genl_ops op; | |
1270 | ||
e992a6ed JK |
1271 | if (ctx->single_op) { |
1272 | int err; | |
1273 | ||
1274 | err = genl_get_cmd(ctx->op, ctx->rt, &op); | |
1275 | if (WARN_ON(err)) | |
1276 | return skb->len; | |
1277 | ||
1278 | /* break out of the loop after this one */ | |
1279 | ctx->opidx = genl_get_cmd_cnt(ctx->rt); | |
1280 | } else { | |
1281 | genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); | |
1282 | } | |
50a896cf JB |
1283 | |
1284 | if (ctrl_dumppolicy_put_op(skb, cb, &op)) | |
1285 | return skb->len; | |
1286 | ||
1287 | ctx->opidx++; | |
1288 | } | |
1289 | ||
1290 | /* completed with the per-op policy index list */ | |
1291 | ctx->policies = true; | |
1292 | } | |
d07dcf9a | 1293 | |
adc84845 | 1294 | while (netlink_policy_dump_loop(ctx->state)) { |
d07dcf9a JB |
1295 | struct nlattr *nest; |
1296 | ||
aa85ee5f | 1297 | hdr = ctrl_dumppolicy_prep(skb, cb); |
d07dcf9a JB |
1298 | if (!hdr) |
1299 | goto nla_put_failure; | |
1300 | ||
d07dcf9a JB |
1301 | nest = nla_nest_start(skb, CTRL_ATTR_POLICY); |
1302 | if (!nest) | |
1303 | goto nla_put_failure; | |
1304 | ||
adc84845 | 1305 | if (netlink_policy_dump_write(skb, ctx->state)) |
d07dcf9a JB |
1306 | goto nla_put_failure; |
1307 | ||
1308 | nla_nest_end(skb, nest); | |
1309 | ||
1310 | genlmsg_end(skb, hdr); | |
d07dcf9a JB |
1311 | } |
1312 | ||
d07dcf9a | 1313 | return skb->len; |
50a896cf JB |
1314 | |
1315 | nla_put_failure: | |
1316 | genlmsg_cancel(skb, hdr); | |
1317 | return skb->len; | |
d07dcf9a JB |
1318 | } |
1319 | ||
949ca6b8 JB |
1320 | static int ctrl_dumppolicy_done(struct netlink_callback *cb) |
1321 | { | |
adc84845 JK |
1322 | struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; |
1323 | ||
1324 | netlink_policy_dump_free(ctx->state); | |
949ca6b8 JB |
1325 | return 0; |
1326 | } | |
1327 | ||
12d8de6d | 1328 | static const struct genl_ops genl_ctrl_ops[] = { |
c53ed742 JB |
1329 | { |
1330 | .cmd = CTRL_CMD_GETFAMILY, | |
ef6243ac | 1331 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
a4bb4f5f JK |
1332 | .policy = ctrl_policy_family, |
1333 | .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, | |
c53ed742 JB |
1334 | .doit = ctrl_getfamily, |
1335 | .dumpit = ctrl_dumpfamily, | |
c53ed742 | 1336 | }, |
d07dcf9a JB |
1337 | { |
1338 | .cmd = CTRL_CMD_GETPOLICY, | |
a4bb4f5f JK |
1339 | .policy = ctrl_policy_policy, |
1340 | .maxattr = ARRAY_SIZE(ctrl_policy_policy) - 1, | |
78ade619 | 1341 | .start = ctrl_dumppolicy_start, |
d07dcf9a | 1342 | .dumpit = ctrl_dumppolicy, |
949ca6b8 | 1343 | .done = ctrl_dumppolicy_done, |
d07dcf9a | 1344 | }, |
482a8524 TG |
1345 | }; |
1346 | ||
12d8de6d | 1347 | static const struct genl_multicast_group genl_ctrl_groups[] = { |
2a94fe48 | 1348 | { .name = "notify", }, |
2dbba6f7 JB |
1349 | }; |
1350 | ||
56989f6d | 1351 | static struct genl_family genl_ctrl __ro_after_init = { |
489111e5 JB |
1352 | .module = THIS_MODULE, |
1353 | .ops = genl_ctrl_ops, | |
1354 | .n_ops = ARRAY_SIZE(genl_ctrl_ops), | |
1355 | .mcgrps = genl_ctrl_groups, | |
1356 | .n_mcgrps = ARRAY_SIZE(genl_ctrl_groups), | |
1357 | .id = GENL_ID_CTRL, | |
1358 | .name = "nlctrl", | |
1359 | .version = 0x2, | |
489111e5 JB |
1360 | .netnsok = true, |
1361 | }; | |
1362 | ||
134e6375 JB |
1363 | static int __net_init genl_pernet_init(struct net *net) |
1364 | { | |
a31f2d17 PNA |
1365 | struct netlink_kernel_cfg cfg = { |
1366 | .input = genl_rcv, | |
9785e10a | 1367 | .flags = NL_CFG_F_NONROOT_RECV, |
a31f2d17 PNA |
1368 | }; |
1369 | ||
134e6375 | 1370 | /* we'll bump the group number right afterwards */ |
9f00d977 | 1371 | net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg); |
134e6375 JB |
1372 | |
1373 | if (!net->genl_sock && net_eq(net, &init_net)) | |
1374 | panic("GENL: Cannot initialize generic netlink\n"); | |
1375 | ||
1376 | if (!net->genl_sock) | |
1377 | return -ENOMEM; | |
1378 | ||
1379 | return 0; | |
1380 | } | |
1381 | ||
1382 | static void __net_exit genl_pernet_exit(struct net *net) | |
1383 | { | |
1384 | netlink_kernel_release(net->genl_sock); | |
1385 | net->genl_sock = NULL; | |
1386 | } | |
1387 | ||
1388 | static struct pernet_operations genl_pernet_ops = { | |
1389 | .init = genl_pernet_init, | |
1390 | .exit = genl_pernet_exit, | |
1391 | }; | |
1392 | ||
482a8524 TG |
1393 | static int __init genl_init(void) |
1394 | { | |
2ae0f17d | 1395 | int err; |
482a8524 | 1396 | |
489111e5 | 1397 | err = genl_register_family(&genl_ctrl); |
482a8524 | 1398 | if (err < 0) |
134e6375 | 1399 | goto problem; |
482a8524 | 1400 | |
134e6375 JB |
1401 | err = register_pernet_subsys(&genl_pernet_ops); |
1402 | if (err) | |
1403 | goto problem; | |
482a8524 TG |
1404 | |
1405 | return 0; | |
1406 | ||
134e6375 | 1407 | problem: |
482a8524 | 1408 | panic("GENL: Cannot register controller: %d\n", err); |
482a8524 TG |
1409 | } |
1410 | ||
c62e7ac3 | 1411 | core_initcall(genl_init); |
482a8524 | 1412 | |
15e47304 | 1413 | static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, |
134e6375 JB |
1414 | gfp_t flags) |
1415 | { | |
1416 | struct sk_buff *tmp; | |
1417 | struct net *net, *prev = NULL; | |
cb9f7a9a | 1418 | bool delivered = false; |
134e6375 JB |
1419 | int err; |
1420 | ||
1421 | for_each_net_rcu(net) { | |
1422 | if (prev) { | |
1423 | tmp = skb_clone(skb, flags); | |
1424 | if (!tmp) { | |
1425 | err = -ENOMEM; | |
1426 | goto error; | |
1427 | } | |
1428 | err = nlmsg_multicast(prev->genl_sock, tmp, | |
15e47304 | 1429 | portid, group, flags); |
cb9f7a9a ND |
1430 | if (!err) |
1431 | delivered = true; | |
1432 | else if (err != -ESRCH) | |
134e6375 JB |
1433 | goto error; |
1434 | } | |
1435 | ||
1436 | prev = net; | |
1437 | } | |
1438 | ||
cb9f7a9a ND |
1439 | err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags); |
1440 | if (!err) | |
1441 | delivered = true; | |
1442 | else if (err != -ESRCH) | |
02a2385f | 1443 | return err; |
cb9f7a9a | 1444 | return delivered ? 0 : -ESRCH; |
134e6375 JB |
1445 | error: |
1446 | kfree_skb(skb); | |
1447 | return err; | |
1448 | } | |
1449 | ||
2ae0f17d JB |
1450 | int genlmsg_multicast_allns(const struct genl_family *family, |
1451 | struct sk_buff *skb, u32 portid, | |
1452 | unsigned int group, gfp_t flags) | |
134e6375 | 1453 | { |
220815a9 | 1454 | if (WARN_ON_ONCE(group >= family->n_mcgrps)) |
2a94fe48 JB |
1455 | return -EINVAL; |
1456 | group = family->mcgrp_offset + group; | |
15e47304 | 1457 | return genlmsg_mcast(skb, portid, group, flags); |
134e6375 JB |
1458 | } |
1459 | EXPORT_SYMBOL(genlmsg_multicast_allns); | |
263ba61d | 1460 | |
2ae0f17d | 1461 | void genl_notify(const struct genl_family *family, struct sk_buff *skb, |
92c14d9b | 1462 | struct genl_info *info, u32 group, gfp_t flags) |
263ba61d | 1463 | { |
92c14d9b | 1464 | struct net *net = genl_info_net(info); |
263ba61d PS |
1465 | struct sock *sk = net->genl_sock; |
1466 | int report = 0; | |
1467 | ||
92c14d9b JB |
1468 | if (info->nlhdr) |
1469 | report = nlmsg_report(info->nlhdr); | |
263ba61d | 1470 | |
220815a9 | 1471 | if (WARN_ON_ONCE(group >= family->n_mcgrps)) |
2a94fe48 JB |
1472 | return; |
1473 | group = family->mcgrp_offset + group; | |
92c14d9b | 1474 | nlmsg_notify(sk, skb, info->snd_portid, group, report, flags); |
263ba61d PS |
1475 | } |
1476 | EXPORT_SYMBOL(genl_notify); |