summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Brewer <benbrewer@codethink.co.uk>2012-11-19 17:54:24 +0000
committerBen Brewer <benbrewer@codethink.co.uk>2012-11-19 17:54:24 +0000
commitda53d55583196c05e470c3dd8e24b24e22c3b741 (patch)
treeedafcd2f7858a668bb9c07fcee46cc2341fa4988
parent5edf3233eb6a511a33d15dde7a97a069e8a47498 (diff)
downloadlinux-da53d55583196c05e470c3dd8e24b24e22c3b741.tar.gz
Applied AF_DBUS patch to kernelbaserock/genivi/af_dbus
-rw-r--r--Documentation/networking/af_bus.txt558
-rw-r--r--drivers/connector/connector.c32
-rw-r--r--include/linux/bus.h34
-rw-r--r--include/linux/connector.h1
-rw-r--r--include/linux/security.h11
-rw-r--r--include/linux/socket.h5
-rw-r--r--include/net/af_bus.h273
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/bus/Kconfig17
-rw-r--r--net/bus/Makefile10
-rw-r--r--net/bus/af_bus.c2688
-rw-r--r--net/bus/garbage.c322
-rw-r--r--net/bus/nfdbus/Kconfig12
-rw-r--r--net/bus/nfdbus/Makefile6
-rw-r--r--net/bus/nfdbus/matchrule.c1132
-rw-r--r--net/bus/nfdbus/matchrule.h82
-rw-r--r--net/bus/nfdbus/message.c194
-rw-r--r--net/bus/nfdbus/message.h71
-rw-r--r--net/bus/nfdbus/nfdbus.c456
-rw-r--r--net/bus/nfdbus/nfdbus.h44
-rw-r--r--net/core/scm.c3
-rw-r--r--net/core/sock.c6
-rw-r--r--security/capability.c7
-rw-r--r--security/security.c7
-rw-r--r--security/selinux/hooks.c35
26 files changed, 6003 insertions, 5 deletions
diff --git a/Documentation/networking/af_bus.txt b/Documentation/networking/af_bus.txt
new file mode 100644
index 000000000000..a0b078f9fe3f
--- /dev/null
+++ b/Documentation/networking/af_bus.txt
@@ -0,0 +1,558 @@
+ The AF_BUS socket address family
+ ================================
+
+Introduction
+------------
+
+AF_BUS is a message oriented inter process communication system.
+
+The principle features are:
+
+ - Reliable datagram based communication (all sockets are of type
+ SOCK_SEQPACKET)
+
+ - Multicast message delivery (one to many, unicast as a subset)
+
+ - Strict ordering (messages are delivered to every client in the same order)
+
+ - Ability to pass file descriptors
+
+ - Ability to pass credentials
+
+The basic concept is to provide a virtual bus on which multiple
+processes can communicate and policy is imposed by a "bus master".
+
+A process can create buses to which other processes can connect and
+communicate with each other by sending messages. Processes' addresses
+are automatically assigned by the bus on connect and are
+unique. Messages can be sent either to a process' unique address or to
+a bus multicast addresses.
+
+Netfilter rules or Berkeley Packet Filter can be used to restrict the
+messages that each peer is allowed to receive. This is especially
+important when sending to multicast addresses.
+
+Besides messages, process can send and receive ancillary data (i.e.,
+SCM_RIGHTS for passing file descriptors or SCM_CREDENTIALS for passing
+Unix credentials). In the case of a multicast message all recipients
+of a message may obtain a copy a file descriptor or credentials.
+
+A bus is created by processes connecting on an AF_BUS socket. The
+"bus master" binds itself instead of connecting to the NULL address.
+
+The socket address is made up of a path component and a numeric
+component. The path component is either a pathname or an abstract
+socket similar to a unix socket. The numeric component is used to
+uniquely identify each connection to the bus. Thus the path identifies
+a specific bus and the numeric component the attachment to that bus.
+
+The process that calls bind(2) on the socket is the owner of the bus
+and is called the bus master. The master is a special client of the
+bus and has some responsibility for the bus' operation. The master is
+assigned a fixed address with all the bits zero (0x0000000000000000).
+
+Each process connected to an AF_BUS socket has one or more addresses
+within that bus. These addresses are 64-bit unsigned integers,
+interpreted by splitting the address into two parts: the most
+significant 16 bits are a prefix identifying the type of address, and
+the remaining 48 bits are the actual client address within that
+prefix, as shown in this figure:
+
+Bit: 0 15 16 63
+ +----------------+------------------------------------------------+
+ | Type prefix | Client address |
+ +----------------+------------------------------------------------+
+
+The prefix with all bits zero is reserved for use by the kernel, which
+automatically assigns one address from this prefix to each client on
+connection. The address in this prefix with all bits zero is always
+assigned to the bus master. Addresses on the prefix 0x0000 are unique
+and will never repeat for the lifetime of the bus master.
+
+A client may have multiple addresses. When data is sent to other
+clients, those clients will always see the sender address that is in
+the prefix 0x0000 address space when calling recvmsg(2) or
+recvfrom(2). Similarly, the prefix 0x0000 address is returned by calls
+to getsockname(2) and getpeername(2).
+
+For each prefix, the address where the least significant 48 bits are
+all 1 (i.e., 0xffffffffffff) is also reserved, and can be used to send
+multicast messages to all the peers on a prefix.
+
+The non-reserved addresses in each of the remaining prefixes are
+managed by the bus master, which may assign additional addresses to
+any other connected socket.
+
+Having different name-spaces has two advantages:
+
+ - Clients can have addresses on different mutually-exclusive
+ scopes. This permits sending multicast packets to only clients
+ that have addresses on a given prefix.
+
+ - The addressing scheme can be more flexible. The kernel will only
+ assign unique addresses on the all-bits-zero prefix (0x0000) and
+ allows the bus master process to assign additional addresses to
+ clients on other prefixes. By having different prefixes, the
+ kernel and bus master assignments will not collide.
+
+AF_BUS transport can support two network topologies. When a process
+first connects to the bus master, it can only communicate with the bus
+master. The process can't send and receive packets from other peers on
+the bus. So, from the client process point of view the network
+topology is point-to-point.
+
+The bus master can allow the connected peer to be part of the bus and
+start to communicate with other peers by setting a socket option with
+the setsockopt(2) system call using the accepted socket descriptor. At
+this point, the topology becomes a bus to the client process.
+
+Packets whose destination address is not assigned to any client are
+routed by default to the bus master (the client accepted socket
+descriptor).
+
+
+Semantics
+---------
+
+Bus features:
+
+ - Unicast and multicast addressing scheme.
+ - Ability to assign addresses from user-space with different prefixes.
+ - Automatic address assignment.
+ - Ordered packets delivery (FIFO, total ordering).
+ - File descriptor and credentials passing.
+ - Support for both point-to-point and bus network topologies.
+ - Bus control access managed from user-space.
+ - Netfilter hooks for packet sending, routing and receiving.
+
+A process (the "bus master") can create an AF_BUS bus with socket(2)
+and use bind(2) to assign an address to the bus. Then it can listen(2)
+on the created socket to start accepting incoming connections with
+accept(2).
+
+Processes can connect to the bus by creating a socket with socket(2)
+and using connect(2). The kernel will assign a unique address to each
+connection and messages can be sent and received by using BSD socket
+primitives.
+
+This uses the connect(2) semantic in a non-traditional way, with
+AF_BUS sockets, it's not possible to connect "my" socket to a specific
+peer socket whereas the traditional BSD sockets API usage, connect(2)
+either connects to stream sockets, or assigns a peer address to a
+datagram socket (so that send(2) can be used instead of sendto()).
+
+An AF_BUS socket address is represented as a combination of a bus
+address and a bus path name. Address are unique within a path. The
+unique bus address is further subdivided into a prefix and a client
+address. Thus the path identifies a specific bus and the numeric
+component the attachment to that bus.
+
+#define BUS_PATH_MAX 108
+
+/* Bus address */
+struct bus_addr {
+ uint64_t s_addr; /* 16-bit prefix + 48-bit client address */
+};
+
+/* Structure describing an AF_BUS socket address. */
+struct sockaddr_bus {
+ sa_family_t sbus_family; /* AF_BUS */
+ struct bus_addr sbus_addr; /* bus address */
+ char sbus_path[BUS_PATH_MAX]; /* pathname */
+};
+
+A process becomes a bus master for a given struct sockaddr_bus by
+calling bind(2) on an AF_BUS addresses. The argument must be { AF_BUS,
+0, path }.
+
+AF_BUS supports both abstract and non-abstract path names. Abstract
+names are distinguished by the fact that sbus_path[0] == '\0' and they
+don't represent file system paths while non-abstract paths are bound
+to a file system path name. (See the unix(7) man page for a discussion
+of abstract socket addresses in the AF_UNIX address family.)
+
+Then the process calls listen(2) to accept incoming connections. If
+that process calls getsockname(2), the returned address will be {
+AF_BUS, 0, path }.
+
+The conventional string form of the full address is path + ":" +
+prefix + "/" + client address. Prefix and client address are
+represented in hex.
+
+For example the address:
+
+struct sockaddr_bus addr;
+addr.sbus_family = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.sbus_addr.s_addr = 0x0002f00ddeadbeef;
+
+would be represented using the string /tmp/test:0002/f00ddeadbeef.
+
+If the bus_addr is 0, then both the prefix and client address may be
+omitted from the string form. To connect to a bus as a client it is
+sufficient to specify the path, since the listening address always has
+bus_addr == 0. it is not meanigful to specify 'bus_addr' as other than
+0 on connect()
+
+The AF_BUS implementation will automatically assign a unique address
+to each client but the bus master can assign additional addresses on a
+different prefix by means of the setsockopt(2) system call. For
+example:
+
+struct bus_addr addr;
+addr.s_addr = 0x0001deadfee1dead;
+ret = setsockopt(afd, SOL_BUS, BUS_ADD_ADDR, &addr, sizeof(addr));
+
+where afd is the accepted socket descriptor in the daemon. To show graphically:
+
+ L The AF_BUS listening socket }
+ / | \ }-- listener process
+ A1 A2 A3 The AF_BUS accepted sockets }
+ | | |
+ C1 C2 C3 The AF_BUS connected sockets }-- client processes
+
+So if setsockopt(A1, SOL_BUS, BUS_ADD_ADDR, &addr, sizeof(addr)) is
+called, C1 will get the new address.
+
+The inverse operation is BUS_DEL_ADDR, which the bus master can use to
+remove a client socket AF_BUS address:
+
+ret = setsockopt(afd, SOL_BUS, BUS_DEL_ADDR, &addr, sizeof(addr));
+
+Besides assigning additional addresses, the bus master has to allow a
+client process to communicate with other peers on the bus using a
+setsockopt(2):
+
+ret = setsockopt(afd, SOL_BUS, BUS_JOIN_BUS, NULL, 0);
+
+Clients are not meant to send messages to each other until the master
+tells them (in a protocol-specific way) that the BUS_JOIN_BUS
+setsockopt(2) call was made.
+
+If a client sends a message to a destination other than the bus
+master's all-zero address before joining the bus, a EHOSTUNREACH (No
+route to host) error is returned since the only host that exists in
+the point-to-point network before the client joins the bus are the
+client and the bus master.
+
+A EHOSTUNREACH is returned if a client that joined a bus tries to send
+a packet to a client from another bus. Cross-bus communication is not
+permited.
+
+When a process wants to send a unicast message to a peer, it fills a
+sockaddr structure and performs a socket operation (i.e., sendto(2))
+
+struct sockaddr_bus addr;
+char *msg = "Hello world";
+
+addr.sbus_family = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.sbus_addr.s_addr = 0x0001f00ddeadbeef;
+
+ret = sendto(sockfd, "Hello world", strlen("Hello world"), 0,
+ (struct sockaddr*)&addr, sizeof(addr));
+
+The current implementation requires that the addr.sbus_path component
+match the one used to conenct() to the bus but in future this
+requirement will be removed.
+
+The kernel will first check that the socket is connected and that the
+bus path of the socket correspond with the destination, then it will
+extract the prefix and client address from the bus address using a
+fixed 16 -bit bitmask.
+
+prefix = bus address >> 48 & 0xffff
+client address = bus address & 0xffff
+
+If the client address is not all bits one, then the message is unicast
+and is delivered to the socket with that assigned address
+(0x0001f00ddeadbeef). Otherwise the message is multicast and is
+delivered to all the peers with this address prefix (0x0001 in this
+case).
+
+So, when a process wants to send a multicast message, it just has to
+fill the address structure with the address prefix + 0xffffffffffff:
+
+struct sockaddr_bus addr;
+char *msg = "Hello world";
+
+addr.bus_family = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.bus_addr = 0x0001ffffffffffff;
+
+ret = sendto(sockfd, "Hello world", strlen("Hello world"), 0,
+ (struct sockaddr*)&addr, sizeof(addr));
+
+The kernel, will apply the binary and operation, learn that the
+address is 0xffffffffffff and send the message to all the peers on
+this prefix (0x0001).
+
+Socket transmit queued bytes are limited by a maximum send buffer size
+(sysctl_wmem_max) defined in the kernel and can be modified at runtime
+using the sysctl interface on /proc/sys/net/core/wmem_default. This
+parameter is global for all the sockets families in a Linux system.
+
+AF_BUS permits the definition of a per-bus maximum send buffer size
+using the BUS_SET_SENDBUF socket option. The bus master can call the
+setsockopt(2) system call using as a parameter the listening socket.
+The command sets a maximum write buffer that will be imposed on each
+new socket that connects to the bus:
+
+ret = setsockopt(serverfd, SOL_BUS, BUS_SET_SENDBUF, &sndbuf,
+sizeof(int));
+
+In the transmission path both Berkeley Packet Filters and Netfilter
+hooks are available, so they can be used to filter sending packets.
+
+
+Using this addressing scheme with D-Bus
+---------------------------------------
+
+As an example of a use case for AF_BUS, let's analyze how the D-Bus
+IPC system can be implemented on top of it.
+
+We define a new D-Bus address type "afbus".
+
+A D-Bus client may connect to an address of the form "afbus:path=X"
+where X is a string. This means that it connect()s to { AF_BUS, 0, X }.
+
+For example: afbus:path=/tmp/test connects to { AF_BUS, 0, /tmp/test }.
+
+A D-Bus daemon may listen on the address "afbus:", which means that it
+binds to { AF_BUS, 0, /tmp/test }. It will advertise an address of the
+form "afbus:path=/tmp/test" to clients, for instance via the
+--print-address option, or via dbus-launch setting the
+DBUS_SESSION_BUS_ADDRESS environment variable. For instance, "afbus:"
+is an appropriate default listening address for the session bus,
+resulting in dbus-launch setting the DBUS_SESSION_BUS_ADDRESS
+environment variable to something like
+"afbus:path=/tmp/test,guid=...".
+
+A D-Bus daemon may listen on the address "afbus:file=/some/file",
+which means that it will do as above, then write its path into the
+given well-known file. For instance,
+"afbus:file=/run/dbus/system_bus.afbus" is an appropriate listening
+address for the system bus. Only processes with suitable privileges to
+write to that file can impersonate the system bus.
+
+D-Bus clients wishing to connect to the well-known system bus should
+attempt to connect to afbus:file=/run/dbus/system_bus.afbus, falling
+back to unix:path=/var/run/dbus/system_bus_socket if that fails. On
+Linux systems, the well-known system bus daemon should attempt to
+listen on both of those addresses.
+
+The D-Bus daemon will serve as bus master as well since it will be the
+process that creates and listens on the AF_BUS socket.
+
+D-Bus clients will use the fixed bus master address (all zero bits) to
+send messages to the D-Bus daemon and the client's unique address to
+send messages to other D-Bus clients using the bus.
+
+When initially connected, D-Bus clients will only be able to
+communicate with the D-Bus daemon and will send authentication
+information (AUTH message and SCM_CREDENTIALS ancillary
+messages). Since the D-Bus daemon is also the bus master, it can allow
+D-Bus clients to join the bus and be able to send and receive D-Bus
+messages from other peers.
+
+On connection, the kernel will assign to each client an address in the
+prefix 0x0000. If a client attempts to send messages to clients other
+than the bus master, this is considered to be an error, and is
+prevented by the kernel.
+
+When the D-Bus daemon has authenticated a client and determined that
+it is authorized to be on this bus, it uses a setsockopt(2) call to
+tell the kernel that this client has permission to send messages. The
+D-Bus daemon then tells the client by sending the Hello() reply that
+it has made the setsockopt(2) call and that now is able to send
+messages to other peers on the bus.
+
+Well-known names are represented by addresses in the 0x0001, ... prefixes.
+
+Addresses in prefix 0x0000 must be mapped to D-Bus unique names in a
+way that can't collide with unique names allocated by the dbus-daemon
+for legacy clients.
+
+In order to be consistent with current D-Bus unique naming, the AF_BUS
+addresses can be mapped directly to D-Bus unique names, for example
+(0000/0000deadbeef to ":0.deadbeef"). Leading zeroes can be suppressed
+since the common case should be relatively small numbers (the kernel
+allocates client addresses sequentially, and machines could be
+rebooted occasionally).
+
+By having both AF_BUS and legacy D-Bus clients use the same address
+space, the D-Bus daemon can act as a proxy between clients and can be
+sure that D-Bus unique names will be unique for both AF_BUS and legacy
+clients.
+
+To act as a proxy between AF_BUS and legacy clients, each time the
+D-Bus daemon accepts a legacy connection (i.e., AF_UNIX), it will
+create an AF_BUS socket and establish a connection with itself. It
+will then associate this newly created connection with the legacy one.
+
+To explain it graphically:
+
+ L The AF_BUS listening socket }
+ / | \ }-- listener process
+ A1 A2 A3 The AF_BUS accepted sockets }
+ | | |
+ C1 C2 C3 The AF_BUS connected sockets, where:
+ | * C1 belongs to the listener process
+ | * C2 and C3 belongs to the client processes
+ |
+ L2--A4 The AF_UNIX listening and accepted sockets \
+ | in the listener process
+ C4 The AF_UNIX connected socket in the legacy client process
+
+
+where C2 and C3 are normal AF_BUS clients and C4 is a legacy
+client. The D-Bus daemon after accepting the connection using the
+legacy transport (A4), will create an AF_BUS socket pair (C1, A1)
+associated with the legacy client.
+
+Legacy clients will send messages to the D-Bus daemon using their
+legacy socket and the D-Bus daemon will extract the destination
+address, resolve to the corresponding AF_BUS address and use this to
+send the message to the right peer.
+
+Conversely, when an AF_BUS client sends a D-Bus message to a legacy
+client, it will use the AF_BUS address of the connection associated
+with that client. The D-Bus daemon will receive the message, modify
+the message's content to set SENDER headers based on the AF_BUS source
+address and use the legacy transport to send the D-Bus message to the
+legacy client.
+
+As a special case, the bus daemon's all-zeroes address maps to
+"org.freedesktop.DBus" and vice versa.
+
+When a D-Bus client receives an AF_BUS message from the bus master
+(0/0), it must use the SENDER header field in the D-Bus message, as
+for any other D-Bus transport, to determine whether the message is
+actually from the D-Bus daemon (the SENDER is "org.freedesktop.DBus"
+or missing), or from another client (the SENDER starts with ":"). It
+is valid for messages from another AF_BUS client to be received via
+the D-Bus daemon; if they are, the SENDER header field will always be
+set.
+
+Besides its unique name, D-Bus services can have well-known names such
+as org.gnome.Keyring or org.freedesktop.Telepathy. These well-known
+names can also be used as a D-Bus message destination
+address. Well-known names are not numeric and AF_BUS is not able to
+parse D-Bus messages.
+
+To solve this, the D-Bus daemon will assign an additional AF_BUS
+address to each D-Bus client that owns a well-known name. The mapping
+between well-known names and AF_BUS address is maintained by the D-Bus
+daemon on a persistent data structure.
+
+D-Bus client libraries will maintain a cache of these mappings so they
+can send messages to services with well-known names using their mapped
+AF_BUS address.
+
+If a client intending to send a D-Bus message to a given well-known
+name does not have that well-known name in its cache, it must send the
+AF_BUS message to the listener (0000/000000000000) instead.
+
+The listener must forward the D-Bus message to the owner of that
+well-known name, setting the SENDER header field if necessary. It may
+also send this AF_BUS-specific D-Bus signal to the sender, so that the
+sender can update its cache:
+
+ org.freedesktop.DBus.AF_BUS.Forwarded (STRING well_known_name,
+ UINT64 af_bus_client)
+
+ Emitted by the D-Bus daemon with sender "org.freedesktop.DBus"
+ and object path "/org/freedesktop/DBus" to indicate that
+ the well-known name well_known_name is represented by the
+ AF_BUS address { AF_BUS, af_bus_client, path } where
+ path is the path name used by this bus.
+
+ For instance, if the well-known name "org.gnome.Keyring"
+ is represented by AF_BUS address 0001/0000deadbeef,
+ the signal would have arguments ("org.gnome.Keyring",
+ 0x00010000deadbeef), corresponding to the AF_BUS
+ address { AF_BUS, 0x00010000deadbeef, /tmp/test }.
+
+If the D-Bus service for that well-known name is not active, then the
+D-Bus daemon will first do the service activation, assign an
+additional address to the recently activated service, store the
+well-known service to numeric address mapping on its persistent cache,
+and then send the AF_BUS.Forwarded signal back to the client.
+
+Once the mapping has been made, the AF_BUS address associated with a
+well-known name cannot be reused for the lifetime of the D-Bus daemon
+(which is the same as the lifetime of the socket).
+
+Nevertheless the AF_BUS address associated with a well-known name can
+change, for example if a service goes away and a new instance gets
+activated. This new instance can have a different AF_BUS address. The
+D-Bus daemon will maintain a list of the mappings that are currently
+valid so it can send the AF_BUS.
+
+Forwarded signal with the mapping information to the clients. Client
+libraries will maintain a fixed-size Last Recently Used (LRU) cache
+with previous mappings sent by the D-Bus daemon.
+
+If the clients overwrite a mapping due to the LRU replace policy and
+later want to send a D-Bus message to the overwritten well-known name,
+they will send the D-Bus message back to the D-Bus daemon and this
+will send the signal with the mapping information.
+
+If a service goes away or if the service AF_BUS address changed and
+the client still has the old AF_BUS address in its cache, it will send
+the D-Bus message to the old destination.
+
+Since packets whose destination AF_BUS addresses are not assigned to
+any process are routed by default to the bus master, the D-Bus daemon
+will receive these D-bus messages and send an AF_BUS.
+
+Forwarded signal back to the client with the new AF_BUS address so it
+can update its cache with the new mapping.
+
+For well-known names, the D-Bus daemon will use a different address
+prefix (0x0001) so it doesn't conflict with the D-Bus unique names
+address prefix (0x0000).
+
+Besides D-Bus method call messages which are unicast, D-Bus allows
+clients to send multicast messages (D-Bus signals). Clients can send
+signals messages using the bus unique name prefix multicast address
+(0x0001ffffffffffff).
+
+A netfilter hook is used to filter these multicast messages and only
+deliver to the correct peers based on match rules.
+
+
+D-Bus aware netfilter module
+----------------------------
+
+AF_BUS is designed to be a generic bus transport supporting both
+unicast and multicast communications.
+
+In order for D-Bus to operate efficiently, the transport method has to
+know the D-Bus message wire-protocol and D-Bus message structure. But
+adding this D-Bus specific knowledge to AF_BUS will break one of the
+fundamental design principles of any network protocol stack, namely
+layer-independence: layer n must not make any assumptions about the
+payload in layer n + 1.
+
+So, in order to have a clean protocol design but be able to allow the
+transport to analyze the D-Bus messages, netfilter hooks are used to
+do the filtering based on match rules.
+
+The kernel module has to maintain the match rules and the D-Bus daemon
+is responsible for managing this information. Every time an add match
+rule message is processed by the D-Bus daemon, this will update the
+netfilter module match rules set so the netfilter hook function can
+use that information to do the match rules based filtering.
+
+The D-Bus daemon and the netfilter module will use the generic netlink
+subsystem to do the kernel-to-user-space communication. Netlink is
+already used by most of the networking subsystem in Linux
+(iptables/netfilter, ip/routing, etc).
+
+We enforce a security scheme so only the bus master's user ID can
+update the netfilter module match rules set.
+
+The advantage of using the netfilter subsystem is that we decouple the
+mechanism from the policy. AF_BUS will only add a set of hook points
+and external modules will be used to enforce a given policy.
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 82fa4f0f91d6..c96678aea2fa 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -118,6 +118,38 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
EXPORT_SYMBOL_GPL(cn_netlink_send);
/*
+ * Send an unicast reply from a connector callback
+ *
+ */
+int cn_netlink_reply(struct cn_msg *msg, u32 pid, gfp_t gfp_mask)
+{
+ unsigned int size;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ struct cn_msg *data;
+ struct cn_dev *dev = &cdev;
+
+ size = NLMSG_SPACE(sizeof(*msg) + msg->len);
+
+ skb = alloc_skb(size, gfp_mask);
+ if (!skb)
+ return -ENOMEM;
+
+ nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
+ if (nlh == NULL) {
+ kfree_skb(skb);
+ return -EMSGSIZE;
+ }
+
+ data = nlmsg_data(nlh);
+
+ memcpy(data, msg, sizeof(*data) + msg->len);
+
+ return netlink_unicast(dev->nls, skb, pid, 1);
+}
+EXPORT_SYMBOL_GPL(cn_netlink_reply);
+
+/*
* Callback helper - queues work and setup destructor for given data.
*/
static int cn_call_callback(struct sk_buff *skb)
diff --git a/include/linux/bus.h b/include/linux/bus.h
new file mode 100644
index 000000000000..19cac36727e1
--- /dev/null
+++ b/include/linux/bus.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_BUS_H
+#define _LINUX_BUS_H
+
+#include <linux/socket.h>
+
+/* 'protocol' to use in socket(AF_BUS, SOCK_SEQPACKET, protocol) */
+#define BUS_PROTO_NONE 0
+#define BUS_PROTO_DBUS 1
+#define BUS_PROTO_MAX 1
+
+#define BUS_PATH_MAX 108
+
+/**
+ * struct bus_addr - af_bus address
+ * @s_addr: an af_bus address (16-bit prefix + 48-bit client address)
+ */
+struct bus_addr {
+ u64 s_addr;
+};
+
+
+/**
+ * struct sockaddr_bus - af_bus socket address
+ * @sbus_family: the socket address family
+ * @sbus_addr: an af_bus address
+ * @sbus_path: a path name
+ */
+struct sockaddr_bus {
+ __kernel_sa_family_t sbus_family;
+ struct bus_addr sbus_addr;
+ char sbus_path[BUS_PATH_MAX];
+};
+
+#endif /* _LINUX_BUS_H */
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 76384074262d..c27be606b5f0 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -125,6 +125,7 @@ int cn_add_callback(struct cb_id *id, const char *name,
void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
void cn_del_callback(struct cb_id *);
int cn_netlink_send(struct cn_msg *, u32, gfp_t);
+int cn_netlink_reply(struct cn_msg *, u32, gfp_t);
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
struct cb_id *id,
diff --git a/include/linux/security.h b/include/linux/security.h
index d143b8e01954..ca961124159c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1577,6 +1577,8 @@ struct security_operations {
#ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct sock *sock, struct sock *other, struct sock *newsk);
+ int (*bus_connect) (struct sock *sock, struct sock *other,
+ struct sock *newsk);
int (*unix_may_send) (struct socket *sock, struct socket *other);
int (*socket_create) (int family, int type, int protocol, int kern);
@@ -2518,6 +2520,8 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
#ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk);
+int security_bus_connect(struct sock *sock, struct sock *other,
+ struct sock *newsk);
int security_unix_may_send(struct socket *sock, struct socket *other);
int security_socket_create(int family, int type, int protocol, int kern);
int security_socket_post_create(struct socket *sock, int family,
@@ -2565,6 +2569,13 @@ static inline int security_unix_stream_connect(struct sock *sock,
return 0;
}
+static inline int security_bus_connect(struct socket *sock,
+ struct sock *other,
+ struct sock *newsk)
+{
+ return 0;
+}
+
static inline int security_unix_may_send(struct socket *sock,
struct socket *other)
{
diff --git a/include/linux/socket.h b/include/linux/socket.h
index ba7b2e817cfa..7d6d2a4e2670 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -195,7 +195,8 @@ struct ucred {
#define AF_CAIF 37 /* CAIF sockets */
#define AF_ALG 38 /* Algorithm sockets */
#define AF_NFC 39 /* NFC sockets */
-#define AF_MAX 40 /* For now.. */
+#define AF_BUS 40 /* BUS sockets */
+#define AF_MAX 41 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -238,6 +239,7 @@ struct ucred {
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
#define PF_NFC AF_NFC
+#define PF_BUS AF_BUS
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -313,6 +315,7 @@ struct ucred {
#define SOL_IUCV 277
#define SOL_CAIF 278
#define SOL_ALG 279
+#define SOL_BUS 280
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/net/af_bus.h b/include/net/af_bus.h
new file mode 100644
index 000000000000..e63eb49b6afb
--- /dev/null
+++ b/include/net/af_bus.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2012, GENIVI Alliance
+ *
+ * Authors: Javier Martinez Canillas, <javier.martinez@collabora.co.uk>
+ * Alban Crequy, <alban.crequy@collabora.co.uk>
+ *
+ * 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).
+ */
+
+#ifndef __LINUX_NET_AFBUS_H
+#define __LINUX_NET_AFBUS_H
+
+#include <linux/socket.h>
+#include <linux/bus.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+
+extern void bus_inflight(struct file *fp);
+extern void bus_notinflight(struct file *fp);
+extern void bus_gc(void);
+extern void wait_for_bus_gc(void);
+extern struct sock *bus_get_socket(struct file *filp);
+extern struct sock *bus_peer_get(struct sock *);
+
+#define BUS_HASH_SIZE 256
+
+extern spinlock_t bus_address_lock;
+extern struct hlist_head bus_address_table[BUS_HASH_SIZE];
+
+#define BUS_MAX_QLEN 10
+#define BUS_MASTER_ADDR 0x0
+#define BUS_PREFIX_BITS 16
+#define BUS_CLIENT_BITS 48
+#define BUS_PREFIX_MASK 0xffff000000000000
+#define BUS_CLIENT_MASK 0x0000ffffffffffff
+
+/* AF_BUS socket options */
+#define BUS_ADD_ADDR 1
+#define BUS_JOIN_BUS 2
+#define BUS_DEL_ADDR 3
+#define BUS_SET_EAVESDROP 4
+#define BUS_UNSET_EAVESDROP 5
+#define BUS_SET_SENDBUF 6
+#define BUS_SET_MAXQLEN 7
+#define BUS_GET_QLENFULL 8
+
+/* Connection and socket states */
+enum {
+ BUS_ESTABLISHED = TCP_ESTABLISHED,
+ BUS_CLOSE = TCP_CLOSE,
+ BUS_LISTEN = TCP_LISTEN,
+ BUS_MAX_STATES
+};
+
+#define NF_BUS_SENDING 1
+
+extern unsigned int bus_tot_inflight;
+extern spinlock_t bus_table_lock;
+extern struct hlist_head bus_socket_table[BUS_HASH_SIZE + 1];
+
+/**
+ * struct bus_address - an af_bus address associated with an af_bus sock
+ * @refcnt: address reference counter
+ * @len: address length
+ * @hash: address hash value
+ * @addr_node: member of struct bus_sock.addr_list
+ * @table_node: member of struct hlist_head bus_address_table[hash]
+ * @sock: the af_bus sock that owns this address
+ * @name: the socket address for this address
+ */
+struct bus_address {
+ atomic_t refcnt;
+ int len;
+ unsigned hash;
+ struct hlist_node addr_node;
+ struct hlist_node table_node;
+ struct sock *sock;
+ struct sockaddr_bus name[0];
+};
+
+/**
+ * struct bus_send_context - sending context for an socket buffer
+ * @sender_socket: the sender socket associated with this sk_buff
+ * @siocb: used to send ancillary data
+ * @timeo: sending timeout
+ * @max_level: file descriptor passing maximum recursion level
+ * @namelen: length of socket address name
+ * @hash: socket name hash value
+ * @other: destination sock
+ * @sender: sender socket address name
+ * @recipient: recipient socket address name
+ * @authenticated: flag whether the sock already joined the bus
+ * @bus_master_side: flag whether the sock is an accepted socket
+ * @to_master: flag whether the destination is the bus master
+ * @multicast: flag whether the destination is a multicast address
+ * @deliver: flag whether the skb has to be delivered
+ * @eavesdropper: flag whether the sock is allowed to eavesdrop
+ * @main_recipient: flag whether the sock is the main recipient
+ */
+struct bus_send_context {
+ struct socket *sender_socket;
+ struct sock_iocb *siocb;
+ long timeo;
+ int max_level;
+ int namelen;
+ unsigned hash;
+ struct sock *other;
+ struct sockaddr_bus *sender;
+ struct sockaddr_bus *recipient;
+ unsigned int authenticated:1;
+ unsigned int bus_master_side:1;
+ unsigned int to_master:1;
+ unsigned int multicast:1;
+ unsigned int deliver:1;
+ unsigned int eavesdropper:1;
+ unsigned int main_recipient:1;
+};
+
+/**
+ * struct bus_skb_parms - socket buffer parameters
+ * @pid: process id
+ * @cred: skb credentials
+ * @fp: passed file descriptors
+ * @secid: security id
+ * @sendctx: skb sending context
+ */
+struct bus_skb_parms {
+ struct pid *pid;
+ const struct cred *cred;
+ struct scm_fp_list *fp;
+#ifdef CONFIG_SECURITY_NETWORK
+ u32 secid;
+#endif
+ struct bus_send_context *sendctx;
+};
+
+#define BUSCB(skb) (*(struct bus_skb_parms *)&((skb)->cb))
+#define BUSSID(skb) (&BUSCB((skb)).secid)
+
+#define bus_state_lock(s) spin_lock(&bus_sk(s)->lock)
+#define bus_state_unlock(s) spin_unlock(&bus_sk(s)->lock)
+#define bus_state_lock_nested(s) \
+ spin_lock_nested(&bus_sk(s)->lock, \
+ SINGLE_DEPTH_NESTING)
+
+/**
+ * struct bus - a communication bus
+ * @master: the bus master sock
+ * @peers: list of struct bus_sock.bus_node allowed to join the bus
+ * @lock: protect peers concurrent access
+ * @send_lock: enforce atomic multicast delivery
+ * @kref: bus reference counter
+ * @addr_cnt: address number counter to assign prefix 0x0000 addresses
+ * @eavesdropper_cnt: eavesdroppers counter
+ */
+struct bus {
+ struct sock *master;
+ struct hlist_head peers;
+ spinlock_t lock;
+ spinlock_t send_lock;
+ struct kref kref;
+ atomic64_t addr_cnt;
+ atomic64_t eavesdropper_cnt;
+};
+
+/**
+ * struct bus_sock - an af_bus socket
+ * @sk: associated sock
+ * @addr: sock principal address
+ * @addr_list: list of struct bus_address.addr_node
+ * @path: sock path name
+ * @readlock: protect from concurrent reading
+ * @peer: peer sock
+ * @other: the listening sock
+ * @link: list of candidates for garbage collection
+ * @inflight: number of times the file descriptor is in flight
+ * @lock: protect the sock from concurrent access
+ * @gc_candidate: flag whether the is a candidate for gc
+ * @gc_maybe_cycle: flag whether could be a cyclic reference
+ * @recursion_level: file passing current recursion level
+ * @peer_wq: peer sock wait queue
+ * @bus: bus that this sock belongs to
+ * @bus_master: flag whether the sock is the bus master
+ * @bus_master_side: flag whether is an accepted socket
+ * @authenticated: flag whether the sock joined the bus
+ * @eavesdropper: flag whether the sock is allowed to eavesdrop
+ * @bus_node: member of struct bus.peers list of joined socks
+ */
+struct bus_sock {
+ /* WARNING: sk has to be the first member */
+ struct sock sk;
+ struct bus_address *addr;
+ struct hlist_head addr_list;
+ struct path path;
+ struct mutex readlock;
+ struct sock *peer;
+ struct sock *other;
+ struct list_head link;
+ atomic_long_t inflight;
+ spinlock_t lock;
+ unsigned int gc_candidate:1;
+ unsigned int gc_maybe_cycle:1;
+ unsigned char recursion_level;
+ struct socket_wq peer_wq;
+ struct bus *bus;
+ bool bus_master;
+ bool bus_master_side;
+ bool authenticated;
+ bool eavesdropper;
+ struct hlist_node bus_node;
+};
+#define bus_sk(__sk) ((struct bus_sock *)__sk)
+
+#define peer_wait peer_wq.wait
+
+/**
+ * bus_same_bus - Test if two socket address belongs to the same bus
+ * @sbusaddr1: socket address name
+ * @sbusaddr2: socket address name
+ */
+static inline bool bus_same_bus(struct sockaddr_bus *sbusaddr1,
+ struct sockaddr_bus *sbusaddr2)
+{
+ int offset;
+
+ if (sbusaddr1->sbus_path[0] != sbusaddr2->sbus_path[0])
+ return false;
+
+ /*
+ * abstract path names start with a null byte character,
+ * so they have to be compared starting at the second char.
+ */
+ offset = (sbusaddr1->sbus_path[0] == '\0');
+
+ return !strncmp(sbusaddr1->sbus_path + offset,
+ sbusaddr2->sbus_path + offset,
+ BUS_PATH_MAX);
+}
+
+static inline unsigned int bus_hash_fold(__wsum n)
+{
+ unsigned int hash = (__force unsigned int)n;
+ hash ^= hash>>16;
+ hash ^= hash>>8;
+ return hash&(BUS_HASH_SIZE-1);
+}
+
+static inline unsigned int bus_compute_hash(struct bus_addr addr)
+{
+ return bus_hash_fold(csum_partial((void *)&addr, sizeof(addr), 0));
+}
+
+long bus_inq_len(struct sock *sk);
+long bus_outq_len(struct sock *sk);
+
+#ifdef CONFIG_SYSCTL
+extern int bus_sysctl_register(struct net *net);
+extern void bus_sysctl_unregister(struct net *net);
+#else
+static inline int bus_sysctl_register(struct net *net) { return 0; }
+static inline void bus_sysctl_unregister(struct net *net) {}
+#endif
+
+bool bus_can_write(struct net *net, struct sockaddr_bus *addr, int len,
+ int protocol);
+
+#endif /* __LINUX_NET_AFBUS_H */
diff --git a/net/Kconfig b/net/Kconfig
index 245831bec09a..339a6309d87e 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -47,6 +47,7 @@ menu "Networking options"
source "net/packet/Kconfig"
source "net/unix/Kconfig"
+source "net/bus/Kconfig"
source "net/xfrm/Kconfig"
source "net/iucv/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 4f4ee083064c..ad0e90099fb6 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_INET) += ipv4/
obj-$(CONFIG_XFRM) += xfrm/
obj-$(CONFIG_UNIX) += unix/
+obj-$(CONFIG_AF_BUS) += bus/
obj-$(CONFIG_NET) += ipv6/
obj-$(CONFIG_PACKET) += packet/
obj-$(CONFIG_NET_KEY) += key/
diff --git a/net/bus/Kconfig b/net/bus/Kconfig
new file mode 100644
index 000000000000..4ec0beec30ea
--- /dev/null
+++ b/net/bus/Kconfig
@@ -0,0 +1,17 @@
+#
+# Bus Domain Sockets
+#
+
+config AF_BUS
+ tristate "Bus domain sockets (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ ---help---
+ If you say Y here, you will include support for Bus domain sockets.
+ These sockets are used to create communication buses for IPC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called bus.
+
+ Say N unless you know what you are doing.
+
+source "net/bus/nfdbus/Kconfig"
diff --git a/net/bus/Makefile b/net/bus/Makefile
new file mode 100644
index 000000000000..a6ba26331ce2
--- /dev/null
+++ b/net/bus/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux bus domain socket layer.
+#
+
+obj-$(CONFIG_AF_BUS) += af-bus.o
+
+af-bus-y := af_bus.o garbage.o
+
+# Dbus
+obj-$(CONFIG_NETFILTER_DBUS) += nfdbus/
diff --git a/net/bus/af_bus.c b/net/bus/af_bus.c
new file mode 100644
index 000000000000..d25af65d79a0
--- /dev/null
+++ b/net/bus/af_bus.c
@@ -0,0 +1,2688 @@
+/*
+ * Implementation of Bus domain sockets.
+ *
+ * Copyright (c) 2012, GENIVI Alliance
+ *
+ * Authors: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
+ * Alban Crequy <alban.crequy@collabora.co.uk>
+ *
+ * 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;
+}
+
+static inline void bus_copy_path(struct sockaddr_bus *dest,
+ struct sockaddr_bus *src)
+{
+ int offset;
+
+ /*
+ * abstract path names start with a null byte character,
+ * so they have to be compared starting at the second char.
+ */
+ offset = (src->sbus_path[0] == '\0');
+
+ strncpy(dest->sbus_path + offset,
+ src->sbus_path + offset,
+ BUS_PATH_MAX);
+}
+
+/**
+ * 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_copy_path(sbusaddr, u->addr->name);
+
+ 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, false);
+ 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_get_qlenfull(struct sock *sk)
+{
+ struct sock *peer;
+ struct bus_sock *u = bus_sk(sk), *peeru;
+ int ret = 0;
+
+ peer = bus_peer_get(sk);
+ if (!peer)
+ return -ENOTCONN;
+
+ peeru = bus_sk(peer);
+
+ if (!u->bus_master_side || peeru->authenticated) {
+ ret = -EINVAL;
+ goto sock_put_out;
+ }
+
+ if (sk->sk_state != BUS_ESTABLISHED) {
+ ret = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ if (peer->sk_shutdown != 0) {
+ ret = -ENOTCONN;
+ goto sock_put_out;
+ }
+
+ ret = bus_recvq_full(peer);
+
+sock_put_out:
+ sock_put(peer);
+ return ret;
+}
+
+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;
+ case BUS_GET_QLENFULL:
+ res = bus_get_qlenfull(sock->sk);
+
+ if (copy_to_user(&res, optval, optlen)) {
+ res = -EFAULT;
+ break;
+ }
+ res = 0;
+ 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);
diff --git a/net/bus/garbage.c b/net/bus/garbage.c
new file mode 100644
index 000000000000..2435f38a0849
--- /dev/null
+++ b/net/bus/garbage.c
@@ -0,0 +1,322 @@
+/*
+ * Garbage Collector For AF_BUS sockets
+ *
+ * Based on Garbage Collector For AF_UNIX sockets (net/unix/garbage.c).
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/un.h>
+#include <linux/net.h>
+#include <linux/fs.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+#include <net/sock.h>
+#include <net/af_bus.h>
+#include <net/scm.h>
+#include <net/tcp_states.h>
+
+/* Internal data structures and random procedures: */
+
+static LIST_HEAD(gc_inflight_list);
+static LIST_HEAD(gc_candidates);
+static DEFINE_SPINLOCK(bus_gc_lock);
+static DECLARE_WAIT_QUEUE_HEAD(bus_gc_wait);
+
+unsigned int bus_tot_inflight;
+
+
+struct sock *bus_get_socket(struct file *filp)
+{
+ struct sock *u_sock = NULL;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+
+ /*
+ * Socket ?
+ */
+ if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
+ struct socket *sock = SOCKET_I(inode);
+ struct sock *s = sock->sk;
+
+ /*
+ * PF_BUS ?
+ */
+ if (s && sock->ops && sock->ops->family == PF_BUS)
+ u_sock = s;
+ }
+ return u_sock;
+}
+
+/*
+ * Keep the number of times in flight count for the file
+ * descriptor if it is for an AF_BUS socket.
+ */
+
+void bus_inflight(struct file *fp)
+{
+ struct sock *s = bus_get_socket(fp);
+ if (s) {
+ struct bus_sock *u = bus_sk(s);
+ spin_lock(&bus_gc_lock);
+ if (atomic_long_inc_return(&u->inflight) == 1) {
+ BUG_ON(!list_empty(&u->link));
+ list_add_tail(&u->link, &gc_inflight_list);
+ } else {
+ BUG_ON(list_empty(&u->link));
+ }
+ bus_tot_inflight++;
+ spin_unlock(&bus_gc_lock);
+ }
+}
+
+void bus_notinflight(struct file *fp)
+{
+ struct sock *s = bus_get_socket(fp);
+ if (s) {
+ struct bus_sock *u = bus_sk(s);
+ spin_lock(&bus_gc_lock);
+ BUG_ON(list_empty(&u->link));
+ if (atomic_long_dec_and_test(&u->inflight))
+ list_del_init(&u->link);
+ bus_tot_inflight--;
+ spin_unlock(&bus_gc_lock);
+ }
+}
+
+static void scan_inflight(struct sock *x, void (*func)(struct bus_sock *),
+ struct sk_buff_head *hitlist)
+{
+ struct sk_buff *skb;
+ struct sk_buff *next;
+
+ spin_lock(&x->sk_receive_queue.lock);
+ skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
+ /*
+ * Do we have file descriptors ?
+ */
+ if (BUSCB(skb).fp) {
+ bool hit = false;
+ /*
+ * Process the descriptors of this socket
+ */
+ int nfd = BUSCB(skb).fp->count;
+ struct file **fp = BUSCB(skb).fp->fp;
+ while (nfd--) {
+ /*
+ * Get the socket the fd matches
+ * if it indeed does so
+ */
+ struct sock *sk = bus_get_socket(*fp++);
+ if (sk) {
+ struct bus_sock *u = bus_sk(sk);
+
+ /*
+ * Ignore non-candidates, they could
+ * have been added to the queues after
+ * starting the garbage collection
+ */
+ if (u->gc_candidate) {
+ hit = true;
+ func(u);
+ }
+ }
+ }
+ if (hit && hitlist != NULL) {
+ __skb_unlink(skb, &x->sk_receive_queue);
+ __skb_queue_tail(hitlist, skb);
+ }
+ }
+ }
+ spin_unlock(&x->sk_receive_queue.lock);
+}
+
+static void scan_children(struct sock *x, void (*func)(struct bus_sock *),
+ struct sk_buff_head *hitlist)
+{
+ if (x->sk_state != TCP_LISTEN)
+ scan_inflight(x, func, hitlist);
+ else {
+ struct sk_buff *skb;
+ struct sk_buff *next;
+ struct bus_sock *u;
+ LIST_HEAD(embryos);
+
+ /*
+ * For a listening socket collect the queued embryos
+ * and perform a scan on them as well.
+ */
+ spin_lock(&x->sk_receive_queue.lock);
+ skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
+ u = bus_sk(skb->sk);
+
+ /*
+ * An embryo cannot be in-flight, so it's safe
+ * to use the list link.
+ */
+ BUG_ON(!list_empty(&u->link));
+ list_add_tail(&u->link, &embryos);
+ }
+ spin_unlock(&x->sk_receive_queue.lock);
+
+ while (!list_empty(&embryos)) {
+ u = list_entry(embryos.next, struct bus_sock, link);
+ scan_inflight(&u->sk, func, hitlist);
+ list_del_init(&u->link);
+ }
+ }
+}
+
+static void dec_inflight(struct bus_sock *usk)
+{
+ atomic_long_dec(&usk->inflight);
+}
+
+static void inc_inflight(struct bus_sock *usk)
+{
+ atomic_long_inc(&usk->inflight);
+}
+
+static void inc_inflight_move_tail(struct bus_sock *u)
+{
+ atomic_long_inc(&u->inflight);
+ /*
+ * If this still might be part of a cycle, move it to the end
+ * of the list, so that it's checked even if it was already
+ * passed over
+ */
+ if (u->gc_maybe_cycle)
+ list_move_tail(&u->link, &gc_candidates);
+}
+
+static bool gc_in_progress = false;
+#define BUS_INFLIGHT_TRIGGER_GC 16000
+
+void wait_for_bus_gc(void)
+{
+ /*
+ * If number of inflight sockets is insane,
+ * force a garbage collect right now.
+ */
+ if (bus_tot_inflight > BUS_INFLIGHT_TRIGGER_GC && !gc_in_progress)
+ bus_gc();
+ wait_event(bus_gc_wait, gc_in_progress == false);
+}
+
+/* The external entry point: bus_gc() */
+void bus_gc(void)
+{
+ struct bus_sock *u;
+ struct bus_sock *next;
+ struct sk_buff_head hitlist;
+ struct list_head cursor;
+ LIST_HEAD(not_cycle_list);
+
+ spin_lock(&bus_gc_lock);
+
+ /* Avoid a recursive GC. */
+ if (gc_in_progress)
+ goto out;
+
+ gc_in_progress = true;
+ /*
+ * First, select candidates for garbage collection. Only
+ * in-flight sockets are considered, and from those only ones
+ * which don't have any external reference.
+ *
+ * Holding bus_gc_lock will protect these candidates from
+ * being detached, and hence from gaining an external
+ * reference. Since there are no possible receivers, all
+ * buffers currently on the candidates' queues stay there
+ * during the garbage collection.
+ *
+ * We also know that no new candidate can be added onto the
+ * receive queues. Other, non candidate sockets _can_ be
+ * added to queue, so we must make sure only to touch
+ * candidates.
+ */
+ list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
+ long total_refs;
+ long inflight_refs;
+
+ total_refs = file_count(u->sk.sk_socket->file);
+ inflight_refs = atomic_long_read(&u->inflight);
+
+ BUG_ON(inflight_refs < 1);
+ BUG_ON(total_refs < inflight_refs);
+ if (total_refs == inflight_refs) {
+ list_move_tail(&u->link, &gc_candidates);
+ u->gc_candidate = 1;
+ u->gc_maybe_cycle = 1;
+ }
+ }
+
+ /*
+ * Now remove all internal in-flight reference to children of
+ * the candidates.
+ */
+ list_for_each_entry(u, &gc_candidates, link)
+ scan_children(&u->sk, dec_inflight, NULL);
+
+ /*
+ * Restore the references for children of all candidates,
+ * which have remaining references. Do this recursively, so
+ * only those remain, which form cyclic references.
+ *
+ * Use a "cursor" link, to make the list traversal safe, even
+ * though elements might be moved about.
+ */
+ list_add(&cursor, &gc_candidates);
+ while (cursor.next != &gc_candidates) {
+ u = list_entry(cursor.next, struct bus_sock, link);
+
+ /* Move cursor to after the current position. */
+ list_move(&cursor, &u->link);
+
+ if (atomic_long_read(&u->inflight) > 0) {
+ list_move_tail(&u->link, &not_cycle_list);
+ u->gc_maybe_cycle = 0;
+ scan_children(&u->sk, inc_inflight_move_tail, NULL);
+ }
+ }
+ list_del(&cursor);
+
+ /*
+ * not_cycle_list contains those sockets which do not make up a
+ * cycle. Restore these to the inflight list.
+ */
+ while (!list_empty(&not_cycle_list)) {
+ u = list_entry(not_cycle_list.next, struct bus_sock, link);
+ u->gc_candidate = 0;
+ list_move_tail(&u->link, &gc_inflight_list);
+ }
+
+ /*
+ * Now gc_candidates contains only garbage. Restore original
+ * inflight counters for these as well, and remove the skbuffs
+ * which are creating the cycle(s).
+ */
+ skb_queue_head_init(&hitlist);
+ list_for_each_entry(u, &gc_candidates, link)
+ scan_children(&u->sk, inc_inflight, &hitlist);
+
+ spin_unlock(&bus_gc_lock);
+
+ /* Here we are. Hitlist is filled. Die. */
+ __skb_queue_purge(&hitlist);
+
+ spin_lock(&bus_gc_lock);
+
+ /* All candidates should have been detached by now. */
+ BUG_ON(!list_empty(&gc_candidates));
+ gc_in_progress = false;
+ wake_up(&bus_gc_wait);
+
+ out:
+ spin_unlock(&bus_gc_lock);
+}
diff --git a/net/bus/nfdbus/Kconfig b/net/bus/nfdbus/Kconfig
new file mode 100644
index 000000000000..25699a12e845
--- /dev/null
+++ b/net/bus/nfdbus/Kconfig
@@ -0,0 +1,12 @@
+#
+# Netfilter D-Bus module configuration
+#
+config NETFILTER_DBUS
+ tristate "Netfilter D-bus (EXPERIMENTAL)"
+ depends on AF_BUS && CONNECTOR && EXPERIMENTAL
+ ---help---
+ If you say Y here, you will include support for a netfilter hook to
+ parse D-Bus messages sent using the AF_BUS socket address family.
+
+ To compile this as a module, choose M here: the module will be
+ called netfilter_dbus.
diff --git a/net/bus/nfdbus/Makefile b/net/bus/nfdbus/Makefile
new file mode 100644
index 000000000000..1a825f83f9c9
--- /dev/null
+++ b/net/bus/nfdbus/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the netfilter D-Bus module
+#
+obj-$(CONFIG_NETFILTER_DBUS) += netfilter_dbus.o
+
+netfilter_dbus-y := nfdbus.o message.o matchrule.o
diff --git a/net/bus/nfdbus/matchrule.c b/net/bus/nfdbus/matchrule.c
new file mode 100644
index 000000000000..4106bd5deed2
--- /dev/null
+++ b/net/bus/nfdbus/matchrule.c
@@ -0,0 +1,1132 @@
+/*
+ * matchrule.c D-Bus match rule implementation
+ *
+ * Based on signals.c from dbus
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ * Copyright (C) 2003, 2005 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "matchrule.h"
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "message.h"
+
+enum bus_match_flags {
+ BUS_MATCH_MESSAGE_TYPE = 1 << 0,
+ BUS_MATCH_INTERFACE = 1 << 1,
+ BUS_MATCH_MEMBER = 1 << 2,
+ BUS_MATCH_SENDER = 1 << 3,
+ BUS_MATCH_DESTINATION = 1 << 4,
+ BUS_MATCH_PATH = 1 << 5,
+ BUS_MATCH_ARGS = 1 << 6,
+ BUS_MATCH_PATH_NAMESPACE = 1 << 7,
+ BUS_MATCH_CLIENT_IS_EAVESDROPPING = 1 << 8
+};
+
+struct bus_match_rule {
+ /* For debugging only*/
+ char *rule_text;
+
+ unsigned int flags; /**< BusMatchFlags */
+
+ int message_type;
+ char *interface;
+ char *member;
+ char *sender;
+ char *destination;
+ char *path;
+
+ unsigned int *arg_lens;
+ char **args;
+ int args_len;
+
+ /* bus_match_rule is attached to rule_pool, either in a simple
+ * double-linked list if the rule does not have any interface, or in a
+ * red-black tree sorted by interface. If several rules can have the
+ * same interface, the first one is attached with struct rb_node and the
+ * next ones are in the list
+ */
+
+ struct rb_node node;
+ /* Doubly-linked non-circular list. If the rule has an interface, it is
+ * in the rb tree and the single head is right here. Otherwise, the
+ * single head is in rule_pool->rules_without_iface. With this data
+ * structure, we don't need any allocation to insert or remove the rule.
+ */
+ struct hlist_head first;
+ struct hlist_node list;
+
+ /* used to delete all names from the tree */
+ struct list_head del_list;
+};
+
+struct dbus_name {
+ struct rb_node node;
+ char *name;
+
+ /* used to delete all names from the tree */
+ struct list_head del_list;
+};
+
+#define BUS_MATCH_ARG_IS_PATH 0x8000000u
+
+#define DBUS_STRING_MAX_LENGTH 1024
+
+/** Max length of a match rule string; to keep people from hosing the
+ * daemon with some huge rule
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+struct bus_match_rule *bus_match_rule_new(gfp_t gfp_flags)
+{
+ struct bus_match_rule *rule;
+
+ rule = kzalloc(sizeof(struct bus_match_rule), gfp_flags);
+ if (rule == NULL)
+ return NULL;
+
+ return rule;
+}
+
+void bus_match_rule_free(struct bus_match_rule *rule)
+{
+ kfree(rule->rule_text);
+ kfree(rule->interface);
+ kfree(rule->member);
+ kfree(rule->sender);
+ kfree(rule->destination);
+ kfree(rule->path);
+ kfree(rule->arg_lens);
+
+ /* can't use dbus_free_string_array() since there
+ * are embedded NULL
+ */
+ if (rule->args) {
+ int i;
+
+ i = 0;
+ while (i < rule->args_len) {
+ kfree(rule->args[i]);
+ ++i;
+ }
+
+ kfree(rule->args);
+ }
+
+ kfree(rule);
+}
+
+static int
+bus_match_rule_set_message_type(struct bus_match_rule *rule,
+ int type,
+ gfp_t gfp_flags)
+{
+ rule->flags |= BUS_MATCH_MESSAGE_TYPE;
+
+ rule->message_type = type;
+
+ return 1;
+}
+
+static int
+bus_match_rule_set_interface(struct bus_match_rule *rule,
+ const char *interface,
+ gfp_t gfp_flags)
+{
+ char *new;
+
+ WARN_ON(!interface);
+
+ new = kstrdup(interface, gfp_flags);
+ if (new == NULL)
+ return 0;
+
+ rule->flags |= BUS_MATCH_INTERFACE;
+ kfree(rule->interface);
+ rule->interface = new;
+
+ return 1;
+}
+
+static int
+bus_match_rule_set_member(struct bus_match_rule *rule,
+ const char *member,
+ gfp_t gfp_flags)
+{
+ char *new;
+
+ WARN_ON(!member);
+
+ new = kstrdup(member, gfp_flags);
+ if (new == NULL)
+ return 0;
+
+ rule->flags |= BUS_MATCH_MEMBER;
+ kfree(rule->member);
+ rule->member = new;
+
+ return 1;
+}
+
+static int
+bus_match_rule_set_sender(struct bus_match_rule *rule,
+ const char *sender,
+ gfp_t gfp_flags)
+{
+ char *new;
+
+ WARN_ON(!sender);
+
+ new = kstrdup(sender, gfp_flags);
+ if (new == NULL)
+ return 0;
+
+ rule->flags |= BUS_MATCH_SENDER;
+ kfree(rule->sender);
+ rule->sender = new;
+
+ return 1;
+}
+
+static int
+bus_match_rule_set_destination(struct bus_match_rule *rule,
+ const char *destination,
+ gfp_t gfp_flags)
+{
+ char *new;
+
+ WARN_ON(!destination);
+
+ new = kstrdup(destination, gfp_flags);
+ if (new == NULL)
+ return 0;
+
+ rule->flags |= BUS_MATCH_DESTINATION;
+ kfree(rule->destination);
+ rule->destination = new;
+
+ return 1;
+}
+
+#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || \
+ ((c) == '\r'))
+
+static int find_key(const char *str, int start, char *key, int *value_pos)
+{
+ const char *p;
+ const char *s;
+ const char *key_start;
+ const char *key_end;
+
+ s = str;
+
+ p = s + start;
+
+ while (*p && ISWHITE(*p))
+ ++p;
+
+ key_start = p;
+
+ while (*p && *p != '=' && !ISWHITE(*p))
+ ++p;
+
+ key_end = p;
+
+ while (*p && ISWHITE(*p))
+ ++p;
+
+ if (key_start == key_end) {
+ /* Empty match rules or trailing whitespace are OK */
+ *value_pos = p - s;
+ return 1;
+ }
+
+ if (*p != '=') {
+ pr_warn("Match rule has a key with no subsequent '=' character");
+ return 0;
+ }
+ ++p;
+
+ strncat(key, key_start, key_end - key_start);
+
+ *value_pos = p - s;
+
+ return 1;
+}
+
+static int find_value(const char *str, int start, const char *key, char *value,
+ int *value_end)
+{
+ const char *p;
+ const char *s;
+ char quote_char;
+ int orig_len;
+
+ orig_len = strlen(value);
+
+ s = str;
+
+ p = s + start;
+
+ quote_char = '\0';
+
+ while (*p) {
+ if (quote_char == '\0') {
+ switch (*p) {
+ case '\0':
+ goto done;
+
+ case '\'':
+ quote_char = '\'';
+ goto next;
+
+ case ',':
+ ++p;
+ goto done;
+
+ case '\\':
+ quote_char = '\\';
+ goto next;
+
+ default:
+ strncat(value, p, 1);
+ }
+ } else if (quote_char == '\\') {
+ /*\ only counts as an escape if escaping a quote mark */
+ if (*p != '\'')
+ strncat(value, "\\", 1);
+
+ strncat(value, p, 1);
+
+ quote_char = '\0';
+ } else {
+ if (*p == '\'')
+ quote_char = '\0';
+ else
+ strncat(value, p, 1);
+ }
+
+next:
+ ++p;
+ }
+
+done:
+
+ if (quote_char == '\\')
+ strncat(value, "\\", 1);
+ else if (quote_char == '\'') {
+ pr_warn("Unbalanced quotation marks in match rule");
+ return 0;
+ }
+
+ /* Zero-length values are allowed */
+
+ *value_end = p - s;
+
+ return 1;
+}
+
+/* duplicates aren't allowed so the real legitimate max is only 6 or
+ * so. Leaving extra so we don't have to bother to update it.
+ * FIXME this is sort of busted now with arg matching, but we let
+ * you match on up to 10 args for now
+ */
+#define MAX_RULE_TOKENS 16
+
+/* this is slightly too high level to be termed a "token"
+ * but let's not be pedantic.
+ */
+struct rule_token {
+ char *key;
+ char *value;
+};
+
+static int tokenize_rule(const char *rule_text,
+ struct rule_token tokens[MAX_RULE_TOKENS],
+ gfp_t gfp_flags)
+{
+ int i;
+ int pos;
+ int retval;
+
+ retval = 0;
+
+ i = 0;
+ pos = 0;
+ while (i < MAX_RULE_TOKENS &&
+ pos < strlen(rule_text)) {
+ char *key;
+ char *value;
+
+ key = kzalloc(DBUS_STRING_MAX_LENGTH, gfp_flags);
+ if (!key) {
+ pr_err("Out of memory");
+ return 0;
+ }
+
+ value = kzalloc(DBUS_STRING_MAX_LENGTH, gfp_flags);
+ if (!value) {
+ kfree(key);
+ pr_err("Out of memory");
+ return 0;
+ }
+
+ if (!find_key(rule_text, pos, key, &pos))
+ goto out;
+
+ if (strlen(key) == 0)
+ goto next;
+
+ tokens[i].key = key;
+
+ if (!find_value(rule_text, pos, tokens[i].key, value, &pos))
+ goto out;
+
+ tokens[i].value = value;
+
+next:
+ ++i;
+ }
+
+ retval = 1;
+
+out:
+ if (!retval) {
+ i = 0;
+ while (tokens[i].key || tokens[i].value) {
+ kfree(tokens[i].key);
+ kfree(tokens[i].value);
+ tokens[i].key = NULL;
+ tokens[i].value = NULL;
+ ++i;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * The format is comma-separated with strings quoted with single quotes
+ * as for the shell (to escape a literal single quote, use '\'').
+ *
+ * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',
+ * member='Foo', path='/bar/foo',destination=':452345.34'
+ *
+ */
+struct bus_match_rule *bus_match_rule_parse(const char *rule_text,
+ gfp_t gfp_flags)
+{
+ struct bus_match_rule *rule;
+ struct rule_token tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
+ int i;
+
+ if (strlen(rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH) {
+ pr_warn("Match rule text is %ld bytes, maximum is %d",
+ strlen(rule_text),
+ DBUS_MAXIMUM_MATCH_RULE_LENGTH);
+ return NULL;
+ }
+
+ memset(tokens, '\0', sizeof(tokens));
+
+ rule = bus_match_rule_new(gfp_flags);
+ if (rule == NULL) {
+ pr_err("Out of memory");
+ goto failed;
+ }
+
+ rule->rule_text = kstrdup(rule_text, gfp_flags);
+ if (rule->rule_text == NULL) {
+ pr_err("Out of memory");
+ goto failed;
+ }
+
+ if (!tokenize_rule(rule_text, tokens, gfp_flags))
+ goto failed;
+
+ i = 0;
+ while (tokens[i].key != NULL) {
+ const char *key = tokens[i].key;
+ const char *value = tokens[i].value;
+
+ if (strcmp(key, "type") == 0) {
+ int t;
+
+ if (rule->flags & BUS_MATCH_MESSAGE_TYPE) {
+ pr_warn("Key %s specified twice in match rule\n",
+ key);
+ goto failed;
+ }
+
+ t = dbus_message_type_from_string(value);
+
+ if (t == DBUS_MESSAGE_TYPE_INVALID) {
+ pr_warn("Invalid message type (%s) in match rule\n",
+ value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_message_type(rule, t,
+ gfp_flags)) {
+ pr_err("Out of memeory");
+ goto failed;
+ }
+ } else if (strcmp(key, "sender") == 0) {
+ if (rule->flags & BUS_MATCH_SENDER) {
+ pr_warn("Key %s specified twice in match rule\n",
+ key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_sender(rule, value,
+ gfp_flags)) {
+ pr_err("Out of memeory");
+ goto failed;
+ }
+ } else if (strcmp(key, "interface") == 0) {
+ if (rule->flags & BUS_MATCH_INTERFACE) {
+ pr_warn("Key %s specified twice in match rule\n",
+ key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_interface(rule, value,
+ gfp_flags)) {
+ pr_err("Out of memeory");
+ goto failed;
+ }
+ } else if (strcmp(key, "member") == 0) {
+ if (rule->flags & BUS_MATCH_MEMBER) {
+ pr_warn("Key %s specified twice in match rule\n",
+ key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_member(rule, value,
+ gfp_flags)) {
+ pr_err("Out of memeory");
+ goto failed;
+ }
+ } else if (strcmp(key, "destination") == 0) {
+ if (rule->flags & BUS_MATCH_DESTINATION) {
+ pr_warn("Key %s specified twice in match rule\n",
+ key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_destination(rule, value,
+ gfp_flags)) {
+ pr_err("Out of memeory");
+ goto failed;
+ }
+ } else if (strcmp(key, "eavesdrop") == 0) {
+ if (strcmp(value, "true") == 0) {
+ rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+ } else if (strcmp(value, "false") == 0) {
+ rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+ } else {
+ pr_warn("eavesdrop='%s' is invalid, " \
+ "it should be 'true' or 'false'\n",
+ value);
+ goto failed;
+ }
+ } else if (strncmp(key, "arg", 3) != 0) {
+ pr_warn("Unknown key \"%s\" in match rule\n",
+ key);
+ goto failed;
+ }
+
+ ++i;
+ }
+
+ goto out;
+
+failed:
+ if (rule) {
+ bus_match_rule_free(rule);
+ rule = NULL;
+ }
+
+out:
+
+ i = 0;
+ while (tokens[i].key || tokens[i].value) {
+ WARN_ON(i >= MAX_RULE_TOKENS);
+ kfree(tokens[i].key);
+ kfree(tokens[i].value);
+ ++i;
+ }
+
+ return rule;
+}
+
+/* return the match rule containing the hlist_head. It may not be the first
+ * match rule in the list. */
+struct bus_match_rule *match_rule_search(struct rb_root *root,
+ const char *interface)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct bus_match_rule *data =
+ container_of(node, struct bus_match_rule, node);
+ int result;
+
+ result = strcmp(interface, data->interface);
+
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+void match_rule_insert(struct rb_root *root, struct bus_match_rule *data)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct bus_match_rule *this =
+ container_of(*new, struct bus_match_rule, node);
+ int result = strcmp(data->interface, this->interface);
+
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else {
+ /* the head is not used */
+ INIT_HLIST_HEAD(&data->first);
+ /* Add it at the beginning of the list */
+ hlist_add_head(&data->list, &this->first);
+ return;
+ }
+ }
+
+ /* this rule is single in its list */
+ INIT_HLIST_HEAD(&data->first);
+ hlist_add_head(&data->list, &data->first);
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+}
+
+struct bus_match_maker *bus_matchmaker_new(gfp_t gfp_flags)
+{
+ struct bus_match_maker *matchmaker;
+ int i;
+
+ matchmaker = kzalloc(sizeof(struct bus_match_maker), gfp_flags);
+ if (matchmaker == NULL)
+ return NULL;
+
+ for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) {
+ struct rule_pool *p = matchmaker->rules_by_type + i;
+
+ p->rules_by_iface = RB_ROOT;
+ }
+
+ kref_init(&matchmaker->kref);
+
+ return matchmaker;
+}
+
+void bus_matchmaker_free(struct kref *kref)
+{
+ struct bus_match_maker *matchmaker;
+ struct list_head del_list;
+ struct rb_node *n;
+ int i;
+
+ matchmaker = container_of(kref, struct bus_match_maker, kref);
+
+ /* free names */
+ INIT_LIST_HEAD(&del_list);
+ n = matchmaker->names.rb_node;
+ if (n) {
+ struct dbus_name *dbus_name, *cur, *tmp;
+
+ dbus_name = rb_entry(n, struct dbus_name, node);
+ list_add_tail(&dbus_name->del_list, &del_list);
+
+ list_for_each_entry(cur, &del_list, del_list) {
+ struct dbus_name *right, *left;
+ if (cur->node.rb_right) {
+ right = rb_entry(cur->node.rb_right,
+ struct dbus_name, node);
+ list_add_tail(&right->del_list, &del_list);
+ }
+ if (cur->node.rb_left) {
+ left = rb_entry(cur->node.rb_left,
+ struct dbus_name, node);
+ list_add_tail(&left->del_list, &del_list);
+ }
+ }
+ list_for_each_entry_safe(dbus_name, tmp, &del_list, del_list) {
+ kfree(dbus_name->name);
+ list_del(&dbus_name->del_list);
+ kfree(dbus_name);
+ }
+ }
+ WARN_ON(!list_empty_careful(&del_list));
+
+ /* free match rules */
+ for (i = 0 ; i < DBUS_NUM_MESSAGE_TYPES ; i++) {
+ struct rule_pool *pool = matchmaker->rules_by_type + i;
+ struct bus_match_rule *match_rule, *cur, *tmp;
+ struct hlist_node *list_tmp, *list_tmp2;
+
+ /* free match rules from the list */
+ hlist_for_each_entry_safe(cur, list_tmp, list_tmp2,
+ &pool->rules_without_iface, list) {
+ bus_match_rule_free(cur);
+ }
+
+ /* free match rules from the tree */
+ if (!pool->rules_by_iface.rb_node)
+ continue;
+ match_rule = rb_entry(pool->rules_by_iface.rb_node,
+ struct bus_match_rule, node);
+ list_add_tail(&match_rule->del_list, &del_list);
+
+ list_for_each_entry(cur, &del_list, del_list) {
+ struct bus_match_rule *right, *left;
+ if (cur->node.rb_right) {
+ right = rb_entry(cur->node.rb_right,
+ struct bus_match_rule, node);
+ list_add_tail(&right->del_list, &del_list);
+ }
+ if (cur->node.rb_left) {
+ left = rb_entry(cur->node.rb_left,
+ struct bus_match_rule, node);
+ list_add_tail(&left->del_list, &del_list);
+ }
+ }
+ list_for_each_entry_safe(match_rule, tmp, &del_list, del_list) {
+ /* keep a ref during the loop to ensure the first
+ * iteration of the loop does not delete it */
+ hlist_for_each_entry_safe(cur, list_tmp, list_tmp2,
+ &match_rule->first, list) {
+ if (cur != match_rule)
+ bus_match_rule_free(cur);
+ }
+ list_del(&match_rule->del_list);
+ bus_match_rule_free(match_rule);
+ }
+ WARN_ON(!list_empty_careful(&del_list));
+ }
+
+ kfree(matchmaker);
+}
+
+/* The rule can't be modified after it's added. */
+int bus_matchmaker_add_rule(struct bus_match_maker *matchmaker,
+ struct bus_match_rule *rule)
+{
+ struct rule_pool *pool;
+
+ WARN_ON(rule->message_type < 0);
+ WARN_ON(rule->message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+ pool = matchmaker->rules_by_type + rule->message_type;
+
+ if (rule->interface)
+ match_rule_insert(&pool->rules_by_iface, rule);
+ else
+ hlist_add_head(&rule->list, &pool->rules_without_iface);
+
+ return 1;
+}
+
+static int match_rule_equal(struct bus_match_rule *a,
+ struct bus_match_rule *b)
+{
+ if (a->flags != b->flags)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
+ a->message_type != b->message_type)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_MEMBER) &&
+ strcmp(a->member, b->member) != 0)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_PATH) &&
+ strcmp(a->path, b->path) != 0)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_INTERFACE) &&
+ strcmp(a->interface, b->interface) != 0)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_SENDER) &&
+ strcmp(a->sender, b->sender) != 0)
+ return 0;
+
+ if ((a->flags & BUS_MATCH_DESTINATION) &&
+ strcmp(a->destination, b->destination) != 0)
+ return 0;
+
+ if (a->flags & BUS_MATCH_ARGS) {
+ int i;
+
+ if (a->args_len != b->args_len)
+ return 0;
+
+ i = 0;
+ while (i < a->args_len) {
+ int length;
+
+ if ((a->args[i] != NULL) != (b->args[i] != NULL))
+ return 0;
+
+ if (a->arg_lens[i] != b->arg_lens[i])
+ return 0;
+
+ length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+
+ if (a->args[i] != NULL) {
+ WARN_ON(!b->args[i]);
+ if (memcmp(a->args[i], b->args[i], length) != 0)
+ return 0;
+ }
+
+ ++i;
+ }
+ }
+
+ return 1;
+}
+
+/* Remove a single rule which is equal to the given rule by value */
+void bus_matchmaker_remove_rule_by_value(struct bus_match_maker *matchmaker,
+ struct bus_match_rule *rule)
+{
+ struct rule_pool *pool;
+
+ WARN_ON(rule->message_type < 0);
+ WARN_ON(rule->message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+ pool = matchmaker->rules_by_type + rule->message_type;
+
+ if (rule->interface) {
+ struct bus_match_rule *head =
+ match_rule_search(&pool->rules_by_iface,
+ rule->interface);
+
+ struct hlist_node *cur;
+ struct bus_match_rule *cur_rule;
+ hlist_for_each_entry(cur_rule, cur, &head->first, list) {
+ if (match_rule_equal(cur_rule, rule)) {
+ hlist_del(cur);
+ if (hlist_empty(&head->first))
+ rb_erase(&head->node,
+ &pool->rules_by_iface);
+ bus_match_rule_free(cur_rule);
+ break;
+ }
+ }
+ } else {
+ struct hlist_head *head = &pool->rules_without_iface;
+
+ struct hlist_node *cur;
+ struct bus_match_rule *cur_rule;
+ hlist_for_each_entry(cur_rule, cur, head, list) {
+ if (match_rule_equal(cur_rule, rule)) {
+ hlist_del(cur);
+ bus_match_rule_free(cur_rule);
+ break;
+ }
+ }
+ }
+
+}
+
+static int connection_is_primary_owner(struct bus_match_maker *connection,
+ const char *service_name)
+{
+ struct rb_node *node = connection->names.rb_node;
+
+ if (!service_name)
+ return 0;
+
+ while (node) {
+ struct dbus_name *data = container_of(node, struct dbus_name,
+ node);
+ int result;
+
+ result = strcmp(service_name, data->name);
+
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int match_rule_matches(struct bus_match_maker *matchmaker,
+ struct bus_match_maker *sender,
+ int eavesdrop,
+ struct bus_match_rule *rule,
+ const struct dbus_message *message)
+{
+ /* Don't consider the rule if this is a eavesdropping match rule
+ * and eavesdropping is not allowed on that peer */
+ if ((rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) && !eavesdrop)
+ return 0;
+
+ /* Since D-Bus 1.5.6, match rules do not match messages which have a
+ * DESTINATION field unless the match rule specifically requests this
+ * by specifying eavesdrop='true' in the match rule. */
+ if (message->destination &&
+ !(rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING))
+ return 0;
+
+ if (rule->flags & BUS_MATCH_MEMBER) {
+ const char *member;
+
+ WARN_ON(!rule->member);
+
+ member = message->member;
+ if (member == NULL)
+ return 0;
+
+ if (strcmp(member, rule->member) != 0)
+ return 0;
+ }
+
+ if (rule->flags & BUS_MATCH_SENDER) {
+ WARN_ON(!rule->sender);
+
+ if (sender == NULL) {
+ if (strcmp(rule->sender,
+ "org.freedesktop.DBus") != 0)
+ return 0;
+ } else
+ if (!connection_is_primary_owner(sender, rule->sender))
+ return 0;
+ }
+
+ if (rule->flags & BUS_MATCH_DESTINATION) {
+ const char *destination;
+
+ WARN_ON(!rule->destination);
+
+ destination = message->destination;
+ if (destination == NULL)
+ return 0;
+
+ /* This will not just work out of the box because it this is
+ * an eavesdropping match rule. */
+ if (matchmaker == NULL) {
+ if (strcmp(rule->destination,
+ "org.freedesktop.DBus") != 0)
+ return 0;
+ } else
+ if (!connection_is_primary_owner(matchmaker,
+ rule->destination))
+ return 0;
+ }
+
+ if (rule->flags & BUS_MATCH_PATH) {
+ const char *path;
+
+ WARN_ON(!rule->path);
+
+ path = message->path;
+ if (path == NULL)
+ return 0;
+
+ if (strcmp(path, rule->path) != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool get_recipients_from_list(struct bus_match_maker *matchmaker,
+ struct bus_match_maker *sender,
+ int eavesdrop,
+ struct hlist_head *rules,
+ const struct dbus_message *message)
+{
+ struct hlist_node *cur;
+ struct bus_match_rule *rule;
+
+ if (rules == NULL) {
+ pr_debug("no rules of this type\n");
+ return 0;
+ }
+
+ hlist_for_each_entry(rule, cur, rules, list) {
+ if (match_rule_matches(matchmaker, sender, eavesdrop, rule,
+ message)) {
+ pr_debug("[YES] deliver with match rule \"%s\"\n",
+ rule->rule_text);
+ return 1;
+ } else {
+ pr_debug("[NO] deliver with match rule \"%s\"\n",
+ rule->rule_text);
+ }
+ }
+ pr_debug("[NO] no match rules\n");
+ return 0;
+}
+
+static struct hlist_head
+*bus_matchmaker_get_rules(struct bus_match_maker *matchmaker,
+ int message_type, const char *interface)
+{
+ static struct hlist_head empty = {0,};
+ struct rule_pool *p;
+
+ WARN_ON(message_type < 0);
+ WARN_ON(message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+ p = matchmaker->rules_by_type + message_type;
+
+ if (interface == NULL)
+ return &p->rules_without_iface;
+ else {
+ struct bus_match_rule *rule =
+ match_rule_search(&p->rules_by_iface, interface);
+ if (rule)
+ return &rule->first;
+ else
+ return &empty;
+ }
+}
+
+bool bus_matchmaker_filter(struct bus_match_maker *matchmaker,
+ struct bus_match_maker *sender,
+ int eavesdrop,
+ const struct dbus_message *message)
+{
+ int type;
+ const char *interface;
+ struct hlist_head *neither, *just_type, *just_iface, *both;
+
+ type = message->type;
+ interface = message->interface;
+
+ neither = bus_matchmaker_get_rules(matchmaker,
+ DBUS_MESSAGE_TYPE_INVALID, NULL);
+ just_type = just_iface = both = NULL;
+
+ if (interface != NULL)
+ just_iface = bus_matchmaker_get_rules(matchmaker,
+ DBUS_MESSAGE_TYPE_INVALID,
+ interface);
+
+ if (type > DBUS_MESSAGE_TYPE_INVALID && type < DBUS_NUM_MESSAGE_TYPES) {
+ just_type = bus_matchmaker_get_rules(matchmaker, type, NULL);
+
+ if (interface != NULL)
+ both = bus_matchmaker_get_rules(matchmaker, type,
+ interface);
+ }
+
+ if (get_recipients_from_list(matchmaker, sender, eavesdrop, neither,
+ message))
+ return 1;
+ if (get_recipients_from_list(matchmaker, sender, eavesdrop, just_iface,
+ message))
+ return 1;
+ if (get_recipients_from_list(matchmaker, sender, eavesdrop, just_type,
+ message))
+ return 1;
+ if (get_recipients_from_list(matchmaker, sender, eavesdrop, both,
+ message))
+ return 1;
+
+ return connection_is_primary_owner(matchmaker, message->destination);
+}
+
+void bus_matchmaker_add_name(struct bus_match_maker *matchmaker,
+ const char *name,
+ gfp_t gfp_flags)
+{
+ struct dbus_name *dbus_name;
+ struct rb_node **new = &(matchmaker->names.rb_node), *parent = NULL;
+
+ dbus_name = kmalloc(sizeof(struct dbus_name), gfp_flags);
+ if (!dbus_name)
+ return;
+ dbus_name->name = kstrdup(name, gfp_flags);
+ if (!dbus_name->name)
+ return;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct dbus_name *this = container_of(*new, struct dbus_name,
+ node);
+ int result = strcmp(dbus_name->name, this->name);
+
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else
+ return;
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&dbus_name->node, parent, new);
+ rb_insert_color(&dbus_name->node, &matchmaker->names);
+}
+
+void bus_matchmaker_remove_name(struct bus_match_maker *matchmaker,
+ const char *name)
+{
+ struct rb_node *node = matchmaker->names.rb_node;
+
+ while (node) {
+ struct dbus_name *data = container_of(node, struct dbus_name,
+ node);
+ int result;
+
+ result = strcmp(name, data->name);
+
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else {
+ rb_erase(&data->node, &matchmaker->names);
+ kfree(data->name);
+ kfree(data);
+ }
+ }
+
+}
+
diff --git a/net/bus/nfdbus/matchrule.h b/net/bus/nfdbus/matchrule.h
new file mode 100644
index 000000000000..e16580c9b7d2
--- /dev/null
+++ b/net/bus/nfdbus/matchrule.h
@@ -0,0 +1,82 @@
+/*
+ * signals.h Bus signal connection implementation
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BUS_SIGNALS_H
+#define BUS_SIGNALS_H
+
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <net/af_bus.h>
+
+#include "message.h"
+
+struct bus_match_rule *bus_match_rule_new(gfp_t gfp_flags);
+void bus_match_rule_free(struct bus_match_rule *rule);
+
+struct bus_match_rule *bus_match_rule_parse(const char *rule_text,
+ gfp_t gfp_flags);
+
+struct rule_pool {
+ /* Maps non-NULL interface names to a list of bus_match_rule */
+ struct rb_root rules_by_iface;
+
+ /* List of bus_match_rule which don't specify an interface */
+ struct hlist_head rules_without_iface;
+};
+
+struct bus_match_maker {
+ struct sockaddr_bus addr;
+
+ struct hlist_node table_node;
+
+ /* Pools of rules, grouped by the type of message they match. 0
+ * (DBUS_MESSAGE_TYPE_INVALID) represents rules that do not specify a
+ * message type.
+ */
+ struct rule_pool rules_by_type[DBUS_NUM_MESSAGE_TYPES];
+
+ struct rb_root names;
+
+ struct kref kref;
+};
+
+
+struct bus_match_maker *bus_matchmaker_new(gfp_t gfp_flags);
+void bus_matchmaker_free(struct kref *kref);
+
+int bus_matchmaker_add_rule(struct bus_match_maker *matchmaker,
+ struct bus_match_rule *rule);
+void bus_matchmaker_remove_rule_by_value(struct bus_match_maker *matchmaker,
+ struct bus_match_rule *value);
+
+bool bus_matchmaker_filter(struct bus_match_maker *matchmaker,
+ struct bus_match_maker *sender,
+ int eavesdrop,
+ const struct dbus_message *message);
+
+void bus_matchmaker_add_name(struct bus_match_maker *matchmaker,
+ const char *name, gfp_t gfp_flags);
+void bus_matchmaker_remove_name(struct bus_match_maker *matchmaker,
+ const char *name);
+
+#endif /* BUS_SIGNALS_H */
diff --git a/net/bus/nfdbus/message.c b/net/bus/nfdbus/message.c
new file mode 100644
index 000000000000..93c409c6f16c
--- /dev/null
+++ b/net/bus/nfdbus/message.c
@@ -0,0 +1,194 @@
+/*
+ * message.c Basic D-Bus message parsing
+ *
+ * Copyright (C) 2010-2012 Collabora Ltd
+ * Authors: Alban Crequy <alban.crequy@collabora.co.uk>
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
+ * Copyright (C) 2002, 2003 CodeFactory AB
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "message.h"
+
+int dbus_message_type_from_string(const char *type_str)
+{
+ if (strcmp(type_str, "method_call") == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_CALL;
+ if (strcmp(type_str, "method_return") == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_RETURN;
+ else if (strcmp(type_str, "signal") == 0)
+ return DBUS_MESSAGE_TYPE_SIGNAL;
+ else if (strcmp(type_str, "error") == 0)
+ return DBUS_MESSAGE_TYPE_ERROR;
+ else
+ return DBUS_MESSAGE_TYPE_INVALID;
+}
+
+int dbus_message_parse(unsigned char *message, size_t len,
+ struct dbus_message *dbus_message)
+{
+ unsigned char *cur;
+ int array_header_len;
+
+ dbus_message->message = message;
+
+ if (len < 4 + 4 + 4 + 4 || message[1] == 0 || message[1] > 4)
+ return -EINVAL;
+
+ dbus_message->type = message[1];
+ dbus_message->body_length = *((u32 *)(message + 4));
+ cur = message + 12;
+ array_header_len = *(u32 *)cur;
+ dbus_message->len_offset = 12;
+ cur += 4;
+ while (cur < message + len
+ && cur < message + 12 + 4 + array_header_len) {
+ int header_code;
+ int signature_len;
+ unsigned char *signature;
+ int str_len;
+ unsigned char *str;
+
+ /* D-Bus alignment craziness */
+ if ((cur - message) % 8 != 0)
+ cur += 8 - (cur - message) % 8;
+
+ header_code = *(char *)cur;
+ cur++;
+ signature_len = *(char *)cur;
+ /* All header fields of the current D-Bus spec have a simple
+ * type, either o, s, g, or u */
+ if (signature_len != 1)
+ return -EINVAL;
+ cur++;
+ signature = cur;
+ cur += signature_len + 1;
+ if (signature[0] != 'o' &&
+ signature[0] != 's' &&
+ signature[0] != 'g' &&
+ signature[0] != 'u')
+ return -EINVAL;
+
+ if (signature[0] == 'u') {
+ cur += 4;
+ continue;
+ }
+
+ if (signature[0] != 'g') {
+ str_len = *(u32 *)cur;
+ cur += 4;
+ } else {
+ str_len = *(char *)cur;
+ cur += 1;
+ }
+
+ str = cur;
+ switch (header_code) {
+ case 1:
+ dbus_message->path = str;
+ break;
+ case 2:
+ dbus_message->interface = str;
+ break;
+ case 3:
+ dbus_message->member = str;
+ break;
+ case 6:
+ dbus_message->destination = str;
+ break;
+ case 7:
+ dbus_message->sender = str;
+ break;
+ case 8:
+ dbus_message->body_signature = str;
+ break;
+ }
+ cur += str_len + 1;
+ }
+
+ dbus_message->padding_end = (8 - (cur - message) % 8) % 8;
+
+ /* Jump to body D-Bus alignment craziness */
+ if ((cur - message) % 8 != 0)
+ cur += 8 - (cur - message) % 8;
+ dbus_message->new_header_offset = cur - message;
+
+ if (dbus_message->new_header_offset
+ + dbus_message->body_length != len) {
+ pr_warn("Message truncated? " \
+ "Header %d + Body %d != Length %zd\n",
+ dbus_message->new_header_offset,
+ dbus_message->body_length, len);
+ return -EINVAL;
+ }
+
+ if (dbus_message->body_signature &&
+ dbus_message->body_signature[0] == 's') {
+ int str_len;
+ str_len = *(u32 *)cur;
+ cur += 4;
+ dbus_message->arg0 = cur;
+ cur += str_len + 1;
+ }
+
+ if ((cur - message) % 4 != 0)
+ cur += 4 - (cur - message) % 4;
+
+ if (dbus_message->body_signature &&
+ dbus_message->body_signature[0] == 's' &&
+ dbus_message->body_signature[1] == 's') {
+ int str_len;
+ str_len = *(u32 *)cur;
+ cur += 4;
+ dbus_message->arg1 = cur;
+ cur += str_len + 1;
+ }
+
+ if ((cur - message) % 4 != 0)
+ cur += 4 - (cur - message) % 4;
+
+ if (dbus_message->body_signature &&
+ dbus_message->body_signature[0] == 's' &&
+ dbus_message->body_signature[1] == 's' &&
+ dbus_message->body_signature[2] == 's') {
+ int str_len;
+ str_len = *(u32 *)cur;
+ cur += 4;
+ dbus_message->arg2 = cur;
+ cur += str_len + 1;
+ }
+
+ if ((cur - message) % 4 != 0)
+ cur += 4 - (cur - message) % 4;
+
+ if (dbus_message->type == DBUS_MESSAGE_TYPE_SIGNAL &&
+ dbus_message->sender && dbus_message->path &&
+ dbus_message->interface && dbus_message->member &&
+ dbus_message->arg0 &&
+ strcmp(dbus_message->sender, "org.freedesktop.DBus") == 0 &&
+ strcmp(dbus_message->interface, "org.freedesktop.DBus") == 0 &&
+ strcmp(dbus_message->path, "/org/freedesktop/DBus") == 0) {
+ if (strcmp(dbus_message->member, "NameAcquired") == 0)
+ dbus_message->name_acquired = dbus_message->arg0;
+ else if (strcmp(dbus_message->member, "NameLost") == 0)
+ dbus_message->name_lost = dbus_message->arg0;
+ }
+
+ return 0;
+}
diff --git a/net/bus/nfdbus/message.h b/net/bus/nfdbus/message.h
new file mode 100644
index 000000000000..e3ea4d3253b6
--- /dev/null
+++ b/net/bus/nfdbus/message.h
@@ -0,0 +1,71 @@
+/*
+ * message.h Basic D-Bus message parsing
+ *
+ * Copyright (C) 2010 Collabora Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef DBUS_MESSAGE_H
+#define DBUS_MESSAGE_H
+
+#include <linux/list.h>
+
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+/* Types of message */
+
+#define DBUS_MESSAGE_TYPE_INVALID 0
+#define DBUS_MESSAGE_TYPE_METHOD_CALL 1
+#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
+#define DBUS_MESSAGE_TYPE_ERROR 3
+#define DBUS_MESSAGE_TYPE_SIGNAL 4
+#define DBUS_NUM_MESSAGE_TYPES 5
+
+/* No need to implement a feature-complete parser. It only implement what is
+ * needed by the bus. */
+struct dbus_message {
+ char *message;
+ size_t len;
+ size_t new_len;
+
+ /* direct pointers to the fields */
+ int type;
+ char *path;
+ char *interface;
+ char *member;
+ char *destination;
+ char *sender;
+ char *body_signature;
+ int body_length;
+ char *arg0;
+ char *arg1;
+ char *arg2;
+ char *name_acquired;
+ char *name_lost;
+
+ /* How to add the 'sender' field in the headers */
+ int new_header_offset;
+ int len_offset;
+ int padding_end;
+};
+
+int dbus_message_type_from_string(const char *type_str);
+
+int dbus_message_parse(unsigned char *message, size_t len,
+ struct dbus_message *dbus_message);
+
+#endif /* DBUS_MESSAGE_H */
diff --git a/net/bus/nfdbus/nfdbus.c b/net/bus/nfdbus/nfdbus.c
new file mode 100644
index 000000000000..22a33e8a44eb
--- /dev/null
+++ b/net/bus/nfdbus/nfdbus.c
@@ -0,0 +1,456 @@
+/*
+ * nfdbus.c - Netfilter module for AF_BUS/BUS_PROTO_DBUS.
+ */
+
+#define DRIVER_AUTHOR "Alban Crequy"
+#define DRIVER_DESC "Netfilter module for AF_BUS/BUS_PROTO_DBUS."
+
+#include "nfdbus.h"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter.h>
+#include <linux/connector.h>
+#include <net/af_bus.h>
+
+#include "message.h"
+#include "matchrule.h"
+
+static struct nf_hook_ops nfho_dbus;
+
+static struct cb_id cn_cmd_id = { CN_IDX_NFDBUS, CN_VAL_NFDBUS };
+
+static unsigned int hash;
+
+/* Scoped by AF_BUS address */
+struct hlist_head matchrules_table[BUS_HASH_SIZE];
+DEFINE_SPINLOCK(matchrules_lock);
+
+static struct bus_match_maker *find_match_maker(struct sockaddr_bus *addr,
+ bool create, bool delete)
+{
+ u64 hash;
+ struct hlist_node *node;
+ struct bus_match_maker *matchmaker;
+ int path_len = strlen(addr->sbus_path);
+
+ hash = csum_partial(addr->sbus_path,
+ strlen(addr->sbus_path), 0);
+ hash ^= addr->sbus_addr.s_addr;
+ hash ^= hash >> 32;
+ hash ^= hash >> 16;
+ hash ^= hash >> 8;
+ hash &= 0xff;
+
+ spin_lock(&matchrules_lock);
+ hlist_for_each_entry(matchmaker, node, &matchrules_table[hash],
+ table_node) {
+ if (addr->sbus_family == matchmaker->addr.sbus_family &&
+ addr->sbus_addr.s_addr == matchmaker->addr.sbus_addr.s_addr &&
+ !memcmp(addr->sbus_path, matchmaker->addr.sbus_path,
+ path_len)) {
+ kref_get(&matchmaker->kref);
+ if (delete)
+ hlist_del(&matchmaker->table_node);
+ spin_unlock(&matchrules_lock);
+ pr_debug("Found matchmaker for hash %llu", hash);
+ return matchmaker;
+ }
+ }
+ spin_unlock(&matchrules_lock);
+
+ if (!create) {
+ pr_debug("Matchmaker for hash %llu not found", hash);
+ return NULL;
+ }
+
+ matchmaker = bus_matchmaker_new(GFP_ATOMIC);
+ matchmaker->addr.sbus_family = addr->sbus_family;
+ matchmaker->addr.sbus_addr.s_addr = addr->sbus_addr.s_addr;
+ memcpy(matchmaker->addr.sbus_path, addr->sbus_path, BUS_PATH_MAX);
+
+ pr_debug("Create new matchmaker for hash %llu\n", hash);
+ spin_lock(&matchrules_lock);
+ hlist_add_head(&matchmaker->table_node, &matchrules_table[hash]);
+ kref_get(&matchmaker->kref);
+ spin_unlock(&matchrules_lock);
+ return matchmaker;
+}
+
+static unsigned int dbus_filter(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct bus_send_context *sendctx;
+ struct bus_match_maker *matchmaker = NULL;
+ struct bus_match_maker *sender = NULL;
+ struct dbus_message msg = {0,};
+ unsigned char *data;
+ size_t len;
+ int err;
+ int ret;
+
+ if (!skb->sk || skb->sk->sk_family != PF_BUS) {
+ WARN(1, "netfilter_dbus received an invalid skb");
+ return NF_DROP;
+ }
+
+ data = skb->data;
+ sendctx = BUSCB(skb).sendctx;
+ if (!sendctx || !sendctx->sender || !sendctx->sender_socket) {
+ WARN(1, "netfilter_dbus received an AF_BUS packet" \
+ " without context. This is a bug. Dropping the"
+ " packet.");
+ return NF_DROP;
+ }
+
+ if (sendctx->sender_socket->sk->sk_protocol != BUS_PROTO_DBUS) {
+ /* This kernel module is for D-Bus. It must not
+ * interfere with other users of AF_BUS. */
+ return NF_ACCEPT;
+ }
+ if (sendctx->recipient)
+ matchmaker = find_match_maker(sendctx->recipient, false, false);
+
+ len = skb_tail_pointer(skb) - data;
+
+ if (sendctx->to_master && sendctx->main_recipient) {
+ pr_debug("AF_BUS packet to the bus master. ACCEPT.\n");
+ ret = NF_ACCEPT;
+ goto out;
+ }
+
+ if (sendctx->main_recipient && !sendctx->bus_master_side) {
+ pr_debug("AF_BUS packet from a peer to a peer (unicast). ACCEPT.\n");
+ ret = NF_ACCEPT;
+ goto out;
+ }
+
+ err = dbus_message_parse(data, len, &msg);
+ if (err) {
+ if (!sendctx->main_recipient) {
+ pr_debug("AF_BUS packet for an eavesdropper or " \
+ "multicast is not parsable. DROP.\n");
+ ret = NF_DROP;
+ goto out;
+ } else if (sendctx->bus_master_side) {
+ pr_debug("AF_BUS packet from bus master is not parsable. ACCEPT.\n");
+ ret = NF_ACCEPT;
+ goto out;
+ } else {
+ pr_debug("AF_BUS packet from peer is not parsable. DROP.\n");
+ ret = NF_DROP;
+ goto out;
+ }
+ }
+
+ if (sendctx->bus_master_side && !sendctx->main_recipient) {
+ pr_debug("AF_BUS packet '%s' from the bus master is for an " \
+ "eavesdropper. DROP.\n",
+ msg.member ? msg.member : "");
+ ret = NF_DROP;
+ goto out;
+ }
+ if (sendctx->bus_master_side) {
+ if (msg.name_acquired) {
+ pr_debug("New name: %s [%p %p].\n",
+ msg.name_acquired, sendctx->sender,
+ sendctx->recipient);
+
+ sender = find_match_maker(sendctx->sender, true, false);
+ bus_matchmaker_add_name(sender, msg.name_acquired,
+ GFP_ATOMIC);
+ }
+ if (msg.name_lost) {
+ pr_debug("Lost name: %s [%p %p].\n",
+ msg.name_lost, sendctx->sender,
+ sendctx->recipient);
+
+ sender = find_match_maker(sendctx->sender, true, false);
+ bus_matchmaker_remove_name(sender, msg.name_acquired);
+ }
+
+ pr_debug("AF_BUS packet '%s' from the bus master. ACCEPT.\n",
+ msg.member ? msg.member : "");
+ ret = NF_ACCEPT;
+ goto out;
+ }
+
+ pr_debug("Multicast AF_BUS packet, %ld bytes, " \
+ "considering recipient %lld...\n", len,
+ sendctx->recipient ? sendctx->recipient->sbus_addr.s_addr : 0);
+
+ pr_debug("Message type %d %s->%s [iface: %s][member: %s][matchmaker=%p]...\n",
+ msg.type,
+ msg.sender ? msg.sender : "",
+ msg.destination ? msg.destination : "",
+ msg.interface ? msg.interface : "",
+ msg.member ? msg.member : "",
+ matchmaker);
+
+ if (!matchmaker) {
+ pr_debug("No match rules for this recipient. DROP.\n");
+ ret = NF_DROP;
+ goto out;
+ }
+
+ sender = find_match_maker(sendctx->sender, true, false);
+ err = bus_matchmaker_filter(matchmaker, sender, sendctx->eavesdropper,
+ &msg);
+ if (err) {
+ pr_debug("Matchmaker: ACCEPT.\n");
+ ret = NF_ACCEPT;
+ goto out;
+ } else {
+ pr_debug("Matchmaker: DROP.\n");
+ ret = NF_DROP;
+ goto out;
+ }
+
+out:
+ if (matchmaker)
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ if (sender)
+ kref_put(&sender->kref, bus_matchmaker_free);
+ return ret;
+}
+
+/* Taken from drbd_nl_send_reply() */
+static void nfdbus_nl_send_reply(struct cn_msg *msg, int ret_code)
+{
+ char buffer[sizeof(struct cn_msg)+sizeof(struct nfdbus_nl_cfg_reply)];
+ struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+ struct nfdbus_nl_cfg_reply *reply =
+ (struct nfdbus_nl_cfg_reply *)cn_reply->data;
+ int rr;
+
+ memset(buffer, 0, sizeof(buffer));
+ cn_reply->id = msg->id;
+
+ cn_reply->seq = msg->seq;
+ cn_reply->ack = msg->ack + 1;
+ cn_reply->len = sizeof(struct nfdbus_nl_cfg_reply);
+ cn_reply->flags = 0;
+
+ reply->ret_code = ret_code;
+
+ rr = cn_netlink_send(cn_reply, 0, GFP_NOIO);
+ if (rr && rr != -ESRCH)
+ pr_debug("nfdbus: cn_netlink_send()=%d\n", rr);
+}
+
+/**
+ * nfdbus_check_perm - check if a pid is allowed to update match rules
+ * @sockaddr_bus: the socket address of the bus
+ * @pid: the process id that wants to update the match rules set
+ *
+ * Test if a given process id is allowed to update the match rules set
+ * for this bus. Only the process that owns the bus master listen socket
+ * is allowed to update the match rules set for the bus.
+ */
+static bool nfdbus_check_perm(struct sockaddr_bus *sbusname, pid_t pid)
+{
+ struct net *net = get_net_ns_by_pid(pid);
+ struct sock *s;
+ struct bus_address *addr;
+ struct hlist_node *node;
+ int offset = (sbusname->sbus_path[0] == '\0');
+ int path_len = strnlen(sbusname->sbus_path + offset, BUS_PATH_MAX);
+ int len;
+ if (!net)
+ return false;
+
+ len = path_len + 1 + sizeof(__kernel_sa_family_t) +
+ sizeof(struct bus_addr);
+
+ spin_lock(&bus_address_lock);
+
+ hlist_for_each_entry(addr, node, &bus_address_table[hash],
+ table_node) {
+ s = addr->sock;
+
+ if (s->sk_protocol != BUS_PROTO_DBUS)
+ 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 == BUS_MASTER_ADDR &&
+ bus_same_bus(addr->name, sbusname) &&
+ pid_nr(s->sk_peer_pid) == pid) {
+ spin_unlock(&bus_address_lock);
+ return true;
+ }
+ }
+
+ spin_unlock(&bus_address_lock);
+
+ return false;
+}
+
+static void cn_cmd_cb(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+{
+ struct nfdbus_nl_cfg_req *nlp = (struct nfdbus_nl_cfg_req *)msg->data;
+ struct cn_msg *cn_reply;
+ struct nfdbus_nl_cfg_reply *reply;
+ int retcode, rr;
+ pid_t pid = task_tgid_vnr(current);
+ int reply_size = sizeof(struct cn_msg)
+ + sizeof(struct nfdbus_nl_cfg_reply);
+
+ pr_debug("nfdbus: %s nsp->pid=%d pid=%d\n", __func__, nsp->portid, pid);
+
+ if (!nfdbus_check_perm(&nlp->addr, pid)) {
+ pr_debug(KERN_ERR "nfdbus: pid=%d is not allowed!\n", pid);
+ retcode = EPERM;
+ goto fail;
+ }
+
+ cn_reply = kzalloc(reply_size, GFP_KERNEL);
+ if (!cn_reply) {
+ retcode = ENOMEM;
+ goto fail;
+ }
+ reply = (struct nfdbus_nl_cfg_reply *) cn_reply->data;
+
+ if (msg->len < sizeof(struct nfdbus_nl_cfg_req)) {
+ reply->ret_code = EINVAL;
+ } else if (nlp->cmd == NFDBUS_CMD_ADDMATCH) {
+ struct bus_match_rule *rule;
+ struct bus_match_maker *matchmaker;
+ reply->ret_code = 0;
+
+ if (msg->len == 0)
+ reply->ret_code = EINVAL;
+
+ rule = bus_match_rule_parse(nlp->data, GFP_ATOMIC);
+ if (rule) {
+ matchmaker = find_match_maker(&nlp->addr, true, false);
+ pr_debug("Add match rule for matchmaker %p\n",
+ matchmaker);
+ bus_matchmaker_add_rule(matchmaker, rule);
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ } else {
+ reply->ret_code = EINVAL;
+ }
+ } else if (nlp->cmd == NFDBUS_CMD_REMOVEMATCH) {
+ struct bus_match_rule *rule;
+ struct bus_match_maker *matchmaker;
+
+ rule = bus_match_rule_parse(nlp->data, GFP_ATOMIC);
+ matchmaker = find_match_maker(&nlp->addr, false, false);
+ if (!matchmaker) {
+ reply->ret_code = EINVAL;
+ } else {
+ pr_debug("Remove match rule for matchmaker %p\n",
+ matchmaker);
+ bus_matchmaker_remove_rule_by_value(matchmaker, rule);
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ reply->ret_code = 0;
+ }
+ bus_match_rule_free(rule);
+
+ } else if (nlp->cmd == NFDBUS_CMD_REMOVEALLMATCH) {
+ struct bus_match_maker *matchmaker;
+
+ matchmaker = find_match_maker(&nlp->addr, false, true);
+ if (!matchmaker) {
+ reply->ret_code = EINVAL;
+ } else {
+ pr_debug("Remove matchmaker %p\n", matchmaker);
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ reply->ret_code = 0;
+ }
+
+ } else {
+ reply->ret_code = EINVAL;
+ }
+
+ cn_reply->id = msg->id;
+ cn_reply->seq = msg->seq;
+ cn_reply->ack = msg->ack + 1;
+ cn_reply->len = sizeof(struct nfdbus_nl_cfg_reply);
+ cn_reply->flags = 0;
+
+ rr = cn_netlink_reply(cn_reply, nsp->portid, GFP_KERNEL);
+ if (rr && rr != -ESRCH)
+ pr_debug("nfdbus: cn_netlink_send()=%d\n", rr);
+ pr_debug("nfdbus: cn_netlink_reply(pid=%d)=%d\n", nsp->portid, rr);
+
+ kfree(cn_reply);
+ return;
+fail:
+ nfdbus_nl_send_reply(msg, retcode);
+}
+
+static int __init nfdbus_init(void)
+{
+ int err;
+ struct bus_addr master_addr;
+
+ master_addr.s_addr = BUS_MASTER_ADDR;
+ hash = bus_compute_hash(master_addr);
+
+ pr_debug("Loading netfilter_dbus\n");
+
+ /* Install D-Bus netfilter hook */
+ nfho_dbus.hook = dbus_filter;
+ nfho_dbus.hooknum = NF_BUS_SENDING;
+ nfho_dbus.pf = NFPROTO_BUS; /* Do not use PF_BUS, you fool! */
+ nfho_dbus.priority = 0;
+ nfho_dbus.owner = THIS_MODULE;
+ err = nf_register_hook(&nfho_dbus);
+ if (err)
+ return err;
+ pr_debug("Netfilter hook for D-Bus: installed.\n");
+
+ /* Install connector hook */
+ err = cn_add_callback(&cn_cmd_id, "nfdbus", cn_cmd_cb);
+ if (err)
+ goto err_cn_cmd_out;
+ pr_debug("Connector hook: installed.\n");
+
+ return 0;
+
+err_cn_cmd_out:
+ nf_unregister_hook(&nfho_dbus);
+
+ return err;
+}
+
+static void __exit nfdbus_cleanup(void)
+{
+ int i;
+ struct hlist_node *node, *tmp;
+ struct bus_match_maker *matchmaker;
+ nf_unregister_hook(&nfho_dbus);
+
+ cn_del_callback(&cn_cmd_id);
+
+ spin_lock(&matchrules_lock);
+ for (i = 0; i < BUS_HASH_SIZE; i++) {
+ hlist_for_each_entry_safe(matchmaker, node, tmp,
+ &matchrules_table[i], table_node) {
+ hlist_del(&matchmaker->table_node);
+ kref_put(&matchmaker->kref, bus_matchmaker_free);
+ }
+ }
+ spin_unlock(&matchrules_lock);
+
+ pr_debug("Unloading netfilter_dbus\n");
+}
+
+module_init(nfdbus_init);
+module_exit(nfdbus_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS_NET_PF_PROTO(PF_BUS, BUS_PROTO_DBUS);
diff --git a/net/bus/nfdbus/nfdbus.h b/net/bus/nfdbus/nfdbus.h
new file mode 100644
index 000000000000..477bde32a60f
--- /dev/null
+++ b/net/bus/nfdbus/nfdbus.h
@@ -0,0 +1,44 @@
+/*
+ * nfdbus.h Netfilter module for AF_BUS/BUS_PROTO_DBUS.
+ *
+ * Copyright (C) 2012 Collabora Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef NETFILTER_DBUS_H
+#define NETFILTER_DBUS_H
+
+#include <linux/types.h>
+#include <linux/bus.h>
+
+#define NFDBUS_CMD_ADDMATCH 0x01
+#define NFDBUS_CMD_REMOVEMATCH 0x02
+#define NFDBUS_CMD_REMOVEALLMATCH 0x03
+
+struct nfdbus_nl_cfg_req {
+ __u32 cmd;
+ __u32 len;
+ struct sockaddr_bus addr;
+ __u64 pad;
+ unsigned char data[0];
+};
+
+struct nfdbus_nl_cfg_reply {
+ __u32 ret_code;
+};
+
+#endif /* NETFILTER_DBUS_H */
diff --git a/net/core/scm.c b/net/core/scm.c
index 040cebeed45b..d34cfc41ed3a 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -142,7 +142,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
- if (!sock->ops || sock->ops->family != PF_UNIX)
+ if (!sock->ops || (sock->ops->family != PF_UNIX &&
+ sock->ops->family != PF_BUS))
goto error;
err=scm_fp_copy(cmsg, &p->fp);
if (err<0)
diff --git a/net/core/sock.c b/net/core/sock.c
index a6000fbad294..29ad1f8132b3 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -208,7 +208,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
"sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG" ,
- "sk_lock-AF_NFC" , "sk_lock-AF_MAX"
+ "sk_lock-AF_NFC" , "sk_lock-AF_BUS" , "sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
@@ -224,7 +224,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
"slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" ,
- "slock-AF_NFC" , "slock-AF_MAX"
+ "slock-AF_NFC" , "slock-AF_BUS" , "slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
@@ -240,7 +240,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
"clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG" ,
- "clock-AF_NFC" , "clock-AF_MAX"
+ "clock-AF_NFC" , "clock-AF_BUS" , "clock-AF_MAX"
};
/*
diff --git a/security/capability.c b/security/capability.c
index 61095df8b89a..ea57f2b664ba 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -563,6 +563,12 @@ static int cap_unix_may_send(struct socket *sock, struct socket *other)
return 0;
}
+static int cap_bus_connect(struct sock *sock, struct sock *other,
+ struct sock *newsk)
+{
+ return 0;
+}
+
static int cap_socket_create(int family, int type, int protocol, int kern)
{
return 0;
@@ -1016,6 +1022,7 @@ void __init security_fixup_ops(struct security_operations *ops)
#ifdef CONFIG_SECURITY_NETWORK
set_to_cap_if_null(ops, unix_stream_connect);
set_to_cap_if_null(ops, unix_may_send);
+ set_to_cap_if_null(ops, bus_connect);
set_to_cap_if_null(ops, socket_create);
set_to_cap_if_null(ops, socket_post_create);
set_to_cap_if_null(ops, socket_bind);
diff --git a/security/security.c b/security/security.c
index 860aeb349cb3..efacf0292b51 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1060,6 +1060,13 @@ int security_unix_may_send(struct socket *sock, struct socket *other)
}
EXPORT_SYMBOL(security_unix_may_send);
+int security_bus_connect(struct sock *sock, struct sock *other,
+ struct sock *newsk)
+{
+ return security_ops->bus_connect(sock, other, newsk);
+}
+EXPORT_SYMBOL(security_bus_connect);
+
int security_socket_create(int family, int type, int protocol, int kern)
{
return security_ops->socket_create(family, type, protocol, kern);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6c77f63c7591..c5b0bbc6137b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -67,6 +67,7 @@
#include <linux/quota.h>
#include <linux/un.h> /* for Unix socket types */
#include <net/af_unix.h> /* for Unix socket types */
+#include <net/af_bus.h> /* for Bus socket types */
#include <linux/parser.h>
#include <linux/nfs_mount.h>
#include <net/ipv6.h>
@@ -4106,6 +4107,39 @@ static int selinux_socket_unix_may_send(struct socket *sock,
&ad);
}
+static int selinux_socket_bus_connect(struct sock *sock, struct sock *other,
+ struct sock *newsk)
+{
+ struct sk_security_struct *sksec_sock = sock->sk_security;
+ struct sk_security_struct *sksec_other = other->sk_security;
+ struct sk_security_struct *sksec_new = newsk->sk_security;
+ struct common_audit_data ad;
+ struct lsm_network_audit net = {0,};
+ int err;
+
+ ad.type = LSM_AUDIT_DATA_NET;
+ ad.u.net = &net;
+ ad.u.net->sk = other;
+
+ err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
+ sksec_other->sclass,
+ UNIX_STREAM_SOCKET__CONNECTTO, &ad);
+ if (err)
+ return err;
+
+ /* server child socket */
+ sksec_new->peer_sid = sksec_sock->sid;
+ err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid,
+ &sksec_new->sid);
+ if (err)
+ return err;
+
+ /* connecting socket */
+ sksec_sock->peer_sid = sksec_new->sid;
+
+ return 0;
+}
+
static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
u32 peer_sid,
struct common_audit_data *ad)
@@ -5648,6 +5682,7 @@ static struct security_operations selinux_ops = {
.unix_stream_connect = selinux_socket_unix_stream_connect,
.unix_may_send = selinux_socket_unix_may_send,
+ .bus_connect = selinux_socket_bus_connect,
.socket_create = selinux_socket_create,
.socket_post_create = selinux_socket_post_create,