From bb8165172ac2ef8c7092e8e82928cc7f5f310ab3 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 26 Sep 2018 06:47:52 -0700 Subject: bpo-31425: Expose AF_QIPCRTR in socket module (GH-3706) The AF_QIPCRTR address family was introduced in Linux v4.7. Co-authored-by: Bjorn Andersson --- Doc/library/socket.rst | 14 ++++++ Lib/test/test_socket.py | 41 +++++++++++++++++ .../2017-10-24-10-18-35.bpo-31425.1lgw47.rst | 3 ++ Modules/socketmodule.c | 51 +++++++++++++++++++++- Modules/socketmodule.h | 12 +++++ configure | 22 ++++++++++ configure.ac | 10 +++++ pyconfig.h.in | 3 ++ 8 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-10-24-10-18-35.bpo-31425.1lgw47.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 32e7c5e..44370fe 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -193,6 +193,13 @@ created. Socket addresses are represented as follows: - *addr* - Optional bytes-like object specifying the hardware physical address, whose interpretation depends on the device. +- :const:`AF_QIPCRTR` is a Linux-only socket based interface for communicating + with services running on co-processors in Qualcomm platforms. The address + family is represented as a ``(node, port)`` tuple where the *node* and *port* + are non-negative integers. + + .. versionadded:: 3.7 + If you use a hostname in the *host* portion of IPv4/v6 socket address, the program may show a nondeterministic behavior, as Python uses the first address returned from the DNS resolution. The socket address will be resolved @@ -481,6 +488,13 @@ Constants :const:`HCI_DATA_DIR` are not available for FreeBSD, NetBSD, or DragonFlyBSD. +.. data:: AF_QIPCRTR + + Constant for Qualcomm's IPC router protocol, used to communicate with + service providing remote processors. + + Availability: Linux >= 4.7. + Functions ^^^^^^^^^ diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index bd4fad1..bbbf27b 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -94,6 +94,16 @@ def _have_socket_alg(): s.close() return True +def _have_socket_qipcrtr(): + """Check whether AF_QIPCRTR sockets are supported on this host.""" + try: + s = socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM, 0) + except (AttributeError, OSError): + return False + else: + s.close() + return True + def _have_socket_vsock(): """Check whether AF_VSOCK sockets are supported on this host.""" ret = get_cid() is not None @@ -113,6 +123,8 @@ HAVE_SOCKET_RDS = _have_socket_rds() HAVE_SOCKET_ALG = _have_socket_alg() +HAVE_SOCKET_QIPCRTR = _have_socket_qipcrtr() + HAVE_SOCKET_VSOCK = _have_socket_vsock() # Size in bytes of the int type @@ -2054,6 +2066,34 @@ class RDSTest(ThreadedRDSSocketTest): self.data = b'select' self.cli.sendto(self.data, 0, (HOST, self.port)) +@unittest.skipUnless(HAVE_SOCKET_QIPCRTR, + 'QIPCRTR sockets required for this test.') +class BasicQIPCRTRTest(unittest.TestCase): + + def testCrucialConstants(self): + socket.AF_QIPCRTR + + def testCreateSocket(self): + with socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM) as s: + pass + + def testUnbound(self): + with socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM) as s: + self.assertEqual(s.getsockname()[1], 0) + + def testBindSock(self): + with socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM) as s: + support.bind_port(s, host=s.getsockname()[0]) + self.assertNotEqual(s.getsockname()[1], 0) + + def testInvalidBindSock(self): + with socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM) as s: + self.assertRaises(OSError, support.bind_port, s, host=-2) + + def testAutoBindSock(self): + with socket.socket(socket.AF_QIPCRTR, socket.SOCK_DGRAM) as s: + s.connect((123, 123)) + self.assertNotEqual(s.getsockname()[1], 0) @unittest.skipIf(fcntl is None, "need fcntl") @unittest.skipUnless(HAVE_SOCKET_VSOCK, @@ -5978,6 +6018,7 @@ def test_main(): tests.extend([BasicCANTest, CANTest]) tests.extend([BasicRDSTest, RDSTest]) tests.append(LinuxKernelCryptoAPI) + tests.append(BasicQIPCRTRTest) tests.extend([ BasicVSOCKTest, ThreadedVSOCKSocketStreamTest, diff --git a/Misc/NEWS.d/next/Library/2017-10-24-10-18-35.bpo-31425.1lgw47.rst b/Misc/NEWS.d/next/Library/2017-10-24-10-18-35.bpo-31425.1lgw47.rst new file mode 100644 index 0000000..c5d6467 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-24-10-18-35.bpo-31425.1lgw47.rst @@ -0,0 +1,3 @@ +Add support for sockets of the AF_QIPCRTR address family, supported by the +Linux kernel. This is used to communicate with services, such as GPS or +radio, running on Qualcomm devices. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index adaefad..9149641 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7,8 +7,8 @@ This module provides an interface to Berkeley socket IPC. Limitations: - Only AF_INET, AF_INET6 and AF_UNIX address families are supported in a - portable manner, though AF_PACKET, AF_NETLINK and AF_TIPC are supported - under Linux. + portable manner, though AF_PACKET, AF_NETLINK, AF_QIPCRTR and AF_TIPC are + supported under Linux. - No read/write operations (use sendall/recv or makefile instead). - Additional restrictions apply on some non-Unix platforms (compensated for by socket.py). @@ -55,6 +55,8 @@ Module interface: the Ethernet protocol number to be received. For example: ("eth0",0x1234). Optional 3rd,4th,5th elements in the tuple specify packet-type and ha-type/addr. +- an AF_QIPCRTR socket address is a (node, port) tuple where the + node and port are non-negative integers. - an AF_TIPC socket address is expressed as (addr_type, v1, v2, v3 [, scope]); where addr_type can be one of: TIPC_ADDR_NAMESEQ, TIPC_ADDR_NAME, and TIPC_ADDR_ID; @@ -1293,6 +1295,14 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) } #endif /* AF_NETLINK */ +#if defined(AF_QIPCRTR) + case AF_QIPCRTR: + { + struct sockaddr_qrtr *a = (struct sockaddr_qrtr *) addr; + return Py_BuildValue("II", a->sq_node, a->sq_port); + } +#endif /* AF_QIPCRTR */ + #if defined(AF_VSOCK) case AF_VSOCK: { @@ -1668,6 +1678,30 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif /* AF_NETLINK */ +#if defined(AF_QIPCRTR) + case AF_QIPCRTR: + { + struct sockaddr_qrtr* addr; + unsigned int node, port; + addr = (struct sockaddr_qrtr *)addr_ret; + if (!PyTuple_Check(args)) { + PyErr_Format( + PyExc_TypeError, + "getsockaddrarg: " + "AF_QIPCRTR address must be tuple, not %.500s", + Py_TYPE(args)->tp_name); + return 0; + } + if (!PyArg_ParseTuple(args, "II:getsockaddrarg", &node, &port)) + return 0; + addr->sq_family = AF_QIPCRTR; + addr->sq_node = node; + addr->sq_port = port; + *len_ret = sizeof(*addr); + return 1; + } +#endif /* AF_QIPCRTR */ + #if defined(AF_VSOCK) case AF_VSOCK: { @@ -2263,6 +2297,14 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) } #endif /* AF_NETLINK */ +#if defined(AF_QIPCRTR) + case AF_QIPCRTR: + { + *len_ret = sizeof (struct sockaddr_qrtr); + return 1; + } +#endif /* AF_QIPCRTR */ + #if defined(AF_VSOCK) case AF_VSOCK: { @@ -6983,6 +7025,11 @@ PyInit__socket(void) #endif #endif /* AF_NETLINK */ +#ifdef AF_QIPCRTR + /* Qualcomm IPCROUTER */ + PyModule_AddIntMacro(m, AF_QIPCRTR); +#endif + #ifdef AF_VSOCK PyModule_AddIntConstant(m, "AF_VSOCK", AF_VSOCK); PyModule_AddIntConstant(m, "SO_VM_SOCKETS_BUFFER_SIZE", 0); diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 0b2edc1..dff1f8f 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -54,6 +54,15 @@ typedef int socklen_t; # undef AF_NETLINK #endif +#ifdef HAVE_LINUX_QRTR_H +# ifdef HAVE_ASM_TYPES_H +# include +# endif +# include +#else +# undef AF_QIPCRTR +#endif + #ifdef HAVE_BLUETOOTH_BLUETOOTH_H #include #include @@ -203,6 +212,9 @@ typedef union sock_addr { #ifdef HAVE_SOCKADDR_ALG struct sockaddr_alg alg; #endif +#ifdef AF_QIPCRTR + struct sockaddr_qrtr sq; +#endif #ifdef AF_VSOCK struct sockaddr_vm vm; #endif diff --git a/configure b/configure index 38546d6..9d2c4e4 100755 --- a/configure +++ b/configure @@ -8023,6 +8023,28 @@ fi done +# On Linux, qrtr.h requires asm/types.h +for ac_header in linux/qrtr.h +do : + ac_fn_c_check_header_compile "$LINENO" "linux/qrtr.h" "ac_cv_header_linux_qrtr_h" " +#ifdef HAVE_ASM_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +" +if test "x$ac_cv_header_linux_qrtr_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LINUX_QRTR_H 1 +_ACEOF + +fi + +done + + for ac_header in linux/vm_sockets.h do : ac_fn_c_check_header_compile "$LINENO" "linux/vm_sockets.h" "ac_cv_header_linux_vm_sockets_h" " diff --git a/configure.ac b/configure.ac index 96331ec..2235a13 100644 --- a/configure.ac +++ b/configure.ac @@ -2112,6 +2112,16 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[ #endif ]) +# On Linux, qrtr.h requires asm/types.h +AC_CHECK_HEADERS(linux/qrtr.h,,,[ +#ifdef HAVE_ASM_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +]) + AC_CHECK_HEADERS(linux/vm_sockets.h,,,[ #ifdef HAVE_SYS_SOCKET_H #include diff --git a/pyconfig.h.in b/pyconfig.h.in index 41e0479..254a2dc 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -615,6 +615,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETLINK_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_QRTR_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_RANDOM_H -- cgit v0.12