Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions Doc/library/socket.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand DownExpand Up@@ -341,6 +345,16 @@ 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.

Availability: Linux >= 2.6.25

.. versionadded:: 3.7


.. data:: AF_RDS
PF_RDS
SOL_RDS
Expand DownExpand Up@@ -427,7 +441,7 @@ The following functions all create :ref:`socket objects <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
Expand All@@ -445,6 +459,8 @@ The following functions all create :ref:`socket objects <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]]])

Expand DownExpand Up@@ -1661,7 +1677,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::

Expand All@@ -1671,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
Expand Down
55 changes: 55 additions & 0 deletions Lib/test/test_socket.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,16 @@ def _have_socket_can():
s.close()
returnTrue

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):
returnFalse
else:
s.close()
returnTrue

def_have_socket_rds():
"""Check whether RDS sockets are supported on this host."""
try:
Expand All@@ -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()
Expand DownExpand Up@@ -1709,6 +1721,49 @@ def testBCM(self):
self.assertEqual(bytes_sent, len(header_plus_frame))


@unittest.skipUnless(HAVE_SOCKET_CAN_ISOTP, 'CAN ISOTP required for this test.')
classISOTPTest(unittest.TestCase):

def__init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.interface="vcan0"

deftestCrucialConstants(self):
socket.AF_CAN
socket.PF_CAN
socket.CAN_ISOTP
socket.SOCK_DGRAM

deftestCreateSocket(self):
withsocket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) ass:
pass

@unittest.skipUnless(hasattr(socket, "CAN_ISOTP"),
'socket.CAN_ISOTP required for this test.')
deftestCreateISOTPSocket(self):
withsocket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) ass:
pass

deftestTooLongInterfaceName(self):
# most systems limit IFNAMSIZ to 16, take 1024 to be sure
withsocket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) ass:
withself.assertRaisesRegex(OSError, 'interface name too long'):
s.bind(('x'*1024, 1, 2))

deftestBind(self):
try:
withsocket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP) ass:
addr=self.interface, 0x123, 0x456
s.bind(addr)
self.assertEqual(s.getsockname(), addr)
exceptOSErrorase:
ife.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.')
classBasicRDSTest(unittest.TestCase):

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line numberDiff line numberDiff line change
Expand Up@@ -904,6 +904,7 @@ John Lenton
Kostyantyn Leschenko
Benno Leslie
Christopher Tur Lesniewski-Laas
Pier-Yves Lessard
Alain Leufroy
Mark Levinson
Mark Levitt
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
Added support for CAN ISO-TP protocol in the socket module.
75 changes: 70 additions & 5 deletions Modules/socketmodule.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -1373,9 +1373,22 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
ifname=ifr.ifr_name;
}

returnPy_BuildValue("O&h", PyUnicode_DecodeFSDefault,
ifname,
a->can_family);
switch (proto) {
#ifdefCAN_ISOTP
caseCAN_ISOTP:
{
returnPy_BuildValue("O&kk", PyUnicode_DecodeFSDefault,
ifname,
a->can_addr.tp.rx_id,
a->can_addr.tp.tx_id);
}
#endif
default:
{
returnPy_BuildValue("O&", PyUnicode_DecodeFSDefault,
ifname);
}
}
}
#endif

Expand DownExpand Up@@ -1869,7 +1882,9 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
}
#endif

#if defined(AF_CAN) && defined(CAN_RAW) && defined(CAN_BCM)
#ifdefAF_CAN

#if defined(CAN_RAW) && defined(CAN_BCM)
caseAF_CAN:
switch (s->sock_proto) {
caseCAN_RAW:
Expand All@@ -1880,7 +1895,6 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
PyObject*interfaceName;
structifreqifr;
Py_ssize_tlen;

addr= (structsockaddr_can*)addr_ret;

if (!PyArg_ParseTuple(args, "O&", PyUnicode_FSConverter,
Expand DownExpand Up@@ -1913,6 +1927,54 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
Py_DECREF(interfaceName);
return1;
}
#endif

#ifdefCAN_ISOTP
caseCAN_ISOTP:
{
structsockaddr_can*addr;
PyObject*interfaceName;
structifreqifr;
Py_ssize_tlen;
unsigned long intrx_id, tx_id;

addr= (structsockaddr_can*)addr_ret;

if (!PyArg_ParseTuple(args, "O&kk", PyUnicode_FSConverter,
&interfaceName,
&rx_id,
&tx_id))
return0;

len=PyBytes_GET_SIZE(interfaceName);

if (len==0) {
ifr.ifr_ifindex=0;
} elseif ((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);
return0;
}
} else {
PyErr_SetString(PyExc_OSError,
"AF_CAN interface name too long");
Py_DECREF(interfaceName);
return0;
}

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);
return1;
}
#endif
default:
PyErr_SetString(PyExc_OSError,
"getsockaddrarg: unsupported CAN protocol");
Expand DownExpand Up@@ -6993,6 +7055,9 @@ PyInit__socket(void)
PyModule_AddIntMacro(m, CAN_SFF_MASK);
PyModule_AddIntMacro(m, CAN_EFF_MASK);
PyModule_AddIntMacro(m, CAN_ERR_MASK);
#ifdefCAN_ISOTP
PyModule_AddIntMacro(m, CAN_ISOTP);
#endif
#endif
#ifdefHAVE_LINUX_CAN_RAW_H
PyModule_AddIntMacro(m, CAN_RAW_FILTER);
Expand Down