diff -r 40e97c9dae7a Doc/library/socket.rst --- a/Doc/library/socket.rst Tue Oct 18 17:55:18 2016 +0200 +++ b/Doc/library/socket.rst Mon Nov 07 16:13:59 2016 -0500 @@ -149,6 +149,33 @@ .. versionadded:: 3.6 +- :const:`AF_VSOCK` sockets are represented as a (CID, port) tuple + where the context ID or CID and port are integers. + + - :const:`VMADDR_CID_ANY` will bind to any CID. Equivalent of INADDR_ANY. + + - :const:`VMADDR_CID_HOST` is the destination CID in an address when + referring to the host. + + - :const:`VMADDR_PORT_ANY` will bind to any available port. + + - :const:`IOCTL_VM_SOCKETS_GET_LOCAL_CID` an IOCTL available to discover + the local CID address. + + - :const:`SO_VM_SOCKETS_BUFFER_SIZE` is used as the option name in + setsockopt or getsockopt to set or get an unsigned long long that + specifies the size of the underlying stream socket buffer. + + - :const:`SO_VM_SOCKETS_BUFFER_MIN_SIZE` is used as the option name in + setsockopt or getsockopt to set or get an unsigned long long that + specifies the minimum size allowed for the underying stream socket buffer. + + - :const:`SO_VM_SOCKETS_BUFFER_MAX_SIZE` is used as the option name in + setsockopt or getsockopt to set or get an unsigned long long that + specifies the maximum size allowed for the underying socket buffer. + + .. versionadded:: 3.7 + - Certain other address families (:const:`AF_PACKET`, :const:`AF_CAN`) support specific representations. diff -r 40e97c9dae7a Lib/test/test_socket.py --- a/Lib/test/test_socket.py Tue Oct 18 17:55:18 2016 +0200 +++ b/Lib/test/test_socket.py Mon Nov 07 16:13:59 2016 -0500 @@ -33,6 +33,8 @@ HOST = support.HOST MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return +VSOCKPORT = 1234 + try: import _thread as thread import threading @@ -75,12 +77,24 @@ s.close() return True +def _have_socket_vsock(): + """Check whether AF_VSOCK sockets are supported on this host.""" + try: + s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM, 0) + except (AttributeError, OSError): + return False + else: + s.close() + return True + HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_RDS = _have_socket_rds() HAVE_SOCKET_ALG = _have_socket_alg() +HAVE_SOCKET_VSOCK = _have_socket_vsock() + # Size in bytes of the int type SIZEOF_INT = array.array("i").itemsize @@ -191,7 +205,6 @@ except OSError: self.skipTest('unable to bind RDS socket') - class ThreadableTest: """Threadable Test class @@ -369,6 +382,81 @@ self.cli = None ThreadableTest.clientTearDown(self) +def getCid(): + import struct + import fcntl + + try: + fd = open("/dev/vsock", "rb") + except: + return None + try: + r = fcntl.ioctl(fd, socket.IOCTL_VM_SOCKETS_GET_LOCAL_CID, " ") + except: + fd.close() + return None + fd.close() + return struct.unpack("I", r)[0] + +@unittest.skipUnless(HAVE_SOCKET_VSOCK, 'VSOCK sockets required for this test.') +@unittest.skipUnless(getCid() != None, + "Cannot obtain the VSOCK cid. The vsock module may not be loaded") +class ThreadedVSOCKSocketStreamTest(unittest.TestCase, ThreadableTest): + + def __init__(self, methodName = 'runTest'): + unittest.TestCase.__init__(self, methodName = methodName) + ThreadableTest.__init__(self) + + def setUp(self): + self.serv = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) + self.addCleanup(self.serv.close) + self.serv.bind((socket.VMADDR_CID_ANY, VSOCKPORT)) + self.serv.listen() + self.serverExplicitReady() + self.conn, self.connaddr = self.serv.accept() + self.addCleanup(self.conn.close) + + def clientSetUp(self): + time.sleep(0.1) + self.cli = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) + self.addCleanup(self.cli.close) + cid = getCid() + self.cli.connect((cid, VSOCKPORT)) + + def testStream(self): + msg = self.conn.recv(1024) + self.assertEqual(msg, MSG) + + def _testStream(self): + self.cli.send(MSG) + self.cli.close() + +@unittest.skipUnless(HAVE_SOCKET_VSOCK, 'VSOCK sockets required for this test.') +@unittest.skipUnless(getCid() != None, + "Cannot obtain the VSOCK cid. The vsock module may not be loaded") +class ThreadedVSOCKSocketDgramTest(unittest.TestCase, ThreadableTest): + + def __init__(self, methodName = 'runTest'): + unittest.TestCase.__init__(self, methodName = methodName) + ThreadableTest.__init__(self) + + def setUp(self): + self.serv = socket.socket(socket.AF_VSOCK, socket.SOCK_DGRAM) + self.addCleanup(self.serv.close) + self.serv.bind((socket.VMADDR_CID_ANY, VSOCKPORT)) + + def clientSetUp(self): + self.cli = socket.socket(socket.AF_VSOCK, socket.SOCK_DGRAM) + self.addCleanup(self.cli.close) + + def testDgram(self): + msg, address = self.serv.recvfrom(1024) + self.assertEqual(msg, MSG) + + def _testDgram(self): + cid = getCid() + self.cli.sendto(MSG, (cid, VSOCKPORT)) + class SocketConnectedTest(ThreadedTCPSocketTest): """Socket tests for client-server connection. @@ -1786,6 +1874,43 @@ self.assertIn(self.serv, r) +@unittest.skipUnless(HAVE_SOCKET_VSOCK, 'VSOCK sockets required for this test.') +class BasicVSOCKTest(unittest.TestCase): + + def testCrucialConstants(self): + socket.AF_VSOCK + + def testVSOCKConstants(self): + socket.SO_VM_SOCKETS_BUFFER_SIZE + socket.SO_VM_SOCKETS_BUFFER_MIN_SIZE + socket.SO_VM_SOCKETS_BUFFER_MAX_SIZE + socket.VMADDR_CID_ANY + socket.VMADDR_PORT_ANY + socket.VMADDR_CID_HOST + socket.VM_SOCKETS_INVALID_VERSION + socket.IOCTL_VM_SOCKETS_GET_LOCAL_CID + + def testCreateSocket(self): + with socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) as s: + pass + + def testSocketBufferSize(self): + with socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) as s: + orig_max = s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MAX_SIZE) + orig = s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_SIZE) + orig_min = s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MIN_SIZE) + + s.setsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MAX_SIZE, orig_max * 2) + s.setsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_SIZE, orig * 2) + s.setsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MIN_SIZE, orig_min * 2) + + self.assertEqual(orig_max * 2, + s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MAX_SIZE)) + self.assertEqual(orig * 2, + s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_SIZE)) + self.assertEqual(orig_min * 2, + s.getsockopt(socket.AF_VSOCK, socket.SO_VM_SOCKETS_BUFFER_MIN_SIZE)) + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): @@ -5574,6 +5699,11 @@ tests.extend([BasicRDSTest, RDSTest]) tests.append(LinuxKernelCryptoAPI) tests.extend([ + BasicVSOCKTest, + ThreadedVSOCKSocketStreamTest, + ThreadedVSOCKSocketDgramTest + ]) + tests.extend([ CmsgMacroTests, SendmsgUDPTest, RecvmsgUDPTest, diff -r 40e97c9dae7a Modules/socketmodule.c --- a/Modules/socketmodule.c Tue Oct 18 17:55:18 2016 +0200 +++ b/Modules/socketmodule.c Mon Nov 07 16:13:59 2016 -0500 @@ -316,6 +316,13 @@ #endif /* HAVE_SOCKADDR_ALG */ +#ifdef HAVE_LINUX_VM_SOCKETS_H +#include +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif +#endif + /* Generic socket object definitions and includes */ #define PySocket_BUILDING_SOCKET #include "socketmodule.h" @@ -1254,6 +1261,14 @@ } #endif /* AF_NETLINK */ +#if defined(AF_VSOCK) + case AF_VSOCK: + { + struct sockaddr_vm *a = (struct sockaddr_vm *) addr; + return Py_BuildValue("II", a->svm_cid, a->svm_port); + } +#endif /* AF_VSOCK */ + #ifdef ENABLE_IPV6 case AF_INET6: { @@ -1602,6 +1617,32 @@ } #endif +#if defined(AF_VSOCK) + case AF_VSOCK: + { + struct sockaddr_vm* addr; + int port, cid; + addr = (struct sockaddr_vm *)addr_ret; + memset(addr, 0, sizeof(struct sockaddr_vm)); + if (!PyTuple_Check(args)) { + PyErr_Format( + PyExc_TypeError, + "getsockaddrarg: " + "AF_VSOCK address must be tuple, not %.500s", + Py_TYPE(args)->tp_name); + return 0; + } + if (!PyArg_ParseTuple(args, "II:getsockaddrarg", &cid, &port)) + return 0; + addr->svm_family = s->sock_family; + addr->svm_port = port; + addr->svm_cid = cid; + *len_ret = sizeof(*addr); + return 1; + } +#endif + + #ifdef AF_RDS case AF_RDS: /* RDS sockets use sockaddr_in: fall-through */ @@ -2070,6 +2111,14 @@ } #endif +#if defined(AF_VSOCK) + case AF_VSOCK: + { + *len_ret = sizeof (struct sockaddr_vm); + return 1; + } +#endif + #ifdef AF_RDS case AF_RDS: /* RDS sockets use sockaddr_in: fall-through */ @@ -2566,6 +2615,21 @@ unsigned int optlen; PyObject *none; +#ifdef AF_VSOCK + if (s->sock_family == AF_VSOCK) { + uint64_t vflag; + /* setsockopt(level, opt, flag) */ + if (PyArg_ParseTuple(args, "iiK:setsockopt", + &level, &optname, &vflag)) { + // level should always be set to AF_VSOCK + res = setsockopt(s->sock_fd, level, optname, + (void*)&vflag, sizeof vflag); + goto done; + } + return NULL; + } +#endif + /* setsockopt(level, opt, flag) */ if (PyArg_ParseTuple(args, "iii:setsockopt", &level, &optname, &flag)) { @@ -2636,20 +2700,39 @@ int res; PyObject *buf; socklen_t buflen = 0; + int flag = 0; + socklen_t flagsize; if (!PyArg_ParseTuple(args, "ii|i:getsockopt", &level, &optname, &buflen)) return NULL; if (buflen == 0) { - int flag = 0; - socklen_t flagsize = sizeof flag; +#ifdef AF_VSOCK + if (s->sock_family == AF_VSOCK) { + uint64_t vflag = 0; + flagsize = sizeof vflag; + res = getsockopt(s->sock_fd, level, optname, + (void *)&vflag, &flagsize); + if (res < 0) + return s->errorhandler(); + return PyLong_FromLong(vflag); + } +#endif + flagsize = sizeof flag; res = getsockopt(s->sock_fd, level, optname, (void *)&flag, &flagsize); if (res < 0) return s->errorhandler(); return PyLong_FromLong(flag); } +#ifdef AF_VSOCK + if (s->sock_family == AF_VSOCK) { + PyErr_SetString(PyExc_OSError, + "getsockopt string buffer not allowed"); + return NULL; + } +#endif if (buflen <= 0 || buflen > 1024) { PyErr_SetString(PyExc_OSError, "getsockopt buflen out of range"); @@ -6720,6 +6803,19 @@ PyModule_AddIntMacro(m, NETLINK_CRYPTO); #endif #endif /* AF_NETLINK */ + +#ifdef AF_VSOCK + PyModule_AddIntConstant(m, "AF_VSOCK", AF_VSOCK); + PyModule_AddIntConstant(m, "SO_VM_SOCKETS_BUFFER_SIZE", 0); + PyModule_AddIntConstant(m, "SO_VM_SOCKETS_BUFFER_MIN_SIZE", 1); + PyModule_AddIntConstant(m, "SO_VM_SOCKETS_BUFFER_MAX_SIZE", 2); + PyModule_AddIntConstant(m, "VMADDR_CID_ANY", 0xffffffff); + PyModule_AddIntConstant(m, "VMADDR_PORT_ANY", 0xffffffff); + PyModule_AddIntConstant(m, "VMADDR_CID_HOST", 2); + PyModule_AddIntConstant(m, "VM_SOCKETS_INVALID_VERSION", 0xffffffff); + PyModule_AddIntConstant(m, "IOCTL_VM_SOCKETS_GET_LOCAL_CID", _IO(7, 0xb9)); +#endif + #ifdef AF_ROUTE /* Alias to emulate 4.4BSD */ PyModule_AddIntMacro(m, AF_ROUTE); diff -r 40e97c9dae7a configure.ac --- a/configure.ac Tue Oct 18 17:55:18 2016 +0200 +++ b/configure.ac Mon Nov 07 16:13:59 2016 -0500 @@ -2083,6 +2083,13 @@ #endif ]) +AC_CHECK_HEADERS(linux/vm_sockets.h,,,[ +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +]) + + # On Linux, can.h and can/raw.h require sys/socket.h AC_CHECK_HEADERS(linux/can.h linux/can/raw.h linux/can/bcm.h,,,[ #ifdef HAVE_SYS_SOCKET_H