diff options
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r-- | net/netlink/af_netlink.c | 167 |
1 files changed, 93 insertions, 74 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index de3988ba1f46..6b178e1247b5 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -156,7 +156,7 @@ static void netlink_sock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_receive_queue); if (!sock_flag(sk, SOCK_DEAD)) { - printk("Freeing alive netlink socket %p\n", sk); + printk(KERN_ERR "Freeing alive netlink socket %p\n", sk); return; } BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc)); @@ -164,13 +164,14 @@ static void netlink_sock_destruct(struct sock *sk) BUG_TRAP(!nlk_sk(sk)->groups); } -/* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on SMP. - * Look, when several writers sleep and reader wakes them up, all but one +/* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on + * SMP. Look, when several writers sleep and reader wakes them up, all but one * immediately hit write lock and grab all the cpus. Exclusive sleep solves * this, _but_ remember, it adds useless work on UP machines. */ static void netlink_table_grab(void) + __acquires(nl_table_lock) { write_lock_irq(&nl_table_lock); @@ -178,7 +179,7 @@ static void netlink_table_grab(void) DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&nl_table_wait, &wait); - for(;;) { + for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&nl_table_users) == 0) break; @@ -192,13 +193,14 @@ static void netlink_table_grab(void) } } -static __inline__ void netlink_table_ungrab(void) +static void netlink_table_ungrab(void) + __releases(nl_table_lock) { write_unlock_irq(&nl_table_lock); wake_up(&nl_table_wait); } -static __inline__ void +static inline void netlink_lock_table(void) { /* read_lock() synchronizes us to netlink_table_grab */ @@ -208,14 +210,15 @@ netlink_lock_table(void) read_unlock(&nl_table_lock); } -static __inline__ void +static inline void netlink_unlock_table(void) { if (atomic_dec_and_test(&nl_table_users)) wake_up(&nl_table_wait); } -static __inline__ struct sock *netlink_lookup(struct net *net, int protocol, u32 pid) +static inline struct sock *netlink_lookup(struct net *net, int protocol, + u32 pid) { struct nl_pid_hash *hash = &nl_table[protocol].hash; struct hlist_head *head; @@ -236,13 +239,14 @@ found: return sk; } -static inline struct hlist_head *nl_pid_hash_alloc(size_t size) +static inline struct hlist_head *nl_pid_hash_zalloc(size_t size) { if (size <= PAGE_SIZE) - return kmalloc(size, GFP_ATOMIC); + return kzalloc(size, GFP_ATOMIC); else return (struct hlist_head *) - __get_free_pages(GFP_ATOMIC, get_order(size)); + __get_free_pages(GFP_ATOMIC | __GFP_ZERO, + get_order(size)); } static inline void nl_pid_hash_free(struct hlist_head *table, size_t size) @@ -271,11 +275,10 @@ static int nl_pid_hash_rehash(struct nl_pid_hash *hash, int grow) size *= 2; } - table = nl_pid_hash_alloc(size); + table = nl_pid_hash_zalloc(size); if (!table) return 0; - memset(table, 0, size); otable = hash->table; hash->table = table; hash->mask = mask; @@ -428,7 +431,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol) if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) return -ESOCKTNOSUPPORT; - if (protocol<0 || protocol >= MAX_LINKS) + if (protocol < 0 || protocol >= MAX_LINKS) return -EPROTONOSUPPORT; netlink_lock_table(); @@ -445,7 +448,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol) cb_mutex = nl_table[protocol].cb_mutex; netlink_unlock_table(); - if ((err = __netlink_create(net, sock, cb_mutex, protocol)) < 0) + err = __netlink_create(net, sock, cb_mutex, protocol); + if (err < 0) goto out_module; nlk = nlk_sk(sock->sk); @@ -494,9 +498,12 @@ static int netlink_release(struct socket *sock) netlink_table_grab(); if (netlink_is_kernel(sk)) { - kfree(nl_table[sk->sk_protocol].listeners); - nl_table[sk->sk_protocol].module = NULL; - nl_table[sk->sk_protocol].registered = 0; + BUG_ON(nl_table[sk->sk_protocol].registered == 0); + if (--nl_table[sk->sk_protocol].registered == 0) { + kfree(nl_table[sk->sk_protocol].listeners); + nl_table[sk->sk_protocol].module = NULL; + nl_table[sk->sk_protocol].registered = 0; + } } else if (nlk->subscriptions) netlink_update_listeners(sk); netlink_table_ungrab(); @@ -590,7 +597,7 @@ static int netlink_realloc_groups(struct sock *sk) err = -ENOMEM; goto out_unlock; } - memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0, + memset((char *)new_groups + NLGRPSZ(nlk->ngroups), 0, NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups)); nlk->groups = new_groups; @@ -600,7 +607,8 @@ static int netlink_realloc_groups(struct sock *sk) return err; } -static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +static int netlink_bind(struct socket *sock, struct sockaddr *addr, + int addr_len) { struct sock *sk = sock->sk; struct net *net = sk->sk_net; @@ -651,7 +659,7 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, int err = 0; struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); - struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr; + struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; if (addr->sa_family == AF_UNSPEC) { sk->sk_state = NETLINK_UNCONNECTED; @@ -678,11 +686,12 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, return err; } -static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) +static int netlink_getname(struct socket *sock, struct sockaddr *addr, + int *addr_len, int peer) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); - struct sockaddr_nl *nladdr=(struct sockaddr_nl *)addr; + struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; nladdr->nl_family = AF_NETLINK; nladdr->nl_pad = 0; @@ -885,6 +894,7 @@ retry: return netlink_sendskb(sk, skb); } +EXPORT_SYMBOL(netlink_unicast); int netlink_has_listeners(struct sock *sk, unsigned int group) { @@ -905,7 +915,8 @@ int netlink_has_listeners(struct sock *sk, unsigned int group) } EXPORT_SYMBOL_GPL(netlink_has_listeners); -static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) +static inline int netlink_broadcast_deliver(struct sock *sk, + struct sk_buff *skb) { struct netlink_sock *nlk = nlk_sk(sk); @@ -1026,6 +1037,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, return -ENOBUFS; return -ESRCH; } +EXPORT_SYMBOL(netlink_broadcast); struct netlink_set_err_data { struct sock *exclude_sk; @@ -1182,7 +1194,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); - struct sockaddr_nl *addr=msg->msg_name; + struct sockaddr_nl *addr = msg->msg_name; u32 dst_pid; u32 dst_group; struct sk_buff *skb; @@ -1221,7 +1233,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, goto out; err = -ENOBUFS; skb = alloc_skb(len, GFP_KERNEL); - if (skb==NULL) + if (skb == NULL) goto out; NETLINK_CB(skb).pid = nlk->pid; @@ -1237,7 +1249,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, */ err = -EFAULT; - if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) { + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { kfree_skb(skb); goto out; } @@ -1276,8 +1288,8 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, copied = 0; - skb = skb_recv_datagram(sk,flags,noblock,&err); - if (skb==NULL) + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (skb == NULL) goto out; msg->msg_namelen = 0; @@ -1292,7 +1304,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_name) { - struct sockaddr_nl *addr = (struct sockaddr_nl*)msg->msg_name; + struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name; addr->nl_family = AF_NETLINK; addr->nl_pad = 0; addr->nl_pid = NETLINK_CB(skb).pid; @@ -1344,7 +1356,7 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, BUG_ON(!nl_table); - if (unit<0 || unit>=MAX_LINKS) + if (unit < 0 || unit >= MAX_LINKS) return NULL; if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) @@ -1380,9 +1392,13 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, nl_table[unit].registered = 1; } else { kfree(listeners); + nl_table[unit].registered++; } netlink_table_ungrab(); + /* Do not hold an extra referrence to a namespace as this socket is + * internal to a namespace and does not prevent it to stop. */ + put_net(net); return sk; out_sock_release: @@ -1390,6 +1406,30 @@ out_sock_release: sock_release(sock); return NULL; } +EXPORT_SYMBOL(netlink_kernel_create); + + +void +netlink_kernel_release(struct sock *sk) +{ + if (sk == NULL || sk->sk_socket == NULL) + return; + + /* + * Last sock_put should drop referrence to sk->sk_net. It has already + * been dropped in netlink_kernel_create. Taking referrence to stopping + * namespace is not an option. + * Take referrence to a socket to remove it from netlink lookup table + * _alive_ and after that destroy it in the context of init_net. + */ + sock_hold(sk); + sock_release(sk->sk_socket); + + sk->sk_net = get_net(&init_net); + sock_put(sk); +} +EXPORT_SYMBOL(netlink_kernel_release); + /** * netlink_change_ngroups - change number of multicast groups @@ -1461,6 +1501,7 @@ void netlink_set_nonroot(int protocol, unsigned int flags) if ((unsigned int)protocol < MAX_LINKS) nl_table[protocol].nl_nonroot = flags; } +EXPORT_SYMBOL(netlink_set_nonroot); static void netlink_destroy_callback(struct netlink_callback *cb) { @@ -1529,8 +1570,9 @@ errout: int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct nlmsghdr *nlh, - int (*dump)(struct sk_buff *skb, struct netlink_callback*), - int (*done)(struct netlink_callback*)) + int (*dump)(struct sk_buff *skb, + struct netlink_callback *), + int (*done)(struct netlink_callback *)) { struct netlink_callback *cb; struct sock *sk; @@ -1571,6 +1613,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, */ return -EINTR; } +EXPORT_SYMBOL(netlink_dump_start); void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) { @@ -1605,6 +1648,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh)); netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); } +EXPORT_SYMBOL(netlink_ack); int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, struct nlmsghdr *)) @@ -1638,7 +1682,7 @@ ack: netlink_ack(skb, nlh, err); skip: - msglen = NLMSG_ALIGN(nlh->nlmsg_len); + msglen = NLMSG_ALIGN(nlh->nlmsg_len); if (msglen > skb->len) msglen = skb->len; skb_pull(skb, msglen); @@ -1646,6 +1690,7 @@ skip: return 0; } +EXPORT_SYMBOL(netlink_rcv_skb); /** * nlmsg_notify - send a notification netlink message @@ -1678,10 +1723,11 @@ int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid, return err; } +EXPORT_SYMBOL(nlmsg_notify); #ifdef CONFIG_PROC_FS struct nl_seq_iter { - struct net *net; + struct seq_net_private p; int link; int hash_idx; }; @@ -1694,12 +1740,12 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) struct hlist_node *node; loff_t off = 0; - for (i=0; i<MAX_LINKS; i++) { + for (i = 0; i < MAX_LINKS; i++) { struct nl_pid_hash *hash = &nl_table[i].hash; for (j = 0; j <= hash->mask; j++) { sk_for_each(s, node, &hash->table[j]) { - if (iter->net != s->sk_net) + if (iter->p.net != s->sk_net) continue; if (off == pos) { iter->link = i; @@ -1714,6 +1760,7 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) } static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(nl_table_lock) { read_lock(&nl_table_lock); return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN; @@ -1734,7 +1781,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) s = v; do { s = sk_next(s); - } while (s && (iter->net != s->sk_net)); + } while (s && (iter->p.net != s->sk_net)); if (s) return s; @@ -1746,7 +1793,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) for (; j <= hash->mask; j++) { s = sk_head(&hash->table[j]); - while (s && (iter->net != s->sk_net)) + while (s && (iter->p.net != s->sk_net)) s = sk_next(s); if (s) { iter->link = i; @@ -1762,6 +1809,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void netlink_seq_stop(struct seq_file *seq, void *v) + __releases(nl_table_lock) { read_unlock(&nl_table_lock); } @@ -1802,27 +1850,8 @@ static const struct seq_operations netlink_seq_ops = { static int netlink_seq_open(struct inode *inode, struct file *file) { - struct nl_seq_iter *iter; - - iter = __seq_open_private(file, &netlink_seq_ops, sizeof(*iter)); - if (!iter) - return -ENOMEM; - - iter->net = get_proc_net(inode); - if (!iter->net) { - seq_release_private(inode, file); - return -ENXIO; - } - - return 0; -} - -static int netlink_seq_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct nl_seq_iter *iter = seq->private; - put_net(iter->net); - return seq_release_private(inode, file); + return seq_open_net(inode, file, &netlink_seq_ops, + sizeof(struct nl_seq_iter)); } static const struct file_operations netlink_seq_fops = { @@ -1830,7 +1859,7 @@ static const struct file_operations netlink_seq_fops = { .open = netlink_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = netlink_seq_release, + .release = seq_release_net, }; #endif @@ -1839,11 +1868,13 @@ int netlink_register_notifier(struct notifier_block *nb) { return atomic_notifier_chain_register(&netlink_chain, nb); } +EXPORT_SYMBOL(netlink_register_notifier); int netlink_unregister_notifier(struct notifier_block *nb) { return atomic_notifier_chain_unregister(&netlink_chain, nb); } +EXPORT_SYMBOL(netlink_unregister_notifier); static const struct proto_ops netlink_ops = { .family = PF_NETLINK, @@ -1922,7 +1953,7 @@ static int __init netlink_proto_init(void) for (i = 0; i < MAX_LINKS; i++) { struct nl_pid_hash *hash = &nl_table[i].hash; - hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); + hash->table = nl_pid_hash_zalloc(1 * sizeof(*hash->table)); if (!hash->table) { while (i-- > 0) nl_pid_hash_free(nl_table[i].hash.table, @@ -1930,7 +1961,6 @@ static int __init netlink_proto_init(void) kfree(nl_table); goto panic; } - memset(hash->table, 0, 1 * sizeof(*hash->table)); hash->max_shift = order; hash->shift = 0; hash->mask = 0; @@ -1948,14 +1978,3 @@ panic: } core_initcall(netlink_proto_init); - -EXPORT_SYMBOL(netlink_ack); -EXPORT_SYMBOL(netlink_rcv_skb); -EXPORT_SYMBOL(netlink_broadcast); -EXPORT_SYMBOL(netlink_dump_start); -EXPORT_SYMBOL(netlink_kernel_create); -EXPORT_SYMBOL(netlink_register_notifier); -EXPORT_SYMBOL(netlink_set_nonroot); -EXPORT_SYMBOL(netlink_unicast); -EXPORT_SYMBOL(netlink_unregister_notifier); -EXPORT_SYMBOL(nlmsg_notify); |