diff options
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 69 |
1 files changed, 51 insertions, 18 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 444ed223ee43..58d4ca42ac32 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -60,21 +60,24 @@ | |||
60 | #include <net/scm.h> | 60 | #include <net/scm.h> |
61 | 61 | ||
62 | #define Nprintk(a...) | 62 | #define Nprintk(a...) |
63 | #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) | ||
63 | 64 | ||
64 | struct netlink_sock { | 65 | struct netlink_sock { |
65 | /* struct sock has to be the first member of netlink_sock */ | 66 | /* struct sock has to be the first member of netlink_sock */ |
66 | struct sock sk; | 67 | struct sock sk; |
67 | u32 pid; | 68 | u32 pid; |
68 | unsigned int groups; | ||
69 | u32 dst_pid; | 69 | u32 dst_pid; |
70 | u32 dst_group; | 70 | u32 dst_group; |
71 | u32 flags; | ||
72 | u32 subscriptions; | ||
73 | u32 ngroups; | ||
74 | unsigned long *groups; | ||
71 | unsigned long state; | 75 | unsigned long state; |
72 | wait_queue_head_t wait; | 76 | wait_queue_head_t wait; |
73 | struct netlink_callback *cb; | 77 | struct netlink_callback *cb; |
74 | spinlock_t cb_lock; | 78 | spinlock_t cb_lock; |
75 | void (*data_ready)(struct sock *sk, int bytes); | 79 | void (*data_ready)(struct sock *sk, int bytes); |
76 | struct module *module; | 80 | struct module *module; |
77 | u32 flags; | ||
78 | }; | 81 | }; |
79 | 82 | ||
80 | #define NETLINK_KERNEL_SOCKET 0x1 | 83 | #define NETLINK_KERNEL_SOCKET 0x1 |
@@ -101,6 +104,7 @@ struct netlink_table { | |||
101 | struct nl_pid_hash hash; | 104 | struct nl_pid_hash hash; |
102 | struct hlist_head mc_list; | 105 | struct hlist_head mc_list; |
103 | unsigned int nl_nonroot; | 106 | unsigned int nl_nonroot; |
107 | unsigned int groups; | ||
104 | struct module *module; | 108 | struct module *module; |
105 | int registered; | 109 | int registered; |
106 | }; | 110 | }; |
@@ -138,6 +142,7 @@ static void netlink_sock_destruct(struct sock *sk) | |||
138 | BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc)); | 142 | BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc)); |
139 | BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); | 143 | BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); |
140 | BUG_TRAP(!nlk_sk(sk)->cb); | 144 | BUG_TRAP(!nlk_sk(sk)->cb); |
145 | BUG_TRAP(!nlk_sk(sk)->groups); | ||
141 | } | 146 | } |
142 | 147 | ||
143 | /* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on SMP. | 148 | /* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on SMP. |
@@ -333,7 +338,7 @@ static void netlink_remove(struct sock *sk) | |||
333 | netlink_table_grab(); | 338 | netlink_table_grab(); |
334 | if (sk_del_node_init(sk)) | 339 | if (sk_del_node_init(sk)) |
335 | nl_table[sk->sk_protocol].hash.entries--; | 340 | nl_table[sk->sk_protocol].hash.entries--; |
336 | if (nlk_sk(sk)->groups) | 341 | if (nlk_sk(sk)->subscriptions) |
337 | __sk_del_bind_node(sk); | 342 | __sk_del_bind_node(sk); |
338 | netlink_table_ungrab(); | 343 | netlink_table_ungrab(); |
339 | } | 344 | } |
@@ -369,6 +374,8 @@ static int __netlink_create(struct socket *sock, int protocol) | |||
369 | static int netlink_create(struct socket *sock, int protocol) | 374 | static int netlink_create(struct socket *sock, int protocol) |
370 | { | 375 | { |
371 | struct module *module = NULL; | 376 | struct module *module = NULL; |
377 | struct netlink_sock *nlk; | ||
378 | unsigned int groups; | ||
372 | int err = 0; | 379 | int err = 0; |
373 | 380 | ||
374 | sock->state = SS_UNCONNECTED; | 381 | sock->state = SS_UNCONNECTED; |
@@ -392,15 +399,23 @@ static int netlink_create(struct socket *sock, int protocol) | |||
392 | module = nl_table[protocol].module; | 399 | module = nl_table[protocol].module; |
393 | else | 400 | else |
394 | err = -EPROTONOSUPPORT; | 401 | err = -EPROTONOSUPPORT; |
402 | groups = nl_table[protocol].groups; | ||
395 | netlink_unlock_table(); | 403 | netlink_unlock_table(); |
396 | 404 | ||
397 | if (err) | 405 | if (err || (err = __netlink_create(sock, protocol) < 0)) |
398 | goto out; | 406 | goto out_module; |
407 | |||
408 | nlk = nlk_sk(sock->sk); | ||
399 | 409 | ||
400 | if ((err = __netlink_create(sock, protocol) < 0)) | 410 | nlk->groups = kmalloc(NLGRPSZ(groups), GFP_KERNEL); |
411 | if (nlk->groups == NULL) { | ||
412 | err = -ENOMEM; | ||
401 | goto out_module; | 413 | goto out_module; |
414 | } | ||
415 | memset(nlk->groups, 0, NLGRPSZ(groups)); | ||
416 | nlk->ngroups = groups; | ||
402 | 417 | ||
403 | nlk_sk(sock->sk)->module = module; | 418 | nlk->module = module; |
404 | out: | 419 | out: |
405 | return err; | 420 | return err; |
406 | 421 | ||
@@ -437,7 +452,7 @@ static int netlink_release(struct socket *sock) | |||
437 | 452 | ||
438 | skb_queue_purge(&sk->sk_write_queue); | 453 | skb_queue_purge(&sk->sk_write_queue); |
439 | 454 | ||
440 | if (nlk->pid && !nlk->groups) { | 455 | if (nlk->pid && !nlk->subscriptions) { |
441 | struct netlink_notify n = { | 456 | struct netlink_notify n = { |
442 | .protocol = sk->sk_protocol, | 457 | .protocol = sk->sk_protocol, |
443 | .pid = nlk->pid, | 458 | .pid = nlk->pid, |
@@ -455,6 +470,9 @@ static int netlink_release(struct socket *sock) | |||
455 | netlink_table_ungrab(); | 470 | netlink_table_ungrab(); |
456 | } | 471 | } |
457 | 472 | ||
473 | kfree(nlk->groups); | ||
474 | nlk->groups = NULL; | ||
475 | |||
458 | sock_put(sk); | 476 | sock_put(sk); |
459 | return 0; | 477 | return 0; |
460 | } | 478 | } |
@@ -503,6 +521,18 @@ static inline int netlink_capable(struct socket *sock, unsigned int flag) | |||
503 | capable(CAP_NET_ADMIN); | 521 | capable(CAP_NET_ADMIN); |
504 | } | 522 | } |
505 | 523 | ||
524 | static void | ||
525 | netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions) | ||
526 | { | ||
527 | struct netlink_sock *nlk = nlk_sk(sk); | ||
528 | |||
529 | if (nlk->subscriptions && !subscriptions) | ||
530 | __sk_del_bind_node(sk); | ||
531 | else if (!nlk->subscriptions && subscriptions) | ||
532 | sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list); | ||
533 | nlk->subscriptions = subscriptions; | ||
534 | } | ||
535 | |||
506 | static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) | 536 | static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) |
507 | { | 537 | { |
508 | struct sock *sk = sock->sk; | 538 | struct sock *sk = sock->sk; |
@@ -528,15 +558,14 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len | |||
528 | return err; | 558 | return err; |
529 | } | 559 | } |
530 | 560 | ||
531 | if (!nladdr->nl_groups && !nlk->groups) | 561 | if (!nladdr->nl_groups && !(u32)nlk->groups[0]) |
532 | return 0; | 562 | return 0; |
533 | 563 | ||
534 | netlink_table_grab(); | 564 | netlink_table_grab(); |
535 | if (nlk->groups && !nladdr->nl_groups) | 565 | netlink_update_subscriptions(sk, nlk->subscriptions + |
536 | __sk_del_bind_node(sk); | 566 | hweight32(nladdr->nl_groups) - |
537 | else if (!nlk->groups && nladdr->nl_groups) | 567 | hweight32(nlk->groups[0])); |
538 | sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list); | 568 | nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; |
539 | nlk->groups = nladdr->nl_groups; | ||
540 | netlink_table_ungrab(); | 569 | netlink_table_ungrab(); |
541 | 570 | ||
542 | return 0; | 571 | return 0; |
@@ -590,7 +619,7 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr | |||
590 | nladdr->nl_groups = netlink_group_mask(nlk->dst_group); | 619 | nladdr->nl_groups = netlink_group_mask(nlk->dst_group); |
591 | } else { | 620 | } else { |
592 | nladdr->nl_pid = nlk->pid; | 621 | nladdr->nl_pid = nlk->pid; |
593 | nladdr->nl_groups = nlk->groups; | 622 | nladdr->nl_groups = nlk->groups[0]; |
594 | } | 623 | } |
595 | return 0; | 624 | return 0; |
596 | } | 625 | } |
@@ -791,7 +820,8 @@ static inline int do_one_broadcast(struct sock *sk, | |||
791 | if (p->exclude_sk == sk) | 820 | if (p->exclude_sk == sk) |
792 | goto out; | 821 | goto out; |
793 | 822 | ||
794 | if (nlk->pid == p->pid || !(nlk->groups & netlink_group_mask(p->group))) | 823 | if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups || |
824 | !test_bit(p->group - 1, nlk->groups)) | ||
795 | goto out; | 825 | goto out; |
796 | 826 | ||
797 | if (p->failure) { | 827 | if (p->failure) { |
@@ -887,7 +917,8 @@ static inline int do_one_set_err(struct sock *sk, | |||
887 | if (sk == p->exclude_sk) | 917 | if (sk == p->exclude_sk) |
888 | goto out; | 918 | goto out; |
889 | 919 | ||
890 | if (nlk->pid == p->pid || !(nlk->groups & netlink_group_mask(p->group))) | 920 | if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups || |
921 | !test_bit(p->group - 1, nlk->groups)) | ||
891 | goto out; | 922 | goto out; |
892 | 923 | ||
893 | sk->sk_err = p->code; | 924 | sk->sk_err = p->code; |
@@ -1112,6 +1143,7 @@ netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct | |||
1112 | nlk->flags |= NETLINK_KERNEL_SOCKET; | 1143 | nlk->flags |= NETLINK_KERNEL_SOCKET; |
1113 | 1144 | ||
1114 | netlink_table_grab(); | 1145 | netlink_table_grab(); |
1146 | nl_table[unit].groups = 32; | ||
1115 | nl_table[unit].module = module; | 1147 | nl_table[unit].module = module; |
1116 | nl_table[unit].registered = 1; | 1148 | nl_table[unit].registered = 1; |
1117 | netlink_table_ungrab(); | 1149 | netlink_table_ungrab(); |
@@ -1358,7 +1390,8 @@ static int netlink_seq_show(struct seq_file *seq, void *v) | |||
1358 | s, | 1390 | s, |
1359 | s->sk_protocol, | 1391 | s->sk_protocol, |
1360 | nlk->pid, | 1392 | nlk->pid, |
1361 | nlk->groups, | 1393 | nlk->flags & NETLINK_KERNEL_SOCKET ? |
1394 | 0 : (unsigned int)nlk->groups[0], | ||
1362 | atomic_read(&s->sk_rmem_alloc), | 1395 | atomic_read(&s->sk_rmem_alloc), |
1363 | atomic_read(&s->sk_wmem_alloc), | 1396 | atomic_read(&s->sk_wmem_alloc), |
1364 | nlk->cb, | 1397 | nlk->cb, |