Blame view
net/netlink/genetlink.c
21.5 KB
482a8524f [NETLINK]: Generi... |
1 2 3 4 5 |
/* * NETLINK Generic Netlink Family * * Authors: Jamal Hadi Salim * Thomas Graf <tgraf@suug.ch> |
2dbba6f77 [GENETLINK]: Dyna... |
6 |
* Johannes Berg <johannes@sipsolutions.net> |
482a8524f [NETLINK]: Generi... |
7 |
*/ |
482a8524f [NETLINK]: Generi... |
8 9 |
#include <linux/module.h> #include <linux/kernel.h> |
5a0e3ad6a include cleanup: ... |
10 |
#include <linux/slab.h> |
482a8524f [NETLINK]: Generi... |
11 12 13 14 15 |
#include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> #include <linux/string.h> #include <linux/skbuff.h> |
14cc3e2b6 [PATCH] sem2mutex... |
16 |
#include <linux/mutex.h> |
2dbba6f77 [GENETLINK]: Dyna... |
17 |
#include <linux/bitmap.h> |
482a8524f [NETLINK]: Generi... |
18 19 |
#include <net/sock.h> #include <net/genetlink.h> |
14cc3e2b6 [PATCH] sem2mutex... |
20 |
static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ |
482a8524f [NETLINK]: Generi... |
21 |
|
f408e0ce4 netlink: Export g... |
22 |
void genl_lock(void) |
482a8524f [NETLINK]: Generi... |
23 |
{ |
14cc3e2b6 [PATCH] sem2mutex... |
24 |
mutex_lock(&genl_mutex); |
482a8524f [NETLINK]: Generi... |
25 |
} |
f408e0ce4 netlink: Export g... |
26 |
EXPORT_SYMBOL(genl_lock); |
482a8524f [NETLINK]: Generi... |
27 |
|
f408e0ce4 netlink: Export g... |
28 |
void genl_unlock(void) |
482a8524f [NETLINK]: Generi... |
29 |
{ |
14cc3e2b6 [PATCH] sem2mutex... |
30 |
mutex_unlock(&genl_mutex); |
482a8524f [NETLINK]: Generi... |
31 |
} |
f408e0ce4 netlink: Export g... |
32 |
EXPORT_SYMBOL(genl_unlock); |
482a8524f [NETLINK]: Generi... |
33 34 35 36 37 |
#define GENL_FAM_TAB_SIZE 16 #define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) static struct list_head family_ht[GENL_FAM_TAB_SIZE]; |
2dbba6f77 [GENETLINK]: Dyna... |
38 39 40 41 42 43 44 45 46 47 |
/* * Bitmap of multicast groups that are currently in use. * * To avoid an allocation at boot of just one unsigned long, * declare it global instead. * Bit 0 is marked as already used since group 0 is invalid. */ static unsigned long mc_group_start = 0x1; static unsigned long *mc_groups = &mc_group_start; static unsigned long mc_groups_longs = 1; |
482a8524f [NETLINK]: Generi... |
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
static int genl_ctrl_event(int event, void *data); static inline unsigned int genl_family_hash(unsigned int id) { return id & GENL_FAM_TAB_MASK; } static inline struct list_head *genl_family_chain(unsigned int id) { return &family_ht[genl_family_hash(id)]; } static struct genl_family *genl_family_find_byid(unsigned int id) { struct genl_family *f; list_for_each_entry(f, genl_family_chain(id), family_list) if (f->id == id) return f; return NULL; } static struct genl_family *genl_family_find_byname(char *name) { struct genl_family *f; int i; for (i = 0; i < GENL_FAM_TAB_SIZE; i++) list_for_each_entry(f, genl_family_chain(i), family_list) if (strcmp(f->name, name) == 0) return f; return NULL; } static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) { struct genl_ops *ops; list_for_each_entry(ops, &family->ops_list, ops_list) if (ops->cmd == cmd) return ops; return NULL; } /* Of course we are going to have problems once we hit * 2^16 alive types, but that can only happen by year 2K */ static inline u16 genl_generate_id(void) { |
988ade6b8 genetlink: Optimi... |
101 102 |
static u16 id_gen_idx = GENL_MIN_ID; int i; |
482a8524f [NETLINK]: Generi... |
103 |
|
988ade6b8 genetlink: Optimi... |
104 105 106 107 |
for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) { if (!genl_family_find_byid(id_gen_idx)) return id_gen_idx; if (++id_gen_idx > GENL_MAX_ID) |
482a8524f [NETLINK]: Generi... |
108 |
id_gen_idx = GENL_MIN_ID; |
988ade6b8 genetlink: Optimi... |
109 |
} |
482a8524f [NETLINK]: Generi... |
110 |
|
988ade6b8 genetlink: Optimi... |
111 |
return 0; |
482a8524f [NETLINK]: Generi... |
112 |
} |
2dbba6f77 [GENETLINK]: Dyna... |
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
static struct genl_multicast_group notify_grp; /** * genl_register_mc_group - register a multicast group * * Registers the specified multicast group and notifies userspace * about the new group. * * Returns 0 on success or a negative error code. * * @family: The generic netlink family the group shall be registered for. * @grp: The group to register, must have a name. */ int genl_register_mc_group(struct genl_family *family, struct genl_multicast_group *grp) { int id; unsigned long *new_groups; |
b1f571955 netlink: silence ... |
131 |
int err = 0; |
2dbba6f77 [GENETLINK]: Dyna... |
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
BUG_ON(grp->name[0] == '\0'); genl_lock(); /* special-case our own group */ if (grp == ¬ify_grp) id = GENL_ID_CTRL; else id = find_first_zero_bit(mc_groups, mc_groups_longs * BITS_PER_LONG); if (id >= mc_groups_longs * BITS_PER_LONG) { size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long); if (mc_groups == &mc_group_start) { new_groups = kzalloc(nlen, GFP_KERNEL); if (!new_groups) { err = -ENOMEM; goto out; } mc_groups = new_groups; *mc_groups = mc_group_start; } else { new_groups = krealloc(mc_groups, nlen, GFP_KERNEL); if (!new_groups) { err = -ENOMEM; goto out; } mc_groups = new_groups; mc_groups[mc_groups_longs] = 0; } mc_groups_longs++; } |
134e63756 genetlink: make n... |
167 168 |
if (family->netnsok) { struct net *net; |
d136f1bd3 genetlink: fix ne... |
169 |
netlink_table_grab(); |
134e63756 genetlink: make n... |
170 171 |
rcu_read_lock(); for_each_net_rcu(net) { |
d136f1bd3 genetlink: fix ne... |
172 |
err = __netlink_change_ngroups(net->genl_sock, |
134e63756 genetlink: make n... |
173 174 175 176 177 178 179 180 181 |
mc_groups_longs * BITS_PER_LONG); if (err) { /* * No need to roll back, can only fail if * memory allocation fails and then the * number of _possible_ groups has been * increased on some sockets which is ok. */ rcu_read_unlock(); |
d136f1bd3 genetlink: fix ne... |
182 |
netlink_table_ungrab(); |
134e63756 genetlink: make n... |
183 184 185 186 |
goto out; } } rcu_read_unlock(); |
d136f1bd3 genetlink: fix ne... |
187 |
netlink_table_ungrab(); |
134e63756 genetlink: make n... |
188 189 190 191 192 193 |
} else { err = netlink_change_ngroups(init_net.genl_sock, mc_groups_longs * BITS_PER_LONG); if (err) goto out; } |
2dbba6f77 [GENETLINK]: Dyna... |
194 195 196 197 198 199 200 201 202 |
grp->id = id; set_bit(id, mc_groups); list_add_tail(&grp->list, &family->mcast_groups); grp->family = family; genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp); out: genl_unlock(); |
79d310d01 [GENETLINK]: Corr... |
203 |
return err; |
2dbba6f77 [GENETLINK]: Dyna... |
204 205 |
} EXPORT_SYMBOL(genl_register_mc_group); |
79dc4386a [GENETLINK]: Fix ... |
206 207 208 |
static void __genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp) { |
134e63756 genetlink: make n... |
209 |
struct net *net; |
79dc4386a [GENETLINK]: Fix ... |
210 |
BUG_ON(grp->family != family); |
134e63756 genetlink: make n... |
211 |
|
b8273570f genetlink: fix ne... |
212 |
netlink_table_grab(); |
134e63756 genetlink: make n... |
213 214 |
rcu_read_lock(); for_each_net_rcu(net) |
b8273570f genetlink: fix ne... |
215 |
__netlink_clear_multicast_users(net->genl_sock, grp->id); |
134e63756 genetlink: make n... |
216 |
rcu_read_unlock(); |
b8273570f genetlink: fix ne... |
217 |
netlink_table_ungrab(); |
134e63756 genetlink: make n... |
218 |
|
79dc4386a [GENETLINK]: Fix ... |
219 220 221 222 223 224 |
clear_bit(grp->id, mc_groups); list_del(&grp->list); genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); grp->id = 0; grp->family = NULL; } |
2dbba6f77 [GENETLINK]: Dyna... |
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
/** * genl_unregister_mc_group - unregister a multicast group * * Unregisters the specified multicast group and notifies userspace * about it. All current listeners on the group are removed. * * Note: It is not necessary to unregister all multicast groups before * unregistering the family, unregistering the family will cause * all assigned multicast groups to be unregistered automatically. * * @family: Generic netlink family the group belongs to. * @grp: The group to unregister, must have been registered successfully * previously. */ void genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp) { |
2dbba6f77 [GENETLINK]: Dyna... |
242 |
genl_lock(); |
79dc4386a [GENETLINK]: Fix ... |
243 |
__genl_unregister_mc_group(family, grp); |
2dbba6f77 [GENETLINK]: Dyna... |
244 245 |
genl_unlock(); } |
3efb40c2c genetlink: export... |
246 |
EXPORT_SYMBOL(genl_unregister_mc_group); |
2dbba6f77 [GENETLINK]: Dyna... |
247 248 249 250 251 252 |
static void genl_unregister_mc_groups(struct genl_family *family) { struct genl_multicast_group *grp, *tmp; list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list) |
79dc4386a [GENETLINK]: Fix ... |
253 |
__genl_unregister_mc_group(family, grp); |
2dbba6f77 [GENETLINK]: Dyna... |
254 |
} |
482a8524f [NETLINK]: Generi... |
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
/** * genl_register_ops - register generic netlink operations * @family: generic netlink family * @ops: operations to be registered * * Registers the specified operations and assigns them to the specified * family. Either a doit or dumpit callback must be specified or the * operation will fail. Only one operation structure per command * identifier may be registered. * * See include/net/genetlink.h for more documenation on the operations * structure. * * Returns 0 on success or a negative error code. */ int genl_register_ops(struct genl_family *family, struct genl_ops *ops) { int err = -EINVAL; if (ops->dumpit == NULL && ops->doit == NULL) goto errout; if (genl_get_cmd(ops->cmd, family)) { err = -EEXIST; goto errout; } |
334c29a64 [GENETLINK]: Move... |
281 |
if (ops->dumpit) |
334c29a64 [GENETLINK]: Move... |
282 |
ops->flags |= GENL_CMD_CAP_DUMP; |
48d4ed7a8 [GENETLINK]: Fix ... |
283 284 |
if (ops->doit) ops->flags |= GENL_CMD_CAP_DO; |
334c29a64 [GENETLINK]: Move... |
285 286 |
if (ops->policy) ops->flags |= GENL_CMD_CAP_HASPOL; |
482a8524f [NETLINK]: Generi... |
287 288 289 290 291 292 293 294 295 |
genl_lock(); list_add_tail(&ops->ops_list, &family->ops_list); genl_unlock(); genl_ctrl_event(CTRL_CMD_NEWOPS, ops); err = 0; errout: return err; } |
416c2f9cf genetlink: cleanu... |
296 |
EXPORT_SYMBOL(genl_register_ops); |
482a8524f [NETLINK]: Generi... |
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
/** * genl_unregister_ops - unregister generic netlink operations * @family: generic netlink family * @ops: operations to be unregistered * * Unregisters the specified operations and unassigns them from the * specified family. The operation blocks until the current message * processing has finished and doesn't start again until the * unregister process has finished. * * Note: It is not necessary to unregister all operations before * unregistering the family, unregistering the family will cause * all assigned operations to be unregistered automatically. * * Returns 0 on success or a negative error code. */ int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops) { struct genl_ops *rc; genl_lock(); list_for_each_entry(rc, &family->ops_list, ops_list) { if (rc == ops) { list_del(&ops->ops_list); genl_unlock(); genl_ctrl_event(CTRL_CMD_DELOPS, ops); return 0; } } genl_unlock(); return -ENOENT; } |
416c2f9cf genetlink: cleanu... |
331 |
EXPORT_SYMBOL(genl_unregister_ops); |
482a8524f [NETLINK]: Generi... |
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
/** * genl_register_family - register a generic netlink family * @family: generic netlink family * * Registers the specified family after validating it first. Only one * family may be registered with the same family name or identifier. * The family id may equal GENL_ID_GENERATE causing an unique id to * be automatically generated and assigned. * * Return 0 on success or a negative error code. */ int genl_register_family(struct genl_family *family) { int err = -EINVAL; if (family->id && family->id < GENL_MIN_ID) goto errout; if (family->id > GENL_MAX_ID) goto errout; INIT_LIST_HEAD(&family->ops_list); |
2dbba6f77 [GENETLINK]: Dyna... |
355 |
INIT_LIST_HEAD(&family->mcast_groups); |
482a8524f [NETLINK]: Generi... |
356 357 358 359 360 361 362 |
genl_lock(); if (genl_family_find_byname(family->name)) { err = -EEXIST; goto errout_locked; } |
482a8524f [NETLINK]: Generi... |
363 364 365 366 367 368 369 370 371 |
if (family->id == GENL_ID_GENERATE) { u16 newid = genl_generate_id(); if (!newid) { err = -ENOMEM; goto errout_locked; } family->id = newid; |
93860b08e genetlink: Optimi... |
372 373 374 |
} else if (genl_family_find_byid(family->id)) { err = -EEXIST; goto errout_locked; |
482a8524f [NETLINK]: Generi... |
375 376 377 378 379 380 381 |
} if (family->maxattr) { family->attrbuf = kmalloc((family->maxattr+1) * sizeof(struct nlattr *), GFP_KERNEL); if (family->attrbuf == NULL) { err = -ENOMEM; |
e200bd806 [NETLINK] genetli... |
382 |
goto errout_locked; |
482a8524f [NETLINK]: Generi... |
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
} } else family->attrbuf = NULL; list_add_tail(&family->family_list, genl_family_chain(family->id)); genl_unlock(); genl_ctrl_event(CTRL_CMD_NEWFAMILY, family); return 0; errout_locked: genl_unlock(); errout: return err; } |
416c2f9cf genetlink: cleanu... |
399 |
EXPORT_SYMBOL(genl_register_family); |
482a8524f [NETLINK]: Generi... |
400 401 |
/** |
a7b11d738 genetlink: Introd... |
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
* genl_register_family_with_ops - register a generic netlink family * @family: generic netlink family * @ops: operations to be registered * @n_ops: number of elements to register * * Registers the specified family and operations from the specified table. * Only one family may be registered with the same family name or identifier. * * The family id may equal GENL_ID_GENERATE causing an unique id to * be automatically generated and assigned. * * Either a doit or dumpit callback must be specified for every registered * operation or the function will fail. Only one operation structure per * command identifier may be registered. * * See include/net/genetlink.h for more documenation on the operations * structure. * * This is equivalent to calling genl_register_family() followed by * genl_register_ops() for every operation entry in the table taking * care to unregister the family on error path. * * Return 0 on success or a negative error code. */ int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops) { int err, i; err = genl_register_family(family); if (err) return err; for (i = 0; i < n_ops; ++i, ++ops) { err = genl_register_ops(family, ops); if (err) goto err_out; } return 0; err_out: genl_unregister_family(family); return err; } EXPORT_SYMBOL(genl_register_family_with_ops); /** |
482a8524f [NETLINK]: Generi... |
448 449 450 451 452 453 454 455 456 457 458 459 |
* genl_unregister_family - unregister generic netlink family * @family: generic netlink family * * Unregisters the specified family. * * Returns 0 on success or a negative error code. */ int genl_unregister_family(struct genl_family *family) { struct genl_family *rc; genl_lock(); |
910d6c320 [GENETLINK]: Rela... |
460 |
genl_unregister_mc_groups(family); |
482a8524f [NETLINK]: Generi... |
461 462 463 464 465 466 467 |
list_for_each_entry(rc, genl_family_chain(family->id), family_list) { if (family->id != rc->id || strcmp(rc->name, family->name)) continue; list_del(&rc->family_list); INIT_LIST_HEAD(&family->ops_list); genl_unlock(); |
482a8524f [NETLINK]: Generi... |
468 469 470 471 472 473 474 475 476 |
kfree(family->attrbuf); genl_ctrl_event(CTRL_CMD_DELFAMILY, family); return 0; } genl_unlock(); return -ENOENT; } |
416c2f9cf genetlink: cleanu... |
477 |
EXPORT_SYMBOL(genl_unregister_family); |
482a8524f [NETLINK]: Generi... |
478 |
|
1d00a4eb4 [NETLINK]: Remove... |
479 |
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) |
482a8524f [NETLINK]: Generi... |
480 481 482 |
{ struct genl_ops *ops; struct genl_family *family; |
134e63756 genetlink: make n... |
483 |
struct net *net = sock_net(skb->sk); |
482a8524f [NETLINK]: Generi... |
484 485 |
struct genl_info info; struct genlmsghdr *hdr = nlmsg_data(nlh); |
1d00a4eb4 [NETLINK]: Remove... |
486 |
int hdrlen, err; |
482a8524f [NETLINK]: Generi... |
487 |
|
746fac4dc [NET] NETLINK: Fi... |
488 |
family = genl_family_find_byid(nlh->nlmsg_type); |
1d00a4eb4 [NETLINK]: Remove... |
489 490 |
if (family == NULL) return -ENOENT; |
482a8524f [NETLINK]: Generi... |
491 |
|
134e63756 genetlink: make n... |
492 493 494 |
/* this family doesn't exist in this netns */ if (!family->netnsok && !net_eq(net, &init_net)) return -ENOENT; |
482a8524f [NETLINK]: Generi... |
495 496 |
hdrlen = GENL_HDRLEN + family->hdrsize; if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) |
1d00a4eb4 [NETLINK]: Remove... |
497 |
return -EINVAL; |
482a8524f [NETLINK]: Generi... |
498 499 |
ops = genl_get_cmd(hdr->cmd, family); |
1d00a4eb4 [NETLINK]: Remove... |
500 501 |
if (ops == NULL) return -EOPNOTSUPP; |
482a8524f [NETLINK]: Generi... |
502 |
|
1d00a4eb4 [NETLINK]: Remove... |
503 504 505 |
if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) return -EPERM; |
482a8524f [NETLINK]: Generi... |
506 |
|
b8f3ab429 Revert "netlink: ... |
507 |
if (nlh->nlmsg_flags & NLM_F_DUMP) { |
1d00a4eb4 [NETLINK]: Remove... |
508 509 |
if (ops->dumpit == NULL) return -EOPNOTSUPP; |
482a8524f [NETLINK]: Generi... |
510 |
|
6d1a3fb56 netlink: genl: fi... |
511 |
genl_unlock(); |
134e63756 genetlink: make n... |
512 |
err = netlink_dump_start(net->genl_sock, skb, nlh, |
c7ac8679b rtnetlink: Comput... |
513 |
ops->dumpit, ops->done, 0); |
6d1a3fb56 netlink: genl: fi... |
514 515 |
genl_lock(); return err; |
482a8524f [NETLINK]: Generi... |
516 |
} |
1d00a4eb4 [NETLINK]: Remove... |
517 518 |
if (ops->doit == NULL) return -EOPNOTSUPP; |
482a8524f [NETLINK]: Generi... |
519 520 521 522 523 |
if (family->attrbuf) { err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr, ops->policy); if (err < 0) |
1d00a4eb4 [NETLINK]: Remove... |
524 |
return err; |
482a8524f [NETLINK]: Generi... |
525 526 527 528 529 530 531 532 |
} info.snd_seq = nlh->nlmsg_seq; info.snd_pid = NETLINK_CB(skb).pid; info.nlhdr = nlh; info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = family->attrbuf; |
134e63756 genetlink: make n... |
533 |
genl_info_net_set(&info, net); |
ff4c92d85 genetlink: introd... |
534 |
memset(&info.user_ptr, 0, sizeof(info.user_ptr)); |
482a8524f [NETLINK]: Generi... |
535 |
|
ff4c92d85 genetlink: introd... |
536 537 538 539 540 541 542 543 544 545 546 547 |
if (family->pre_doit) { err = family->pre_doit(ops, skb, &info); if (err) return err; } err = ops->doit(skb, &info); if (family->post_doit) family->post_doit(ops, skb, &info); return err; |
482a8524f [NETLINK]: Generi... |
548 |
} |
cd40b7d39 [NET]: make netli... |
549 |
static void genl_rcv(struct sk_buff *skb) |
482a8524f [NETLINK]: Generi... |
550 |
{ |
cd40b7d39 [NET]: make netli... |
551 552 553 |
genl_lock(); netlink_rcv_skb(skb, &genl_rcv_msg); genl_unlock(); |
482a8524f [NETLINK]: Generi... |
554 555 556 557 558 |
} /************************************************************************** * Controller **************************************************************************/ |
17c157c88 [GENL]: Add genlm... |
559 560 561 |
static struct genl_family genl_ctrl = { .id = GENL_ID_CTRL, .name = "nlctrl", |
334c29a64 [GENETLINK]: Move... |
562 |
.version = 0x2, |
17c157c88 [GENL]: Add genlm... |
563 |
.maxattr = CTRL_ATTR_MAX, |
134e63756 genetlink: make n... |
564 |
.netnsok = true, |
17c157c88 [GENL]: Add genlm... |
565 |
}; |
482a8524f [NETLINK]: Generi... |
566 567 568 569 |
static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { void *hdr; |
17c157c88 [GENL]: Add genlm... |
570 |
hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd); |
482a8524f [NETLINK]: Generi... |
571 572 573 574 575 |
if (hdr == NULL) return -1; NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, family->name); NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, family->id); |
eb328111e [GENL]: Provide m... |
576 577 578 |
NLA_PUT_U32(skb, CTRL_ATTR_VERSION, family->version); NLA_PUT_U32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize); NLA_PUT_U32(skb, CTRL_ATTR_MAXATTR, family->maxattr); |
e94ef6820 [GENETLINK] ctrl:... |
579 580 581 582 |
if (!list_empty(&family->ops_list)) { struct nlattr *nla_ops; struct genl_ops *ops; int idx = 1; |
eb328111e [GENL]: Provide m... |
583 |
|
e94ef6820 [GENETLINK] ctrl:... |
584 585 |
nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS); if (nla_ops == NULL) |
eb328111e [GENL]: Provide m... |
586 |
goto nla_put_failure; |
e94ef6820 [GENETLINK] ctrl:... |
587 588 |
list_for_each_entry(ops, &family->ops_list, ops_list) { struct nlattr *nest; |
eb328111e [GENL]: Provide m... |
589 |
|
e94ef6820 [GENETLINK] ctrl:... |
590 591 592 |
nest = nla_nest_start(skb, idx++); if (nest == NULL) goto nla_put_failure; |
eb328111e [GENL]: Provide m... |
593 |
|
e94ef6820 [GENETLINK] ctrl:... |
594 595 |
NLA_PUT_U32(skb, CTRL_ATTR_OP_ID, ops->cmd); NLA_PUT_U32(skb, CTRL_ATTR_OP_FLAGS, ops->flags); |
eb328111e [GENL]: Provide m... |
596 |
|
e94ef6820 [GENETLINK] ctrl:... |
597 598 599 600 601 |
nla_nest_end(skb, nest); } nla_nest_end(skb, nla_ops); } |
482a8524f [NETLINK]: Generi... |
602 |
|
2dbba6f77 [GENETLINK]: Dyna... |
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
if (!list_empty(&family->mcast_groups)) { struct genl_multicast_group *grp; struct nlattr *nla_grps; int idx = 1; nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); if (nla_grps == NULL) goto nla_put_failure; list_for_each_entry(grp, &family->mcast_groups, list) { struct nlattr *nest; nest = nla_nest_start(skb, idx++); if (nest == NULL) goto nla_put_failure; NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id); NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME, grp->name); nla_nest_end(skb, nest); } nla_nest_end(skb, nla_grps); } return genlmsg_end(skb, hdr); nla_put_failure: |
bc3ed28ca netlink: Improve ... |
631 632 |
genlmsg_cancel(skb, hdr); return -EMSGSIZE; |
2dbba6f77 [GENETLINK]: Dyna... |
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 |
} static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { void *hdr; struct nlattr *nla_grps; struct nlattr *nest; hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd); if (hdr == NULL) return -1; NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name); NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id); nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS); if (nla_grps == NULL) goto nla_put_failure; nest = nla_nest_start(skb, 1); if (nest == NULL) goto nla_put_failure; NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id); NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME, grp->name); nla_nest_end(skb, nest); nla_nest_end(skb, nla_grps); |
482a8524f [NETLINK]: Generi... |
664 665 666 |
return genlmsg_end(skb, hdr); nla_put_failure: |
bc3ed28ca netlink: Improve ... |
667 668 |
genlmsg_cancel(skb, hdr); return -EMSGSIZE; |
482a8524f [NETLINK]: Generi... |
669 670 671 672 673 674 675 |
} static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) { int i, n = 0; struct genl_family *rt; |
134e63756 genetlink: make n... |
676 |
struct net *net = sock_net(skb->sk); |
482a8524f [NETLINK]: Generi... |
677 678 |
int chains_to_skip = cb->args[0]; int fams_to_skip = cb->args[1]; |
e1d5a0107 genetlink: optimi... |
679 |
for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) { |
482a8524f [NETLINK]: Generi... |
680 681 |
n = 0; list_for_each_entry(rt, genl_family_chain(i), family_list) { |
134e63756 genetlink: make n... |
682 683 |
if (!rt->netnsok && !net_eq(net, &init_net)) continue; |
482a8524f [NETLINK]: Generi... |
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
if (++n < fams_to_skip) continue; if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, skb, CTRL_CMD_NEWFAMILY) < 0) goto errout; } fams_to_skip = 0; } errout: cb->args[0] = i; cb->args[1] = n; return skb->len; } |
2dbba6f77 [GENETLINK]: Dyna... |
701 702 |
static struct sk_buff *ctrl_build_family_msg(struct genl_family *family, u32 pid, int seq, u8 cmd) |
482a8524f [NETLINK]: Generi... |
703 704 705 |
{ struct sk_buff *skb; int err; |
339bf98ff [NETLINK]: Do pre... |
706 |
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
482a8524f [NETLINK]: Generi... |
707 708 709 710 711 712 713 714 715 716 717 |
if (skb == NULL) return ERR_PTR(-ENOBUFS); err = ctrl_fill_info(family, pid, seq, 0, skb, cmd); if (err < 0) { nlmsg_free(skb); return ERR_PTR(err); } return skb; } |
2dbba6f77 [GENETLINK]: Dyna... |
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 |
static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp, u32 pid, int seq, u8 cmd) { struct sk_buff *skb; int err; skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (skb == NULL) return ERR_PTR(-ENOBUFS); err = ctrl_fill_mcgrp_info(grp, pid, seq, 0, skb, cmd); if (err < 0) { nlmsg_free(skb); return ERR_PTR(err); } return skb; } |
ef7c79ed6 [NETLINK]: Mark n... |
736 |
static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { |
482a8524f [NETLINK]: Generi... |
737 |
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, |
5176f91ea [NETLINK]: Make u... |
738 739 |
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, .len = GENL_NAMSIZ - 1 }, |
482a8524f [NETLINK]: Generi... |
740 741 742 743 744 745 746 747 748 749 750 |
}; static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *msg; struct genl_family *res = NULL; int err = -EINVAL; if (info->attrs[CTRL_ATTR_FAMILY_ID]) { u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); res = genl_family_find_byid(id); |
134e63756 genetlink: make n... |
751 |
err = -ENOENT; |
482a8524f [NETLINK]: Generi... |
752 753 754 |
} if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { |
5176f91ea [NETLINK]: Make u... |
755 |
char *name; |
482a8524f [NETLINK]: Generi... |
756 |
|
5176f91ea [NETLINK]: Make u... |
757 |
name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); |
482a8524f [NETLINK]: Generi... |
758 |
res = genl_family_find_byname(name); |
134e63756 genetlink: make n... |
759 |
err = -ENOENT; |
482a8524f [NETLINK]: Generi... |
760 |
} |
134e63756 genetlink: make n... |
761 762 763 764 765 766 |
if (res == NULL) return err; if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { /* family doesn't exist here */ return -ENOENT; |
482a8524f [NETLINK]: Generi... |
767 |
} |
2dbba6f77 [GENETLINK]: Dyna... |
768 769 |
msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, CTRL_CMD_NEWFAMILY); |
134e63756 genetlink: make n... |
770 771 |
if (IS_ERR(msg)) return PTR_ERR(msg); |
482a8524f [NETLINK]: Generi... |
772 |
|
134e63756 genetlink: make n... |
773 |
return genlmsg_reply(msg, info); |
482a8524f [NETLINK]: Generi... |
774 775 776 777 778 |
} static int genl_ctrl_event(int event, void *data) { struct sk_buff *msg; |
134e63756 genetlink: make n... |
779 780 |
struct genl_family *family; struct genl_multicast_group *grp; |
482a8524f [NETLINK]: Generi... |
781 |
|
134e63756 genetlink: make n... |
782 783 |
/* genl is still initialising */ if (!init_net.genl_sock) |
482a8524f [NETLINK]: Generi... |
784 785 786 787 788 |
return 0; switch (event) { case CTRL_CMD_NEWFAMILY: case CTRL_CMD_DELFAMILY: |
134e63756 genetlink: make n... |
789 790 |
family = data; msg = ctrl_build_family_msg(family, 0, 0, event); |
2dbba6f77 [GENETLINK]: Dyna... |
791 792 793 |
break; case CTRL_CMD_NEWMCAST_GRP: case CTRL_CMD_DELMCAST_GRP: |
134e63756 genetlink: make n... |
794 795 |
grp = data; family = grp->family; |
2dbba6f77 [GENETLINK]: Dyna... |
796 |
msg = ctrl_build_mcgrp_msg(data, 0, 0, event); |
482a8524f [NETLINK]: Generi... |
797 |
break; |
134e63756 genetlink: make n... |
798 799 800 801 802 803 804 805 806 807 808 809 810 811 |
default: return -EINVAL; } if (IS_ERR(msg)) return PTR_ERR(msg); if (!family->netnsok) { genlmsg_multicast_netns(&init_net, msg, 0, GENL_ID_CTRL, GFP_KERNEL); } else { rcu_read_lock(); genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC); rcu_read_unlock(); |
482a8524f [NETLINK]: Generi... |
812 813 814 815 816 817 818 819 820 821 822 |
} return 0; } static struct genl_ops genl_ctrl_ops = { .cmd = CTRL_CMD_GETFAMILY, .doit = ctrl_getfamily, .dumpit = ctrl_dumpfamily, .policy = ctrl_policy, }; |
2dbba6f77 [GENETLINK]: Dyna... |
823 824 825 |
static struct genl_multicast_group notify_grp = { .name = "notify", }; |
134e63756 genetlink: make n... |
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 |
static int __net_init genl_pernet_init(struct net *net) { /* we'll bump the group number right afterwards */ net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0, genl_rcv, &genl_mutex, THIS_MODULE); if (!net->genl_sock && net_eq(net, &init_net)) panic("GENL: Cannot initialize generic netlink "); if (!net->genl_sock) return -ENOMEM; return 0; } static void __net_exit genl_pernet_exit(struct net *net) { netlink_kernel_release(net->genl_sock); net->genl_sock = NULL; } static struct pernet_operations genl_pernet_ops = { .init = genl_pernet_init, .exit = genl_pernet_exit, }; |
482a8524f [NETLINK]: Generi... |
853 854 855 856 857 858 |
static int __init genl_init(void) { int i, err; for (i = 0; i < GENL_FAM_TAB_SIZE; i++) INIT_LIST_HEAD(&family_ht[i]); |
652c67174 genetlink: use ge... |
859 |
err = genl_register_family_with_ops(&genl_ctrl, &genl_ctrl_ops, 1); |
482a8524f [NETLINK]: Generi... |
860 |
if (err < 0) |
134e63756 genetlink: make n... |
861 |
goto problem; |
482a8524f [NETLINK]: Generi... |
862 863 |
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); |
2dbba6f77 [GENETLINK]: Dyna... |
864 |
|
134e63756 genetlink: make n... |
865 866 867 |
err = register_pernet_subsys(&genl_pernet_ops); if (err) goto problem; |
482a8524f [NETLINK]: Generi... |
868 |
|
2dbba6f77 [GENETLINK]: Dyna... |
869 870 |
err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); if (err < 0) |
134e63756 genetlink: make n... |
871 |
goto problem; |
2dbba6f77 [GENETLINK]: Dyna... |
872 |
|
482a8524f [NETLINK]: Generi... |
873 |
return 0; |
134e63756 genetlink: make n... |
874 |
problem: |
482a8524f [NETLINK]: Generi... |
875 876 |
panic("GENL: Cannot register controller: %d ", err); |
482a8524f [NETLINK]: Generi... |
877 878 879 |
} subsys_initcall(genl_init); |
134e63756 genetlink: make n... |
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 |
static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, gfp_t flags) { struct sk_buff *tmp; struct net *net, *prev = NULL; int err; for_each_net_rcu(net) { if (prev) { tmp = skb_clone(skb, flags); if (!tmp) { err = -ENOMEM; goto error; } err = nlmsg_multicast(prev->genl_sock, tmp, pid, group, flags); if (err) goto error; } prev = net; } return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags); error: kfree_skb(skb); return err; } int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group, gfp_t flags) { return genlmsg_mcast(skb, pid, group, flags); } EXPORT_SYMBOL(genlmsg_multicast_allns); |