Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a30f6d4

Browse filesBrowse files
pylessardtiran
authored andcommitted
bpo-30987 - Support for ISO-TP protocol in SocketCAN (#2956)
* Added support for CAN_ISOTP protocol * Added unit tests for CAN ISOTP * Updated documentation for ISO-TP protocol * Removed trailing whitespace in documentation * Added blurb NEWS.d file * updated Misc/ACKS * Fixed broken unit test that was using isotp const outside of skippable section * Removed dependecy over third party project * Added implementation for getsockname + unit tests * Missing newline at end of ACKS file * Accidentally inserted a type in ACKS file * Followed tiran changes review #1 recommendations * Added spaces after comma
1 parent ed94a8b commit a30f6d4
Copy full SHA for a30f6d4

File tree

5 files changed

+146
-8
lines changed
Filter options

5 files changed

+146
-8
lines changed

‎Doc/library/socket.rst

Copy file name to clipboardExpand all lines: Doc/library/socket.rst
+19-3Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ created. Socket addresses are represented as follows:
103103
``'can0'``. The network interface name ``''`` can be used to receive packets
104104
from all network interfaces of this family.
105105

106+
- :const:`CAN_ISOTP` protocol require a tuple ``(interface, rx_addr, tx_addr)``
107+
where both additional parameters are unsigned long integer that represent a
108+
CAN identifier (standard or extended).
109+
106110
- A string or a tuple ``(id, unit)`` is used for the :const:`SYSPROTO_CONTROL`
107111
protocol of the :const:`PF_SYSTEM` family. The string is the name of a
108112
kernel control using a dynamically-assigned ID. The tuple can be used if ID
@@ -341,6 +345,16 @@ Constants
341345

342346
.. versionadded:: 3.5
343347

348+
.. data:: CAN_ISOTP
349+
350+
CAN_ISOTP, in the CAN protocol family, is the ISO-TP (ISO 15765-2) protocol.
351+
ISO-TP constants, documented in the Linux documentation.
352+
353+
Availability: Linux >= 2.6.25
354+
355+
.. versionadded:: 3.7
356+
357+
344358
.. data:: AF_RDS
345359
PF_RDS
346360
SOL_RDS
@@ -427,7 +441,7 @@ The following functions all create :ref:`socket objects <socket-objects>`.
427441
:const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_``
428442
constants. The protocol number is usually zero and may be omitted or in the
429443
case where the address family is :const:`AF_CAN` the protocol should be one
430-
of :const:`CAN_RAW` or :const:`CAN_BCM`. If *fileno* is specified, the other
444+
of :const:`CAN_RAW`, :const:`CAN_BCM` or :const:`CAN_ISOTP`. If *fileno* is specified, the other
431445
arguments are ignored, causing the socket with the specified file descriptor
432446
to return. Unlike :func:`socket.fromfd`, *fileno* will return the same
433447
socket and not a duplicate. This may help close a detached socket using
@@ -445,6 +459,8 @@ The following functions all create :ref:`socket objects <socket-objects>`.
445459
.. versionchanged:: 3.4
446460
The returned socket is now non-inheritable.
447461

462+
.. versionchanged:: 3.7
463+
The CAN_ISOTP protocol was added.
448464

449465
.. function:: socketpair([family[, type[, proto]]])
450466

@@ -1661,7 +1677,7 @@ the interface::
16611677
# disabled promiscuous mode
16621678
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
16631679

1664-
The last example shows how to use the socket interface to communicate to a CAN
1680+
The next example shows how to use the socket interface to communicate to a CAN
16651681
network using the raw socket protocol. To use CAN with the broadcast
16661682
manager protocol instead, open a socket with::
16671683

@@ -1671,7 +1687,7 @@ After binding (:const:`CAN_RAW`) or connecting (:const:`CAN_BCM`) the socket, yo
16711687
can use the :meth:`socket.send`, and the :meth:`socket.recv` operations (and
16721688
their counterparts) on the socket object as usual.
16731689

1674-
This example might require special privileges::
1690+
This last example might require special privileges::
16751691

16761692
import socket
16771693
import struct

‎Lib/test/test_socket.py

Copy file name to clipboardExpand all lines: Lib/test/test_socket.py
+55Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ def _have_socket_can():
5555
s.close()
5656
return True
5757

58+
def _have_socket_can_isotp():
59+
"""Check whether CAN ISOTP sockets are supported on this host."""
60+
try:
61+
s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)
62+
except (AttributeError, OSError):
63+
return False
64+
else:
65+
s.close()
66+
return True
67+
5868
def _have_socket_rds():
5969
"""Check whether RDS sockets are supported on this host."""
6070
try:
@@ -77,6 +87,8 @@ def _have_socket_alg():
7787

