From fb2d25a15432bf53bd62c1033114732f03a22b8d Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 7 Jan 2008 16:12:44 +0000 Subject: [PATCH] Issue #1646: Make socket support TIPC. The socket module now has support for TIPC under Linux, see http://tipc.sf.net/ for more information. Thanks to Alberto Bertogli for the patch --- Doc/library/socket.rst | 27 +++++++ Lib/test/test_socket.py | 82 +++++++++++++++++++++ Misc/NEWS | 5 ++ Modules/socketmodule.c | 154 +++++++++++++++++++++++++++++++++++++++- Modules/socketmodule.h | 4 ++ configure | 5 +- configure.in | 2 +- pyconfig.h.in | 4 +- 8 files changed, 278 insertions(+), 5 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 40e6d197f86..0e3aa198167 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -66,6 +66,27 @@ numeric address in *host* portion. .. versionadded:: 2.5 AF_NETLINK sockets are represented as pairs ``pid, groups``. +.. versionadded:: 2.6 + Linux-only support for TIPC is also available using the :const:`AF_TIPC` + address family. TIPC is an open, non-IP based networked protocol designed + for use in clustered computer environments. Addresses are represented by a + tuple, and the fields depend on the address type. The general tuple form is + ``(addr_type, v1, v2, v3 [, scope])``, where: + + - *addr_type* is one of TIPC_ADDR_NAMESEQ, TIPC_ADDR_NAME, or + TIPC_ADDR_ID. + - *scope* is one of TIPC_ZONE_SCOPE, TIPC_CLUSTER_SCOPE, and + TIPC_NODE_SCOPE. + - If *addr_type* is TIPC_ADDR_NAME, then *v1* is the server type, *v2* is + the port identifier, and *v3* should be 0. + + If *addr_type* is TIPC_ADDR_NAMESEQ, then *v1* is the server type, *v2* + is the lower port number, and *v3* is the upper port number. + + If *addr_type* is TIPC_ADDR_ID, then *v1* is the node, *v2* is the + reference, and *v3* should be set to 0. + + All errors raise exceptions. The normal exceptions for invalid argument types and out-of-memory conditions can be raised; errors related to socket or address semantics raise the error :exc:`socket.error`. @@ -169,6 +190,12 @@ The module :mod:`socket` exports the following constants and functions: .. versionadded:: 2.6 +.. data:: TIPC_* + + TIPC related constants, matching the ones exported by the C socket API. See + the TIPC documentation for more information. + + .. versionadded:: 2.6 .. data:: has_ipv6 diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 8f69a40f0b0..45f0434b1db 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1084,6 +1084,85 @@ class BufferIOTest(SocketConnectedTest): buf = buffer(MSG) self.serv_conn.send(buf) + +TIPC_STYPE = 2000 +TIPC_LOWER = 200 +TIPC_UPPER = 210 + +def isTipcAvailable(): + """Check if the TIPC module is loaded + + The TIPC module is not loaded automatically on Ubuntu and probably + other Linux distros. + """ + if not hasattr(socket, "AF_TIPC"): + return False + if not os.path.isfile("/proc/modules"): + return False + with open("/proc/modules") as f: + for line in f: + if line.startswith("tipc "): + return True + if test_support.debug: + print "TIPC module is not loaded, please 'sudo modprobe tipc'" + return False + +class TIPCTest (unittest.TestCase): + def testRDM(self): + srv = socket.socket(socket.AF_TIPC, socket.SOCK_RDM) + cli = socket.socket(socket.AF_TIPC, socket.SOCK_RDM) + + srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + srvaddr = (socket.TIPC_ADDR_NAMESEQ, TIPC_STYPE, + TIPC_LOWER, TIPC_UPPER) + srv.bind(srvaddr) + + sendaddr = (socket.TIPC_ADDR_NAME, TIPC_STYPE, + TIPC_LOWER + (TIPC_UPPER - TIPC_LOWER) / 2, 0) + cli.sendto(MSG, sendaddr) + + msg, recvaddr = srv.recvfrom(1024) + + self.assertEqual(cli.getsockname(), recvaddr) + self.assertEqual(msg, MSG) + + +class TIPCThreadableTest (unittest.TestCase, ThreadableTest): + def __init__(self, methodName = 'runTest'): + unittest.TestCase.__init__(self, methodName = methodName) + ThreadableTest.__init__(self) + + def setUp(self): + self.srv = socket.socket(socket.AF_TIPC, socket.SOCK_STREAM) + self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + srvaddr = (socket.TIPC_ADDR_NAMESEQ, TIPC_STYPE, + TIPC_LOWER, TIPC_UPPER) + self.srv.bind(srvaddr) + self.srv.listen(5) + self.serverExplicitReady() + self.conn, self.connaddr = self.srv.accept() + + def clientSetUp(self): + # The is a hittable race between serverExplicitReady() and the + # accept() call; sleep a little while to avoid it, otherwise + # we could get an exception + time.sleep(0.1) + self.cli = socket.socket(socket.AF_TIPC, socket.SOCK_STREAM) + addr = (socket.TIPC_ADDR_NAME, TIPC_STYPE, + TIPC_LOWER + (TIPC_UPPER - TIPC_LOWER) / 2, 0) + self.cli.connect(addr) + self.cliaddr = self.cli.getsockname() + + def testStream(self): + msg = self.conn.recv(1024) + self.assertEqual(msg, MSG) + self.assertEqual(self.cliaddr, self.connaddr) + + def _testStream(self): + self.cli.send(MSG) + self.cli.close() + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2] @@ -1105,6 +1184,9 @@ def test_main(): tests.append(BasicSocketPairTest) if sys.platform == 'linux2': tests.append(TestLinuxAbstractNamespace) + if isTipcAvailable(): + tests.append(TIPCTest) + tests.append(TIPCThreadableTest) thread_info = test_support.threading_setup() test_support.run_unittest(*tests) diff --git a/Misc/NEWS b/Misc/NEWS index 47321139732..3896dd86bea 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -342,6 +342,8 @@ Core and builtins Library ------- +- Issue #1646: Make socket support the TIPC protocol. + - Bug #1742: return os.curdir from os.path.relpath() if both arguments are equal instead of raising an exception. @@ -927,6 +929,9 @@ Library Extension Modules ----------------- +- Issue #1646: Make socket support TIPC. The socket module now has support + for TIPC under Linux, see http://tipc.sf.net/ for more information. + - Added interface for Windows' WSAIoctl to socket object and added an example for a simple network sniffer. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c30f1b3aae3..e3f81db7602 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7,7 +7,8 @@ This module provides an interface to Berkeley socket IPC. Limitations: - Only AF_INET, AF_INET6 and AF_UNIX address families are supported in a - portable manner, though AF_PACKET and AF_NETLINK are supported under Linux. + portable manner, though AF_PACKET, AF_NETLINK and AF_TIPC are supported + under Linux. - No read/write operations (use sendall/recv or makefile instead). - Additional restrictions apply on some non-Unix platforms (compensated for by socket.py). @@ -52,6 +53,25 @@ Module interface: the Ethernet protocol number to be received. For example: ("eth0",0x1234). Optional 3rd,4th,5th elements in the tuple specify packet-type and ha-type/addr. +- an AF_TIPC socket address is expressed as + (addr_type, v1, v2, v3 [, scope]); where addr_type can be one of: + TIPC_ADDR_NAMESEQ, TIPC_ADDR_NAME, and TIPC_ADDR_ID; + and scope can be one of: + TIPC_ZONE_SCOPE, TIPC_CLUSTER_SCOPE, and TIPC_NODE_SCOPE. + The meaning of v1, v2 and v3 depends on the value of addr_type: + if addr_type is TIPC_ADDR_NAME: + v1 is the server type + v2 is the port identifier + v3 is ignored + if addr_type is TIPC_ADDR_NAMESEQ: + v1 is the server type + v2 is the lower port number + v3 is the upper port number + if addr_type is TIPC_ADDR_ID: + v1 is the node + v2 is the ref + v3 is ignored + Local naming conventions: @@ -1094,6 +1114,39 @@ makesockaddr(int sockfd, struct sockaddr *addr, int addrlen, int proto) } #endif +#ifdef HAVE_LINUX_TIPC_H + case AF_TIPC: + { + struct sockaddr_tipc *a = (struct sockaddr_tipc *) addr; + if (a->addrtype == TIPC_ADDR_NAMESEQ) { + return Py_BuildValue("IIIII", + a->addrtype, + a->addr.nameseq.type, + a->addr.nameseq.lower, + a->addr.nameseq.upper, + a->scope); + } else if (a->addrtype == TIPC_ADDR_NAME) { + return Py_BuildValue("IIIII", + a->addrtype, + a->addr.name.name.type, + a->addr.name.name.instance, + a->addr.name.name.instance, + a->scope); + } else if (a->addrtype == TIPC_ADDR_ID) { + return Py_BuildValue("IIIII", + a->addrtype, + a->addr.id.node, + a->addr.id.ref, + 0, + a->scope); + } else { + PyErr_SetString(PyExc_TypeError, + "Invalid address type"); + return NULL; + } + } +#endif + /* More cases here... */ default: @@ -1379,6 +1432,56 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif +#ifdef HAVE_LINUX_TIPC_H + case AF_TIPC: + { + unsigned int atype, v1, v2, v3; + unsigned int scope = TIPC_CLUSTER_SCOPE; + struct sockaddr_tipc *addr; + + if (!PyTuple_Check(args)) { + PyErr_Format( + PyExc_TypeError, + "getsockaddrarg: " + "AF_TIPC address must be tuple, not %.500s", + Py_TYPE(args)->tp_name); + return 0; + } + + if (!PyArg_ParseTuple(args, + "IIII|I;Invalid TIPC address format", + &atype, &v1, &v2, &v3, &scope)) + return 0; + + addr = (struct sockaddr_tipc *) addr_ret; + memset(addr, 0, sizeof(struct sockaddr_tipc)); + + addr->family = AF_TIPC; + addr->scope = scope; + addr->addrtype = atype; + + if (atype == TIPC_ADDR_NAMESEQ) { + addr->addr.nameseq.type = v1; + addr->addr.nameseq.lower = v2; + addr->addr.nameseq.upper = v3; + } else if (atype == TIPC_ADDR_NAME) { + addr->addr.name.name.type = v1; + addr->addr.name.name.instance = v2; + } else if (atype == TIPC_ADDR_ID) { + addr->addr.id.node = v1; + addr->addr.id.ref = v2; + } else { + /* Shouldn't happen */ + PyErr_SetString(PyExc_TypeError, "Invalid address type"); + return 0; + } + + *len_ret = sizeof(*addr); + + return 1; + } +#endif + /* More cases here... */ default: @@ -1464,6 +1567,14 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) } #endif +#ifdef HAVE_LINUX_TIPC_H + case AF_TIPC: + { + *len_ret = sizeof (struct sockaddr_tipc); + return 1; + } +#endif + /* More cases here... */ default: @@ -4448,6 +4559,47 @@ init_socket(void) PyModule_AddIntConstant(m, "PACKET_FASTROUTE", PACKET_FASTROUTE); #endif +#ifdef HAVE_LINUX_TIPC_H + PyModule_AddIntConstant(m, "AF_TIPC", AF_TIPC); + + /* for addresses */ + PyModule_AddIntConstant(m, "TIPC_ADDR_NAMESEQ", TIPC_ADDR_NAMESEQ); + PyModule_AddIntConstant(m, "TIPC_ADDR_NAME", TIPC_ADDR_NAME); + PyModule_AddIntConstant(m, "TIPC_ADDR_ID", TIPC_ADDR_ID); + + PyModule_AddIntConstant(m, "TIPC_ZONE_SCOPE", TIPC_ZONE_SCOPE); + PyModule_AddIntConstant(m, "TIPC_CLUSTER_SCOPE", TIPC_CLUSTER_SCOPE); + PyModule_AddIntConstant(m, "TIPC_NODE_SCOPE", TIPC_NODE_SCOPE); + + /* for setsockopt() */ + PyModule_AddIntConstant(m, "SOL_TIPC", SOL_TIPC); + PyModule_AddIntConstant(m, "TIPC_IMPORTANCE", TIPC_IMPORTANCE); + PyModule_AddIntConstant(m, "TIPC_SRC_DROPPABLE", TIPC_SRC_DROPPABLE); + PyModule_AddIntConstant(m, "TIPC_DEST_DROPPABLE", + TIPC_DEST_DROPPABLE); + PyModule_AddIntConstant(m, "TIPC_CONN_TIMEOUT", TIPC_CONN_TIMEOUT); + + PyModule_AddIntConstant(m, "TIPC_LOW_IMPORTANCE", + TIPC_LOW_IMPORTANCE); + PyModule_AddIntConstant(m, "TIPC_MEDIUM_IMPORTANCE", + TIPC_MEDIUM_IMPORTANCE); + PyModule_AddIntConstant(m, "TIPC_HIGH_IMPORTANCE", + TIPC_HIGH_IMPORTANCE); + PyModule_AddIntConstant(m, "TIPC_CRITICAL_IMPORTANCE", + TIPC_CRITICAL_IMPORTANCE); + + /* for subscriptions */ + PyModule_AddIntConstant(m, "TIPC_SUB_PORTS", TIPC_SUB_PORTS); + PyModule_AddIntConstant(m, "TIPC_SUB_SERVICE", TIPC_SUB_SERVICE); + PyModule_AddIntConstant(m, "TIPC_SUB_CANCEL", TIPC_SUB_CANCEL); + PyModule_AddIntConstant(m, "TIPC_WAIT_FOREVER", TIPC_WAIT_FOREVER); + PyModule_AddIntConstant(m, "TIPC_PUBLISHED", TIPC_PUBLISHED); + PyModule_AddIntConstant(m, "TIPC_WITHDRAWN", TIPC_WITHDRAWN); + PyModule_AddIntConstant(m, "TIPC_SUBSCR_TIMEOUT", TIPC_SUBSCR_TIMEOUT); + PyModule_AddIntConstant(m, "TIPC_CFG_SRV", TIPC_CFG_SRV); + PyModule_AddIntConstant(m, "TIPC_TOP_SRV", TIPC_TOP_SRV); +#endif + /* Socket types */ PyModule_AddIntConstant(m, "SOCK_STREAM", SOCK_STREAM); PyModule_AddIntConstant(m, "SOCK_DGRAM", SOCK_DGRAM); diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 95bc2339cc1..285c1fe6e55 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -60,6 +60,10 @@ # include #endif +#ifdef HAVE_LINUX_TIPC_H +# include +#endif + #ifndef Py__SOCKET_H #define Py__SOCKET_H #ifdef __cplusplus diff --git a/configure b/configure index fb79c063567..5929c23aacc 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 59558 . +# From configure.in Revision: 59611 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61 for python 2.6. # @@ -5415,6 +5415,7 @@ done + for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ @@ -5428,7 +5429,7 @@ sys/param.h sys/poll.h sys/select.h sys/socket.h sys/statvfs.h sys/stat.h \ sys/time.h \ sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \ sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \ -bluetooth/bluetooth.h +bluetooth/bluetooth.h linux/tipc.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then diff --git a/configure.in b/configure.in index cdf1366346d..2ff6244c7fe 100644 --- a/configure.in +++ b/configure.in @@ -1107,7 +1107,7 @@ sys/param.h sys/poll.h sys/select.h sys/socket.h sys/statvfs.h sys/stat.h \ sys/time.h \ sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \ sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \ -bluetooth/bluetooth.h) +bluetooth/bluetooth.h linux/tipc.h) AC_HEADER_DIRENT AC_HEADER_MAJOR diff --git a/pyconfig.h.in b/pyconfig.h.in index 26197fb01b4..ef04c12374c 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -354,6 +354,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETLINK_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_TIPC_H + /* Define this if you have the type long long. */ #undef HAVE_LONG_LONG @@ -1040,4 +1043,3 @@ #endif /*Py_PYCONFIG_H*/ -