[PATCH net-next 08/15] net: bus: Add implementation of Bus domain sockets

From: Vincent Sanders
Date: Fri Jun 29 2012 - 12:46:58 EST


From: Javier Martinez Canillas <javier.martinez@xxxxxxxxxxxxxxx>

This is the core impolementation of the AF_BUS socket family its
design and operation are fully covered in
Documentation/networking/af_bus.txt

Signed-off-by: Javier Martinez Canillas <javier.martinez@xxxxxxxxxxxxxxx>
Signed-off-by: Vincent Sanders <vincent.sanders@xxxxxxxxxxxxxxx>
---
net/bus/af_bus.c | 2629 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2629 insertions(+)
create mode 100644 net/bus/af_bus.c

diff --git a/net/bus/af_bus.c b/net/bus/af_bus.c
new file mode 100644
index 0000000..0b79754
--- /dev/null
+++ b/net/bus/af_bus.c
@@ -0,0 +1,2629 @@
+/*
+ * Implementation of Bus domain sockets.
+ *
+ * Copyright (c) 2012, GENIVI Alliance
+ *
+ * Authors: Javier Martinez Canillas <javier.martinez@xxxxxxxxxxxxxxx>
+ * Alban Crequy <alban.crequy@xxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on BSD Unix domain sockets (net/unix).
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/socket.h>
+#include <linux/bus.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <net/af_bus.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/scm.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/rtnetlink.h>
+#include <linux/mount.h>
+#include <net/checksum.h>
+#include <linux/security.h>
+
+struct hlist_head bus_socket_table[BUS_HASH_SIZE + 1];
+EXPORT_SYMBOL_GPL(bus_socket_table);
+struct hlist_head bus_address_table[BUS_HASH_SIZE];
+EXPORT_SYMBOL_GPL(bus_address_table);
+DEFINE_SPINLOCK(bus_table_lock);
+DEFINE_SPINLOCK(bus_address_lock);
+EXPORT_SYMBOL_GPL(bus_address_lock);
+static atomic_long_t bus_nr_socks;
+
+#define bus_sockets_unbound (&bus_socket_table[BUS_HASH_SIZE])
+
+#define BUS_ABSTRACT(sk) (bus_sk(sk)->addr->hash != BUS_HASH_SIZE)
+
+#ifdef CONFIG_SECURITY_NETWORK
+static void bus_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{
+ memcpy(BUSSID(skb), &scm->secid, sizeof(u32));
+}
+
+static inline void bus_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{
+ scm->secid = *BUSSID(skb);
+}
+#else
+static inline void bus_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{ }
+
+static inline void bus_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{ }
+#endif /* CONFIG_SECURITY_NETWORK */
+
+/*
+ * SMP locking strategy:
+ * bus_socket_table hash table is protected with spinlock bus_table_lock
+ * bus_address_table hash table is protected with spinlock bus_address_lock
+ * each bus is protected by a separate spin lock.
+ * multicast atomic sending is protected by a separate spin lock.
+ * each socket state is protected by a separate spin lock.
+ * each socket address is protected by a separate spin lock.
+ *
+ * When holding more than one lock, use the following hierarchy:
+ * - bus_table_lock.
+ * - bus_address_lock.
+ * - socket lock.
+ * - bus lock.
+ * - bus send_lock.
+ * - sock address lock.
+ */
+
+#define bus_peer(sk) (bus_sk(sk)->peer)
+
+static inline int bus_our_peer(struct sock *sk, struct sock *osk)
+{
+ return bus_peer(osk) == sk;
+}
+
+static inline int bus_recvq_full(struct sock const *sk)
+{
+ return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog;
+}
+
+static inline u16 bus_addr_prefix(struct sockaddr_bus *busaddr)
+{
+ return (busaddr->sbus_addr.s_addr & BUS_PREFIX_MASK) >> BUS_CLIENT_BITS;
+}
+
+static inline u64 bus_addr_client(struct sockaddr_bus *sbusaddr)
+{
+ return sbusaddr->sbus_addr.s_addr & BUS_CLIENT_MASK;
+}
+
+static inline bool bus_mc_addr(struct sockaddr_bus *sbusaddr)
+{
+ return bus_addr_client(sbusaddr) == BUS_CLIENT_MASK;
+}
+
+struct sock *bus_peer_get(struct sock *s)
+{
+ struct sock *peer;
+
+ bus_state_lock(s);
+ peer = bus_peer(s);
+ if (peer)
+ sock_hold(peer);
+ bus_state_unlock(s);
+ return peer;
+}
+EXPORT_SYMBOL_GPL(bus_peer_get);
+
+static inline void bus_release_addr(struct bus_address *addr)
+{
+ if (atomic_dec_and_test(&addr->refcnt))
+ kfree(addr);
+}
+
+/*
+ * Check bus socket name:
+ * - should be not zero length.
+ * - if started by not zero, should be NULL terminated (FS object)
+ * - if started by zero, it is abstract name.
+ */
+
+static int bus_mkname(struct sockaddr_bus *sbusaddr, int len,
+ unsigned int *hashp)
+{
+ int offset = (sbusaddr->sbus_path[0] == '\0');
+
+ if (len <= sizeof(short) || len > sizeof(*sbusaddr))
+ return -EINVAL;
+ if (!sbusaddr || sbusaddr->sbus_family != AF_BUS)
+ return -EINVAL;
+
+ len = strnlen(sbusaddr->sbus_path + offset, BUS_PATH_MAX) + 1 +
+ sizeof(__kernel_sa_family_t) +
+ sizeof(struct bus_addr);
+
+ *hashp = bus_compute_hash(sbusaddr->sbus_addr);
+ return len;
+}
+
+static void __bus_remove_address(struct bus_address *addr)
+{
+ hlist_del(&addr->table_node);
+}
+
+static void __bus_insert_address(struct hlist_head *list,
+ struct bus_address *addr)
+{
+ hlist_add_head(&addr->table_node, list);
+}
+
+static inline void bus_remove_address(struct bus_address *addr)
+{
+ spin_lock(&bus_address_lock);
+ __bus_remove_address(addr);
+ spin_unlock(&bus_address_lock);
+}
+
+static inline void bus_insert_address(struct hlist_head *list,
+ struct bus_address *addr)
+{
+ spin_lock(&bus_address_lock);
+ __bus_insert_address(list, addr);
+ spin_unlock(&bus_address_lock);
+}
+
+static void __bus_remove_socket(struct sock *sk)
+{
+ sk_del_node_init(sk);
+}
+
+static void __bus_insert_socket(struct hlist_head *list, struct sock *sk)
+{
+ WARN_ON(!sk_unhashed(sk));
+ sk_add_node(sk, list);
+}
+
+static inline void bus_remove_socket(struct sock *sk)
+{
+ spin_lock(&bus_table_lock);
+ __bus_remove_socket(sk);
+ spin_unlock(&bus_table_lock);
+}
+
+static inline void bus_insert_socket(struct hlist_head *list, struct sock *sk)
+{
+ spin_lock(&bus_table_lock);
+ __bus_insert_socket(list, sk);
+ spin_unlock(&bus_table_lock);
+}
+
+static inline bool __bus_has_prefix(struct sock *sk, u16 prefix)
+{
+ struct bus_sock *u = bus_sk(sk);
+ struct bus_address *addr;
+ struct hlist_node *node;
+ bool ret = false;
+
+ hlist_for_each_entry(addr, node, &u->addr_list, addr_node) {
+ if (bus_addr_prefix(addr->name) == prefix)
+ ret = true;
+ }
+
+ return ret;
+}
+
+static inline bool bus_has_prefix(struct sock *sk, u16 prefix)
+{
+ bool ret;
+
+ bus_state_lock(sk);
+ ret = __bus_has_prefix(sk, prefix);
+ bus_state_unlock(sk);
+
+ return ret;
+}
+
+static inline bool __bus_eavesdropper(struct sock *sk, u16 condition)
+{
+ struct bus_sock *u = bus_sk(sk);
+
+ return u->eavesdropper;
+}
+
+static inline bool bus_eavesdropper(struct sock *sk, u16 condition)
+{
+ bool ret;
+
+ bus_state_lock(sk);
+ ret = __bus_eavesdropper(sk, condition);
+ bus_state_unlock(sk);
+
+ return ret;
+}
+
+static inline bool bus_has_prefix_eavesdropper(struct sock *sk, u16 prefix)
+{
+ bool ret;
+
+ bus_state_lock(sk);
+ ret = __bus_has_prefix(sk, prefix) || __bus_eavesdropper(sk, 0);
+ bus_state_unlock(sk);
+
+ return ret;
+}
+
+static inline struct bus_address *__bus_get_address(struct sock *sk,
+ struct bus_addr *sbus_addr)
+{
+ struct bus_sock *u = bus_sk(sk);
+ struct bus_address *addr = NULL;
+ struct hlist_node *node;
+
+ hlist_for_each_entry(addr, node, &u->addr_list, addr_node) {
+ if (addr->name->sbus_addr.s_addr == sbus_addr->s_addr)
+ return addr;
+ }
+
+ return NULL;
+}
+
+static inline struct bus_address *bus_get_address(struct sock *sk,
+ struct bus_addr *sbus_addr)
+{
+ struct bus_address *addr;
+
+ bus_state_lock(sk);
+ addr = __bus_get_address(sk, sbus_addr);
+ bus_state_unlock(sk);
+
+ return addr;
+}
+
+static struct sock *__bus_find_socket_byname(struct net *net,
+ struct sockaddr_bus *sbusname,
+ int len, unsigned int hash)
+{
+ struct sock *s;
+ struct hlist_node *node;
+
+ sk_for_each(s, node, &bus_socket_table[hash]) {
+ struct bus_sock *u = bus_sk(s);
+
+ if (!net_eq(sock_net(s), net))
+ continue;
+
+ if (u->addr->len == len &&
+ !memcmp(u->addr->name, sbusname, len))
+ return s;
+ }
+
+ return NULL;
+}
+
+static inline struct sock *bus_find_socket_byname(struct net *net,
+ struct sockaddr_bus *sbusname,
+ int len, unsigned int hash)
+{
+ struct sock *s;
+
+ spin_lock(&bus_table_lock);
+ s = __bus_find_socket_byname(net, sbusname, len, hash);
+ if (s)
+ sock_hold(s);
+ spin_unlock(&bus_table_lock);
+ return s;
+}
+
+static struct sock *__bus_find_socket_byaddress(struct net *net,
+ struct sockaddr_bus *sbusname,
+ int len, int protocol,
+ unsigned int hash)
+{
+ struct sock *s;
+ struct bus_address *addr;
+ struct hlist_node *node;
+ struct bus_sock *u;
+ int offset = (sbusname->sbus_path[0] == '\0');
+ int path_len = strnlen(sbusname->sbus_path + offset, BUS_PATH_MAX);
+
+ len = path_len + 1 + sizeof(__kernel_sa_family_t) +
+ sizeof(struct bus_addr);
+
+ hlist_for_each_entry(addr, node, &bus_address_table[hash],
+ table_node) {
+ s = addr->sock;
+ u = bus_sk(s);
+
+ if (s->sk_protocol != protocol)
+ continue;
+
+ if (!net_eq(sock_net(s), net))
+ continue;
+
+ if (addr->len == len &&
+ addr->name->sbus_family == sbusname->sbus_family &&
+ addr->name->sbus_addr.s_addr == sbusname->sbus_addr.s_addr
+ && bus_same_bus(addr->name, sbusname))
+ goto found;
+ }
+ s = NULL;
+found:
+ return s;
+}
+
+static inline struct sock *bus_find_socket_byaddress(struct net *net,
+ struct sockaddr_bus *name,
+ int len, int protocol,
+ unsigned int hash)
+{
+ struct sock *s;
+
+ spin_lock(&bus_address_lock);
+ s = __bus_find_socket_byaddress(net, name, len, protocol, hash);
+ if (s)
+ sock_hold(s);
+ spin_unlock(&bus_address_lock);
+ return s;
+}
+
+static inline int bus_writable(struct sock *sk)
+{
+ return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
+}
+
+static void bus_write_space(struct sock *sk)
+{
+ struct bus_sock *u = bus_sk(sk);
+ struct bus_sock *p;
+ struct hlist_node *node;
+ struct socket_wq *wq;
+
+ if (bus_writable(sk)) {
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait,
+ POLLOUT | POLLWRNORM | POLLWRBAND);
+ sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+
+ if (u && u->bus) {
+ spin_lock(&u->bus->lock);
+ hlist_for_each_entry(p, node, &u->bus->peers,
+ bus_node) {
+ wake_up_interruptible_sync_poll(sk_sleep(&p->sk),
+ POLLOUT |
+ POLLWRNORM |
+ POLLWRBAND);
+ sk_wake_async(&p->sk, SOCK_WAKE_SPACE,
+ POLL_OUT);
+ }
+ spin_unlock(&u->bus->lock);
+ }
+ }
+}
+
+static void bus_bus_release(struct kref *kref)
+{
+ struct bus *bus;
+
+ bus = container_of(kref, struct bus, kref);
+
+ kfree(bus);
+}
+
+static void bus_sock_destructor(struct sock *sk)
+{
+ struct bus_sock *u = bus_sk(sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+ WARN_ON(!sk_unhashed(sk));
+ WARN_ON(sk->sk_socket);
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ pr_info("Attempt to release alive bus socket: %p\n", sk);
+ return;
+ }
+
+ if (u->bus) {
+ kref_put(&u->bus->kref, bus_bus_release);
+ u->bus = NULL;
+ }
+
+ atomic_long_dec(&bus_nr_socks);
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ local_bh_enable();
+#ifdef BUS_REFCNT_DEBUG
+ pr_debug("BUS %p is destroyed, %ld are still alive.\n", sk,
+ atomic_long_read(&bus_nr_socks));
+#endif
+}
+
+static int bus_release_sock(struct sock *sk, int embrion)
+{
+ struct bus_sock *u = bus_sk(sk);
+ struct path path;
+ struct sock *skpair;
+ struct sk_buff *skb;
+ int state;
+ struct bus_address *addr;
+ struct hlist_node *node, *tmp;
+
+ bus_remove_socket(sk);
+
+ if (u->bus && u->authenticated &&
+ !u->bus_master && !u->bus_master_side) {
+ spin_lock(&u->bus->lock);
+ hlist_del(&u->bus_node);
+ if (u->eavesdropper)
+ atomic64_dec(&u->bus->eavesdropper_cnt);
+ spin_unlock(&u->bus->lock);
+ }
+
+ /* Clear state */
+ bus_state_lock(sk);
+ sock_orphan(sk);
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ path = u->path;
+ u->path.dentry = NULL;
+ u->path.mnt = NULL;
+ state = sk->sk_state;
+ sk->sk_state = BUS_CLOSE;
+
+ if (u->bus_master)
+ u->bus->master = NULL;
+
+ if (u->bus_master_side) {
+ bus_release_addr(u->addr);
+ u->addr = NULL;
+ } else {
+ u->addr = NULL;
+
+ spin_lock(&bus_address_lock);
+ hlist_for_each_entry_safe(addr, node, tmp, &u->addr_list,
+ addr_node) {
+ hlist_del(&addr->addr_node);
+ __bus_remove_address(addr);
+ bus_release_addr(addr);
+ }
+ spin_unlock(&bus_address_lock);
+ }
+
+ bus_state_unlock(sk);
+
+ wake_up_interruptible_all(&u->peer_wait);
+
+ skpair = bus_peer(sk);
+
+ if (skpair != NULL) {
+ bus_state_lock(skpair);
+ /* No more writes */
+ skpair->sk_shutdown = SHUTDOWN_MASK;
+ if (!skb_queue_empty(&sk->sk_receive_queue) || embrion)
+ skpair->sk_err = ECONNRESET;
+ bus_state_unlock(skpair);
+ skpair->sk_state_change(skpair);
+ sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP);
+ sock_put(skpair); /* It may now die */
+ bus_peer(sk) = NULL;
+ }
+
+ /* Try to flush out this socket. Throw out buffers at least */
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+ if (state == BUS_LISTEN)
+ bus_release_sock(skb->sk, 1);
+ /* passed fds are erased in the kfree_skb hook */
+ kfree_skb(skb);
+ }
+
+ if (path.dentry)
+ path_put(&path);
+
+ sock_put(sk);
+
+ /* ---- Socket is dead now and most probably destroyed ---- */
+
+ if (bus_tot_inflight)
+ bus_gc(); /* Garbage collect fds */
+
+ return 0;
+}
+
+static void init_peercred(struct sock *sk)
+{
+ put_pid(sk->sk_peer_pid);
+ if (sk->sk_peer_cred)
+ put_cred(sk->sk_peer_cred);
+ sk->sk_peer_pid = get_pid(task_tgid(current));
+ sk->sk_peer_cred = get_current_cred();
+}
+
+static void copy_peercred(struct sock *sk, struct sock *peersk)
+{
+ put_pid(sk->sk_peer_pid);
+ if (sk->sk_peer_cred)
+ put_cred(sk->sk_peer_cred);
+ sk->sk_peer_pid = get_pid(peersk->sk_peer_pid);
+ sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
+}
+
+static int bus_listen(struct socket *sock, int backlog)
+{
+ int err;
+ struct sock *sk = sock->sk;
+ struct bus_sock *u = bus_sk(sk);
+ struct pid *old_pid = NULL;
+ const struct cred *old_cred = NULL;
+
+ err = -EINVAL;
+ if (!u->addr || !u->bus_master)
+ goto out; /* Only listens on an bound an master socket */
+ bus_state_lock(sk);
+ if (sk->sk_state != BUS_CLOSE && sk->sk_state != BUS_LISTEN)
+ goto out_unlock;
+ if (backlog > sk->sk_max_ack_backlog)
+ wake_up_interruptible_all(&u->peer_wait);
+ sk->sk_max_ack_backlog = backlog;
+ sk->sk_state = BUS_LISTEN;
+ /* set credentials so connect can copy them */
+ init_peercred(sk);
+ err = 0;
+
+out_unlock:
+ bus_state_unlock(sk);
+ put_pid(old_pid);
+ if (old_cred)
+ put_cred(old_cred);
+out:
+ return err;
+}
+
+static int bus_release(struct socket *);
+static int bus_bind(struct socket *, struct sockaddr *, int);
+static int bus_connect(struct socket *, struct sockaddr *,
+ int addr_len, int flags);
+static int bus_accept(struct socket *, struct socket *, int);
+static int bus_getname(struct socket *, struct sockaddr *, int *, int);
+static unsigned int bus_poll(struct file *, struct socket *,
+ poll_table *);
+static int bus_ioctl(struct socket *, unsigned int, unsigned long);
+static int bus_shutdown(struct socket *, int);
+static int bus_setsockopt(struct socket *, int, int, char __user *,
+ unsigned int);
+static int bus_sendmsg(struct kiocb *, struct socket *,
+ struct msghdr *, size_t);
+static int bus_recvmsg(struct kiocb *, struct socket *,
+ struct msghdr *, size_t, int);
+
+static void bus_set_peek_off(struct sock *sk, int val)
+{
+ struct bus_sock *u = bus_sk(sk);
+
+ mutex_lock(&u->readlock);
+ sk->sk_peek_off = val;
+ mutex_unlock(&u->readlock);
+}
+
+static const struct proto_ops bus_seqpacket_ops = {
+ .family = PF_BUS,
+ .owner = THIS_MODULE,
+ .release = bus_release,
+ .bind = bus_bind,
+ .connect = bus_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = bus_accept,
+ .getname = bus_getname,
+ .poll = bus_poll,
+ .ioctl = bus_ioctl,
+ .listen = bus_listen,
+ .shutdown = bus_shutdown,
+ .setsockopt = bus_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = bus_sendmsg,
+ .recvmsg = bus_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+ .set_peek_off = bus_set_peek_off,
+};
+
+static struct proto bus_proto = {
+ .name = "BUS",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct bus_sock),
+};
+
+/*
+ * AF_BUS sockets do not interact with hardware, hence they
+ * dont trigger interrupts - so it's safe for them to have
+ * bh-unsafe locking for their sk_receive_queue.lock. Split off
+ * this special lock-class by reinitializing the spinlock key:
+ */
+static struct lock_class_key af_bus_sk_receive_queue_lock_key;
+
+static struct sock *bus_create1(struct net *net, struct socket *sock)
+{
+ struct sock *sk = NULL;
+ struct bus_sock *u;
+
+ atomic_long_inc(&bus_nr_socks);
+ if (atomic_long_read(&bus_nr_socks) > 2 * get_max_files())
+ goto out;
+
+ sk = sk_alloc(net, PF_BUS, GFP_KERNEL, &bus_proto);
+ if (!sk)
+ goto out;
+
+ sock_init_data(sock, sk);
+ lockdep_set_class(&sk->sk_receive_queue.lock,
+ &af_bus_sk_receive_queue_lock_key);
+
+ sk->sk_write_space = bus_write_space;
+ sk->sk_max_ack_backlog = BUS_MAX_QLEN;
+ sk->sk_destruct = bus_sock_destructor;
+ u = bus_sk(sk);
+ u->path.dentry = NULL;
+ u->path.mnt = NULL;
+ u->bus = NULL;
+ u->bus_master = false;
+ u->authenticated = false;
+ u->eavesdropper = false;
+ spin_lock_init(&u->lock);
+ atomic_long_set(&u->inflight, 0);
+ INIT_LIST_HEAD(&u->link);
+ INIT_HLIST_HEAD(&u->addr_list);
+ INIT_HLIST_NODE(&u->bus_node);
+ mutex_init(&u->readlock); /* single task reading lock */
+ init_waitqueue_head(&u->peer_wait);
+ bus_insert_socket(bus_sockets_unbound, sk);
+out:
+ if (sk == NULL)
+ atomic_long_dec(&bus_nr_socks);
+ else {
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ local_bh_enable();
+ }
+ return sk;
+}
+
+static int bus_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk;
+
+ if (protocol < BUS_PROTO_NONE || protocol > BUS_PROTO_DBUS)
+ return -EPROTONOSUPPORT;
+
+ if (protocol != BUS_PROTO_NONE)
+ request_module("net-pf-%d-proto-%d", PF_BUS, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (sock->type == SOCK_SEQPACKET)
+ sock->ops = &bus_seqpacket_ops;
+ else
+ return -ESOCKTNOSUPPORT;
+
+ sk = bus_create1(net, sock);
+ if (!sk)
+ return -ENOMEM;
+
+ sk->sk_protocol = protocol;
+
+ return 0;
+}
+
+static int bus_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return 0;
+
+ sock->sk = NULL;
+
+ return bus_release_sock(sk, 0);
+}
+
+static struct sock *bus_find_other(struct net *net,
+ struct sockaddr_bus *sbusname, int len,
+ int protocol, unsigned int hash, int *error)
+{
+ struct sock *u;
+ struct path path;
+ int err = 0;
+
+ if (sbusname->sbus_path[0]) {
+ struct inode *inode;
+ err = kern_path(sbusname->sbus_path, LOOKUP_FOLLOW, &path);
+ if (err)
+ goto fail;
+ inode = path.dentry->d_inode;
+ err = inode_permission(inode, MAY_WRITE);
+ if (err)
+ goto put_fail;
+
+ err = -ECONNREFUSED;
+ if (!S_ISSOCK(inode->i_mode))
+ goto put_fail;
+ u = bus_find_socket_byaddress(net, sbusname, len, protocol,
+ hash);
+ if (!u)
+ goto put_fail;
+
+ touch_atime(&path);
+ path_put(&path);
+
+ } else {
+ err = -ECONNREFUSED;
+ u = bus_find_socket_byaddress(net, sbusname, len, protocol, hash);
+ if (u) {
+ struct dentry *dentry;
+ dentry = bus_sk(u)->path.dentry;
+ if (dentry)
+ touch_atime(&bus_sk(u)->path);
+ } else
+ goto fail;
+ }
+
+ return u;
+
+put_fail:
+ path_put(&path);
+fail:
+ *error = err;
+ return NULL;
+}
+
+
+static int bus_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct net *net = sock_net(sk);
+ struct bus_sock *u = bus_sk(sk);
+ struct sockaddr_bus *sbusaddr = (struct sockaddr_bus *)uaddr;
+ char *sbus_path = sbusaddr->sbus_path;
+ struct dentry *dentry = NULL;
+ struct path path;
+ int err;
+ unsigned int hash;
+ struct bus_address *addr;
+ struct hlist_head *list;
+ struct bus *bus;
+
+ err = -EINVAL;
+ if (sbusaddr->sbus_family != AF_BUS)
+ goto out;
+
+ /* If the address is available, the socket is the bus master */
+ sbusaddr->sbus_addr.s_addr = BUS_MASTER_ADDR;
+
+ err = bus_mkname(sbusaddr, addr_len, &hash);
+ if (err < 0)
+ goto out;
+ addr_len = err;
+
+ mutex_lock(&u->readlock);
+
+ err = -EINVAL;
+ if (u->addr)
+ goto out_up;
+
+ err = -ENOMEM;
+ addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+ if (!addr)
+ goto out_up;
+
+ memcpy(addr->name, sbusaddr, sizeof(struct sockaddr_bus));
+ addr->len = addr_len;
+ addr->hash = hash;
+ atomic_set(&addr->refcnt, 1);
+ addr->sock = sk;
+ INIT_HLIST_NODE(&addr->addr_node);
+ INIT_HLIST_NODE(&addr->table_node);
+
+ if (sbus_path[0]) {
+ umode_t mode;
+ err = 0;
+ /*
+ * Get the parent directory, calculate the hash for last
+ * component.
+ */
+ dentry = kern_path_create(AT_FDCWD, sbus_path, &path, 0);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out_mknod_parent;
+
+ /*
+ * All right, let's create it.
+ */
+ mode = S_IFSOCK |
+ (SOCK_INODE(sock)->i_mode & ~current_umask());
+ err = mnt_want_write(path.mnt);
+ if (err)
+ goto out_mknod_dput;
+ err = security_path_mknod(&path, dentry, mode, 0);
+ if (err)
+ goto out_mknod_drop_write;
+ err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+out_mknod_drop_write:
+ mnt_drop_write(path.mnt);
+ if (err)
+ goto out_mknod_dput;
+ mutex_unlock(&path.dentry->d_inode->i_mutex);
+ dput(path.dentry);
+ path.dentry = dentry;
+ }
+
+ err = -ENOMEM;
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ goto out_unlock;
+
+ spin_lock(&bus_table_lock);
+
+ if (!sbus_path[0]) {
+ err = -EADDRINUSE;
+ if (__bus_find_socket_byname(net, sbusaddr, addr_len, hash)) {
+ bus_release_addr(addr);
+ kfree(bus);
+ goto out_unlock;
+ }
+
+ list = &bus_socket_table[addr->hash];
+ } else {
+ list = &bus_socket_table[dentry->d_inode->i_ino &
+ (BUS_HASH_SIZE-1)];
+ u->path = path;
+ }
+
+ kref_init(&bus->kref);
+ bus->master = sk;
+ INIT_HLIST_HEAD(&bus->peers);
+ spin_lock_init(&bus->lock);
+ spin_lock_init(&bus->send_lock);
+ atomic64_set(&bus->addr_cnt, 0);
+ atomic64_set(&bus->eavesdropper_cnt, 0);
+
+ hlist_add_head(&addr->addr_node, &u->addr_list);
+
+ err = 0;
+ __bus_remove_socket(sk);
+ u->addr = addr;
+ u->bus_master = true;
+ u->bus = bus;
+ __bus_insert_socket(list, sk);
+ bus_insert_address(&bus_address_table[addr->hash], addr);
+
+out_unlock:
+ spin_unlock(&bus_table_lock);
+out_up:
+ mutex_unlock(&u->readlock);
+out:
+ return err;
+
+out_mknod_dput:
+ dput(dentry);
+ mutex_unlock(&path.dentry->d_inode->i_mutex);
+ path_put(&path);
+out_mknod_parent:
+ if (err == -EEXIST)
+ err = -EADDRINUSE;
+ bus_release_addr(addr);
+ goto out_up;
+}
+
+static long bus_wait_for_peer(struct sock *other, long timeo)
+{
+ struct bus_sock *u = bus_sk(other);
+ int sched;
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait_exclusive(&u->peer_wait, &wait, TASK_INTERRUPTIBLE);
+
+ sched = !sock_flag(other, SOCK_DEAD) &&
+ !(other->sk_shutdown & RCV_SHUTDOWN) &&
+ bus_recvq_full(other);
+
+ bus_state_unlock(other);
+
+ if (sched)
+ timeo = schedule_timeout(timeo);
+
+ finish_wait(&u->peer_wait, &wait);
+ return timeo;
+}
+
+static int bus_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sockaddr_bus *sbusaddr = (struct sockaddr_bus *)uaddr;
+ struct sock *sk = sock->sk;
+ struct net *net = sock_net(sk);
+ struct bus_sock *u = bus_sk(sk), *newu, *otheru;
+ struct sock *newsk = NULL;
+ struct sock *other = NULL;
+ struct sk_buff *skb = NULL;
+ struct bus_address *addr = NULL;
+ unsigned int hash;
+ int st;
+ int err;
+ long timeo;
+
+ /* Only connections to the bus master is allowed */
+ sbusaddr->sbus_addr.s_addr = BUS_MASTER_ADDR;
+
+ err = bus_mkname(sbusaddr, addr_len, &hash);
+ if (err < 0)
+ goto out;
+ addr_len = err;
+
+ err = -ENOMEM;
+ addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+ if (!addr)
+ goto out;
+
+ atomic_set(&addr->refcnt, 1);
+ INIT_HLIST_NODE(&addr->addr_node);
+ INIT_HLIST_NODE(&addr->table_node);
+
+ timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+ /* First of all allocate resources.
+ If we will make it after state is locked,
+ we will have to recheck all again in any case.
+ */
+
+ err = -ENOMEM;
+
+ /* create new sock for complete connection */
+ newsk = bus_create1(sock_net(sk), NULL);
+ if (newsk == NULL)
+ goto out;
+
+ /* Allocate skb for sending to listening sock */
+ skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+restart:
+ /* Find listening sock. */
+ other = bus_find_other(net, sbusaddr, addr_len, sk->sk_protocol, hash,
+ &err);
+ if (!other)
+ goto out;
+
+ /* Latch state of peer */
+ bus_state_lock(other);
+
+ /* Apparently VFS overslept socket death. Retry. */
+ if (sock_flag(other, SOCK_DEAD)) {
+ bus_state_unlock(other);
+ sock_put(other);
+ goto restart;
+ }
+
+ err = -ECONNREFUSED;
+ if (other->sk_state != BUS_LISTEN)
+ goto out_unlock;
+ if (other->sk_shutdown & RCV_SHUTDOWN)
+ goto out_unlock;
+
+ if (bus_recvq_full(other)) {
+ err = -EAGAIN;
+ if (!timeo)
+ goto out_unlock;
+
+ timeo = bus_wait_for_peer(other, timeo);
+
+ err = sock_intr_errno(timeo);
+ if (signal_pending(current))
+ goto out;
+ sock_put(other);
+ goto restart;
+ }
+
+ /* Latch our state.
+
+ It is tricky place. We need to grab our state lock and cannot
+ drop lock on peer. It is dangerous because deadlock is
+ possible. Connect to self case and simultaneous
+ attempt to connect are eliminated by checking socket
+ state. other is BUS_LISTEN, if sk is BUS_LISTEN we
+ check this before attempt to grab lock.
+
+ Well, and we have to recheck the state after socket locked.
+ */
+ st = sk->sk_state;
+
+ switch (st) {
+ case BUS_CLOSE:
+ /* This is ok... continue with connect */
+ break;
+ case BUS_ESTABLISHED:
+ /* Socket is already connected */
+ err = -EISCONN;
+ goto out_unlock;
+ default:
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ bus_state_lock_nested(sk);
+
+ if (sk->sk_state != st) {
+ bus_state_unlock(sk);
+ bus_state_unlock(other);
+ sock_put(other);
+ goto restart;
+ }
+
+ err = security_bus_connect(sk, other, newsk);
+ if (err) {
+ bus_state_unlock(sk);
+ goto out_unlock;
+ }
+
+ /* The way is open! Fastly set all the necessary fields... */
+
+ sock_hold(sk);
+ bus_peer(newsk) = sk;
+ newsk->sk_state = BUS_ESTABLISHED;
+ newsk->sk_type = sk->sk_type;
+ newsk->sk_protocol = sk->sk_protocol;
+ init_peercred(newsk);
+ newu = bus_sk(newsk);
+ RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
+ otheru = bus_sk(other);
+
+ /* copy address information from listening to new sock*/
+ if (otheru->addr && otheru->bus_master) {
+ atomic_inc(&otheru->addr->refcnt);
+ newu->addr = otheru->addr;
+ memcpy(addr->name, otheru->addr->name,
+ sizeof(struct sockaddr_bus));
+ addr->len = otheru->addr->len;
+ addr->name->sbus_addr.s_addr =
+ (atomic64_inc_return(&otheru->bus->addr_cnt) &
+ BUS_CLIENT_MASK);
+ addr->hash = bus_compute_hash(addr->name->sbus_addr);
+ addr->sock = sk;
+ u->addr = addr;
+ kref_get(&otheru->bus->kref);
+ u->bus = otheru->bus;
+ u->bus_master_side = false;
+ kref_get(&otheru->bus->kref);
+ newu->bus = otheru->bus;
+ newu->bus_master_side = true;
+ hlist_add_head(&addr->addr_node, &u->addr_list);
+
+ bus_insert_address(&bus_address_table[addr->hash], addr);
+ }
+ if (otheru->path.dentry) {
+ path_get(&otheru->path);
+ newu->path = otheru->path;
+ }
+
+ /* Set credentials */
+ copy_peercred(sk, other);
+ sk->sk_sndbuf = other->sk_sndbuf;
+ sk->sk_max_ack_backlog = other->sk_max_ack_backlog;
+ newsk->sk_sndbuf = other->sk_sndbuf;
+
+ sock->state = SS_CONNECTED;
+ sk->sk_state = BUS_ESTABLISHED;
+ sock_hold(newsk);
+
+ smp_mb__after_atomic_inc(); /* sock_hold() does an atomic_inc() */
+ bus_peer(sk) = newsk;
+
+ bus_state_unlock(sk);
+
+ /* take ten and and send info to listening sock */
+ spin_lock(&other->sk_receive_queue.lock);
+ __skb_queue_tail(&other->sk_receive_queue, skb);
+ spin_unlock(&other->sk_receive_queue.lock);
+ bus_state_unlock(other);
+ other->sk_data_ready(other, 0);
+ sock_put(other);
+ return 0;
+
+out_unlock:
+ if (other)
+ bus_state_unlock(other);
+
+out:
+ kfree_skb(skb);
+ if (addr)
+ bus_release_addr(addr);
+ if (newsk)
+ bus_release_sock(newsk, 0);
+ if (other)
+ sock_put(other);
+ return err;
+}
+
+static int bus_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sock *tsk;
+ struct sk_buff *skb;
+ int err;
+
+ err = -EINVAL;
+ if (sk->sk_state != BUS_LISTEN)
+ goto out;
+
+ /* If socket state is BUS_LISTEN it cannot change (for now...),
+ * so that no locks are necessary.
+ */
+
+ skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
+ if (!skb) {
+ /* This means receive shutdown. */
+ if (err == 0)
+ err = -EINVAL;
+ goto out;
+ }
+
+ tsk = skb->sk;
+ skb_free_datagram(sk, skb);
+ wake_up_interruptible(&bus_sk(sk)->peer_wait);
+
+ /* attach accepted sock to socket */
+ bus_state_lock(tsk);
+ newsock->state = SS_CONNECTED;
+ sock_graft(tsk, newsock);
+ bus_state_unlock(tsk);
+ return 0;
+
+out:
+ return err;
+}
+
+
+static int bus_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ struct sock *sk = sock->sk;
+ struct bus_sock *u;
+ DECLARE_SOCKADDR(struct sockaddr_bus *, sbusaddr, uaddr);
+ int err = 0;
+
+ if (peer) {
+ sk = bus_peer_get(sk);
+
+ err = -ENOTCONN;
+ if (!sk)
+ goto out;
+ err = 0;
+ } else {
+ sock_hold(sk);
+ }
+
+ u = bus_sk(sk);
+
+ bus_state_lock(sk);
+ if (!u->addr) {
+ sbusaddr->sbus_family = AF_BUS;
+ sbusaddr->sbus_path[0] = 0;
+ *uaddr_len = sizeof(short);
+ } else {
+ struct bus_address *addr = u->addr;
+
+ *uaddr_len = sizeof(struct sockaddr_bus);
+ memcpy(sbusaddr, addr->name, *uaddr_len);
+ }
+ bus_state_unlock(sk);
+ sock_put(sk);
+out:
+ return err;
+}
+
+static void bus_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+{
+ int i;
+
+ scm->fp = BUSCB(skb).fp;
+ BUSCB(skb).fp = NULL;
+
+ for (i = scm->fp->count-1; i >= 0; i--)
+ bus_notinflight(scm->fp->fp[i]);
+}
+
+static void bus_destruct_scm(struct sk_buff *skb)
+{
+ struct scm_cookie scm;
+ memset(&scm, 0, sizeof(scm));
+ scm.pid = BUSCB(skb).pid;
+ scm.cred = BUSCB(skb).cred;
+ if (BUSCB(skb).fp)
+ bus_detach_fds(&scm, skb);
+
+ scm_destroy(&scm);
+ if (skb->sk)
+ sock_wfree(skb);
+}
+
+#define MAX_RECURSION_LEVEL 4
+
+static int bus_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+{
+ int i;
+ unsigned char max_level = 0;
+ int bus_sock_count = 0;
+
+ for (i = scm->fp->count - 1; i >= 0; i--) {
+ struct sock *sk = bus_get_socket(scm->fp->fp[i]);
+
+ if (sk) {
+ bus_sock_count++;
+ max_level = max(max_level,
+ bus_sk(sk)->recursion_level);
+ }
+ }
+ if (unlikely(max_level > MAX_RECURSION_LEVEL))
+ return -ETOOMANYREFS;
+
+ /*
+ * Need to duplicate file references for the sake of garbage
+ * collection. Otherwise a socket in the fps might become a
+ * candidate for GC while the skb is not yet queued.
+ */
+ BUSCB(skb).fp = scm_fp_dup(scm->fp);
+ if (!BUSCB(skb).fp)
+ return -ENOMEM;
+
+ if (bus_sock_count) {
+ for (i = scm->fp->count - 1; i >= 0; i--)
+ bus_inflight(scm->fp->fp[i]);
+ }
+ return max_level;
+}
+
+static int bus_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
+ bool send_fds)
+{
+ int err = 0;
+
+ BUSCB(skb).pid = get_pid(scm->pid);
+ if (scm->cred)
+ BUSCB(skb).cred = get_cred(scm->cred);
+ BUSCB(skb).fp = NULL;
+ if (scm->fp && send_fds)
+ err = bus_attach_fds(scm, skb);
+
+ skb->destructor = bus_destruct_scm;
+ return err;
+}
+
+/*
+ * Some apps rely on write() giving SCM_CREDENTIALS
+ * We include credentials if source or destination socket
+ * asserted SOCK_PASSCRED.
+ */
+static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
+ const struct sock *other)
+{
+ if (BUSCB(skb).cred)
+ return;
+ if (test_bit(SOCK_PASSCRED, &sock->flags) ||
+ !other->sk_socket ||
+ test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
+ BUSCB(skb).pid = get_pid(task_tgid(current));
+ BUSCB(skb).cred = get_current_cred();
+ }
+}
+
+/*
+ * Send AF_BUS data.
+ */
+
+static void bus_deliver_skb(struct sk_buff *skb)
+{
+ struct bus_send_context *sendctx = BUSCB(skb).sendctx;
+ struct socket *sock = sendctx->sender_socket;
+
+ if (sock_flag(sendctx->other, SOCK_RCVTSTAMP))
+ __net_timestamp(skb);
+ maybe_add_creds(skb, sock, sendctx->other);
+ skb_queue_tail(&sendctx->other->sk_receive_queue, skb);
+ if (sendctx->max_level > bus_sk(sendctx->other)->recursion_level)
+ bus_sk(sendctx->other)->recursion_level = sendctx->max_level;
+}
+
+/**
+ * bus_sendmsg_finish - delivery an skb to a destination
+ * @skb: sk_buff to deliver
+ *
+ * Delivers a packet to a destination. The skb control buffer has
+ * all the information about the destination contained on sending
+ * context. If the sending is unicast, then the skb is delivered
+ * and the receiver notified but if the sending is multicast, the
+ * skb is just marked as delivered and the actual delivery is made
+ * outside the function with the bus->send_lock held to ensure that
+ * the multicast sending is atomic.
+ */
+static int bus_sendmsg_finish(struct sk_buff *skb)
+{
+ int err;
+ struct bus_send_context *sendctx;
+ struct socket *sock;
+ struct sock *sk;
+ struct net *net;
+ size_t len = skb->len;
+
+ sendctx = BUSCB(skb).sendctx;
+ sock = sendctx->sender_socket;
+ sk = sock->sk;
+ net = sock_net(sk);
+
+restart:
+ if (!sendctx->other) {
+ err = -ECONNRESET;
+ if (sendctx->recipient == NULL)
+ goto out_free;
+
+ sendctx->other = bus_find_other(net, sendctx->recipient,
+ sendctx->namelen,
+ sk->sk_protocol,
+ sendctx->hash, &err);
+
+ if (sendctx->other == NULL ||
+ !bus_sk(sendctx->other)->authenticated) {
+
+ if (sendctx->other)
+ sock_put(sendctx->other);
+
+ if (!bus_sk(sk)->bus_master_side) {
+ err = -ENOTCONN;
+ sendctx->other = bus_peer_get(sk);
+ if (!sendctx->other)
+ goto out_free;
+ } else {
+ sendctx->other = sk;
+ sock_hold(sendctx->other);
+ }
+ }
+ }
+
+ if (sk_filter(sendctx->other, skb) < 0) {
+ /* Toss the packet but do not return any error to the sender */
+ err = len;
+ goto out_free;
+ }
+
+ bus_state_lock(sendctx->other);
+
+ if (sock_flag(sendctx->other, SOCK_DEAD)) {
+ /*
+ * Check with 1003.1g - what should
+ * datagram error
+ */
+ bus_state_unlock(sendctx->other);
+ sock_put(sendctx->other);
+
+ err = 0;
+ bus_state_lock(sk);
+ if (bus_peer(sk) == sendctx->other) {
+ bus_peer(sk) = NULL;
+ bus_state_unlock(sk);
+ sock_put(sendctx->other);
+ err = -ECONNREFUSED;
+ } else {
+ bus_state_unlock(sk);
+ }
+
+ sendctx->other = NULL;
+ if (err)
+ goto out_free;
+ goto restart;
+ }
+
+ err = -EPIPE;
+ if (sendctx->other->sk_shutdown & RCV_SHUTDOWN)
+ goto out_unlock;
+
+ if (bus_recvq_full(sendctx->other)) {
+ if (!sendctx->timeo) {
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+
+ sendctx->timeo = bus_wait_for_peer(sendctx->other,
+ sendctx->timeo);
+
+ err = sock_intr_errno(sendctx->timeo);
+ if (signal_pending(current))
+ goto out_free;
+
+ goto restart;
+ }
+
+ if (!sendctx->multicast && !sendctx->eavesdropper) {
+ bus_deliver_skb(skb);
+ bus_state_unlock(sendctx->other);
+ sendctx->other->sk_data_ready(sendctx->other, 0);
+ sock_put(sendctx->other);
+ } else {
+ sendctx->deliver = 1;
+ bus_state_unlock(sendctx->other);
+ }
+
+ return len;
+
+out_unlock:
+ bus_state_unlock(sendctx->other);
+out_free:
+ kfree_skb(skb);
+ if (sendctx->other)
+ sock_put(sendctx->other);
+
+ return err;
+}
+
+/**
+ * bus_sendmsg_mcast - do a multicast sending
+ * @skb: sk_buff to deliver
+ *
+ * Send a packet to a multicast destination.
+ * The function is also called for unicast sending when eavesdropping
+ * is enabled. Since the unicast destination and the eavesdroppers
+ * have to receive the packet atomically.
+ */
+static int bus_sendmsg_mcast(struct sk_buff *skb)
+{
+ struct bus_send_context *sendctx;
+ struct bus_send_context *tmpctx;
+ struct socket *sock;
+ struct sock *sk;
+ struct net *net;
+ struct bus_sock *u, *s;
+ struct hlist_node *node;
+ u16 prefix = 0;
+ struct sk_buff **skb_set = NULL;
+ struct bus_send_context **sendctx_set = NULL;
+ int rcp_cnt, send_cnt;
+ int i;
+ int err;
+ int len = skb->len;
+ bool (*is_receiver) (struct sock *, u16);
+ bool main_rcp_found = false;
+
+ sendctx = BUSCB(skb).sendctx;
+ sendctx->deliver = 0;
+ sock = sendctx->sender_socket;
+ sk = sock->sk;
+ u = bus_sk(sk);
+ net = sock_net(sk);
+
+ if (sendctx->multicast) {
+ prefix = bus_addr_prefix(sendctx->recipient);
+ if (sendctx->eavesdropper)
+ is_receiver = &bus_has_prefix_eavesdropper;
+ else
+ is_receiver = &bus_has_prefix;
+ } else {
+ is_receiver = &bus_eavesdropper;
+
+ /*
+ * If the destination is not the peer accepted socket
+ * we have to get the correct destination.
+ */
+ if (!sendctx->to_master && sendctx->recipient) {
+ sendctx->other = bus_find_other(net, sendctx->recipient,
+ sendctx->namelen,
+ sk->sk_protocol,
+ sendctx->hash, &err);
+
+
+ if (sendctx->other == NULL ||
+ !bus_sk(sendctx->other)->authenticated) {
+
+ if (sendctx->other)
+ sock_put(sendctx->other);
+
+ if (sendctx->other == NULL) {
+ if (!bus_sk(sk)->bus_master_side) {
+ err = -ENOTCONN;
+ sendctx->other = bus_peer_get(sk);
+ if (!sendctx->other)
+ goto out;
+ } else {
+ sendctx->other = sk;
+ sock_hold(sendctx->other);
+ }
+ }
+ sendctx->to_master = 1;
+ }
+ }
+ }
+
+
+try_again:
+ rcp_cnt = 0;
+ main_rcp_found = false;
+
+ spin_lock(&u->bus->lock);
+
+ hlist_for_each_entry(s, node, &u->bus->peers, bus_node) {
+
+ if (!net_eq(sock_net(&s->sk), net))
+ continue;
+
+ if (is_receiver(&s->sk, prefix) ||
+ (!sendctx->multicast &&
+ !sendctx->to_master &&
+ &s->sk == sendctx->other))
+ rcp_cnt++;
+ }
+
+ spin_unlock(&u->bus->lock);
+
+ /*
+ * Memory can't be allocated while holding a spinlock so
+ * we have to release the lock, do the allocation for the
+ * array to store each destination peer sk_buff and grab
+ * the bus peer lock again. Peers could have joined the
+ * bus while we relesed the lock so we allocate 5 more
+ * recipients hoping that this will be enough to not having
+ * to try again in case only a few peers joined the bus.
+ */
+ rcp_cnt += 5;
+ skb_set = kzalloc(sizeof(struct sk_buff *) * rcp_cnt, GFP_KERNEL);
+
+ if (!skb_set) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ sendctx_set = kzalloc(sizeof(struct bus_send_context *) * rcp_cnt,
+ GFP_KERNEL);
+ if (!sendctx_set) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < rcp_cnt; i++) {
+ skb_set[i] = skb_clone(skb, GFP_KERNEL);
+ if (!skb_set[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ sendctx_set[i] = BUSCB(skb_set[i]).sendctx
+ = kmalloc(sizeof(*sendctx) * rcp_cnt, GFP_KERNEL);
+ if (!sendctx_set[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ memcpy(sendctx_set[i], sendctx, sizeof(*sendctx));
+ err = bus_scm_to_skb(sendctx_set[i]->siocb->scm,
+ skb_set[i], true);
+ if (err < 0)
+ goto out_free;
+ bus_get_secdata(sendctx_set[i]->siocb->scm,
+ skb_set[i]);
+
+ sendctx_set[i]->other = NULL;
+ }
+
+ send_cnt = 0;
+
+ spin_lock(&u->bus->lock);
+
+ hlist_for_each_entry(s, node, &u->bus->peers, bus_node) {
+
+ if (!net_eq(sock_net(&s->sk), net))
+ continue;
+
+ if (send_cnt >= rcp_cnt) {
+ spin_unlock(&u->bus->lock);
+
+ for (i = 0; i < rcp_cnt; i++) {
+ sock_put(sendctx_set[i]->other);
+ kfree_skb(skb_set[i]);
+ kfree(sendctx_set[i]);
+ }
+ kfree(skb_set);
+ kfree(sendctx_set);
+ sendctx_set = NULL;
+ skb_set = NULL;
+ goto try_again;
+ }
+
+ if (is_receiver(&s->sk, prefix) ||
+ (!sendctx->multicast &&
+ !sendctx->to_master &&
+ &s->sk == sendctx->other)) {
+ skb_set_owner_w(skb_set[send_cnt], &s->sk);
+ tmpctx = BUSCB(skb_set[send_cnt]).sendctx;
+ sock_hold(&s->sk);
+ if (&s->sk == sendctx->other) {
+ tmpctx->main_recipient = 1;
+ main_rcp_found = true;
+ }
+ tmpctx->other = &s->sk;
+ tmpctx->recipient = s->addr->name;
+ tmpctx->eavesdropper = bus_eavesdropper(&s->sk, 0);
+
+ send_cnt++;
+ }
+ }
+
+ spin_unlock(&u->bus->lock);
+
+ /*
+ * Peers have left the bus so we have to free
+ * their pre-allocated bus_send_context and
+ * socket buffers.
+ */
+ if (send_cnt < rcp_cnt) {
+ for (i = send_cnt; i < rcp_cnt; i++) {
+ kfree_skb(skb_set[i]);
+ kfree(sendctx_set[i]);
+ }
+ rcp_cnt = send_cnt;
+ }
+
+ for (i = 0; i < send_cnt; i++) {
+ tmpctx = BUSCB(skb_set[i]).sendctx;
+ tmpctx->deliver = 0;
+ err = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb_set[i],
+ NULL, NULL, bus_sendmsg_finish);
+ if (err == -EPERM)
+ sock_put(tmpctx->other);
+ }
+
+ /*
+ * If the send context is not multicast, the destination
+ * coud be either the peer accepted socket descriptor or
+ * a peer that is not an eavesdropper. If the peer is not
+ * the accepted socket descriptor and has been authenticated,
+ * it is a member of the bus peer list so it has already been
+ * marked for delivery.
+ * But if the destination is the accepted socket descriptor
+ * or is a non-authenticated peer it is not a member of the
+ * bus peer list so the packet has to be explicitly deliver
+ * to it.
+ */
+
+ if (!sendctx->multicast &&
+ (sendctx->to_master ||
+ (sendctx->bus_master_side && !main_rcp_found))) {
+ sendctx->main_recipient = 1;
+ err = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb, NULL, NULL,
+ bus_sendmsg_finish);
+ if (err == -EPERM)
+ sock_put(sendctx->other);
+ }
+
+ spin_lock(&u->bus->send_lock);
+
+ for (i = 0; i < send_cnt; i++) {
+ tmpctx = sendctx_set[i];
+ if (tmpctx->deliver != 1)
+ continue;
+
+ bus_state_lock(tmpctx->other);
+ bus_deliver_skb(skb_set[i]);
+ bus_state_unlock(tmpctx->other);
+ }
+
+ if (!sendctx->multicast &&
+ sendctx->deliver == 1 &&
+ !bus_sk(sendctx->other)->eavesdropper) {
+ bus_state_lock(sendctx->other);
+ bus_deliver_skb(skb);
+ bus_state_unlock(sendctx->other);
+ }
+
+ spin_unlock(&u->bus->send_lock);
+
+ for (i = 0; i < send_cnt; i++) {
+ tmpctx = sendctx_set[i];
+ if (tmpctx->deliver != 1)
+ continue;
+
+ tmpctx->other->sk_data_ready(tmpctx->other, 0);
+ sock_put(tmpctx->other);
+ }
+
+ if (!sendctx->multicast &&
+ sendctx->deliver == 1 &&
+ !bus_sk(sendctx->other)->eavesdropper) {
+ sendctx->other->sk_data_ready(sendctx->other, 0);
+ sock_put(sendctx->other);
+ }
+
+ err = len;
+ goto out;
+
+out_free:
+ for (i = 0; i < rcp_cnt; i++) {
+ if (skb_set[i])
+ kfree_skb(skb_set[i]);
+ }
+
+out:
+ kfree(skb_set);
+ if (sendctx_set) {
+ for (i = 0; i < rcp_cnt; i++)
+ kfree(sendctx_set[i]);
+ kfree(sendctx_set);
+ }
+
+ if (sendctx->deliver == 0) {
+ if (!sendctx->to_master &&
+ !(sendctx->bus_master_side && !main_rcp_found))
+ kfree_skb(skb);
+ if (!sendctx->to_master &&
+ !(sendctx->bus_master_side && !main_rcp_found))
+ if (sendctx->other)
+ sock_put(sendctx->other);
+ }
+ scm_destroy(sendctx->siocb->scm);
+
+ return err;
+}
+
+/**
+ * bus_sendmsg - send an skb to a destination
+ * @kiocb: I/O control block info
+ * @sock: sender socket
+ * @msg: message header
+ * @len: message length
+ *
+ * Send an socket buffer to a destination. The destination could be
+ * either an unicast or a multicast address. In any case, a copy of
+ * the packet has to be send to all the sockets that are allowed to
+ * eavesdrop the communication bus.
+ *
+ * If the destination address is not associated with any socket, the
+ * packet is default routed to the bus master (the sender accepted
+ * socket).
+ *
+ * The af_bus sending path is hooked to the netfilter subsystem so
+ * netfilter hooks can filter or modify the packet before delivery.
+ */
+static int bus_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct bus_sock *u = bus_sk(sk);
+ struct sockaddr_bus *sbusaddr = msg->msg_name;
+ int err;
+ struct sk_buff *skb;
+ struct scm_cookie tmp_scm;
+ bool to_master = false;
+ bool multicast = false;
+ struct bus_send_context sendctx;
+
+ err = sock_error(sk);
+ if (err)
+ return err;
+
+ if (sk->sk_state != BUS_ESTABLISHED)
+ return -ENOTCONN;
+
+ if (!msg->msg_namelen)
+ sbusaddr = NULL;
+
+ if (sbusaddr && !bus_same_bus(sbusaddr, u->addr->name))
+ return -EHOSTUNREACH;
+
+ if ((!sbusaddr && !u->bus_master_side) ||
+ (sbusaddr && sbusaddr->sbus_addr.s_addr == BUS_MASTER_ADDR))
+ to_master = true;
+ else if (sbusaddr && !u->bus_master_side && !u->authenticated)
+ return -EHOSTUNREACH;
+
+ sendctx.namelen = 0; /* fake GCC */
+ sendctx.siocb = kiocb_to_siocb(kiocb);
+ sendctx.other = NULL;
+
+ if (NULL == sendctx.siocb->scm)
+ sendctx.siocb->scm = &tmp_scm;
+ wait_for_bus_gc();
+ err = scm_send(sock, msg, sendctx.siocb->scm);
+ if (err < 0)
+ return err;
+
+ err = -EOPNOTSUPP;
+ if (msg->msg_flags&MSG_OOB)
+ goto out;
+
+ if (sbusaddr && !to_master) {
+ err = bus_mkname(sbusaddr, msg->msg_namelen, &sendctx.hash);
+ if (err < 0)
+ goto out;
+ sendctx.namelen = err;
+ multicast = bus_mc_addr(sbusaddr);
+ } else {
+ err = -ENOTCONN;
+ sendctx.other = bus_peer_get(sk);
+ if (!sendctx.other)
+ goto out;
+ }
+
+ err = -EMSGSIZE;
+ if (len > sk->sk_sndbuf - 32)
+ goto out;
+
+ sendctx.timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+
+restart:
+ bus_state_lock(sk);
+ if (bus_recvq_full(sk)) {
+ err = -EAGAIN;
+ if (!sendctx.timeo) {
+ bus_state_unlock(sk);
+ goto out;
+ }
+
+ sendctx.timeo = bus_wait_for_peer(sk, sendctx.timeo);
+
+ err = sock_intr_errno(sendctx.timeo);
+ if (signal_pending(current))
+ goto out;
+
+ goto restart;
+ } else {
+ bus_state_unlock(sk);
+ }
+
+ skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
+ if (skb == NULL)
+ goto out;
+
+ err = bus_scm_to_skb(sendctx.siocb->scm, skb, true);
+ if (err < 0)
+ goto out_free;
+ sendctx.max_level = err + 1;
+ bus_get_secdata(sendctx.siocb->scm, skb);
+
+ skb_reset_transport_header(skb);
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (err)
+ goto out_free;
+
+ sendctx.sender_socket = sock;
+ if (u->bus_master_side && sendctx.other) {
+ /* if the bus master sent an unicast message to a peer, we
+ * need the address of that peer
+ */
+ sendctx.sender = bus_sk(sendctx.other)->addr->name;
+ } else {
+ sendctx.sender = u->addr->name;
+ }
+ sendctx.recipient = sbusaddr;
+ sendctx.authenticated = u->authenticated;
+ sendctx.bus_master_side = u->bus_master_side;
+ sendctx.to_master = to_master;
+ sendctx.multicast = multicast;
+ sendctx.eavesdropper = atomic64_read(&u->bus->eavesdropper_cnt) ? 1 : 0;
+ BUSCB(skb).sendctx = &sendctx;
+
+ if (sendctx.multicast || sendctx.eavesdropper) {
+ sendctx.main_recipient = 0;
+ err = bus_sendmsg_mcast(skb);
+ return sendctx.multicast ? len : err;
+ } else {
+ sendctx.main_recipient = 1;
+ len = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb, NULL, NULL,
+ bus_sendmsg_finish);
+
+ if (len == -EPERM) {
+ err = len;
+ goto out;
+ } else {
+ scm_destroy(sendctx.siocb->scm);
+ return len;
+ }
+ }
+
+out_free:
+ kfree_skb(skb);
+out:
+ if (sendctx.other)
+ sock_put(sendctx.other);
+ scm_destroy(sendctx.siocb->scm);
+ return err;
+}
+
+static void bus_copy_addr(struct msghdr *msg, struct sock *sk)
+{
+ struct bus_sock *u = bus_sk(sk);
+
+ msg->msg_namelen = 0;
+ if (u->addr) {
+ msg->msg_namelen = u->addr->len;
+ memcpy(msg->msg_name, u->addr->name,
+ sizeof(struct sockaddr_bus));
+ }
+}
+
+static int bus_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock_iocb *siocb = kiocb_to_siocb(iocb);
+ struct scm_cookie tmp_scm;
+ struct sock *sk = sock->sk;
+ struct bus_sock *u = bus_sk(sk);
+ int noblock = flags & MSG_DONTWAIT;
+ struct sk_buff *skb;
+ int err;
+ int peeked, skip;
+
+ if (sk->sk_state != BUS_ESTABLISHED)
+ return -ENOTCONN;
+
+ err = -EOPNOTSUPP;
+ if (flags&MSG_OOB)
+ goto out;
+
+ msg->msg_namelen = 0;
+
+ err = mutex_lock_interruptible(&u->readlock);
+ if (err) {
+ err = sock_intr_errno(sock_rcvtimeo(sk, noblock));
+ goto out;
+ }
+
+ skip = sk_peek_offset(sk, flags);
+
+ skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);
+ if (!skb) {
+ bus_state_lock(sk);
+ /* Signal EOF on disconnected non-blocking SEQPACKET socket. */
+ if (err == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN))
+ err = 0;
+ bus_state_unlock(sk);
+ goto out_unlock;
+ }
+
+ wake_up_interruptible_sync_poll(&u->peer_wait,
+ POLLOUT | POLLWRNORM | POLLWRBAND);
+
+ if (msg->msg_name)
+ bus_copy_addr(msg, skb->sk);
+
+ if (size > skb->len - skip)
+ size = skb->len - skip;
+ else if (size < skb->len - skip)
+ msg->msg_flags |= MSG_TRUNC;
+
+ err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size);
+ if (err)
+ goto out_free;
+
+ if (sock_flag(sk, SOCK_RCVTSTAMP))
+ __sock_recv_timestamp(msg, sk, skb);
+
+ if (!siocb->scm) {
+ siocb->scm = &tmp_scm;
+ memset(&tmp_scm, 0, sizeof(tmp_scm));
+ }
+ scm_set_cred(siocb->scm, BUSCB(skb).pid, BUSCB(skb).cred);
+ bus_set_secdata(siocb->scm, skb);
+
+ if (!(flags & MSG_PEEK)) {
+ if (BUSCB(skb).fp)
+ bus_detach_fds(siocb->scm, skb);
+
+ sk_peek_offset_bwd(sk, skb->len);
+ } else {
+ /* It is questionable: on PEEK we could:
+ - do not return fds - good, but too simple 8)
+ - return fds, and do not return them on read (old strategy,
+ apparently wrong)
+ - clone fds (I chose it for now, it is the most universal
+ solution)
+
+ POSIX 1003.1g does not actually define this clearly
+ at all. POSIX 1003.1g doesn't define a lot of things
+ clearly however!
+
+ */
+
+ sk_peek_offset_fwd(sk, size);
+
+ if (BUSCB(skb).fp)
+ siocb->scm->fp = scm_fp_dup(BUSCB(skb).fp);
+ }
+ err = (flags & MSG_TRUNC) ? skb->len - skip : size;
+
+ scm_recv(sock, msg, siocb->scm, flags);
+
+out_free:
+ skb_free_datagram(sk, skb);
+out_unlock:
+ mutex_unlock(&u->readlock);
+out:
+ return err;
+}
+
+static int bus_shutdown(struct socket *sock, int mode)
+{
+ struct sock *sk = sock->sk;
+ struct sock *other;
+
+ mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN);
+
+ if (!mode)
+ return 0;
+
+ bus_state_lock(sk);
+ sk->sk_shutdown |= mode;
+ other = bus_peer(sk);
+ if (other)
+ sock_hold(other);
+ bus_state_unlock(sk);
+ sk->sk_state_change(sk);
+
+ if (other) {
+
+ int peer_mode = 0;
+
+ if (mode&RCV_SHUTDOWN)
+ peer_mode |= SEND_SHUTDOWN;
+ if (mode&SEND_SHUTDOWN)
+ peer_mode |= RCV_SHUTDOWN;
+ bus_state_lock(other);
+ other->sk_shutdown |= peer_mode;
+ bus_state_unlock(other);
+ other->sk_state_change(other);
+ if (peer_mode == SHUTDOWN_MASK)
+ sk_wake_async(other, SOCK_WAKE_WAITD, POLL_HUP);
+ else if (peer_mode & RCV_SHUTDOWN)
+ sk_wake_async(other, SOCK_WAKE_WAITD, POLL_IN);
+ sock_put(other);
+ }
+
+ return 0;
+}
+
+static int bus_add_addr(struct sock *sk, struct bus_addr *sbus_addr)
+{
+ struct bus_address *addr;
+ struct sock *other;
+ struct bus_sock *u = bus_sk(sk);
+ struct net *net = sock_net(sk);
+ int ret = 0;
+
+ addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(addr->name, u->addr->name, sizeof(struct sockaddr_bus));
+ addr->len = u->addr->len;
+
+ addr->name->sbus_addr.s_addr = sbus_addr->s_addr;
+ addr->hash = bus_compute_hash(addr->name->sbus_addr);
+ other = bus_find_socket_byaddress(net, addr->name, addr->len,
+ sk->sk_protocol, addr->hash);
+
+ if (other) {
+ sock_put(other);
+ kfree(addr);
+ ret = -EADDRINUSE;
+ goto out;
+ }
+
+ atomic_set(&addr->refcnt, 1);
+ INIT_HLIST_NODE(&addr->addr_node);
+ INIT_HLIST_NODE(&addr->table_node);
+
+ addr->sock = sk;
+
+ hlist_add_head(&addr->addr_node, &u->addr_list);
+ bus_insert_address(&bus_address_table[addr->hash], addr);
+
+out:
+ sock_put(sk);
+
+ return ret;
+}
+
+static int bus_del_addr(struct sock *sk, struct bus_addr *sbus_addr)
+{
+ struct bus_address *addr;
+ int ret = 0;
+
+ bus_state_lock(sk);
+ addr = __bus_get_address(sk, sbus_addr);
+ if (!addr) {
+ ret = -EINVAL;
+ bus_state_unlock(sk);
+ goto out;
+ }
+ hlist_del(&addr->addr_node);
+ bus_state_unlock(sk);
+
+ bus_remove_address(addr);
+ bus_release_addr(addr);
+out:
+ sock_put(sk);
+
+ return ret;
+}
+
+static int bus_join_bus(struct sock *sk)
+{
+ struct sock *peer;
+ struct bus_sock *u = bus_sk(sk), *peeru;
+ int err = 0;
+
+ peer = bus_peer_get(sk);
+ if (!peer)
+ return -ENOTCONN;
+ peeru = bus_sk(peer);
+
+ if (!u->bus_master_side || peeru->authenticated) {
+ err = -EINVAL;
+ goto sock_put_out;
+ }
+
+ if (sk->sk_state != BUS_ESTABLISHED) {
+ err = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ if (peer->sk_shutdown != 0) {
+ err = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ bus_state_lock(peer);
+ peeru->authenticated = true;
+ bus_state_unlock(peer);
+
+ spin_lock(&u->bus->lock);
+ hlist_add_head(&peeru->bus_node, &u->bus->peers);
+ spin_unlock(&u->bus->lock);
+
+sock_put_out:
+ sock_put(peer);
+ return err;
+}
+
+static int __bus_set_eavesdrop(struct sock *sk, bool eavesdrop)
+{
+ struct sock *peer = bus_peer_get(sk);
+ struct bus_sock *u = bus_sk(sk), *peeru;
+ int err = 0;
+
+ if (!peer)
+ return -ENOTCONN;
+
+ if (sk->sk_state != BUS_ESTABLISHED) {
+ err = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ peeru = bus_sk(peer);
+
+ if (!u->bus_master_side || !peeru->authenticated) {
+ err = -EINVAL;
+ goto sock_put_out;
+ }
+
+ if (peer->sk_shutdown != 0) {
+ err = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ bus_state_lock(peeru);
+ if (peeru->eavesdropper != eavesdrop) {
+ peeru->eavesdropper = eavesdrop;
+ if (eavesdrop)
+ atomic64_inc(&u->bus->eavesdropper_cnt);
+ else
+ atomic64_dec(&u->bus->eavesdropper_cnt);
+ }
+ bus_state_unlock(peeru);
+
+sock_put_out:
+ sock_put(peer);
+ return err;
+}
+
+static int bus_set_eavesdrop(struct sock *sk)
+{
+ return __bus_set_eavesdrop(sk, true);
+}
+
+static int bus_unset_eavesdrop(struct sock *sk)
+{
+ return __bus_set_eavesdrop(sk, false);
+}
+
+static inline void sk_sendbuf_set(struct sock *sk, int sndbuf)
+{
+ bus_state_lock(sk);
+ sk->sk_sndbuf = sndbuf;
+ bus_state_unlock(sk);
+}
+
+static inline void sk_maxqlen_set(struct sock *sk, int qlen)
+{
+ bus_state_lock(sk);
+ sk->sk_max_ack_backlog = qlen;
+ bus_state_unlock(sk);
+}
+
+static int bus_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct bus_addr addr;
+ int res;
+ int val;
+
+ if (level != SOL_BUS)
+ return -ENOPROTOOPT;
+
+ switch (optname) {
+ case BUS_ADD_ADDR:
+ case BUS_DEL_ADDR:
+ if (optlen < sizeof(struct bus_addr))
+ return -EINVAL;
+
+ if (!bus_sk(sock->sk)->bus_master_side)
+ return -EINVAL;
+
+ if (copy_from_user(&addr, optval, sizeof(struct bus_addr)))
+ return -EFAULT;
+
+ if (optname == BUS_ADD_ADDR)
+ res = bus_add_addr(bus_peer_get(sock->sk), &addr);
+ else
+ res = bus_del_addr(bus_peer_get(sock->sk), &addr);
+ break;
+ case BUS_JOIN_BUS:
+ res = bus_join_bus(sock->sk);
+ break;
+ case BUS_SET_EAVESDROP:
+ res = bus_set_eavesdrop(sock->sk);
+ break;
+ case BUS_UNSET_EAVESDROP:
+ res = bus_unset_eavesdrop(sock->sk);
+ break;
+ case BUS_SET_SENDBUF:
+ case BUS_SET_MAXQLEN:
+ if (sock->sk->sk_state != BUS_LISTEN) {
+ res = -EINVAL;
+ } else {
+ res = -EFAULT;
+
+ if (copy_from_user(&val, optval, optlen))
+ break;
+
+ res = 0;
+
+ if (optname == BUS_SET_SENDBUF)
+ sk_sendbuf_set(sock->sk, val);
+ else
+ sk_maxqlen_set(sock->sk, val);
+ }
+ break;
+ default:
+ res = -EINVAL;
+ break;
+ }
+
+ return res;
+}
+
+long bus_inq_len(struct sock *sk)
+{
+ struct sk_buff *skb;
+ long amount = 0;
+
+ if (sk->sk_state == BUS_LISTEN)
+ return -EINVAL;
+
+ spin_lock(&sk->sk_receive_queue.lock);
+ skb_queue_walk(&sk->sk_receive_queue, skb)
+ amount += skb->len;
+ spin_unlock(&sk->sk_receive_queue.lock);
+
+ return amount;
+}
+EXPORT_SYMBOL_GPL(bus_inq_len);
+
+long bus_outq_len(struct sock *sk)
+{
+ return sk_wmem_alloc_get(sk);
+}
+EXPORT_SYMBOL_GPL(bus_outq_len);
+
+static int bus_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ long amount = 0;
+ int err;
+
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = bus_outq_len(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = bus_inq_len(sk);
+ if (amount < 0)
+ err = amount;
+ else
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ break;
+ }
+ return err;
+}
+
+static unsigned int bus_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk, *other;
+ unsigned int mask, writable;
+ struct bus_sock *u = bus_sk(sk), *p;
+ struct hlist_node *node;
+
+ sock_poll_wait(file, sk_sleep(sk), wait);
+ mask = 0;
+
+ /* exceptional events? */
+ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+ mask |= POLLERR;
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ mask |= POLLRDHUP | POLLIN | POLLRDNORM;
+ if (sk->sk_shutdown == SHUTDOWN_MASK)
+ mask |= POLLHUP;
+
+ /* readable? */
+ if (!skb_queue_empty(&sk->sk_receive_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ /* Connection-based need to check for termination and startup */
+ if (sk->sk_state == BUS_CLOSE)
+ mask |= POLLHUP;
+
+ /* No write status requested, avoid expensive OUT tests. */
+ if (!(poll_requested_events(wait) & (POLLWRBAND|POLLWRNORM|POLLOUT)))
+ return mask;
+
+ writable = bus_writable(sk);
+ other = bus_peer_get(sk);
+ if (other) {
+ if (bus_recvq_full(other))
+ writable = 0;
+ sock_put(other);
+ }
+
+ /*
+ * If the socket has already joined the bus we have to check
+ * that each peer receiver queue on the bus is not full.
+ */
+ if (!u->bus_master_side && u->authenticated) {
+ spin_lock(&u->bus->lock);
+ hlist_for_each_entry(p, node, &u->bus->peers, bus_node) {
+ if (bus_recvq_full(&p->sk)) {
+ writable = 0;
+ break;
+ }
+ }
+ spin_unlock(&u->bus->lock);
+ }
+
+ if (writable)
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+ return mask;
+}
+
+#ifdef CONFIG_PROC_FS
+static struct sock *first_bus_socket(int *i)
+{
+ for (*i = 0; *i <= BUS_HASH_SIZE; (*i)++) {
+ if (!hlist_empty(&bus_socket_table[*i]))
+ return __sk_head(&bus_socket_table[*i]);
+ }
+ return NULL;
+}
+
+static struct sock *next_bus_socket(int *i, struct sock *s)
+{
+ struct sock *next = sk_next(s);
+ /* More in this chain? */
+ if (next)
+ return next;
+ /* Look for next non-empty chain. */
+ for ((*i)++; *i <= BUS_HASH_SIZE; (*i)++) {
+ if (!hlist_empty(&bus_socket_table[*i]))
+ return __sk_head(&bus_socket_table[*i]);
+ }
+ return NULL;
+}
+
+struct bus_iter_state {
+ struct seq_net_private p;
+ int i;
+};
+
+static struct sock *bus_seq_idx(struct seq_file *seq, loff_t pos)
+{
+ struct bus_iter_state *iter = seq->private;
+ loff_t off = 0;
+ struct sock *s;
+
+ for (s = first_bus_socket(&iter->i); s;
+ s = next_bus_socket(&iter->i, s)) {
+ if (sock_net(s) != seq_file_net(seq))
+ continue;
+ if (off == pos)
+ return s;
+ ++off;
+ }
+ return NULL;
+}
+
+static void *bus_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(bus_table_lock)
+{
+ spin_lock(&bus_table_lock);
+ return *pos ? bus_seq_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *bus_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct bus_iter_state *iter = seq->private;
+ struct sock *sk = v;
+ ++*pos;
+
+ if (v == SEQ_START_TOKEN)
+ sk = first_bus_socket(&iter->i);
+ else
+ sk = next_bus_socket(&iter->i, sk);
+ while (sk && (sock_net(sk) != seq_file_net(seq)))
+ sk = next_bus_socket(&iter->i, sk);
+ return sk;
+}
+
+static void bus_seq_stop(struct seq_file *seq, void *v)
+ __releases(bus_table_lock)
+{
+ spin_unlock(&bus_table_lock);
+}
+
+static int bus_seq_show(struct seq_file *seq, void *v)
+{
+
+ if (v == SEQ_START_TOKEN)
+ seq_puts(seq, "Num RefCount Protocol Flags Type St " \
+ "Inode Path\n");
+ else {
+ struct sock *s = v;
+ struct bus_sock *u = bus_sk(s);
+ bus_state_lock(s);
+
+ seq_printf(seq, "%pK: %08X %08X %08X %04X %02X %5lu",
+ s,
+ atomic_read(&s->sk_refcnt),
+ 0,
+ s->sk_state == BUS_LISTEN ? __SO_ACCEPTCON : 0,
+ s->sk_type,
+ s->sk_socket ?
+ (s->sk_state == BUS_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
+ (s->sk_state == BUS_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
+ sock_i_ino(s));
+
+ if (u->addr) {
+ int i, len;
+ seq_putc(seq, ' ');
+
+ i = 0;
+ len = u->addr->len - sizeof(short);
+ if (!BUS_ABSTRACT(s))
+ len--;
+ else {
+ seq_putc(seq, '@');
+ i++;
+ }
+ for ( ; i < len; i++)
+ seq_putc(seq, u->addr->name->sbus_path[i]);
+ }
+ bus_state_unlock(s);
+ seq_putc(seq, '\n');
+ }
+
+ return 0;
+}
+
+static const struct seq_operations bus_seq_ops = {
+ .start = bus_seq_start,
+ .next = bus_seq_next,
+ .stop = bus_seq_stop,
+ .show = bus_seq_show,
+};
+
+static int bus_seq_open(struct inode *inode, struct file *file)
+{
+ return seq_open_net(inode, file, &bus_seq_ops,
+ sizeof(struct bus_iter_state));
+}
+
+static const struct file_operations bus_seq_fops = {
+ .owner = THIS_MODULE,
+ .open = bus_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_net,
+};
+
+#endif
+
+static const struct net_proto_family bus_family_ops = {
+ .family = PF_BUS,
+ .create = bus_create,
+ .owner = THIS_MODULE,
+};
+
+static int __init af_bus_init(void)
+{
+ int rc = -1;
+ struct sk_buff *dummy_skb;
+
+ BUILD_BUG_ON(sizeof(struct bus_skb_parms) > sizeof(dummy_skb->cb));
+
+ rc = proto_register(&bus_proto, 1);
+ if (rc != 0) {
+ pr_crit("%s: Cannot create bus_sock SLAB cache!\n", __func__);
+ return rc;
+ }
+
+ sock_register(&bus_family_ops);
+ return rc;
+}
+
+static void __exit af_bus_exit(void)
+{
+ sock_unregister(PF_BUS);
+ proto_unregister(&bus_proto);
+}
+
+module_init(af_bus_init);
+module_exit(af_bus_exit);
+
+MODULE_AUTHOR("Alban Crequy, Javier Martinez Canillas");
+MODULE_DESCRIPTION("Linux Bus domain sockets");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_BUS);
--
1.7.10

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/