7888
HAVE_SOCKET_CAN = _have_socket_can()
7989

90+
HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp()
91+
8092
HAVE_SOCKET_RDS = _have_socket_rds()
8193

8294
HAVE_SOCKET_ALG = _have_socket_alg()
@@ -1709,6 +1721,49 @@ def testBCM(self):
17091721
self.assertEqual(bytes_sent, len(header_plus_frame))
17101722

17111723

1724+
@unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.')
1725+
class ISOTPTest(unittest.TestCase):
1726+
1727+
def __init__(self, *args, **kwargs):
1728+
super().__init__(*args, **kwargs)
1729+
self.interface = "vcan0"
1730+
1731+
def testCrucialConstants(self):
1732+
socket.AF_CAN
1733+
socket.PF_CAN
1734+
socket.CAN_ISOTP
1735+
socket.SOCK_DGRAM
1736+
1737+
def testCreateSocket(self):
1738+
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
1739+
pass
1740+
1741+
@unittest.skipUnless(hasattr(socket, "CAN_ISOTP"),
1742+
'socket.CAN_ISOTP required for this test.')
1743+
def testCreateISOTPSocket(self):
1744+
with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s:
1745+
pass
1746+
1747+
def testTooLongInterfaceName(self):
1748+
# most systems limit IFNAMSIZ to 16, take 1024 to be sure
1749+
with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s:
1750+
with self.assertRaisesRegex(OSError, 'interface name too long'):
1751+
s.bind(('x' * 1024, 1, 2))
1752+
1753+
def testBind(self):
1754+
try:
1755+
with socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) as s:
1756+
addr = self.interface, 0x123, 0x456
1757+
s.bind(addr)
1758+
self.assertEqual(s.getsockname(), addr)
1759+
except OSError as e:
1760+
if e.errno == errno.ENODEV:
1761+
self.skipTest('network interface `%s` does not exist' %
1762+
self.interface)
1763+
else:
1764+
raise
1765+
1766+
17121767
@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.')
17131768
class BasicRDSTest(unittest.TestCase):
17141769

‎Misc/ACKS

Copy file name to clipboardExpand all lines: Misc/ACKS
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ John Lenton
908908
Kostyantyn Leschenko
909909
Benno Leslie
910910
Christopher Tur Lesniewski-Laas
911+
Pier-Yves Lessard
911912
Alain Leufroy
912913
Mark Levinson
913914
Mark Levitt
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for CAN ISO-TP protocol in the socket module.

‎Modules/socketmodule.c

Copy file name to clipboardExpand all lines: Modules/socketmodule.c
+70-5Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,9 +1373,22 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
13731373
ifname = ifr.ifr_name;
13741374
}
13751375

