diff options
Diffstat (limited to 'meta-ivi-bsp')
11 files changed, 7421 insertions, 0 deletions
diff --git a/meta-ivi-bsp/classes/sdcard_image.bbclass b/meta-ivi-bsp/classes/sdcard_image.bbclass new file mode 100644 index 0000000..de3682f --- /dev/null +++ b/meta-ivi-bsp/classes/sdcard_image.bbclass @@ -0,0 +1,87 @@ +inherit image_types + +# +# Create an image that can by written onto a SD card using dd. +# +# The disk layout used is: +# +# 0 -> IMAGE_ROOTFS_ALIGNMENT - reserved for other data +# IMAGE_ROOTFS_ALIGNMENT -> BOOT_SPACE - bootloader and/or kernel +# BOOT_SPACE -> SDIMG_SIZE - rootfs +# + +# Default Free space = 1.3x +# Use IMAGE_OVERHEAD_FACTOR to add more space +# <---------> +# 4KiB 20MiB SDIMG_ROOTFS +# <-----------------------> <----------> <----------------------> +# ------------------------ ------------ ------------------------ ------------------------------- +# | IMAGE_ROOTFS_ALIGNMENT | BOOT_SPACE | ROOTFS_SIZE | IMAGE_ROOTFS_ALIGNMENT | +# ------------------------ ------------ ------------------------ ------------------------------- +# ^ ^ ^ ^ ^ +# | | | | | +# 0 4096 4KiB + 20MiB 4KiB + 20Mib + SDIMG_ROOTFS 4KiB + 20MiB + SDIMG_ROOTFS + 4KiB + + +# Boot partition volume id +BOOTDD_VOLUME_ID ?= "${MACHINE}" + +# Boot partition size [in KiB] +BOOT_SPACE ?= "20480" + +# Set alignment to 4MB [in KiB] +IMAGE_ROOTFS_ALIGNMENT = "4096" + +# Use an uncompressed ext3 by default as rootfs +SDIMG_ROOTFS_TYPE ?= "ext3" +SDIMG_ROOTFS = "${IMAGE_NAME}.rootfs.${SDIMG_ROOTFS_TYPE}" + +IMAGE_DEPENDS_vexpressa9-sdimg = " \ + parted-native \ + mtools-native \ + dosfstools-native \ + virtual/kernel \ + " + +# SD card image name +SDIMG = "${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.rootfs.vexpressa9-sdimg" + +IMAGEDATESTAMP = "${@time.strftime('%Y.%m.%d',time.gmtime())}" + +IMAGE_CMD_vexpressa9-sdimg () { + # Align partitions + BOOT_SPACE_ALIGNED=$(expr ${BOOT_SPACE} + ${IMAGE_ROOTFS_ALIGNMENT} - 1) + BOOT_SPACE_ALIGNED=$(expr ${BOOT_SPACE_ALIGNED} - ${BOOT_SPACE_ALIGNED} % ${IMAGE_ROOTFS_ALIGNMENT}) + SDIMG_SIZE=$(expr ${IMAGE_ROOTFS_ALIGNMENT} + ${BOOT_SPACE_ALIGNED} + $ROOTFS_SIZE + ${IMAGE_ROOTFS_ALIGNMENT}) + + # Initialize sdcard image file + dd if=/dev/zero of=${SDIMG} bs=1 count=0 seek=$(expr 1024 \* ${SDIMG_SIZE}) + + # Create partition table + parted -s ${SDIMG} mklabel msdos + # Create boot partition and mark it as bootable + parted -s ${SDIMG} unit KiB mkpart primary fat32 ${IMAGE_ROOTFS_ALIGNMENT} $(expr ${BOOT_SPACE_ALIGNED} \+ ${IMAGE_ROOTFS_ALIGNMENT}) + parted -s ${SDIMG} set 1 boot on + # Create rootfs partition + parted -s ${SDIMG} unit KiB mkpart primary ext2 $(expr ${BOOT_SPACE_ALIGNED} \+ ${IMAGE_ROOTFS_ALIGNMENT}) $(expr ${BOOT_SPACE_ALIGNED} \+ ${IMAGE_ROOTFS_ALIGNMENT} \+ ${ROOTFS_SIZE}) + parted ${SDIMG} print + + # Create a vfat image with boot files + BOOT_BLOCKS=$(LC_ALL=C parted -s ${SDIMG} unit b print | awk '/ 1 / { print substr($4, 1, length($4 -1)) / 512 /2 }') + mkfs.vfat -n "${BOOTDD_VOLUME_ID}" -S 512 -C ${WORKDIR}/boot.img $BOOT_BLOCKS + mcopy -i ${WORKDIR}/boot.img -s ${DEPLOY_DIR_IMAGE}/${KERNEL_IMAGETYPE}-${MACHINE}.bin ::kernel.img + + # Add stamp file + echo "${IMAGE_NAME}-${IMAGEDATESTAMP}" > ${WORKDIR}/image-version-info + mcopy -i ${WORKDIR}/boot.img -v ${WORKDIR}//image-version-info :: + + # Burn Partitions + dd if=${WORKDIR}/boot.img of=${SDIMG} conv=notrunc seek=1 bs=$(expr ${IMAGE_ROOTFS_ALIGNMENT} \* 1024) && sync && sync + # If SDIMG_ROOTFS_TYPE is a .xz file use xzcat + if [[ "$SDIMG_ROOTFS_TYPE" == *.xz ]] + then + xzcat ${SDIMG_ROOTFS} | dd of=${SDIMG} conv=notrunc seek=1 bs=$(expr 1024 \* ${BOOT_SPACE_ALIGNED} + ${IMAGE_ROOTFS_ALIGNMENT} \* 1024) && sync && sync + else + dd if=${SDIMG_ROOTFS} of=${SDIMG} conv=notrunc seek=1 bs=$(expr 1024 \* ${BOOT_SPACE_ALIGNED} + ${IMAGE_ROOTFS_ALIGNMENT} \* 1024) && sync && sync + fi +} diff --git a/meta-ivi-bsp/recipes-bsp/u-boot/files/no_delay.patch b/meta-ivi-bsp/recipes-bsp/u-boot/files/no_delay.patch new file mode 100644 index 0000000..f280b7e --- /dev/null +++ b/meta-ivi-bsp/recipes-bsp/u-boot/files/no_delay.patch @@ -0,0 +1,13 @@ +Index: git/include/configs/omap3_beagle.h +=================================================================== +--- git.orig/include/configs/omap3_beagle.h 2012-01-20 02:19:54.582012450 +0200 ++++ git/include/configs/omap3_beagle.h 2012-01-20 02:20:33.534011227 +0200 +@@ -195,7 +195,7 @@ + /* partition */ + + /* Environment information */ +-#define CONFIG_BOOTDELAY 10 ++#define CONFIG_BOOTDELAY 0 + + #define CONFIG_EXTRA_ENV_SETTINGS \ + "loadaddr=0x82000000\0" \ diff --git a/meta-ivi-bsp/recipes-bsp/u-boot/u-boot_2011.06.bbappend b/meta-ivi-bsp/recipes-bsp/u-boot/u-boot_2011.06.bbappend new file mode 100644 index 0000000..672a019 --- /dev/null +++ b/meta-ivi-bsp/recipes-bsp/u-boot/u-boot_2011.06.bbappend @@ -0,0 +1,5 @@ +# Find "files" directory +FILESEXTRAPATHS := "${THISDIR}/files" + +# Patch for removing autostart countdown +SRC_URI_append_imx53qsb += "file://no_delay.patch" diff --git a/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config/vexpressa9/xorg.conf b/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config/vexpressa9/xorg.conf new file mode 100644 index 0000000..182fe46 --- /dev/null +++ b/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config/vexpressa9/xorg.conf @@ -0,0 +1,26 @@ + +Section "Device" + Identifier "Graphics Controller" + Driver "fbdev" +EndSection + +Section "Monitor" + Identifier "Generic Monitor" + Option "DPMS" +EndSection + +Section "Screen" + Identifier "Default Screen" + Device "Intel Graphics Driver" + Monitor "Generic Monitor" + DefaultDepth 16 +EndSection + +Section "ServerLayout" + Identifier "Default Layout" + Screen "Default Screen" +EndSection + +Section "ServerFlags" + Option "DontZap" "0" +EndSection diff --git a/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend b/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000..dedce1f --- /dev/null +++ b/meta-ivi-bsp/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend @@ -0,0 +1,4 @@ +# Append path for meta-ivi layer to include bsp xorg.conf +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +PRINC := "${@int(PRINC) + 1}" diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/0001-net-bus-add-the-AF_BUS-socket-address-family.patch b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/0001-net-bus-add-the-AF_BUS-socket-address-family.patch new file mode 100644 index 0000000..1e05dd2 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/0001-net-bus-add-the-AF_BUS-socket-address-family.patch @@ -0,0 +1,6387 @@ +From 8c071e4a4be17b7bb01ab2a911c1697902dc2c35 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas <javier.martinez@collabora.co.uk> +Date: Wed, 20 Jun 2012 12:07:49 +0200 +Subject: [PATCH 1/1] net: bus: add the AF_BUS socket address family + +AF_BUS is a new socket address family that allows both unicast and +multicast I on a local machine with total ordering for messages +(every process on the same bus sees each message in the same order). + +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 process' unique address or to a bus multicast address. + +Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk> +--- + Documentation/networking/af_bus.txt | 558 ++++++++ + drivers/connector/connector.c | 32 + + include/linux/bus.h | 34 + + include/linux/connector.h | 5 +- + include/linux/netfilter.h | 1 + + include/linux/security.h | 11 + + include/linux/socket.h | 5 +- + include/net/af_bus.h | 273 ++++ + net/Kconfig | 1 + + net/Makefile | 1 + + net/bus/Kconfig | 15 + + net/bus/Makefile | 7 + + net/bus/af_bus.c | 2688 +++++++++++++++++++++++++++++++++++ + net/bus/garbage.c | 322 +++++ + net/core/scm.c | 3 +- + net/core/sock.c | 6 +- + net/netfilter/Kconfig | 2 + + net/netfilter/Makefile | 3 + + net/netfilter/nfdbus/Kconfig | 12 + + net/netfilter/nfdbus/Makefile | 6 + + net/netfilter/nfdbus/matchrule.c | 1132 +++++++++++++++ + net/netfilter/nfdbus/matchrule.h | 82 ++ + net/netfilter/nfdbus/message.c | 194 +++ + net/netfilter/nfdbus/message.h | 71 + + net/netfilter/nfdbus/nfdbus.c | 456 ++++++ + net/netfilter/nfdbus/nfdbus.h | 44 + + security/capability.c | 7 + + security/security.c | 7 + + security/selinux/hooks.c | 35 + + 29 files changed, 6007 insertions(+), 6 deletions(-) + create mode 100644 Documentation/networking/af_bus.txt + create mode 100644 include/linux/bus.h + create mode 100644 include/net/af_bus.h + create mode 100644 net/bus/Kconfig + create mode 100644 net/bus/Makefile + create mode 100644 net/bus/af_bus.c + create mode 100644 net/bus/garbage.c + create mode 100644 net/netfilter/nfdbus/Kconfig + create mode 100644 net/netfilter/nfdbus/Makefile + create mode 100644 net/netfilter/nfdbus/matchrule.c + create mode 100644 net/netfilter/nfdbus/matchrule.h + create mode 100644 net/netfilter/nfdbus/message.c + create mode 100644 net/netfilter/nfdbus/message.h + create mode 100644 net/netfilter/nfdbus/nfdbus.c + create mode 100644 net/netfilter/nfdbus/nfdbus.h + +diff --git a/Documentation/networking/af_bus.txt b/Documentation/networking/af_bus.txt +new file mode 100644 +index 0000000..a0b078f +--- /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 dde6a0f..a1f9364 100644 +--- a/drivers/connector/connector.c ++++ b/drivers/connector/connector.c +@@ -118,6 +118,38 @@ nlmsg_failure: + 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 0000000..19cac36 +--- /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 7638407..519d010 100644 +--- a/include/linux/connector.h ++++ b/include/linux/connector.h +@@ -44,8 +44,10 @@ + #define CN_VAL_DRBD 0x1 + #define CN_KVP_IDX 0x9 /* HyperV KVP */ + #define CN_KVP_VAL 0x1 /* queries from the kernel */ ++#define CN_IDX_NFDBUS 0xA /* netfilter D-Bus */ ++#define CN_VAL_NFDBUS 0x1 + +-#define CN_NETLINK_USERS 10 /* Highest index + 1 */ ++#define CN_NETLINK_USERS 11 /* Highest index + 1 */ + + /* + * Maximum connector's message size. +@@ -125,6 +127,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/netfilter.h b/include/linux/netfilter.h +index 29734be..7cff0bd 100644 +--- a/include/linux/netfilter.h ++++ b/include/linux/netfilter.h +@@ -67,6 +67,7 @@ enum { + NFPROTO_BRIDGE = 7, + NFPROTO_IPV6 = 10, + NFPROTO_DECNET = 12, ++ NFPROTO_BUS, + NFPROTO_NUMPROTO, + }; + +diff --git a/include/linux/security.h b/include/linux/security.h +index 673afbb..fa26c6d 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -1578,6 +1578,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); +@@ -2517,6 +2519,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, +@@ -2564,6 +2568,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 b84bbd4..59596d2 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. */ +@@ -312,6 +314,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 0000000..e63eb49 +--- /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 e07272d..c9774a1 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 ad432fa..3033018 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 0000000..5f01410 +--- /dev/null ++++ b/net/bus/Kconfig +@@ -0,0 +1,15 @@ ++# ++# 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. +diff --git a/net/bus/Makefile b/net/bus/Makefile +new file mode 100644 +index 0000000..8c1fea2 +--- /dev/null ++++ b/net/bus/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the Linux bus domain socket layer. ++# ++ ++obj-$(CONFIG_AF_BUS) += af-bus.o ++ ++af-bus-y := af_bus.o garbage.o +diff --git a/net/bus/af_bus.c b/net/bus/af_bus.c +new file mode 100644 +index 0000000..5a00225 +--- /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 0000000..2435f38 +--- /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, ¬_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(¬_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/core/scm.c b/net/core/scm.c +index 611c5ef..87e3152 100644 +--- a/net/core/scm.c ++++ b/net/core/scm.c +@@ -158,7 +158,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 b2e14c0..17abe99 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -205,7 +205,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" , +@@ -221,7 +221,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" , +@@ -237,7 +237,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/net/netfilter/Kconfig b/net/netfilter/Kconfig +index 0c6f67e..58ed81d 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -1151,3 +1151,5 @@ endmenu + source "net/netfilter/ipset/Kconfig" + + source "net/netfilter/ipvs/Kconfig" ++ ++source "net/netfilter/nfdbus/Kconfig" +diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile +index ca36765..89752a0 100644 +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -119,3 +119,6 @@ obj-$(CONFIG_IP_SET) += ipset/ + + # IPVS + obj-$(CONFIG_IP_VS) += ipvs/ ++ ++# Dbus ++obj-$(CONFIG_NETFILTER_DBUS) += nfdbus/ +diff --git a/net/netfilter/nfdbus/Kconfig b/net/netfilter/nfdbus/Kconfig +new file mode 100644 +index 0000000..25699a1 +--- /dev/null ++++ b/net/netfilter/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/netfilter/nfdbus/Makefile b/net/netfilter/nfdbus/Makefile +new file mode 100644 +index 0000000..1a825f8 +--- /dev/null ++++ b/net/netfilter/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/netfilter/nfdbus/matchrule.c b/net/netfilter/nfdbus/matchrule.c +new file mode 100644 +index 0000000..4106bd5 +--- /dev/null ++++ b/net/netfilter/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 ∅ ++ } ++} ++ ++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/netfilter/nfdbus/matchrule.h b/net/netfilter/nfdbus/matchrule.h +new file mode 100644 +index 0000000..e16580c +--- /dev/null ++++ b/net/netfilter/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/netfilter/nfdbus/message.c b/net/netfilter/nfdbus/message.c +new file mode 100644 +index 0000000..93c409c +--- /dev/null ++++ b/net/netfilter/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/netfilter/nfdbus/message.h b/net/netfilter/nfdbus/message.h +new file mode 100644 +index 0000000..e3ea4d3 +--- /dev/null ++++ b/net/netfilter/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/netfilter/nfdbus/nfdbus.c b/net/netfilter/nfdbus/nfdbus.c +new file mode 100644 +index 0000000..f6642e2 +--- /dev/null ++++ b/net/netfilter/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->pid, 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->pid, 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->pid, 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/netfilter/nfdbus/nfdbus.h b/net/netfilter/nfdbus/nfdbus.h +new file mode 100644 +index 0000000..477bde3 +--- /dev/null ++++ b/net/netfilter/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/security/capability.c b/security/capability.c +index 5bb21b1..5b966a6 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; +@@ -1015,6 +1021,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 bf619ff..54582ea 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -1018,6 +1018,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 d85b793..37573f2 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> +@@ -4102,6 +4103,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) +@@ -5656,6 +5690,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, +-- +1.7.7.6 + diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/beagle_qemu.cfg b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/beagle_qemu.cfg new file mode 100644 index 0000000..0ace211 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/beagle_qemu.cfg @@ -0,0 +1,5 @@ +CONFIG_PANEL_GENERIC_DPI=y + +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_OMAP3=y +CONFIG_USB_OHCI_LITTLE_ENDIAN=y diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86.cfg b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86.cfg new file mode 100644 index 0000000..eb78e01 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86.cfg @@ -0,0 +1,13 @@ +#additional configs requested by SysInfra EG +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y + +# config for adding support for AF_BUS sockets +CONFIG_AF_BUS=y +CONFIG_NETFILTER_DBUS=y + +# Activate ecryptfs +CONFIG_EXPERIMENTAL=y +CONFIG_KEYS=y +CONFIG_CRYPTO=y +CONFIG_ECRYPT_FS=y diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86_64.cfg b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86_64.cfg new file mode 100644 index 0000000..eb78e01 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/qemux86_64.cfg @@ -0,0 +1,13 @@ +#additional configs requested by SysInfra EG +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y + +# config for adding support for AF_BUS sockets +CONFIG_AF_BUS=y +CONFIG_NETFILTER_DBUS=y + +# Activate ecryptfs +CONFIG_EXPERIMENTAL=y +CONFIG_KEYS=y +CONFIG_CRYPTO=y +CONFIG_ECRYPT_FS=y diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/vexpress_a9.cfg b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/vexpress_a9.cfg new file mode 100644 index 0000000..bbe9ba3 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto/vexpress_a9.cfg @@ -0,0 +1,843 @@ +# Kernel fragment taken from linaro.org +# http://releases.linaro.org/platform/linaro-n/hwpacks/11.09/ + +CONFIG_AC97_BUS=y +CONFIG_AEABI=y +CONFIG_AIO=y +CONFIG_ALIGNMENT_TRAP=y +CONFIG_ANON_INODES=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_VEXPRESS=y +CONFIG_ARCH_VEXPRESS_CA9X4=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARM=y +CONFIG_ARM_AMBA=y +CONFIG_ARM_CPU_TOPOLOGY=y +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_ERRATA_720789=y +CONFIG_ARM_ERRATA_751472=y +CONFIG_ARM_GIC=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_THUMB=y +CONFIG_ARM_TIMER_SP804=y +CONFIG_ARM_UNWIND=y +CONFIG_AT76C50X_USB=m +CONFIG_ATA=y +CONFIG_ATAGS_PROC=y +CONFIG_ATA_BMDMA=y +CONFIG_ATA_SFF=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_ATH_COMMON=m +CONFIG_AUDIT=y +CONFIG_AUDIT_GENERIC=y +CONFIG_AUTOFS4_FS=m +CONFIG_AVERAGE=y +CONFIG_B43=m +CONFIG_B43LEGACY=m +CONFIG_B43LEGACY_DEBUG=y +CONFIG_B43LEGACY_DMA=y +CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y +CONFIG_B43LEGACY_HWRNG=y +CONFIG_B43LEGACY_LEDS=y +CONFIG_B43LEGACY_PIO=y +CONFIG_B43_HWRNG=y +CONFIG_B43_LEDS=y +CONFIG_B43_PHY_LP=y +CONFIG_B43_PHY_N=y +CONFIG_B43_PIO=y +CONFIG_B43_SSB=y +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +CONFIG_BCMA_POSSIBLE=y +CONFIG_BINARY_PRINTF=y +CONFIG_BINFMT_ELF=y +CONFIG_BITREVERSE=y +CONFIG_BLK_DEV=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_BLK_DEV_SD=y +CONFIG_BLOCK=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_BOUNCE=y +CONFIG_BRANCH_PROFILE_NONE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BT=m +CONFIG_BTRFS_FS=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BUG=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +CONFIG_CAN_PM_TRACE=y +CONFIG_CARL9170=m +CONFIG_CARL9170_LEDS=y +CONFIG_CARL9170_WPC=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CC_STACKPROTECTOR=y +CONFIG_CFG80211=m +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_CFG80211_WEXT=y +CONFIG_CGROUPS=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=ttyAMA0 mem=128M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +CONFIG_CONNECTOR=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_CPUSETS=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_HAS_PMU=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_PM=y +CONFIG_CPU_RMAP=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_V7=y +CONFIG_CRAMFS=y +CONFIG_CRC16=y +CONFIG_CRC32=y +CONFIG_CRC7=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC_T10DIF=y +CONFIG_CROSS_COMPILE="" +CONFIG_CRYPTO=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_PREEMPT=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_USER=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DEFAULT_CFQ=y +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_DEFAULT_SECURITY="apparmor" +CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DNOTIFY=y +CONFIG_DNS_RESOLVER=y +CONFIG_DTC=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_EARLY_PRINTK=y +CONFIG_ELF_CORE=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_EPOLL=y +CONFIG_EVENTFD=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_EVENT_TRACING=y +CONFIG_EXPERIMENTAL=y +CONFIG_EXPERT=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_XATTR=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_FAT_FS=y +CONFIG_FB=y +CONFIG_FB_ARMCLCD=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FILE_LOCKING=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_FONT_8x16=y +CONFIG_FONT_8x8=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAME_WARN=1024 +CONFIG_FREEZER=y +CONFIG_FSNOTIFY=y +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +CONFIG_FTRACE=y +CONFIG_FTRACE_MCOUNT_RECORD=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUTEX=y +CONFIG_FW_LOADER=y +CONFIG_GENERIC_ACL=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LOCKBREAK=y +CONFIG_GENERIC_TRACER=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAVE_AOUT=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_TWD=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PATA_PLATFORM=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GREENASIA=y +CONFIG_HID_GYRATION=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_KYE=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_SUPPORT=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HIGHMEM=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HOTPLUG=y +CONFIG_HOTPLUG_CPU=y +CONFIG_HWMON=m +CONFIG_HW_CONSOLE=y +CONFIG_HW_PERF_EVENTS=y +CONFIG_HW_RANDOM=m +CONFIG_HZ=100 +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_VERSATILE=y +CONFIG_ICST=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_INET=y +CONFIG_INET6_XFRM_MODE_BEET=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_INOTIFY_USER=y +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MOUSE=y +CONFIG_INPUT_MOUSEDEV=m +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_TWL4030_PWRBUTTON=y +CONFIG_INPUT_UINPUT=y +CONFIG_IOMMU_SUPPORT=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IPV6=y +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_SIT=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_WORK=y +CONFIG_ISO9660_FS=m +CONFIG_JBD=y +CONFIG_JBD2=y +CONFIG_JFFS2_CMODE_PRIORITY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JOLIET=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_KERNEL_GZIP=y +CONFIG_KEXEC=y +CONFIG_KEYBOARD_ATKBD=y +CONFIG_KEYBOARD_TWL4030=y +CONFIG_KEYS=y +CONFIG_KPROBES=y +CONFIG_KPROBES_SANITY_TEST=y +CONFIG_KPROBE_EVENT=y +CONFIG_KRETPROBES=y +CONFIG_KS8851=y +CONFIG_KS8851_MLL=y +CONFIG_KTIME_SCALAR=y +CONFIG_LBDAF=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=m +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LIB80211=m +CONFIG_LIBCRC32C=y +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_SPI=m +CONFIG_LIBERTAS_THINFIRM=m +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_USB=m +CONFIG_LOCAL_TIMERS=y +CONFIG_LOCKD=y +CONFIG_LOCKDEP=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LSM_MMAP_MIN_ADDR=0 +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_HWSIM=m +CONFIG_MAC80211_LEDS=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_PID=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MD=y +CONFIG_MII=y +CONFIG_MISC_FILESYSTEMS=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMU=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +CONFIG_MSDOS_FS=y +CONFIG_MSDOS_PARTITION=y +CONFIG_MTD=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_ONENAND=y +CONFIG_MTD_ONENAND_VERIFY_WRITE=y +CONFIG_MTD_OOPS=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BEB_RESERVE=1 +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_NAMESPACES=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEON=y +CONFIG_NET=y +CONFIG_NETDEVICES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETLABEL=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NETWORK_SECMARK=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_NET_SCHED=y +CONFIG_NEW_LEDS=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_NFS_FS=y +CONFIG_NFS_USE_KERNEL_DNS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NLATTR=y +CONFIG_NLS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_ISO8859_1=y +CONFIG_NOP_TRACER=y +CONFIG_NO_HZ=y +CONFIG_NR_CPUS=4 +CONFIG_OC_ETM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_I2C=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OF_SPI=y +CONFIG_OPROFILE=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_P54_COMMON=m +CONFIG_P54_LEDS=y +CONFIG_P54_SPI=m +CONFIG_P54_USB=m +CONFIG_PACKET=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PAGE_OFFSET=0x80000000 +CONFIG_PARTITION_ADVANCED=y +CONFIG_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +CONFIG_PLAT_VERSATILE=y +CONFIG_PLAT_VERSATILE_CLCD=y +CONFIG_PLAT_VERSATILE_SCHED_CLOCK=y +CONFIG_PM=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_PM_CLK=y +CONFIG_PM_DEBUG=y +CONFIG_PM_RUNTIME=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_POWER_SUPPLY=y +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_PREEMPT_RCU=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_PRINTK=y +CONFIG_PRINTK_TIME=y +CONFIG_PROC_EVENTS=y +CONFIG_PROC_FS=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROFILING=y +CONFIG_PROVE_LOCKING=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +CONFIG_RCU_FANOUT=32 +CONFIG_RD_BZIP2=y +CONFIG_RD_GZIP=y +CONFIG_RD_LZMA=y +CONFIG_RD_LZO=y +CONFIG_RD_XZ=y +CONFIG_REGMAP=y +CONFIG_RELAY=y +CONFIG_RFS_ACCEL=y +CONFIG_RING_BUFFER=y +CONFIG_RING_BUFFER_ALLOW_SWAP=y +CONFIG_ROOT_NFS=y +CONFIG_RPS=y +CONFIG_RT2500USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2800_LIB=m +CONFIG_RT2X00=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_LEDS=y +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT73USB=m +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PL031=y +CONFIG_RTC_DRV_TWL4030=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_DEV=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_LIB=y +CONFIG_RTL8187=m +CONFIG_RTL8187_LEDS=y +CONFIG_RTL8192CU=m +CONFIG_RTL8192C_COMMON=m +CONFIG_RTLWIFI=m +CONFIG_RT_MUTEXES=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_DEBUG=y +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +CONFIG_SCSI_LOWLEVEL=y +CONFIG_SCSI_MOD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_PROC_FS=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_WAIT_SCAN=m +CONFIG_SDIO_UART=y +CONFIG_SECCOMP=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PATH=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SMACK=y +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIO=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_SERPORT=y +CONFIG_SHMEM=y +CONFIG_SIGNALFD=y +CONFIG_SLAB=y +CONFIG_SLABINFO=y +CONFIG_SMC91X=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_SMSC911X=y +CONFIG_SMSC_PHY=y +CONFIG_SND=y +CONFIG_SND_AC97_CODEC=y +CONFIG_SND_ARM=y +CONFIG_SND_ARMAACI=y +CONFIG_SND_DEBUG=y +CONFIG_SND_DRIVERS=y +CONFIG_SND_HWDEP=m +CONFIG_SND_JACK=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_PCM=y +CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SOC=m +CONFIG_SND_SOC_I2C_AND_SPI=m +CONFIG_SND_SPI=y +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_TIMER=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_VERBOSE_PRINTK=y +CONFIG_SND_VERBOSE_PROCFS=y +CONFIG_SND_VMASTER=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=m +CONFIG_SPI_MASTER=y +CONFIG_SPI_PL022=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_SSB=m +CONFIG_SSB_BLOCKIO=y +CONFIG_SSB_POSSIBLE=y +CONFIG_SSB_SDIOHOST_POSSIBLE=y +CONFIG_STACKTRACE=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_STANDALONE=y +CONFIG_STOP_MACHINE=y +CONFIG_STRICT_DEVMEM=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SWAP=y +CONFIG_SWP_EMULATE=y +CONFIG_SYN_COOKIES=y +CONFIG_SYSCTL=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSFS=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TICK_ONESHOT=y +CONFIG_TIMERFD=y +CONFIG_TIMER_STATS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_TOUCHSCREEN_ADS7846=m +CONFIG_TRACEPOINTS=y +CONFIG_TRACE_IRQFLAGS=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_TRACING=y +CONFIG_TRACING_SUPPORT=y +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_TUN=y +CONFIG_TWL4030_CORE=y +CONFIG_TWL4030_POWER=y +CONFIG_TWL4030_WATCHDOG=y +CONFIG_TWL6030_USB=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_UID16=y +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +CONFIG_USB=y +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_BELKIN=y +CONFIG_USB_DEBUG=y +CONFIG_USB_DEVICE_CLASS=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_FUSB300=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_DEBUG_FS=y +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_HID=y +CONFIG_USB_ISP1760_HCD=y +CONFIG_USB_KC2190=y +CONFIG_USB_LIBUSUAL=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_MON=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +CONFIG_USB_NET_CDC_NCM=y +CONFIG_USB_NET_CDC_SUBSET=y +CONFIG_USB_NET_NET1080=y +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_ZAURUS=y +CONFIG_USB_OTG=y +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_TEST=y +CONFIG_USB_USBNET=y +CONFIG_USB_WDM=y +CONFIG_USB_ZD1201=m +CONFIG_USB_ZERO=m +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_USE_OF=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_VFAT_FS=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_VIRT_TO_BUS=y +CONFIG_VMSPLIT_2G=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_WATCHDOG=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PRIV=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_WL1251=m +CONFIG_WL1251_SDIO=m +CONFIG_WL1251_SPI=m +CONFIG_WL12XX=m +CONFIG_WL12XX_MENU=m +CONFIG_WL12XX_PLATFORM_DATA=y +CONFIG_WL12XX_SDIO=m +CONFIG_WL12XX_SPI=m +CONFIG_WLAN=y +CONFIG_XFRM=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_USER=y +CONFIG_XPS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_X86=y +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZD1211RW=m +CONFIG_ZISOFS=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA_FLAG=0 + +#additional configs requested by SysInfra EG +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y + +# config for adding support for AF_BUS sockets +CONFIG_AF_BUS=y +CONFIG_NETFILTER_DBUS=y + +CONFIG_REGULATOR=n + +# Activate ecryptfs +CONFIG_EXPERIMENTAL=y +CONFIG_KEYS=y +CONFIG_CRYPTO=y +CONFIG_ECRYPT_FS=y diff --git a/meta-ivi-bsp/recipes-kernel/linux/linux-yocto_3.8.bbappend b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto_3.8.bbappend new file mode 100644 index 0000000..c09d607 --- /dev/null +++ b/meta-ivi-bsp/recipes-kernel/linux/linux-yocto_3.8.bbappend @@ -0,0 +1,25 @@ +PRINC := "${@int(PRINC) + 2}" + +# find defconfig path +FILESEXTRAPATHS := "${THISDIR}/${PN}" + +# Kernel configs +SRC_URI_append_beagleboard = " \ + file://beagle_qemu.cfg \ + " + +SRC_URI_append_vexpressa9 = " \ + file://vexpress_a9.cfg \ + " + +SRC_URI_append_qemux86 = " \ + file://qemux86.cfg \ + " + +SRC_URI_append_qemux86-64 = " \ + file://qemux86_64.cfg \ + " + +KMACHINE_vexpressa9 = "beagleboard" +SRCREV_machine_vexpressa9 ?= "${AUTOREV}" +COMPATIBLE_MACHINE_vexpressa9 = "vexpressa9" |