Skip to content

Commit

Permalink
bpo-27584: New addition of vSockets to the python socket module (pyth…
Browse files Browse the repository at this point in the history
…on#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.
  • Loading branch information
caavery authored and tiran committed Sep 6, 2017
1 parent 5d57844 commit effc12f
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 2 deletions.
20 changes: 20 additions & 0 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.
Expand Down
107 changes: 107 additions & 0 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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."""
Expand Down Expand Up @@ -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()
Expand All @@ -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

Expand Down Expand Up @@ -387,6 +406,42 @@ def clientTearDown(self):
self.cli = None
ThreadableTest.clientTearDown(self)

@unittest.skipIf(fcntl is None, "need fcntl")
@unittest.skipUnless(thread, 'Threading required for this test.')
@unittest.skipUnless(HAVE_SOCKET_VSOCK,
'VSOCK sockets required for this test.')
@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.
Expand Down Expand Up @@ -1874,6 +1929,54 @@ def _testCongestion(self):
self.assertIn(self.serv, r)


@unittest.skipIf(fcntl is None, "need fcntl")
@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):

Expand Down Expand Up @@ -5681,6 +5784,10 @@ def test_main():
tests.extend([BasicCANTest, CANTest])
tests.extend([BasicRDSTest, RDSTest])
tests.append(LinuxKernelCryptoAPI)
tests.extend([
BasicVSOCKTest,
ThreadedVSOCKSocketStreamTest,
])
tests.extend([
CmsgMacroTests,
SendmsgUDPTest,
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ David Ascher
Ammar Askar
Chris AtLee
Aymeric Augustin
Cathy Avery
John Aycock
Donovan Baarda
Arne Babenhauserheide
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
``AF_VSOCK`` has been added to the socket interface which allows
communication between virtual machines and their host.
93 changes: 91 additions & 2 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
{
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions Modules/socketmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand Down
Loading

0 comments on commit effc12f

Please sign in to comment.