Issue #10141: socket: add SocketCAN (PF_CAN) support. Initial patch by Matthias

Fuchs, updated by Tiago Gonçalves.
This commit is contained in:
Charles-François Natali 2011-10-06 19:47:44 +02:00
parent 90c30e87be
commit 47413c1171
10 changed files with 683 additions and 321 deletions

View File

@ -80,6 +80,11 @@ Socket addresses are represented as follows:
If *addr_type* is TIPC_ADDR_ID, then *v1* is the node, *v2* is the If *addr_type* is TIPC_ADDR_ID, then *v1* is the node, *v2* is the
reference, and *v3* should be set to 0. reference, and *v3* should be set to 0.
- A tuple ``(interface, )`` is used for the :const:`AF_CAN` address family,
where *interface* is a string representing a network interface name like
``'can0'``. The network interface name ``''`` can be used to receive packets
from all network interfaces of this family.
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`) - Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`)
support specific representations. support specific representations.
@ -216,6 +221,19 @@ The module :mod:`socket` exports the following constants and functions:
in the Unix header files are defined; for a few symbols, default values are in the Unix header files are defined; for a few symbols, default values are
provided. provided.
.. data:: AF_CAN
PF_CAN
SOL_CAN_*
CAN_*
Many constants of these forms, documented in the Linux documentation, are
also defined in the socket module.
Availability: Linux >= 2.6.25.
.. versionadded:: 3.3
.. data:: SIO_* .. data:: SIO_*
RCVALL_* RCVALL_*
@ -387,10 +405,14 @@ The module :mod:`socket` exports the following constants and functions:
Create a new socket using the given address family, socket type and protocol Create a new socket using the given address family, socket type and protocol
number. The address family should be :const:`AF_INET` (the default), number. The address family should be :const:`AF_INET` (the default),
:const:`AF_INET6` or :const:`AF_UNIX`. The socket type should be :const:`AF_INET6`, :const:`AF_UNIX` or :const:`AF_CAN`. The socket type
:const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM` or perhaps one of the should be :const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM`,
other ``SOCK_`` constants. The protocol number is usually zero and may be :const:`SOCK_RAW` or perhaps one of the other ``SOCK_`` constants. The
omitted in that case. protocol number is usually zero and may be omitted in that case or
:const:`CAN_RAW` in case the address family is :const:`AF_CAN`.
.. versionchanged:: 3.3
The AF_CAN family was added.
.. function:: socketpair([family[, type[, proto]]]) .. function:: socketpair([family[, type[, proto]]])
@ -1213,7 +1235,7 @@ sends traffic to the first one connected successfully. ::
print('Received', repr(data)) print('Received', repr(data))
The last example shows how to write a very simple network sniffer with raw The next example shows how to write a very simple network sniffer with raw
sockets on Windows. The example requires administrator privileges to modify sockets on Windows. The example requires administrator privileges to modify
the interface:: the interface::
@ -1238,6 +1260,45 @@ the interface::
# disabled promiscuous mode # disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
The last example shows how to use the socket interface to communicate to a CAN
network. This example might require special priviledge::
import socket
import struct
# CAN frame packing/unpacking (see `struct can_frame` in <linux/can.h>)
can_frame_fmt = "=IB3x8s"
def build_can_frame(can_id, data):
can_dlc = len(data)
data = data.ljust(8, b'\x00')
return struct.pack(can_frame_fmt, can_id, can_dlc, data)
def dissect_can_frame(frame):
can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
return (can_id, can_dlc, data[:can_dlc])
# create a raw socket and bind it to the `vcan0` interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('vcan0',))
while True:
cf, addr = s.recvfrom(16)
print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))
try:
s.send(cf)
except socket.error:
print('Error sending CAN frame')
try:
s.send(build_can_frame(0x01, b'\x01\x02\x03'))
except socket.error:
print('Error sending CAN frame')
Running an example several times with too small delay between executions, could Running an example several times with too small delay between executions, could
lead to this error:: lead to this error::

View File

