From 4d609c08e748168f03ab5ed8b9976996f3fcdf02 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Tue, 25 Jul 2017 17:54:23 -0700 Subject: [PATCH 01/13] Added support for CAN_ISOTP protocol --- Modules/socketmodule.c | 72 ++++++++++++++++++++++++++++++++++++++++-- Modules/socketmodule.h | 4 +++ configure | 4 +-- configure.ac | 4 +-- pyconfig.h.in | 3 ++ 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index e18dd32d90045d..8def56a959e81a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1869,7 +1869,9 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif -#if defined(AF_CAN) && defined(CAN_RAW) && defined(CAN_BCM) +#if defined(AF_CAN) + +#if defined(CAN_RAW) && defined(CAN_BCM) case AF_CAN: switch (s->sock_proto) { case CAN_RAW: @@ -1880,7 +1882,6 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, PyObject *interfaceName; struct ifreq ifr; Py_ssize_t len; - addr = (struct sockaddr_can *)addr_ret; if (!PyArg_ParseTuple(args, "O&", PyUnicode_FSConverter, @@ -1913,6 +1914,54 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, Py_DECREF(interfaceName); return 1; } +#endif + +#if defined(CAN_ISOTP) + case CAN_ISOTP: + { + struct sockaddr_can *addr; + PyObject *interfaceName; + struct ifreq ifr; + Py_ssize_t len; + unsigned long int rx_id, tx_id; + + addr = (struct sockaddr_can *)addr_ret; + + if (!PyArg_ParseTuple(args, "O&kk", PyUnicode_FSConverter, + &interfaceName, + &rx_id, + &tx_id)) + return 0; + + len = PyBytes_GET_SIZE(interfaceName); + + if (len == 0) { + ifr.ifr_ifindex = 0; + } else if ((size_t)len < sizeof(ifr.ifr_name)) { + strncpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName), sizeof(ifr.ifr_name)); + ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; + if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + s->errorhandler(); + Py_DECREF(interfaceName); + return 0; + } + } else { + PyErr_SetString(PyExc_OSError, + "AF_CAN interface name too long"); + Py_DECREF(interfaceName); + return 0; + } + + addr->can_family = AF_CAN; + addr->can_ifindex = ifr.ifr_ifindex; + addr->can_addr.tp.rx_id = rx_id; + addr->can_addr.tp.tx_id = tx_id; + + *len_ret = sizeof(*addr); + Py_DECREF(interfaceName); + return 1; + } +#endif default: PyErr_SetString(PyExc_OSError, "getsockaddrarg: unsupported CAN protocol"); @@ -7018,6 +7067,25 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT); PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED); #endif +#ifdef HAVE_LINUX_CAN_ISOTP_H + PyModule_AddIntMacro(m, SOL_CAN_ISOTP); + PyModule_AddIntMacro(m, CAN_ISOTP); + PyModule_AddIntMacro(m, CAN_ISOTP_OPTS); + PyModule_AddIntMacro(m, CAN_ISOTP_RECV_FC); + PyModule_AddIntMacro(m, CAN_ISOTP_TX_STMIN); + PyModule_AddIntMacro(m, CAN_ISOTP_RX_STMIN); + PyModule_AddIntMacro(m, CAN_ISOTP_LL_OPTS); + PyModule_AddIntMacro(m, CAN_ISOTP_LISTEN_MODE); + PyModule_AddIntMacro(m, CAN_ISOTP_EXTEND_ADDR); + PyModule_AddIntMacro(m, CAN_ISOTP_TX_PADDING); + PyModule_AddIntMacro(m, CAN_ISOTP_RX_PADDING); + PyModule_AddIntMacro(m, CAN_ISOTP_CHK_PAD_LEN); + PyModule_AddIntMacro(m, CAN_ISOTP_CHK_PAD_DATA); + PyModule_AddIntMacro(m, CAN_ISOTP_HALF_DUPLEX); + PyModule_AddIntMacro(m, CAN_ISOTP_FORCE_TXSTMIN); + PyModule_AddIntMacro(m, CAN_ISOTP_FORCE_RXSTMIN); + PyModule_AddIntMacro(m, CAN_ISOTP_RX_EXT_ADDR); +#endif #ifdef SOL_RDS PyModule_AddIntMacro(m, SOL_RDS); #endif diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 03f982b91083f3..e1141e683b4e87 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -91,6 +91,10 @@ typedef int socklen_t; #include #endif +#ifdef HAVE_LINUX_CAN_ISOTP_H +#include +#endif + #ifdef HAVE_SYS_SYS_DOMAIN_H #include #endif diff --git a/configure b/configure index a6f7d2bd7f0d9e..7a4f61696b571a 100755 --- a/configure +++ b/configure @@ -8127,8 +8127,8 @@ fi done -# On Linux, can.h and can/raw.h require sys/socket.h -for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h +# On Linux, can.h, can/raw.h, can/isotp.h require sys/socket.h +for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h linux/can/isotp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " diff --git a/configure.ac b/configure.ac index a5da830e89623c..3058fb20d7b889 100644 --- a/configure.ac +++ b/configure.ac @@ -2120,8 +2120,8 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[ #endif ]) -# On Linux, can.h and can/raw.h require sys/socket.h -AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h,,,[ +# On Linux, can.h, can/raw.h, can/isotp.h require sys/socket.h +AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h linux/can/isotp.h,,,[ #ifdef HAVE_SYS_SOCKET_H #include #endif diff --git a/pyconfig.h.in b/pyconfig.h.in index 0dd05aa65b8a4f..77b6e90a6c0936 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -559,6 +559,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_CAN_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_CAN_ISOTP_H + /* Define if compiling using Linux 3.6 or later. */ #undef HAVE_LINUX_CAN_RAW_FD_FRAMES From ccd65b8cccd9096c006e10aa6e0dfcac7d9c2fbd Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 30 Jul 2017 19:53:24 -0700 Subject: [PATCH 02/13] Added unit tests for CAN ISOTP --- Lib/test/test_socket.py | 232 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index f28f7d6816e925..7b743e9a4e9913 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -55,6 +55,16 @@ def _have_socket_can(): s.close() return True +def _have_socket_can_isotp(): + """Check whether CAN ISOTP sockets are supported on this host.""" + try: + s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + except (AttributeError, OSError): + return False + else: + s.close() + return True + def _have_socket_rds(): """Check whether RDS sockets are supported on this host.""" try: @@ -77,6 +87,8 @@ def _have_socket_alg(): HAVE_SOCKET_CAN = _have_socket_can() +HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp() + HAVE_SOCKET_RDS = _have_socket_rds() HAVE_SOCKET_ALG = _have_socket_alg() @@ -353,6 +365,48 @@ def clientTearDown(self): self.cli = None ThreadableTest.clientTearDown(self) +class ThreadableISOTPTest(unittest.TestCase, ThreadableTest): + """To be able to run this test, a `vcan0` CAN interface can be created with + the following commands: + # modprobe vcan + # ip link add dev vcan0 type vcan + # ifconfig vcan0 up + """ + interface = 'vcan0' + addr_offset=1 + address_lock = threading.Lock() + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self,*args, **kwargs) + ThreadableTest.__init__(self) + (self.cli_addr, self.srv_addr) = ThreadableISOTPTest.getAddressPair() + + @classmethod + def getAddressPair(cls): + cls.address_lock.acquire_lock() + latched_offset = cls.addr_offset + cls.addr_offset += 2 + if cls.addr_offset > 0x7FE: # 11 bit standard CAN identifier (ISO-11898-2) + cls.addr_offset = 1 + cls.address_lock.release_lock() + return (latched_offset, latched_offset+1) + + def clientSetUp(self): + try: + self.cli = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + if 'cli' in self.opts: + for opt_const in self.opts['cli']: + self.serv.setsockopt(socket.SOL_CAN_ISOTP, opt_const, self.opts['cli'][opt_const]) + self.cli.bind((self.interface, self.srv_addr, self.cli_addr)) + except OSError: + # skipTest should not be called here, and will be called in the + # server instead + pass + + def clientTearDown(self): + self.cli.close() + self.cli = None + ThreadableTest.clientTearDown(self) + class ThreadedRDSSocketTest(SocketRDSTest, ThreadableTest): def __init__(self, methodName='runTest'): @@ -1709,6 +1763,184 @@ def testBCM(self): self.assertEqual(bytes_sent, len(header_plus_frame)) +@unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') +class BasicISOTPTest(unittest.TestCase): + + def testCrucialConstants(self): + socket.AF_CAN + socket.PF_CAN + socket.CAN_ISOTP + socket.SOCK_DGRAM + + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP"), + 'socket.CAN_ISOTP required for this test.') + def testISOTPConstants(self): + socket.CAN_ISOTP + + socket.SOL_CAN_ISOTP # Socket Option Level for ISOTP protocol (AF_CAN + 6) + socket.CAN_ISOTP_OPTS # sets struct can_isotp_options within the driver. + socket.CAN_ISOTP_RECV_FC # set flow control options for receiver + socket.CAN_ISOTP_TX_STMIN # override sepration time received in flow control frame + socket.CAN_ISOTP_RX_STMIN # force to ignore messages received in an interval smaller than this value + socket.CAN_ISOTP_LL_OPTS # sets struct can_isotp_ll_options within the driver + + # Options flags + socket.CAN_ISOTP_LISTEN_MODE # listen only (do not send FC) + socket.CAN_ISOTP_EXTEND_ADDR # enable extended addressing + socket.CAN_ISOTP_TX_PADDING # enable CAN frame padding tx path + socket.CAN_ISOTP_RX_PADDING # enable CAN frame padding rx path + socket.CAN_ISOTP_CHK_PAD_LEN # check received CAN frame padding + socket.CAN_ISOTP_CHK_PAD_DATA # check received CAN frame padding + socket.CAN_ISOTP_HALF_DUPLEX # half duplex error state handling + socket.CAN_ISOTP_FORCE_TXSTMIN # ignore stmin from received FC + socket.CAN_ISOTP_FORCE_RXSTMIN # ignore CFs depending on rx stmin + socket.CAN_ISOTP_RX_EXT_ADDR # different rx extended addressing + + + def testCreateSocket(self): + with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s: + pass + + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP"), + 'socket.CAN_ISOTP required for this test.') + def testCreateISOTPSocket(self): + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + pass + + def testTooLongInterfaceName(self): + # most systems limit IFNAMSIZ to 16, take 1024 to be sure + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + self.assertRaisesRegex(OSError, 'interface name too long', + s.bind, ('x' * 1024,1,2)) + + @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + 'socket.SOL_CAN_ISOTP is required for this test') + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_OPTS"), + 'socket.CAN_ISOTP_OPTS is required for this test') + def testSetOpts(self): + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + flags = 0x01234567 + frame_txtime = 0x89ABCDEF + ext_address = 0x11 + txpad_content = 0x21 + rxpad_content = 0x31 + rx_ext_address = ext_address + opts = struct.pack("=LLBBBB",flags, frame_txtime, ext_address, txpad_content, rxpad_content, rx_ext_address) + s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_OPTS, opts) + readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_OPTS, len(opts)) + self.assertEqual(readopts,opts) + + @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + 'socket.SOL_CAN_ISOTP is required for this test') + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), + 'socket.CAN_ISOTP_RECV_FC is required for this test') + def testSetFCOpts(self): + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + bs = 0x12 + stmin = 0x22 + wftmax = 0x32 + opts = struct.pack("=BBB",bs, stmin, wftmax) + s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, opts) + readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, len(opts)) + self.assertEqual(readopts,opts) + + @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + 'socket.SOL_CAN_ISOTP is required for this test') + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_LL_OPTS"), + 'socket.CAN_ISOTP_LL_OPTS is required for this test') + def testSetLLOpts(self): + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + mtu = 16 + tx_dl = 8 + tx_flags = 0x13 + opts = struct.pack("=BBB",mtu, tx_dl, tx_flags) + s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_LL_OPTS, opts) + readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_LL_OPTS, len(opts)) + self.assertEqual(readopts,opts) + + +@unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') +class ISOTPTest(ThreadableISOTPTest): + + test_opts = { + 'testTiming' : { + 'cli' : { + socket.CAN_ISOTP_RECV_FC : struct.pack("=BBB",10, 0x7F, 10) # bs, stmin, wftmax + }, + 'serv' : { + + } + } + } + + def __init__(self, methodName='runTest', *args, **kwargs): + ThreadableISOTPTest.__init__(self,methodName, *args, **kwargs) + if methodName in ISOTPTest.test_opts: + self.opts = ISOTPTest.test_opts[methodName] + else: + self.opts = {} + + def setUp(self, *args, **kwargs): + self.serv = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + try: + if 'serv' in self.opts: + for opt_const in self.opts['serv']: + self.serv.setsockopt(socket.SOL_CAN_ISOTP, opt_const, self.opts['serv'][opt_const]) + self.serv.bind((self.interface, self.cli_addr, self.srv_addr)) + except OSError: + self.skipTest('network interface `%s` does not exist' % + self.interface) + + def tearDown(self): + self.serv.close() + self.serv=None + + @classmethod + def makeRandomData(cls, n): + data = [random.randint(0,0xFF) for i in range(0,n)] + return struct.pack("B"*len(data), *data) + + def _testFullFrame(self): + self.bindata = self.makeRandomData(4095) + self.cli.send(self.bindata) + + def testFullFrame(self): + time.sleep(0.1) + bindata = self.serv.recv(len(self.bindata)) + self.assertEqual(bindata, self.bindata) + + def _testTiming(self): + self.bindata2 = self.makeRandomData(150) + self.tic = time.time() + self.cli.send(self.bindata2) + + @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + 'socket.SOL_CAN_ISOTP is required for this test') + @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), + 'socket.CAN_ISOTP_RECV_FC is required for this test') + def testTiming(self): + """ + The main goal of this test is to validate that the driver correctly receive options via setsockopt. + We are not trying to validate the proper functioning of the driver itself. + """ + time.sleep(0.1) + bindata2 = self.serv.recv(len(self.bindata2)) + self.toc = time.time() + self.assertEqual(bindata2, self.bindata2) + measured_time = math.ceil(self.toc-self.tic) + min_time = math.floor(self.calcFrameCnt(len(self.bindata2))*0.127) + self.assertGreater(measured_time, min_time) + + def calcFrameCnt(self, size): + if size < 1 or size > 4095: + raise ValueError('ISOTP frame size must be between 1 and 4095') + + if (size < 8): + return 1 + else: + return int(1 + math.ceil((float(size) - 6.0) / 7.0)) + + @unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.') class BasicRDSTest(unittest.TestCase): From 00171565e2566ef7e4ef21c1081c50bc5ddcba71 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 30 Jul 2017 21:19:30 -0700 Subject: [PATCH 03/13] Updated documentation for ISO-TP protocol --- Doc/library/socket.rst | 59 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 8af6bc5439beea..cab4b397529881 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -103,6 +103,10 @@ created. Socket addresses are represented as follows: ``'can0'``. The network interface name ``''`` can be used to receive packets from all network interfaces of this family. + - :const:`CAN_ISOTP` protocol require a tuple ``(interface, rx_addr, tx_addr)`` + where both additional parameters are unsigned long integer that represent a + CAN identifier (standard or extended). + - A string or a tuple ``(id, unit)`` is used for the :const:`SYSPROTO_CONTROL` protocol of the :const:`PF_SYSTEM` family. The string is the name of a kernel control using a dynamically-assigned ID. The tuple can be used if ID @@ -341,6 +345,17 @@ Constants .. versionadded:: 3.5 +.. data:: CAN_ISOTP + + CAN_ISOTP, in the CAN protocol family, is the ISO-TP (ISO 15765-2) protocol. + ISO-TP constants, documented in the Linux documentation, are also + defined in the socket module. + + Availability: Linux >= 2.6.25 + + .. versionadded:: 3.7 + + .. data:: AF_RDS PF_RDS SOL_RDS @@ -427,7 +442,7 @@ The following functions all create :ref:`socket objects `. :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_`` constants. The protocol number is usually zero and may be omitted or in the case where the address family is :const:`AF_CAN` the protocol should be one - of :const:`CAN_RAW` or :const:`CAN_BCM`. If *fileno* is specified, the other + of :const:`CAN_RAW`, :const:`CAN_BCM` or :const:`CAN_ISOTP`. If *fileno* is specified, the other arguments are ignored, causing the socket with the specified file descriptor to return. Unlike :func:`socket.fromfd`, *fileno* will return the same socket and not a duplicate. This may help close a detached socket using @@ -445,6 +460,8 @@ The following functions all create :ref:`socket objects `. .. versionchanged:: 3.4 The returned socket is now non-inheritable. + .. versionchanged:: 3.7 + The CAN_ISOTP protocol was added. .. function:: socketpair([family[, type[, proto]]]) @@ -1661,7 +1678,7 @@ the interface:: # disabled promiscuous mode s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) -The last example shows how to use the socket interface to communicate to a CAN +The next example shows how to use the socket interface to communicate to a CAN network using the raw socket protocol. To use CAN with the broadcast manager protocol instead, open a socket with:: @@ -1729,6 +1746,44 @@ There is a :mod:`socket` flag to set, in order to prevent this, the :data:`SO_REUSEADDR` flag tells the kernel to reuse a local socket in ``TIME_WAIT`` state, without waiting for its natural timeout to expire. +The last example shows how to use the socket interface to communicate to a CAN +network using the ISO-TP protocol. Altough Linux defines the required interface +in , it does not support a native implementation of the protocol. +The appropriate module must be loaded in the kernel. + + modprobe can-isotp + +If the system does not support ISO-TP protocol, creating the socket might return:: + + OSError: [Errno 93] Protocol not supported + +This example might require special privileges:: + + import socket + import struct + + # create a iso-tp socket and bind it to the 'vcan0' interface, with rx_addr = 0x123 and tx_addr=0x456 + s1 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + s2 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + + # set socket some options before binding to the interface. + stmin = 0x32 # Minimum Separation Time : 50 milliseconds between each CAN frame. See ISO 15765-2 for acceptables values. + bs = 3 # Block Size : sends a FlowControl frame every 3 Consecutive Frames + wftmax = 5 # Maximum Wait Frame Transmission - Maximum number of consecutive Wait Frame. + s2.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, struct.pack("=BBB", bs, stmin, wftmax)) + + s1.bind(('vcan0',0x123, 0x456)) + s2.bind(('vcan0',0x456, 0x123)) + + data = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10" + s1.send(data) + s2.recv(len(data)) + + # The following transmission will occur + # vcan0 456 [8] 10 0A 01 02 03 04 05 06 + # vcan0 123 [3] 30 03 32 + # vcan0 456 [5] 21 07 08 09 10 + .. seealso:: From 85a154523464d5103c35856294ed719ed37e50d2 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 30 Jul 2017 21:55:45 -0700 Subject: [PATCH 04/13] Removed trailing whitespace in documentation --- Doc/library/socket.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index cab4b397529881..612caf1d697331 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -103,7 +103,7 @@ created. Socket addresses are represented as follows: ``'can0'``. The network interface name ``''`` can be used to receive packets from all network interfaces of this family. - - :const:`CAN_ISOTP` protocol require a tuple ``(interface, rx_addr, tx_addr)`` + - :const:`CAN_ISOTP` protocol require a tuple ``(interface, rx_addr, tx_addr)`` where both additional parameters are unsigned long integer that represent a CAN identifier (standard or extended). From 32399d4f7960d687becec551a80d04bf74130878 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 30 Jul 2017 22:00:40 -0700 Subject: [PATCH 05/13] Added blurb NEWS.d file --- .../NEWS.d/next/Library/2017-07-30-22-00-12.bpo-30987.228rW0.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2017-07-30-22-00-12.bpo-30987.228rW0.rst diff --git a/Misc/NEWS.d/next/Library/2017-07-30-22-00-12.bpo-30987.228rW0.rst b/Misc/NEWS.d/next/Library/2017-07-30-22-00-12.bpo-30987.228rW0.rst new file mode 100644 index 00000000000000..de30ea8850c33e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-07-30-22-00-12.bpo-30987.228rW0.rst @@ -0,0 +1 @@ +Added support for CAN ISO-TP protocol in the socket module. From fc058f8810a91fc6ea8b659b7ec650503ecbc5a3 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 30 Jul 2017 22:14:10 -0700 Subject: [PATCH 06/13] updated Misc/ACKS --- Misc/ACKS | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/ACKS b/Misc/ACKS index e0640c114ff34f..7b709ba41f4839 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1766,3 +1766,4 @@ Jelle Zijlstra Gennadiy Zlobin Doug Zongker Peter Åstrand +Pier-Yves Lessard From 2aa760dfc9aa1c7f0ff242b2619cdae714d29184 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Mon, 31 Jul 2017 06:38:12 -0700 Subject: [PATCH 07/13] Fixed broken unit test that was using isotp const outside of skippable section --- Lib/test/test_socket.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 7b743e9a4e9913..ae07ec386a6dbb 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -378,6 +378,7 @@ class ThreadableISOTPTest(unittest.TestCase, ThreadableTest): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self,*args, **kwargs) ThreadableTest.__init__(self) + self.test_opts = {} (self.cli_addr, self.srv_addr) = ThreadableISOTPTest.getAddressPair() @classmethod @@ -392,6 +393,7 @@ def getAddressPair(cls): def clientSetUp(self): try: + self.opts = self.test_opts[self._testMethodName] if self._testMethodName in self.test_opts else {} self.cli = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) if 'cli' in self.opts: for opt_const in self.opts['cli']: @@ -1860,28 +1862,25 @@ def testSetLLOpts(self): @unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') +@unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + 'socket.SOL_CAN_ISOTP is required for this test') +@unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), + 'socket.CAN_ISOTP_RECV_FC is required for this test') class ISOTPTest(ThreadableISOTPTest): - test_opts = { - 'testTiming' : { + def __init__(self, methodName='runTest', *args, **kwargs): + ThreadableISOTPTest.__init__(self,methodName, *args, **kwargs) + + def setUp(self, *args, **kwargs): + self.test_opts['testTiming'] = { 'cli' : { socket.CAN_ISOTP_RECV_FC : struct.pack("=BBB",10, 0x7F, 10) # bs, stmin, wftmax }, - 'serv' : { - - } + 'serv' : {} } - } - def __init__(self, methodName='runTest', *args, **kwargs): - ThreadableISOTPTest.__init__(self,methodName, *args, **kwargs) - if methodName in ISOTPTest.test_opts: - self.opts = ISOTPTest.test_opts[methodName] - else: - self.opts = {} - - def setUp(self, *args, **kwargs): self.serv = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) + self.opts = self.test_opts[self._testMethodName] if self._testMethodName in self.test_opts else {} try: if 'serv' in self.opts: for opt_const in self.opts['serv']: @@ -1910,14 +1909,10 @@ def testFullFrame(self): self.assertEqual(bindata, self.bindata) def _testTiming(self): - self.bindata2 = self.makeRandomData(150) + self.bindata2 = self.makeRandomData(512) self.tic = time.time() self.cli.send(self.bindata2) - @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), - 'socket.SOL_CAN_ISOTP is required for this test') - @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), - 'socket.CAN_ISOTP_RECV_FC is required for this test') def testTiming(self): """ The main goal of this test is to validate that the driver correctly receive options via setsockopt. @@ -1929,7 +1924,7 @@ def testTiming(self): self.assertEqual(bindata2, self.bindata2) measured_time = math.ceil(self.toc-self.tic) min_time = math.floor(self.calcFrameCnt(len(self.bindata2))*0.127) - self.assertGreater(measured_time, min_time) + self.assertGreaterEqual(measured_time, min_time) def calcFrameCnt(self, size): if size < 1 or size > 4095: From fba09e6dc3eb1b8967dcfe57eb4c7868a3586b58 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Wed, 2 Aug 2017 19:22:45 -0700 Subject: [PATCH 08/13] Removed dependecy over third party project --- Doc/library/socket.rst | 43 +-------- Lib/test/test_socket.py | 193 ++-------------------------------------- Misc/ACKS | 6 +- Modules/socketmodule.c | 21 +---- Modules/socketmodule.h | 4 - configure | 4 +- configure.ac | 4 +- pyconfig.h.in | 3 - 8 files changed, 18 insertions(+), 260 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 612caf1d697331..c5064e92c2118b 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -348,8 +348,7 @@ Constants .. data:: CAN_ISOTP CAN_ISOTP, in the CAN protocol family, is the ISO-TP (ISO 15765-2) protocol. - ISO-TP constants, documented in the Linux documentation, are also - defined in the socket module. + ISO-TP constants, documented in the Linux documentation. Availability: Linux >= 2.6.25 @@ -1688,7 +1687,7 @@ After binding (:const:`CAN_RAW`) or connecting (:const:`CAN_BCM`) the socket, yo can use the :meth:`socket.send`, and the :meth:`socket.recv` operations (and their counterparts) on the socket object as usual. -This example might require special privileges:: +This last example might require special privileges:: import socket import struct @@ -1746,44 +1745,6 @@ There is a :mod:`socket` flag to set, in order to prevent this, the :data:`SO_REUSEADDR` flag tells the kernel to reuse a local socket in ``TIME_WAIT`` state, without waiting for its natural timeout to expire. -The last example shows how to use the socket interface to communicate to a CAN -network using the ISO-TP protocol. Altough Linux defines the required interface -in , it does not support a native implementation of the protocol. -The appropriate module must be loaded in the kernel. - - modprobe can-isotp - -If the system does not support ISO-TP protocol, creating the socket might return:: - - OSError: [Errno 93] Protocol not supported - -This example might require special privileges:: - - import socket - import struct - - # create a iso-tp socket and bind it to the 'vcan0' interface, with rx_addr = 0x123 and tx_addr=0x456 - s1 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) - s2 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) - - # set socket some options before binding to the interface. - stmin = 0x32 # Minimum Separation Time : 50 milliseconds between each CAN frame. See ISO 15765-2 for acceptables values. - bs = 3 # Block Size : sends a FlowControl frame every 3 Consecutive Frames - wftmax = 5 # Maximum Wait Frame Transmission - Maximum number of consecutive Wait Frame. - s2.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, struct.pack("=BBB", bs, stmin, wftmax)) - - s1.bind(('vcan0',0x123, 0x456)) - s2.bind(('vcan0',0x456, 0x123)) - - data = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10" - s1.send(data) - s2.recv(len(data)) - - # The following transmission will occur - # vcan0 456 [8] 10 0A 01 02 03 04 05 06 - # vcan0 123 [3] 30 03 32 - # vcan0 456 [5] 21 07 08 09 10 - .. seealso:: diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ae07ec386a6dbb..ca39fd0f73db5a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -365,50 +365,6 @@ def clientTearDown(self): self.cli = None ThreadableTest.clientTearDown(self) -class ThreadableISOTPTest(unittest.TestCase, ThreadableTest): - """To be able to run this test, a `vcan0` CAN interface can be created with - the following commands: - # modprobe vcan - # ip link add dev vcan0 type vcan - # ifconfig vcan0 up - """ - interface = 'vcan0' - addr_offset=1 - address_lock = threading.Lock() - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self,*args, **kwargs) - ThreadableTest.__init__(self) - self.test_opts = {} - (self.cli_addr, self.srv_addr) = ThreadableISOTPTest.getAddressPair() - - @classmethod - def getAddressPair(cls): - cls.address_lock.acquire_lock() - latched_offset = cls.addr_offset - cls.addr_offset += 2 - if cls.addr_offset > 0x7FE: # 11 bit standard CAN identifier (ISO-11898-2) - cls.addr_offset = 1 - cls.address_lock.release_lock() - return (latched_offset, latched_offset+1) - - def clientSetUp(self): - try: - self.opts = self.test_opts[self._testMethodName] if self._testMethodName in self.test_opts else {} - self.cli = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) - if 'cli' in self.opts: - for opt_const in self.opts['cli']: - self.serv.setsockopt(socket.SOL_CAN_ISOTP, opt_const, self.opts['cli'][opt_const]) - self.cli.bind((self.interface, self.srv_addr, self.cli_addr)) - except OSError: - # skipTest should not be called here, and will be called in the - # server instead - pass - - def clientTearDown(self): - self.cli.close() - self.cli = None - ThreadableTest.clientTearDown(self) - class ThreadedRDSSocketTest(SocketRDSTest, ThreadableTest): def __init__(self, methodName='runTest'): @@ -1766,7 +1722,7 @@ def testBCM(self): @unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') -class BasicISOTPTest(unittest.TestCase): +class ISOTPTest(unittest.TestCase): def testCrucialConstants(self): socket.AF_CAN @@ -1774,31 +1730,6 @@ def testCrucialConstants(self): socket.CAN_ISOTP socket.SOCK_DGRAM - @unittest.skipUnless(hasattr(socket, "CAN_ISOTP"), - 'socket.CAN_ISOTP required for this test.') - def testISOTPConstants(self): - socket.CAN_ISOTP - - socket.SOL_CAN_ISOTP # Socket Option Level for ISOTP protocol (AF_CAN + 6) - socket.CAN_ISOTP_OPTS # sets struct can_isotp_options within the driver. - socket.CAN_ISOTP_RECV_FC # set flow control options for receiver - socket.CAN_ISOTP_TX_STMIN # override sepration time received in flow control frame - socket.CAN_ISOTP_RX_STMIN # force to ignore messages received in an interval smaller than this value - socket.CAN_ISOTP_LL_OPTS # sets struct can_isotp_ll_options within the driver - - # Options flags - socket.CAN_ISOTP_LISTEN_MODE # listen only (do not send FC) - socket.CAN_ISOTP_EXTEND_ADDR # enable extended addressing - socket.CAN_ISOTP_TX_PADDING # enable CAN frame padding tx path - socket.CAN_ISOTP_RX_PADDING # enable CAN frame padding rx path - socket.CAN_ISOTP_CHK_PAD_LEN # check received CAN frame padding - socket.CAN_ISOTP_CHK_PAD_DATA # check received CAN frame padding - socket.CAN_ISOTP_HALF_DUPLEX # half duplex error state handling - socket.CAN_ISOTP_FORCE_TXSTMIN # ignore stmin from received FC - socket.CAN_ISOTP_FORCE_RXSTMIN # ignore CFs depending on rx stmin - socket.CAN_ISOTP_RX_EXT_ADDR # different rx extended addressing - - def testCreateSocket(self): with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s: pass @@ -1815,125 +1746,15 @@ def testTooLongInterfaceName(self): self.assertRaisesRegex(OSError, 'interface name too long', s.bind, ('x' * 1024,1,2)) - @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), - 'socket.SOL_CAN_ISOTP is required for this test') - @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_OPTS"), - 'socket.CAN_ISOTP_OPTS is required for this test') - def testSetOpts(self): - with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - flags = 0x01234567 - frame_txtime = 0x89ABCDEF - ext_address = 0x11 - txpad_content = 0x21 - rxpad_content = 0x31 - rx_ext_address = ext_address - opts = struct.pack("=LLBBBB",flags, frame_txtime, ext_address, txpad_content, rxpad_content, rx_ext_address) - s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_OPTS, opts) - readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_OPTS, len(opts)) - self.assertEqual(readopts,opts) - - @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), - 'socket.SOL_CAN_ISOTP is required for this test') - @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), - 'socket.CAN_ISOTP_RECV_FC is required for this test') - def testSetFCOpts(self): - with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - bs = 0x12 - stmin = 0x22 - wftmax = 0x32 - opts = struct.pack("=BBB",bs, stmin, wftmax) - s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, opts) - readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_RECV_FC, len(opts)) - self.assertEqual(readopts,opts) - - @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), - 'socket.SOL_CAN_ISOTP is required for this test') - @unittest.skipUnless(hasattr(socket, "CAN_ISOTP_LL_OPTS"), - 'socket.CAN_ISOTP_LL_OPTS is required for this test') - def testSetLLOpts(self): - with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - mtu = 16 - tx_dl = 8 - tx_flags = 0x13 - opts = struct.pack("=BBB",mtu, tx_dl, tx_flags) - s.setsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_LL_OPTS, opts) - readopts = s.getsockopt(socket.SOL_CAN_ISOTP, socket.CAN_ISOTP_LL_OPTS, len(opts)) - self.assertEqual(readopts,opts) - - -@unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') -@unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), - 'socket.SOL_CAN_ISOTP is required for this test') -@unittest.skipUnless(hasattr(socket, "CAN_ISOTP_RECV_FC"), - 'socket.CAN_ISOTP_RECV_FC is required for this test') -class ISOTPTest(ThreadableISOTPTest): - - def __init__(self, methodName='runTest', *args, **kwargs): - ThreadableISOTPTest.__init__(self,methodName, *args, **kwargs) - - def setUp(self, *args, **kwargs): - self.test_opts['testTiming'] = { - 'cli' : { - socket.CAN_ISOTP_RECV_FC : struct.pack("=BBB",10, 0x7F, 10) # bs, stmin, wftmax - }, - 'serv' : {} - } - - self.serv = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) - self.opts = self.test_opts[self._testMethodName] if self._testMethodName in self.test_opts else {} + def testBind(self): try: - if 'serv' in self.opts: - for opt_const in self.opts['serv']: - self.serv.setsockopt(socket.SOL_CAN_ISOTP, opt_const, self.opts['serv'][opt_const]) - self.serv.bind((self.interface, self.cli_addr, self.srv_addr)) - except OSError: - self.skipTest('network interface `%s` does not exist' % - self.interface) - - def tearDown(self): - self.serv.close() - self.serv=None - - @classmethod - def makeRandomData(cls, n): - data = [random.randint(0,0xFF) for i in range(0,n)] - return struct.pack("B"*len(data), *data) + with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: + s.bind(("vcan0",1,2)) - def _testFullFrame(self): - self.bindata = self.makeRandomData(4095) - self.cli.send(self.bindata) + except OSError as e: + if e.errno not in [errno.ENODEV, errno.EPROTONOSUPPORT]: + raise - def testFullFrame(self): - time.sleep(0.1) - bindata = self.serv.recv(len(self.bindata)) - self.assertEqual(bindata, self.bindata) - - def _testTiming(self): - self.bindata2 = self.makeRandomData(512) - self.tic = time.time() - self.cli.send(self.bindata2) - - def testTiming(self): - """ - The main goal of this test is to validate that the driver correctly receive options via setsockopt. - We are not trying to validate the proper functioning of the driver itself. - """ - time.sleep(0.1) - bindata2 = self.serv.recv(len(self.bindata2)) - self.toc = time.time() - self.assertEqual(bindata2, self.bindata2) - measured_time = math.ceil(self.toc-self.tic) - min_time = math.floor(self.calcFrameCnt(len(self.bindata2))*0.127) - self.assertGreaterEqual(measured_time, min_time) - - def calcFrameCnt(self, size): - if size < 1 or size > 4095: - raise ValueError('ISOTP frame size must be between 1 and 4095') - - if (size < 8): - return 1 - else: - return int(1 + math.ceil((float(size) - 6.0) / 7.0)) @unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.') diff --git a/Misc/ACKS b/Misc/ACKS index 7b709ba41f4839..777d10ce3da65e 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -904,6 +904,7 @@ John Lenton Kostyantyn Leschenko Benno Leslie Christopher Tur Lesniewski-Laas +Pier-Yves Lessard Alain Leufroy Mark Levinson Mark Levitt @@ -1356,7 +1357,7 @@ Cheryl Sabella Patrick Sabin Sébastien Sablé Amit Saha -Suman Saha +Suman Shahaf Hajime Saitou George Sakkis Victor Salgado @@ -1765,5 +1766,4 @@ Tarek Ziadé Jelle Zijlstra Gennadiy Zlobin Doug Zongker -Peter Åstrand -Pier-Yves Lessard +Peter Åstrand \ No newline at end of file diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 8def56a959e81a..99580e76558f5e 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7042,6 +7042,8 @@ PyInit__socket(void) PyModule_AddIntMacro(m, CAN_SFF_MASK); PyModule_AddIntMacro(m, CAN_EFF_MASK); PyModule_AddIntMacro(m, CAN_ERR_MASK); + + PyModule_AddIntMacro(m, CAN_ISOTP); #endif #ifdef HAVE_LINUX_CAN_RAW_H PyModule_AddIntMacro(m, CAN_RAW_FILTER); @@ -7067,25 +7069,6 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "CAN_BCM_RX_TIMEOUT", RX_TIMEOUT); PyModule_AddIntConstant(m, "CAN_BCM_RX_CHANGED", RX_CHANGED); #endif -#ifdef HAVE_LINUX_CAN_ISOTP_H - PyModule_AddIntMacro(m, SOL_CAN_ISOTP); - PyModule_AddIntMacro(m, CAN_ISOTP); - PyModule_AddIntMacro(m, CAN_ISOTP_OPTS); - PyModule_AddIntMacro(m, CAN_ISOTP_RECV_FC); - PyModule_AddIntMacro(m, CAN_ISOTP_TX_STMIN); - PyModule_AddIntMacro(m, CAN_ISOTP_RX_STMIN); - PyModule_AddIntMacro(m, CAN_ISOTP_LL_OPTS); - PyModule_AddIntMacro(m, CAN_ISOTP_LISTEN_MODE); - PyModule_AddIntMacro(m, CAN_ISOTP_EXTEND_ADDR); - PyModule_AddIntMacro(m, CAN_ISOTP_TX_PADDING); - PyModule_AddIntMacro(m, CAN_ISOTP_RX_PADDING); - PyModule_AddIntMacro(m, CAN_ISOTP_CHK_PAD_LEN); - PyModule_AddIntMacro(m, CAN_ISOTP_CHK_PAD_DATA); - PyModule_AddIntMacro(m, CAN_ISOTP_HALF_DUPLEX); - PyModule_AddIntMacro(m, CAN_ISOTP_FORCE_TXSTMIN); - PyModule_AddIntMacro(m, CAN_ISOTP_FORCE_RXSTMIN); - PyModule_AddIntMacro(m, CAN_ISOTP_RX_EXT_ADDR); -#endif #ifdef SOL_RDS PyModule_AddIntMacro(m, SOL_RDS); #endif diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index e1141e683b4e87..03f982b91083f3 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -91,10 +91,6 @@ typedef int socklen_t; #include #endif -#ifdef HAVE_LINUX_CAN_ISOTP_H -#include -#endif - #ifdef HAVE_SYS_SYS_DOMAIN_H #include #endif diff --git a/configure b/configure index 7a4f61696b571a..a6f7d2bd7f0d9e 100755 --- a/configure +++ b/configure @@ -8127,8 +8127,8 @@ fi done -# On Linux, can.h, can/raw.h, can/isotp.h require sys/socket.h -for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h linux/can/isotp.h +# On Linux, can.h and can/raw.h require sys/socket.h +for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " diff --git a/configure.ac b/configure.ac index 3058fb20d7b889..a5da830e89623c 100644 --- a/configure.ac +++ b/configure.ac @@ -2120,8 +2120,8 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[ #endif ]) -# On Linux, can.h, can/raw.h, can/isotp.h require sys/socket.h -AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h linux/can/isotp.h,,,[ +# On Linux, can.h and can/raw.h require sys/socket.h +AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h,,,[ #ifdef HAVE_SYS_SOCKET_H #include #endif diff --git a/pyconfig.h.in b/pyconfig.h.in index 77b6e90a6c0936..0dd05aa65b8a4f 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -559,9 +559,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_CAN_H -/* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_CAN_ISOTP_H - /* Define if compiling using Linux 3.6 or later. */ #undef HAVE_LINUX_CAN_RAW_FD_FRAMES From 17c58d70c307ce8383a821cd6c954d5b561d49b0 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Wed, 2 Aug 2017 21:05:34 -0700 Subject: [PATCH 09/13] Added implementation for getsockname + unit tests --- Lib/test/test_socket.py | 15 +++++++++++---- Modules/socketmodule.c | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index ca39fd0f73db5a..611a20e7ad313a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1724,6 +1724,10 @@ def testBCM(self): @unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.') class ISOTPTest(unittest.TestCase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.interface = "vcan0" + def testCrucialConstants(self): socket.AF_CAN socket.PF_CAN @@ -1749,14 +1753,17 @@ def testTooLongInterfaceName(self): def testBind(self): try: with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - s.bind(("vcan0",1,2)) - + addr = (self.interface,0x123,0x456) + s.bind(addr) + self.assertEqual(s.getsockname(), addr) except OSError as e: - if e.errno not in [errno.ENODEV, errno.EPROTONOSUPPORT]: + if e.errno == errno.ENODEV: + self.skipTest('network interface `%s` does not exist' % + self.interface) + else: raise - @unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.') class BasicRDSTest(unittest.TestCase): diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 99580e76558f5e..17e20d2d4f3903 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1373,9 +1373,22 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) ifname = ifr.ifr_name; } - return Py_BuildValue("O&h", PyUnicode_DecodeFSDefault, - ifname, - a->can_family); + switch (proto) { +#ifdef CAN_ISOTP + case CAN_ISOTP: + { + return Py_BuildValue("O&kk", PyUnicode_DecodeFSDefault, + ifname, + a->can_addr.tp.rx_id, + a->can_addr.tp.tx_id); + } +#endif + default: + { + return Py_BuildValue("O&", PyUnicode_DecodeFSDefault, + ifname); + } + } } #endif From 7da8fedcbe2a6a95c05c993a5018211d751380af Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Wed, 2 Aug 2017 21:13:45 -0700 Subject: [PATCH 10/13] Missing newline at end of ACKS file --- Misc/ACKS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ACKS b/Misc/ACKS index 777d10ce3da65e..e91554c4f3210f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1766,4 +1766,4 @@ Tarek Ziadé Jelle Zijlstra Gennadiy Zlobin Doug Zongker -Peter Åstrand \ No newline at end of file +Peter Åstrand From 1365fca342f8cd2ff0a3c919f78729b52f89cf3e Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Thu, 3 Aug 2017 21:12:21 -0700 Subject: [PATCH 11/13] Accidentally inserted a type in ACKS file --- Misc/ACKS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/ACKS b/Misc/ACKS index e91554c4f3210f..6e2ae850ab2aa3 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1357,7 +1357,7 @@ Cheryl Sabella Patrick Sabin Sébastien Sablé Amit Saha -Suman Shahaf +Suman Saha Hajime Saitou George Sakkis Victor Salgado From 280141c99b81411cd000bb61391a9c39935901c6 Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sat, 26 Aug 2017 13:00:12 -0700 Subject: [PATCH 12/13] Followed tiran changes review #1 recommendations --- Lib/test/test_socket.py | 6 +++--- Modules/socketmodule.c | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 611a20e7ad313a..4060a7dbf24c1d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1747,13 +1747,13 @@ def testCreateISOTPSocket(self): def testTooLongInterfaceName(self): # most systems limit IFNAMSIZ to 16, take 1024 to be sure with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - self.assertRaisesRegex(OSError, 'interface name too long', - s.bind, ('x' * 1024,1,2)) + with self.assertRaisesRegex(OSError, 'interface name too long'): + s.bind(('x' * 1024,1,2)) def testBind(self): try: with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - addr = (self.interface,0x123,0x456) + addr = self.interface,0x123,0x456 s.bind(addr) self.assertEqual(s.getsockname(), addr) except OSError as e: diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 17e20d2d4f3903..225551c4c02ac1 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1882,7 +1882,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif -#if defined(AF_CAN) +#ifdef AF_CAN #if defined(CAN_RAW) && defined(CAN_BCM) case AF_CAN: @@ -1929,7 +1929,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif -#if defined(CAN_ISOTP) +#ifdef CAN_ISOTP case CAN_ISOTP: { struct sockaddr_can *addr; @@ -7055,9 +7055,10 @@ PyInit__socket(void) PyModule_AddIntMacro(m, CAN_SFF_MASK); PyModule_AddIntMacro(m, CAN_EFF_MASK); PyModule_AddIntMacro(m, CAN_ERR_MASK); - +#ifdef CAN_ISOTP PyModule_AddIntMacro(m, CAN_ISOTP); #endif +#endif #ifdef HAVE_LINUX_CAN_RAW_H PyModule_AddIntMacro(m, CAN_RAW_FILTER); PyModule_AddIntMacro(m, CAN_RAW_ERR_FILTER); From e2cc6f3146083bed0ddf2fb1f20e9d64d129f9ed Mon Sep 17 00:00:00 2001 From: Pier-Yves Lessard Date: Sun, 27 Aug 2017 13:56:26 -0700 Subject: [PATCH 13/13] Added spaces after comma --- Lib/test/test_socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 4060a7dbf24c1d..84234887747983 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1748,12 +1748,12 @@ def testTooLongInterfaceName(self): # most systems limit IFNAMSIZ to 16, take 1024 to be sure with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: with self.assertRaisesRegex(OSError, 'interface name too long'): - s.bind(('x' * 1024,1,2)) + s.bind(('x' * 1024, 1, 2)) def testBind(self): try: with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s: - addr = self.interface,0x123,0x456 + addr = self.interface, 0x123, 0x456 s.bind(addr) self.assertEqual(s.getsockname(), addr) except OSError as e: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy