diff options
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 104 | ||||
-rw-r--r-- | net/netlink/attr.c | 5 | ||||
-rw-r--r-- | net/netlink/genetlink.c | 66 |
3 files changed, 91 insertions, 84 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index e73d8f546c6..42d2fb94eff 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -56,6 +56,7 @@ #include <linux/types.h> #include <linux/audit.h> #include <linux/selinux.h> +#include <linux/mutex.h> #include <net/sock.h> #include <net/scm.h> @@ -76,7 +77,8 @@ struct netlink_sock { unsigned long state; wait_queue_head_t wait; struct netlink_callback *cb; - spinlock_t cb_lock; + struct mutex *cb_mutex; + struct mutex cb_def_mutex; void (*data_ready)(struct sock *sk, int bytes); struct module *module; }; @@ -108,6 +110,7 @@ struct netlink_table { unsigned long *listeners; unsigned int nl_nonroot; unsigned int groups; + struct mutex *cb_mutex; struct module *module; int registered; }; @@ -118,6 +121,7 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); static int netlink_dump(struct sock *sk); static void netlink_destroy_callback(struct netlink_callback *cb); +static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb); static DEFINE_RWLOCK(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); @@ -370,7 +374,8 @@ static struct proto netlink_proto = { .obj_size = sizeof(struct netlink_sock), }; -static int __netlink_create(struct socket *sock, int protocol) +static int __netlink_create(struct socket *sock, struct mutex *cb_mutex, + int protocol) { struct sock *sk; struct netlink_sock *nlk; @@ -384,7 +389,12 @@ static int __netlink_create(struct socket *sock, int protocol) sock_init_data(sock, sk); nlk = nlk_sk(sk); - spin_lock_init(&nlk->cb_lock); + if (cb_mutex) + nlk->cb_mutex = cb_mutex; + else { + nlk->cb_mutex = &nlk->cb_def_mutex; + mutex_init(nlk->cb_mutex); + } init_waitqueue_head(&nlk->wait); sk->sk_destruct = netlink_sock_destruct; @@ -395,8 +405,8 @@ static int __netlink_create(struct socket *sock, int protocol) static int netlink_create(struct socket *sock, int protocol) { struct module *module = NULL; + struct mutex *cb_mutex; struct netlink_sock *nlk; - unsigned int groups; int err = 0; sock->state = SS_UNCONNECTED; @@ -418,10 +428,10 @@ static int netlink_create(struct socket *sock, int protocol) if (nl_table[protocol].registered && try_module_get(nl_table[protocol].module)) module = nl_table[protocol].module; - groups = nl_table[protocol].groups; + cb_mutex = nl_table[protocol].cb_mutex; netlink_unlock_table(); - if ((err = __netlink_create(sock, protocol)) < 0) + if ((err = __netlink_create(sock, cb_mutex, protocol)) < 0) goto out_module; nlk = nlk_sk(sock->sk); @@ -443,21 +453,21 @@ static int netlink_release(struct socket *sock) return 0; netlink_remove(sk); + sock_orphan(sk); nlk = nlk_sk(sk); - spin_lock(&nlk->cb_lock); + mutex_lock(nlk->cb_mutex); if (nlk->cb) { if (nlk->cb->done) nlk->cb->done(nlk->cb); netlink_destroy_callback(nlk->cb); nlk->cb = NULL; } - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ - sock_orphan(sk); sock->sk = NULL; wake_up_interruptible_all(&nlk->wait); @@ -1215,7 +1225,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, copied = len; } - skb->h.raw = skb->data; + skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_name) { @@ -1242,6 +1252,9 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, scm_recv(sock, msg, siocb->scm, flags); + if (flags & MSG_TRUNC) + copied = skb->len; + out: netlink_rcv_wake(sk); return err ? : copied; @@ -1265,7 +1278,7 @@ static void netlink_data_ready(struct sock *sk, int len) struct sock * netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), - struct module *module) + struct mutex *cb_mutex, struct module *module) { struct socket *sock; struct sock *sk; @@ -1280,7 +1293,7 @@ netlink_kernel_create(int unit, unsigned int groups, if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) return NULL; - if (__netlink_create(sock, unit) < 0) + if (__netlink_create(sock, cb_mutex, unit) < 0) goto out_sock_release; if (groups < 32) @@ -1304,6 +1317,7 @@ netlink_kernel_create(int unit, unsigned int groups, netlink_table_grab(); nl_table[unit].groups = groups; nl_table[unit].listeners = listeners; + nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; nl_table[unit].registered = 1; netlink_table_ungrab(); @@ -1346,7 +1360,7 @@ static int netlink_dump(struct sock *sk) if (!skb) goto errout; - spin_lock(&nlk->cb_lock); + mutex_lock(nlk->cb_mutex); cb = nlk->cb; if (cb == NULL) { @@ -1357,7 +1371,7 @@ static int netlink_dump(struct sock *sk) len = cb->dump(skb, cb); if (len > 0) { - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, len); return 0; @@ -1375,13 +1389,13 @@ static int netlink_dump(struct sock *sk) if (cb->done) cb->done(cb); nlk->cb = NULL; - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); netlink_destroy_callback(cb); return 0; errout_skb: - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); kfree_skb(skb); errout: return err; @@ -1412,20 +1426,25 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, return -ECONNREFUSED; } nlk = nlk_sk(sk); - /* A dump is in progress... */ - spin_lock(&nlk->cb_lock); - if (nlk->cb) { - spin_unlock(&nlk->cb_lock); + /* A dump or destruction is in progress... */ + mutex_lock(nlk->cb_mutex); + if (nlk->cb || sock_flag(sk, SOCK_DEAD)) { + mutex_unlock(nlk->cb_mutex); netlink_destroy_callback(cb); sock_put(sk); return -EBUSY; } nlk->cb = cb; - spin_unlock(&nlk->cb_lock); + mutex_unlock(nlk->cb_mutex); netlink_dump(sk); sock_put(sk); - return 0; + + /* We successfully started a dump, by returning -EINTR we + * signal the queue mangement to interrupt processing of + * any netlink messages so userspace gets a chance to read + * the results. */ + return -EINTR; } void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) @@ -1462,27 +1481,35 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) } static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, - struct nlmsghdr *, int *)) + struct nlmsghdr *)) { struct nlmsghdr *nlh; int err; while (skb->len >= nlmsg_total_size(0)) { - nlh = (struct nlmsghdr *) skb->data; + nlh = nlmsg_hdr(skb); + err = 0; if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) return 0; - if (cb(skb, nlh, &err) < 0) { - /* Not an error, but we have to interrupt processing - * here. Note: that in this case we do not pull - * message from skb, it will be processed later. - */ - if (err == 0) - return -1; + /* Only requests are handled by the kernel */ + if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) + goto skip; + + /* Skip control messages */ + if (nlh->nlmsg_type < NLMSG_MIN_TYPE) + goto skip; + + err = cb(skb, nlh); + if (err == -EINTR) { + /* Not an error, but we interrupt processing */ + netlink_queue_skip(nlh, skb); + return err; + } +skip: + if (nlh->nlmsg_flags & NLM_F_ACK || err) netlink_ack(skb, nlh, err); - } else if (nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(skb, nlh, 0); netlink_queue_skip(nlh, skb); } @@ -1504,9 +1531,14 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, * * qlen must be initialized to 0 before the initial entry, afterwards * the function may be called repeatedly until qlen reaches 0. + * + * The callback function may return -EINTR to signal that processing + * of netlink messages shall be interrupted. In this case the message + * currently being processed will NOT be requeued onto the receive + * queue. */ void netlink_run_queue(struct sock *sk, unsigned int *qlen, - int (*cb)(struct sk_buff *, struct nlmsghdr *, int *)) + int (*cb)(struct sk_buff *, struct nlmsghdr *)) { struct sk_buff *skb; @@ -1537,7 +1569,7 @@ void netlink_run_queue(struct sock *sk, unsigned int *qlen, * Pulls the given netlink message off the socket buffer so the next * call to netlink_queue_run() will not reconsider the message. */ -void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb) +static void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb) { int msglen = NLMSG_ALIGN(nlh->nlmsg_len); @@ -1820,12 +1852,10 @@ core_initcall(netlink_proto_init); EXPORT_SYMBOL(netlink_ack); EXPORT_SYMBOL(netlink_run_queue); -EXPORT_SYMBOL(netlink_queue_skip); EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_dump_start); EXPORT_SYMBOL(netlink_kernel_create); EXPORT_SYMBOL(netlink_register_notifier); -EXPORT_SYMBOL(netlink_set_err); EXPORT_SYMBOL(netlink_set_nonroot); EXPORT_SYMBOL(netlink_unicast); EXPORT_SYMBOL(netlink_unregister_notifier); diff --git a/net/netlink/attr.c b/net/netlink/attr.c index 004139557e0..df5f820a4c3 100644 --- a/net/netlink/attr.c +++ b/net/netlink/attr.c @@ -67,6 +67,11 @@ static int validate_nla(struct nlattr *nla, int maxtype, } break; + case NLA_BINARY: + if (pt->len && attrlen > pt->len) + return -ERANGE; + break; + default: if (pt->len) minlen = pt->len; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c2996794eb2..6e31234a419 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -295,66 +295,46 @@ int genl_unregister_family(struct genl_family *family) return -ENOENT; } -static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, - int *errp) +static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; struct genl_family *family; struct genl_info info; struct genlmsghdr *hdr = nlmsg_data(nlh); - int hdrlen, err = -EINVAL; - - if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) - goto ignore; - - if (nlh->nlmsg_type < NLMSG_MIN_TYPE) - goto ignore; + int hdrlen, err; family = genl_family_find_byid(nlh->nlmsg_type); - if (family == NULL) { - err = -ENOENT; - goto errout; - } + if (family == NULL) + return -ENOENT; hdrlen = GENL_HDRLEN + family->hdrsize; if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) - goto errout; + return -EINVAL; ops = genl_get_cmd(hdr->cmd, family); - if (ops == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops == NULL) + return -EOPNOTSUPP; - if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) { - err = -EPERM; - goto errout; - } + if ((ops->flags & GENL_ADMIN_PERM) && + security_netlink_recv(skb, CAP_NET_ADMIN)) + return -EPERM; if (nlh->nlmsg_flags & NLM_F_DUMP) { - if (ops->dumpit == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops->dumpit == NULL) + return -EOPNOTSUPP; - *errp = err = netlink_dump_start(genl_sock, skb, nlh, - ops->dumpit, ops->done); - if (err == 0) - skb_pull(skb, min(NLMSG_ALIGN(nlh->nlmsg_len), - skb->len)); - return -1; + return netlink_dump_start(genl_sock, skb, nlh, + ops->dumpit, ops->done); } - if (ops->doit == NULL) { - err = -EOPNOTSUPP; - goto errout; - } + if (ops->doit == NULL) + return -EOPNOTSUPP; if (family->attrbuf) { err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr, ops->policy); if (err < 0) - goto errout; + return err; } info.snd_seq = nlh->nlmsg_seq; @@ -364,15 +344,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = family->attrbuf; - *errp = err = ops->doit(skb, &info); - return err; - -ignore: - return 0; - -errout: - *errp = err; - return -1; + return ops->doit(skb, &info); } static void genl_rcv(struct sock *sk, int len) @@ -586,7 +558,7 @@ static int __init genl_init(void) netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID, - genl_rcv, THIS_MODULE); + genl_rcv, NULL, THIS_MODULE); if (genl_sock == NULL) panic("GENL: Cannot initialize generic netlink\n"); |