1376-
return Py_BuildValue("O&h", PyUnicode_DecodeFSDefault,
1377-
ifname,
1378-
a->can_family);
1376+
switch (proto) {
1377+
#ifdef CAN_ISOTP
1378+
case CAN_ISOTP:
1379+
{
1380+
return Py_BuildValue("O&kk", PyUnicode_DecodeFSDefault,
1381+
ifname,
1382+
a->can_addr.tp.rx_id,
1383+
a->can_addr.tp.tx_id);
1384+
}
1385+
#endif
1386+
default:
1387+
{
1388+
return Py_BuildValue("O&", PyUnicode_DecodeFSDefault,
1389+
ifname);
1390+
}
1391+
}
13791392
}
13801393
#endif
13811394

@@ -1869,7 +1882,9 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
18691882
}
18701883
#endif
18711884

1872-
#if defined(AF_CAN) && defined(CAN_RAW) && defined(CAN_BCM)
1885+
#ifdef AF_CAN
1886+
1887+
#if defined(CAN_RAW) && defined(CAN_BCM)
18731888
case AF_CAN:
18741889
switch (s->sock_proto) {
18751890
case CAN_RAW:
@@ -1880,7 +1895,6 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
18801895
PyObject *interfaceName;
18811896
struct ifreq ifr;
18821897
Py_ssize_t len;
1883-
18841898
addr = (struct sockaddr_can *)addr_ret;
18851899

18861900
if (!PyArg_ParseTuple(args, "O&", PyUnicode_FSConverter,
@@ -1913,6 +1927,54 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
19131927
Py_DECREF(interfaceName);
19141928
return 1;
19151929
}
1930+
#endif
1931+
1932+
#ifdef CAN_ISOTP
1933+
case CAN_ISOTP:
1934+
{
1935+
struct sockaddr_can *addr;
1936+
PyObject *interfaceName;
1937+
struct ifreq ifr;
1938+
Py_ssize_t len;
1939+
unsigned long int rx_id, tx_id;
1940+
1941+
addr = (struct sockaddr_can *)addr_ret;
1942+
1943+
if (!PyArg_ParseTuple(args, "O&kk", PyUnicode_FSConverter,
1944+
&interfaceName,
1945+
&rx_id,
1946+
&tx_id))
1947+
return 0;
1948+
1949+
len = PyBytes_GET_SIZE(interfaceName);
1950+
1951+
if (len == 0) {
1952+
ifr.ifr_ifindex = 0;
1953+
} else if ((size_t)len < sizeof(ifr.ifr_name)) {
1954+
strncpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName), sizeof(ifr.ifr_name));
1955+
ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0';
1956+
if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) {
1957+
s->errorhandler();
1958+
Py_DECREF(interfaceName);
1959+
return 0;
1960+
}
1961+
} else {
1962+
PyErr_SetString(PyExc_OSError,
1963+
"AF_CAN interface name too long");
1964+
Py_DECREF(interfaceName);
1965+
return 0;
1966+
}
1967+
1968+
addr->can_family = AF_CAN;
1969+
addr->can_ifindex = ifr.ifr_ifindex;
1970+
addr->can_addr.tp.rx_id = rx_id;
1971+
addr->can_addr.tp.tx_id = tx_id;
1972+
1973+
*len_ret = sizeof(*addr);
1974+
Py_DECREF(interfaceName);
1975+
return 1;
1976+
}
1977+
#endif
19161978
default:
19171979
PyErr_SetString(PyExc_OSError,
19181980
"getsockaddrarg: unsupported CAN protocol");
@@ -6995,6 +7057,9 @@ PyInit__socket(void)
69957057
PyModule_AddIntMacro(m, CAN_SFF_MASK);
69967058
PyModule_AddIntMacro(m, CAN_EFF_MASK);
69977059
PyModule_AddIntMacro(m, CAN_ERR_MASK);
7060+
#ifdef CAN_ISOTP
7061+
PyModule_AddIntMacro(m, CAN_ISOTP);
7062+
#endif
69987063
#endif
69997064
#ifdef HAVE_LINUX_CAN_RAW_H
70007065
PyModule_AddIntMacro(m, CAN_RAW_FILTER);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.