Charles-François Natali <neolo...@free.fr> added the comment:
> - dummy question: why an address is a tuple with 1 string instead of just
> the string? Does AF_UNIX also uses a tuple of 1 string?
I think the reason behind the tuple is future proofing.
Here's the definition of `struct sockaddr_can` in my Linux box's headers:
"""
/**
* struct sockaddr_can - the sockaddr structure for CAN sockets
* @can_family: address family number AF_CAN.
* @can_ifindex: CAN network interface index.
* @can_addr: protocol specific address information
*/
struct sockaddr_can {
sa_family_t can_family;
int can_ifindex;
union {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
/* reserved for future CAN protocols address information */
} can_addr;
};
"""
By making it a tuple, it will be easier to extend the address that
must be passed to bind(2), should it ever evolve, in a backward
compatible way. Well, that's just a guess (I'm by no means a SocketCAN
expert :-).
> - the example should also use struct.pack() to create the frame, I don't
> like hardcoded BLOB
Done.
> - in test_socket: _have_socket_can() interprets permission denied as "CAN
> is not supported", it would be nice to provide a better skip message. Create
> maybe a decorator based?
AFAICT, it shouldn't fail with EPERM or so.
Also, I'm not sure what the message would look like, and it's probably
a bit overkill.
> - _have_socket_can(): you may move s.close() outside the try block (add
> maybe a "else:" block?) because you may hide a real bug in .close()
Changed that.
> - data += b'\0' * (8 - can_dlc): I prefer data = data.ljust(8, '\x00')
Hum... Done.
> - you might add frame encoder/decoder in your example
Done.
> - if (!strcmp(PyBytes_AS_STRING(interfaceName), "")) hum.....
> PyBytes_GET_SIZE(intername)==0 should be enough
Done.
> - you truncate the interface name, it can be surprising, I would prefer an
> error (e.g. "interface name too long: 20 characters, the maximum is 10
> characters" ?)
I changed that, and added a test. Also, note that AF_PACKET suffers
from the same problem.
I'll submit a separate patch.
> - (oh no! don't include horrible configure diff in patches for the bug
> tracker :-p)
Yeah, I usually take care of that, but forgot this time.
> In which Linux version was CAN introduced?
>
Apparently, 2.6.25. Note that we don't need
@support.requires_linux_version() though, it should be catched by
HAVE_SOCKET_CAN (also, you can't use it as a class decorator...).
Here's the updated patch. It passes on all the buildbots (of course,
it's only relevant on Linux).
----------
Added file: http://bugs.python.org/file23234/socketcan_v5.patch
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue10141>
_______________________________________
diff -r a06ef7ab7321 Doc/library/socket.rst
--- a/Doc/library/socket.rst Wed Sep 21 22:05:01 2011 +0200
+++ b/Doc/library/socket.rst Fri Sep 23 23:27:19 2011 +0200
@@ -80,6 +80,11 @@
If *addr_type* is TIPC_ADDR_ID, then *v1* is the node, *v2* is the
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`)
support specific representations.
@@ -216,6 +221,19 @@
in the Unix header files are defined; for a few symbols, default values are
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_*
RCVALL_*
@@ -387,10 +405,14 @@
Create a new socket using the given address family, socket type and protocol
number. The address family should be :const:`AF_INET` (the default),
- :const:`AF_INET6` or :const:`AF_UNIX`. The socket type should be
- :const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM` or perhaps one of
the
- other ``SOCK_`` constants. The protocol number is usually zero and may be
- omitted in that case.
+ :const:`AF_INET6`, :const:`AF_UNIX` or :const:`AF_CAN`. The socket type
+ should be :const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM`,
+ :const:`SOCK_RAW` or perhaps one of the other ``SOCK_`` constants. The
+ 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]]])
@@ -1213,7 +1235,7 @@
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
the interface::
@@ -1238,6 +1260,45 @@
# 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
+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
lead to this error::
diff -r a06ef7ab7321 Doc/whatsnew/3.3.rst
--- a/Doc/whatsnew/3.3.rst Wed Sep 21 22:05:01 2011 +0200
+++ b/Doc/whatsnew/3.3.rst Fri Sep 23 23:27:19 2011 +0200
@@ -246,15 +246,22 @@
socket
------
-The :class:`~socket.socket` class now exposes addititonal methods to
-process ancillary data when supported by the underlying platform:
+* The :class:`~socket.socket` class now exposes addititonal methods to process
+ ancillary data when supported by the underlying platform:
-* :func:`~socket.socket.sendmsg`
-* :func:`~socket.socket.recvmsg`
-* :func:`~socket.socket.recvmsg_into`
+ * :func:`~socket.socket.sendmsg`
+ * :func:`~socket.socket.recvmsg`
+ * :func:`~socket.socket.recvmsg_into`
-(Contributed by David Watson in :issue:`6560`, based on an earlier patch
-by Heiko Wundram)
+ (Contributed by David Watson in :issue:`6560`, based on an earlier patch by
+ 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
---
diff -r a06ef7ab7321 Lib/test/test_socket.py
--- a/Lib/test/test_socket.py Wed Sep 21 22:05:01 2011 +0200
+++ b/Lib/test/test_socket.py Fri Sep 23 23:27:19 2011 +0200
@@ -21,6 +21,7 @@
import signal
import math
import pickle
+import struct
try:
import fcntl
except ImportError:
@@ -36,6 +37,18 @@
thread = 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
SIZEOF_INT = array.array("i").itemsize
@@ -80,6 +93,24 @@
with self._cleanup_lock:
return super().doCleanups(*args, **kwargs)
+class SocketCANTest(unittest.TestCase):
+
+ 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:
"""Threadable Test class
@@ -210,6 +241,26 @@
self.cli = None
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):
"""Socket tests for client-server connection.
@@ -1072,6 +1123,112 @@
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.')
class BasicTCPTest(SocketConnectedTest):
@@ -4185,6 +4342,7 @@
if isTipcAvailable():
tests.append(TIPCTest)
tests.append(TIPCThreadableTest)
+ tests.extend([BasicCANTest, CANTest])
tests.extend([
CmsgMacroTests,
SendmsgUDPTest,
diff -r a06ef7ab7321 Misc/ACKS
--- a/Misc/ACKS Wed Sep 21 22:05:01 2011 +0200
+++ b/Misc/ACKS Fri Sep 23 23:27:19 2011 +0200
@@ -318,6 +318,7 @@
Martin Franklin
Robin Friedrich
Ivan Frohne
+Matthias Fuchs
Jim Fulton
Tadayoshi Funaba
Gyro Funch
@@ -353,6 +354,7 @@
Yannick Gingras
Christoph Gohlke
Tim Golden
+Tiago Gonçalves
Chris Gonnerman
David Goodger
Hans de Graaff
diff -r a06ef7ab7321 Modules/socketmodule.c
--- a/Modules/socketmodule.c Wed Sep 21 22:05:01 2011 +0200
+++ b/Modules/socketmodule.c Fri Sep 23 23:27:19 2011 +0200
@@ -1220,6 +1220,25 @@
}
#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... */
default:
@@ -1587,6 +1606,51 @@
}
#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... */
default:
@@ -1680,6 +1744,14 @@
}
#endif
+#ifdef HAVE_LINUX_CAN_H
+ case AF_CAN:
+ {
+ *len_ret = sizeof (struct sockaddr_can);
+ return 1;
+ }
+#endif
+
/* More cases here... */
default:
@@ -5533,6 +5605,15 @@
PyModule_AddStringConstant(m, "BDADDR_LOCAL", "00:00:00:FF:FF:FF");
#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
PyModule_AddIntMacro(m, AF_PACKET);
#endif
@@ -5803,6 +5884,28 @@
#else
PyModule_AddIntConstant(m, "SOL_UDP", 17);
#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
PyModule_AddIntConstant(m, "IPPROTO_IP", IPPROTO_IP);
#else
diff -r a06ef7ab7321 Modules/socketmodule.h
--- a/Modules/socketmodule.h Wed Sep 21 22:05:01 2011 +0200
+++ b/Modules/socketmodule.h Fri Sep 23 23:27:19 2011 +0200
@@ -72,6 +72,14 @@
# include <linux/tipc.h>
#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
#define Py__SOCKET_H
#ifdef __cplusplus
@@ -126,6 +134,9 @@
#ifdef HAVE_NETPACKET_PACKET_H
struct sockaddr_ll ll;
#endif
+#ifdef HAVE_LINUX_CAN_H
+ struct sockaddr_can can;
+#endif
} sock_addr_t;
/* The object holding a socket. It holds some extra information,
diff -r a06ef7ab7321 configure.in
--- a/configure.in Wed Sep 21 22:05:01 2011 +0200
+++ b/configure.in Fri Sep 23 23:27:19 2011 +0200
@@ -1354,6 +1354,13 @@
#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
was_it_defined=no
AC_MSG_CHECKING(for clock_t in time.h)
diff -r a06ef7ab7321 pyconfig.h.in
--- a/pyconfig.h.in Wed Sep 21 22:05:01 2011 +0200
+++ b/pyconfig.h.in Fri Sep 23 23:27:19 2011 +0200
@@ -467,6 +467,12 @@
/* Define to 1 if you have the `linkat' function. */
#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. */
#undef HAVE_LINUX_NETLINK_H
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com