[Python-checkins] bpo-27584: New addition of vSockets to the python socket module (#2489)

Christian Heimes webhook-mailer at python.org
Wed Sep 6 18:18:13 EDT 2017


https://github.com/python/cpython/commit/effc12f8e9e20d0951d2ba5883587666bd8218e3
commit: effc12f8e9e20d0951d2ba5883587666bd8218e3
branch: master
author: caavery <cavery at redhat.com>
committer: Christian Heimes <christian at python.org>
date: 2017-09-06T15:18:10-07:00
summary:

bpo-27584: New addition of vSockets to the python socket module (#2489)

* bpo-27584: New addition of vSockets to the python socket module

Support for AF_VSOCK on Linux only

* bpo-27584: Fixes for V2

Fixed syntax and naming problems.
Fixed #ifdef AF_VSOCK checking
Restored original aclocal.m4

* bpo-27584: Fixes for V3

Added checking for fcntl and thread modules.

* bpo-27584: Fixes for V4

Fixed white space error

* bpo-27584: Fixes for V5

Added back comma in (CID, port).

* bpo-27584: Fixes for V6

Added news file.
socket.rst now reflects first Linux introduction of AF_VSOCK.
Fixed get_cid in test_socket.py.
Replaced PyLong_FromLong with PyLong_FromUnsignedLong in socketmodule.c
Got rid of extra AF_VSOCK #define.
Added sockaddr_vm to sock_addr.

* bpo-27584: Fixes for V7

Minor cleanup.

* bpo-27584: Fixes for V8

Put back #undef AF_VSOCK as it is  necessary when vm_sockets.h is not installed.

files:
A Misc/NEWS.d/next/Library/2017-08-24-14-03-14.bpo-27584.r11JHZ.rst
M Doc/library/socket.rst
M Lib/test/test_socket.py
M Misc/ACKS
M Modules/socketmodule.c
M Modules/socketmodule.h
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index c5064e92c21..42fd7ea0f0b 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -153,6 +153,14 @@ created.  Socket addresses are represented as follows:
 
   .. versionadded:: 3.6
 
+- :const:`AF_VSOCK` allows communication between virtual machines and
+  their hosts. The sockets are represented as a ``(CID, port)`` tuple
+  where the context ID or CID and port are integers.
+
+  Availability: Linux >= 4.8 QEMU >= 2.8 ESX >= 4.0 ESX Workstation >= 6.5
+
+  .. versionadded:: 3.7
+
 - Certain other address families (:const:`AF_PACKET`, :const:`AF_CAN`)
   support specific representations.
 
@@ -395,6 +403,18 @@ Constants
 
    .. versionadded:: 3.6
 
+
+.. data:: AF_VSOCK
+          IOCTL_VM_SOCKETS_GET_LOCAL_CID
+          VMADDR*
+          SO_VM*
+
+   Constants for Linux host/guest communication.
+
+   Availability: Linux >= 4.8.
+
+   .. versionadded:: 3.7
+
 .. data:: AF_LINK
 
   Availability: BSD, OSX.
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 84234887747..50016ab615a 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -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
@@ -44,6 +46,16 @@
 except ImportError:
     _socket = None
 
+def get_cid():
+    if fcntl is None:
+        return None
+    try:
+        with open("/dev/vsock", "rb") as f:
+            r = fcntl.ioctl(f, socket.IOCTL_VM_SOCKETS_GET_LOCAL_CID, "    ")
+    except OSError:
+        return None
+    else:
+        return struct.unpack("I", r)[0]
 
 def _have_socket_can():
     """Check whether CAN sockets are supported on this host."""
@@ -85,6 +97,11 @@ def _have_socket_alg():
         s.close()
     return True
 
+def _have_socket_vsock():
+    """Check whether AF_VSOCK sockets are supported on this host."""
+    ret = get_cid() is not None
+    return ret
+
 HAVE_SOCKET_CAN = _have_socket_can()
 
 HAVE_SOCKET_CAN_ISOTP = _have_socket_can_isotp()
@@ -93,6 +110,8 @@ def _have_socket_alg():
 
 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
 
@@ -387,6 +406,42 @@ def clientTearDown(self):
         self.cli = None
         ThreadableTest.clientTearDown(self)
 
+ at unittest.skipIf(fcntl is None, "need fcntl")
+ at unittest.skipUnless(thread, 'Threading required for this test.')
+ at unittest.skipUnless(HAVE_SOCKET_VSOCK,
+          'VSOCK sockets required for this test.')
+ at unittest.skipUnless(get_cid() != 2,
+          "This test can only be run on a virtual guest.")
+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 = get_cid()
+        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()
+
 class SocketConnectedTest(ThreadedTCPSocketTest):
     """Socket tests for client-server connection.
 
@@ -1874,6 +1929,54 @@ def _testCongestion(self):
         self.assertIn(self.serv, r)
 
 
+ at unittest.skipIf(fcntl is None, "need fcntl")
+ at 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):
 
@@ -5682,6 +5785,10 @@ def test_main():
     tests.extend([BasicRDSTest, RDSTest])
     tests.append(LinuxKernelCryptoAPI)
     tests.extend([
+        BasicVSOCKTest,
+        ThreadedVSOCKSocketStreamTest,
+    ])
+    tests.extend([
         CmsgMacroTests,
         SendmsgUDPTest,
         RecvmsgUDPTest,
diff --git a/Misc/ACKS b/Misc/ACKS
index e1127bcc72b..eadc5d34c40 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -65,6 +65,7 @@ David Ascher
 Ammar Askar
 Chris AtLee
 Aymeric Augustin
+Cathy Avery
 John Aycock
 Donovan Baarda
 Arne Babenhauserheide
diff --git a/Misc/NEWS.d/next/Library/2017-08-24-14-03-14.bpo-27584.r11JHZ.rst b/Misc/NEWS.d/next/Library/2017-08-24-14-03-14.bpo-27584.r11JHZ.rst
new file mode 100644
index 00000000000..af4c583e186
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2017-08-24-14-03-14.bpo-27584.r11JHZ.rst
@@ -0,0 +1,2 @@
+``AF_VSOCK`` has been added to the socket interface which allows
+communication between virtual machines and their host.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 37626e67cb0..a431e254d57 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1225,6 +1225,14 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
        }
 #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:
     {
@@ -1586,6 +1594,32 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
     }
 #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 */
@@ -2103,6 +2137,14 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
     }
 #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 */
@@ -2598,6 +2640,21 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args)
     unsigned int optlen;
     PyObject *none;
 
+#ifdef AF_VSOCK
+    if (s->sock_family == AF_VSOCK) {
+        uint64_t vflag; // Must be set width of 64 bits
+        /* 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)) {
@@ -2668,20 +2725,39 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args)
     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; // Must be set width of 64 bits
+            flagsize = sizeof vflag;
+            res = getsockopt(s->sock_fd, level, optname,
+                         (void *)&vflag, &flagsize);
+            if (res < 0)
+                return s->errorhandler();
+            return PyLong_FromUnsignedLong(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");
@@ -6645,6 +6721,19 @@ PyInit__socket(void)
     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 --git a/Modules/socketmodule.h b/Modules/socketmodule.h
index 03f982b9108..03dbf18f7bd 100644
--- a/Modules/socketmodule.h
+++ b/Modules/socketmodule.h
@@ -107,6 +107,12 @@ typedef int socklen_t;
 #define SOL_ALG 279
 #endif
 
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+# include <linux/vm_sockets.h>
+#else
+# undef AF_VSOCK
+#endif
+
 /* Linux 3.19 */
 #ifndef ALG_SET_AEAD_ASSOCLEN
 #define ALG_SET_AEAD_ASSOCLEN           4
@@ -193,6 +199,9 @@ typedef union sock_addr {
 #ifdef HAVE_SOCKADDR_ALG
     struct sockaddr_alg alg;
 #endif
+#ifdef AF_VSOCK 
+    struct sockaddr_vm vm;
+#endif
 } sock_addr_t;
 
 /* The object holding a socket.  It holds some extra information,
diff --git a/configure b/configure
index 3880421dba1..75d64324e6b 100755
--- a/configure
+++ b/configure
@@ -8088,6 +8088,24 @@ fi
 done
 
 
+for ac_header in linux/vm_sockets.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "linux/vm_sockets.h" "ac_cv_header_linux_vm_sockets_h" "
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+"
+if test "x$ac_cv_header_linux_vm_sockets_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_VM_SOCKETS_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # On Linux, can.h and can/raw.h require sys/socket.h
 for ac_header in linux/can.h linux/can/raw.h linux/can/bcm.h
 do :
diff --git a/configure.ac b/configure.ac
index 3bf01386d66..00d5abaaaed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2097,6 +2097,12 @@ AC_CHECK_HEADERS(linux/netlink.h,,,[
 #endif
 ])
 
+AC_CHECK_HEADERS(linux/vm_sockets.h,,,[
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#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
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 69033ef9e2e..1356eb58ab4 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -574,6 +574,9 @@
 /* Define to 1 if you have the <linux/tipc.h> header file. */
 #undef HAVE_LINUX_TIPC_H
 
+/* Define to 1 if you have the <linux/vm_sockets.h> header file. */
+#undef HAVE_LINUX_VM_SOCKETS_H
+
 /* Define to 1 if you have the 'lockf' function and the F_LOCK macro. */
 #undef HAVE_LOCKF
 



More information about the Python-checkins mailing list