@ -300,15 +300,22 @@ signal
socket socket
------ ------
The :class:`~socket.socket` class now exposes addititonal methods to * The :class:`~socket.socket` class now exposes additional methods to process
process ancillary data when supported by the underlying platform: ancillary data when supported by the underlying platform:
* :func:`~socket.socket.sendmsg` * :func:`~socket.socket.sendmsg`
* :func:`~socket.socket.recvmsg` * :func:`~socket.socket.recvmsg`
* :func:`~socket.socket.recvmsg_into` * :func:`~socket.socket.recvmsg_into`
(Contributed by David Watson in :issue:`6560`, based on an earlier patch (Contributed by David Watson in :issue:`6560`, based on an earlier patch by
by Heiko Wundram) Heiko Wundram)
* The :class:`~socket.socket` class now supports the PF_CAN protocol family
(http://en.wikipedia.org/wiki/Socketcan), on Linux
(http://lwn.net/Articles/253425).
(Contributed by Matthias Fuchs, updated by Tiago Gonçalves in :issue:`10141`)
ssl ssl
--- ---

View File

@ -21,6 +21,7 @@ from weakref import proxy
import signal import signal
import math import math
import pickle import pickle
import struct
try: try:
import fcntl import fcntl
except ImportError: except ImportError:
@ -36,6 +37,18 @@ except ImportError:
thread = None thread = None
threading = None threading = None
def _have_socket_can():
"""Check whether CAN sockets are supported on this host."""
try:
s = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
except (AttributeError, socket.error, OSError):
return False
else:
s.close()
return True
HAVE_SOCKET_CAN = _have_socket_can()
# Size in bytes of the int type # Size in bytes of the int type
SIZEOF_INT = array.array("i").itemsize SIZEOF_INT = array.array("i").itemsize
@ -80,6 +93,30 @@ class ThreadSafeCleanupTestCase(unittest.TestCase):
with self._cleanup_lock: with self._cleanup_lock:
return super().doCleanups(*args, **kwargs) return super().doCleanups(*args, **kwargs)
class SocketCANTest(unittest.TestCase):
"""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'
bufsize = 128
def setUp(self):
self.s = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
try:
self.s.bind((self.interface,))
except socket.error:
self.skipTest('network interface `%s` does not exist' %
self.interface)
self.s.close()
def tearDown(self):
self.s.close()
self.s = None
class ThreadableTest: class ThreadableTest:
"""Threadable Test class """Threadable Test class
@ -210,6 +247,26 @@ class ThreadedUDPSocketTest(SocketUDPTest, ThreadableTest):
self.cli = None self.cli = None
ThreadableTest.clientTearDown(self) ThreadableTest.clientTearDown(self)
class ThreadedCANSocketTest(SocketCANTest, ThreadableTest):
def __init__(self, methodName='runTest'):
SocketCANTest.__init__(self, methodName=methodName)
ThreadableTest.__init__(self)
def clientSetUp(self):
self.cli = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
try:
self.cli.bind((self.interface,))
except socket.error:
self.skipTest('network interface `%s` does not exist' %
self.interface)
self.cli.close()
def clientTearDown(self):
self.cli.close()
self.cli = None
ThreadableTest.clientTearDown(self)
class SocketConnectedTest(ThreadedTCPSocketTest): class SocketConnectedTest(ThreadedTCPSocketTest):
"""Socket tests for client-server connection. """Socket tests for client-server connection.
@ -1072,6 +1129,112 @@ class GeneralModuleTests(unittest.TestCase):
srv.close() srv.close()
@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
class BasicCANTest(unittest.TestCase):
def testCrucialConstants(self):
socket.AF_CAN
socket.PF_CAN
socket.CAN_RAW
def testCreateSocket(self):
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
pass
def testBindAny(self):
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
s.bind(('', ))
def testTooLongInterfaceName(self):
# most systems limit IFNAMSIZ to 16, take 1024 to be sure
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
self.assertRaisesRegexp(socket.error, 'interface name too long',
s.bind, ('x' * 1024,))
@unittest.skipUnless(hasattr(socket, "CAN_RAW_LOOPBACK"),
'socket.CAN_RAW_LOOPBACK required for this test.')
def testLoopback(self):
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
for loopback in (0, 1):
s.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_LOOPBACK,
loopback)
self.assertEqual(loopback,
s.getsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_LOOPBACK))
@unittest.skipUnless(hasattr(socket, "CAN_RAW_FILTER"),
'socket.CAN_RAW_FILTER required for this test.')
def testFilter(self):
can_id, can_mask = 0x200, 0x700
can_filter = struct.pack("=II", can_id, can_mask)
with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s:
s.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, can_filter)
self.assertEqual(can_filter,
s.getsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, 8))
@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
@unittest.skipUnless(thread, 'Threading required for this test.')
class CANTest(ThreadedCANSocketTest):
"""The CAN frame structure is defined in <linux/can.h>:
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* data length code: 0 .. 8 */
__u8 data[8] __attribute__((aligned(8)));
};
"""
can_frame_fmt = "=IB3x8s"
def __init__(self, methodName='runTest'):
ThreadedCANSocketTest.__init__(self, methodName=methodName)
@classmethod
def build_can_frame(cls, can_id, data):
"""Build a CAN frame."""
can_dlc = len(data)
data = data.ljust(8, b'\x00')
return struct.pack(cls.can_frame_fmt, can_id, can_dlc, data)
@classmethod
def dissect_can_frame(cls, frame):
"""Dissect a CAN frame."""
can_id, can_dlc, data = struct.unpack(cls.can_frame_fmt, frame)
return (can_id, can_dlc, data[:can_dlc])
def testSendFrame(self):
cf, addr = self.s.recvfrom(self.bufsize)
self.assertEqual(self.cf, cf)
self.assertEqual(addr[0], self.interface)
self.assertEqual(addr[1], socket.AF_CAN)
def _testSendFrame(self):
self.cf = self.build_can_frame(0x00, b'\x01\x02\x03\x04\x05')
self.cli.send(self.cf)
def testSendMaxFrame(self):
cf, addr = self.s.recvfrom(self.bufsize)
self.assertEqual(self.cf, cf)
def _testSendMaxFrame(self):
self.cf = self.build_can_frame(0x00, b'\x07' * 8)
self.cli.send(self.cf)
def testSendMultiFrames(self):
cf, addr = self.s.recvfrom(self.bufsize)
self.assertEqual(self.cf1, cf)
cf, addr = self.s.recvfrom(self.bufsize)
self.assertEqual(self.cf2, cf)
def _testSendMultiFrames(self):
self.cf1 = self.build_can_frame(0x07, b'\x44\x33\x22\x11')
self.cli.send(self.cf1)
self.cf2 = self.build_can_frame(0x12, b'\x99\x22\x33')
self.cli.send(self.cf2)
@unittest.skipUnless(thread, 'Threading required for this test.') @unittest.skipUnless(thread, 'Threading required for this test.')
class BasicTCPTest(SocketConnectedTest): class BasicTCPTest(SocketConnectedTest):
@ -4194,6 +4357,7 @@ def test_main():
if isTipcAvailable(): if isTipcAvailable():
tests.append(TIPCTest) tests.append(TIPCTest)
tests.append(TIPCThreadableTest) tests.append(TIPCThreadableTest)
tests.extend([BasicCANTest, CANTest])
tests.extend([ tests.extend([
CmsgMacroTests, CmsgMacroTests,
SendmsgUDPTest, SendmsgUDPTest,

View File

@ -319,6 +319,7 @@ John Fouhy
Martin Franklin Martin Franklin
Robin Friedrich Robin Friedrich
Ivan Frohne Ivan Frohne
Matthias Fuchs
Jim Fulton Jim Fulton
Tadayoshi Funaba Tadayoshi Funaba
Gyro Funch Gyro Funch
@ -354,6 +355,7 @@ Michael Gilfix
Yannick Gingras Yannick Gingras
Christoph Gohlke Christoph Gohlke
Tim Golden Tim Golden
Tiago Gonçalves
Chris Gonnerman Chris Gonnerman
David Goodger David Goodger
Hans de Graaff Hans de Graaff

View File

@ -1322,6 +1322,9 @@ Tools/Demos
Extension Modules Extension Modules
----------------- -----------------
- Issue #10141: socket: Add SocketCAN (PF_CAN) support. Initial patch by
Matthias Fuchs, updated by Tiago Gonçalves.
- Issue #13070: Fix a crash when a TextIOWrapper caught in a reference cycle - Issue #13070: Fix a crash when a TextIOWrapper caught in a reference cycle
would be finalized after the reference to its underlying BufferedRWPair's would be finalized after the reference to its underlying BufferedRWPair's
writer got cleared by the GC. writer got cleared by the GC.

View File

@ -1220,6 +1220,25 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
} }
#endif #endif
#ifdef HAVE_LINUX_CAN_H
case AF_CAN:
{
struct sockaddr_can *a = (struct sockaddr_can *)addr;
char *ifname = "";
struct ifreq ifr;
/* need to look up interface name given index */
if (a->can_ifindex) {
ifr.ifr_ifindex = a->can_ifindex;
if (ioctl(sockfd, SIOCGIFNAME, &ifr) == 0)
ifname = ifr.ifr_name;
}
return Py_BuildValue("O&h", PyUnicode_DecodeFSDefault,
ifname,
a->can_family);
}
#endif
/* More cases here... */ /* More cases here... */
default: default:
@ -1587,6 +1606,53 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
} }
#endif #endif
#ifdef HAVE_LINUX_CAN_H
case AF_CAN:
switch (s->sock_proto) {
case CAN_RAW:
{
struct sockaddr_can *addr;
PyObject *interfaceName;
struct ifreq ifr;
addr = (struct sockaddr_can *)addr_ret;
Py_ssize_t len;
if (!PyArg_ParseTuple(args, "O&", PyUnicode_FSConverter,
&interfaceName))
return 0;
len = PyBytes_GET_SIZE(interfaceName);
if (len == 0) {
ifr.ifr_ifindex = 0;
} else if (len < sizeof(ifr.ifr_name)) {
strcpy(ifr.ifr_name, PyBytes_AS_STRING(interfaceName));
if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) {
s->errorhandler();
Py_DECREF(interfaceName);
return 0;
}
} else {
PyErr_SetString(socket_error,
"AF_CAN interface name too long");
Py_DECREF(interfaceName);
return 0;
}
addr->can_family = AF_CAN;
addr->can_ifindex = ifr.ifr_ifindex;
*len_ret = sizeof(*addr);
Py_DECREF(interfaceName);
return 1;
}
default:
PyErr_SetString(socket_error,
"getsockaddrarg: unsupported CAN protocol");
return 0;
}
#endif
/* More cases here... */ /* More cases here... */
default: default:
@ -1680,6 +1746,14 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
} }
#endif #endif
#ifdef HAVE_LINUX_CAN_H
case AF_CAN:
{
*len_ret = sizeof (struct sockaddr_can);
return 1;
}
#endif
/* More cases here... */ /* More cases here... */
default: default:
@ -5533,6 +5607,15 @@ PyInit__socket(void)
PyModule_AddStringConstant(m, "BDADDR_LOCAL", "00:00:00:FF:FF:FF"); PyModule_AddStringConstant(m, "BDADDR_LOCAL", "00:00:00:FF:FF:FF");
#endif #endif
#ifdef AF_CAN
/* Controller Area Network */
PyModule_AddIntConstant(m, "AF_CAN", AF_CAN);
#endif
#ifdef PF_CAN
/* Controller Area Network */
PyModule_AddIntConstant(m, "PF_CAN", PF_CAN);
#endif
#ifdef AF_PACKET #ifdef AF_PACKET
PyModule_AddIntMacro(m, AF_PACKET); PyModule_AddIntMacro(m, AF_PACKET);
#endif #endif
@ -5803,6 +5886,28 @@ PyInit__socket(void)
#else #else
PyModule_AddIntConstant(m, "SOL_UDP", 17); PyModule_AddIntConstant(m, "SOL_UDP", 17);
#endif #endif
#ifdef SOL_CAN_BASE
PyModule_AddIntConstant(m, "SOL_CAN_BASE", SOL_CAN_BASE);
#endif
#ifdef SOL_CAN_RAW
PyModule_AddIntConstant(m, "SOL_CAN_RAW", SOL_CAN_RAW);
PyModule_AddIntConstant(m, "CAN_RAW", CAN_RAW);
#endif
#ifdef HAVE_LINUX_CAN_H
PyModule_AddIntConstant(m, "CAN_EFF_FLAG", CAN_EFF_FLAG);
PyModule_AddIntConstant(m, "CAN_RTR_FLAG", CAN_RTR_FLAG);
PyModule_AddIntConstant(m, "CAN_ERR_FLAG", CAN_ERR_FLAG);
PyModule_AddIntConstant(m, "CAN_SFF_MASK", CAN_SFF_MASK);
PyModule_AddIntConstant(m, "CAN_EFF_MASK", CAN_EFF_MASK);
PyModule_AddIntConstant(m, "CAN_ERR_MASK", CAN_ERR_MASK);
#endif
#ifdef HAVE_LINUX_CAN_RAW_H
PyModule_AddIntConstant(m, "CAN_RAW_FILTER", CAN_RAW_FILTER);
PyModule_AddIntConstant(m, "CAN_RAW_ERR_FILTER", CAN_RAW_ERR_FILTER);
PyModule_AddIntConstant(m, "CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK);
PyModule_AddIntConstant(m, "CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS);
#endif
#ifdef IPPROTO_IP #ifdef IPPROTO_IP
PyModule_AddIntConstant(m, "IPPROTO_IP", IPPROTO_IP); PyModule_AddIntConstant(m, "IPPROTO_IP", IPPROTO_IP);
#else #else

View File

@ -72,6 +72,14 @@ typedef int socklen_t;
# include <linux/tipc.h> # include <linux/tipc.h>
#endif #endif
#ifdef HAVE_LINUX_CAN_H
#include <linux/can.h>
#endif
#ifdef HAVE_LINUX_CAN_RAW_H
#include <linux/can/raw.h>
#endif
#ifndef Py__SOCKET_H #ifndef Py__SOCKET_H
#define Py__SOCKET_H #define Py__SOCKET_H
#ifdef __cplusplus #ifdef __cplusplus
@ -126,6 +134,9 @@ typedef union sock_addr {
#ifdef HAVE_NETPACKET_PACKET_H #ifdef HAVE_NETPACKET_PACKET_H
struct sockaddr_ll ll; struct sockaddr_ll ll;
#endif #endif
#ifdef HAVE_LINUX_CAN_H
struct sockaddr_can can;
#endif
} sock_addr_t; } sock_addr_t;
/* The object holding a socket. It holds some extra information, /* The object holding a socket. It holds some extra information,

554
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -1376,6 +1376,13 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[
#endif #endif
]) ])
# On Linux, can.h and can/raw.h require sys/socket.h
AC_CHECK_HEADERS(linux/can.h linux/can/raw.h,,,[
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
])
# checks for typedefs # checks for typedefs
was_it_defined=no was_it_defined=no
AC_MSG_CHECKING(for clock_t in time.h) AC_MSG_CHECKING(for clock_t in time.h)

View File

@ -467,6 +467,12 @@
/* Define to 1 if you have the `linkat' function. */ /* Define to 1 if you have the `linkat' function. */
#undef HAVE_LINKAT #undef HAVE_LINKAT
/* Define to 1 if you have the <linux/can.h> header file. */
#undef HAVE_LINUX_CAN_H
/* Define to 1 if you have the <linux/can/raw.h> header file. */
#undef HAVE_LINUX_CAN_RAW_H
/* Define to 1 if you have the <linux/netlink.h> header file. */ /* Define to 1 if you have the <linux/netlink.h> header file. */
#undef HAVE_LINUX_NETLINK_H #undef HAVE_LINUX_NETLINK_H