8000 gh-92658: Add Hyper-V socket support by jborean93 · Pull Request #92755 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-92658: Add Hyper-V socket support #92755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,33 @@ created. Socket addresses are represented as follows:

.. versionadded:: 3.9

- :const:`AF_HYPERV` is a Windows-only socket based interface for communicating
with Hyper-V hosts and guests. The address family is represented as a
``(vm_id, service_id)`` tuple where the ``vm_id`` and ``service_id`` are the
little endian byte representation of a ``uuid.UUID`` object.

The ``vm_id`` is the virtual machine identifier or a set of known VMID values
if the target is not a specific virtual machine. Known VMID values are:

- ``HV_GUID_ZERO 00000000-0000-0000-0000-000000000000`` - Used to bind on
itself and accept connections from all partitions.
- ``HV_GUID_BROADCAST FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF``
- ``HV_GUID_CHILDREN 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd`` - Used to bind on
itself and accept connection from child partitions.
- ``HV_GUID_LOOPBACK e0e16197-dd56-4a10-9195-5ee7a155a838`` - Used as a
target to itself.
- ``HV_GUID_PARENT a42e7cda-d03f-480c-9cc2-a4de20abb878`` - When used as a
bind accepts connection from the parent partition. When used as an address
target it will connect to the parent parition.

The ``service_id`` is the registered service identifier of the registered
service.

The easily get the byte value do
``uuid.UUID("eee5f691-5210-47e8-bbc9-7198bed79b77").bytes_le``.

.. versionadded:: 3.12

If you use a hostname in the *host* portion of IPv4/v6 socket address, the
program may show a nondeterministic behavior, as Python uses the first address
returned from the DNS resolution. The socket address will be resolved
Expand Down Expand Up @@ -589,6 +616,16 @@ Constants

.. availability:: Linux >= 3.9

.. data:: AF_HYPERV
HV_PROTOCOL_RAW
HVSOCKET_*

Constants for Windows Hyper-V sockets for host/guest communications.

.. availability:: Windows.

.. versionadded:: 3.12

Functions
^^^^^^^^^

Expand Down
48 changes: 48 additions & 0 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ def _have_socket_bluetooth():
return True


def _have_socket_hyperv():
"""Check whether AF_HYPERV sockets are supported on this host."""
try:
s = socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW)
except (AttributeError, OSError):
return False
else:
s.close()
return True


@contextlib.contextmanager
def socket_setdefaulttimeout(timeout):
old_timeout = socket.getdefaulttimeout()
Expand Down Expand Up @@ -171,6 +182,8 @@ def socket_setdefaulttimeout(timeout):

HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth()

HAVE_SOCKET_HYPERV = _have_socket_hyperv()

# Size in bytes of the int type
SIZEOF_INT = array.array("i").itemsize

Expand Down Expand Up @@ -2459,6 +2472,41 @@ def testCreateScoSocket(self):
pass


@unittest.skipUnless(HAVE_SOCKET_HYPERV,
'Hyper-V sockets required for this test.')
class BasicHyperVTest(unittest.TestCase):

def testHyperVConstants(self):
socket.HVSOCKET_CONNECT_TIMEOUT
socket.HVSOCKET_CONNECT_TIMEOUT_MAX
socket.HVSOCKET_CONTAINER_PASSTHRU
socket.HVSOCKET_CONNECTED_SUSPEND
socket.HVSOCKET_ADDRESS_FLAG_PASSTHRU

def testCreateHyperVSocketWithUnknownProtoFailure(self):
self.assertRaises(OSError, socket.socket, socket.AF_HYPERV, socket.SOCK_STREAM)

def testCreateHyperVSocketAddrNotTupleFailure(self):
with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s:
self.assertRaises(TypeError, s.connect, b"\x00" * 16)

def testCreateHyperVSocketAddrNotTupleOf2BytesFailure(self):
with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s:
self.assertRaises(TypeError, s.connect, (b"\x00" * 16,))

def testCreateHyperVSocketAddrNotTupleOfBytesFailure(self):
with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s:
self.assertRaises(TypeError, s.connect, (1, 2))

def testCreateHyperVSocketAddrVmIdNotCorrectLengthFailure(self):
with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s:
self.assertRaises(TypeError, s.connect, (b"\x00", b"\x00" * 16))

def testCreateHyperVSocketAddrServiceIdNotCorrectLengthFailure(self):
with socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM, socket.HV_PROTOCOL_RAW) as s:
self.assertRaises(TypeError, s.connect, (b"\x00" * 16, b"\x00"))


class BasicTCPTest(SocketConnectedTest):

def __init__(self, methodName='runTest'):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for connecting and binding to Hyper-V sockets on Windows Hyper-V hosts and guests.
93 changes: 93 additions & 0 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,18 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
}
#endif /* HAVE_SOCKADDR_ALG */

#ifdef AF_HYPERV
case AF_HYPERV:
{
SOCKADDR_HV *a = (SOCKADDR_HV *) addr;
return Py_BuildValue("y#y#",
a->VmId,
sizeof(GUID),
a->ServiceId,
sizeof(GUID));
}
#endif /* AF_HYPERV */

/* More cases here... */

default:
Expand Down Expand Up @@ -2375,6 +2387,66 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 1;
}
#endif /* HAVE_SOCKADDR_ALG */
#ifdef AF_HYPERV
case AF_HYPERV:
{
switch (s->sock_proto) {
case HV_PROTOCOL_RAW:
{
GUID *vm_id;
Py_ssize_t vm_id_len = 0;

GUID *service_id;
Py_ssize_t service_id_len = 0;

SOCKADDR_HV *addr = &addrbuf->hv;

memset(addr, 0, sizeof(*addr));
addr->Family = AF_HYPERV;

if (!PyTuple_Check(args)) {
PyErr_Format(PyExc_TypeError,
"%s(): AF_HYPERV address must be tuple, "
"not %.500s",
caller, Py_TYPE(args)->tp_name);
return 0;
}
if (!PyArg_ParseTuple(args,
"y#y#;AF_HYPERV address must be a tuple "
"(vm_id, service_id)",
&vm_id, &vm_id_len, &service_id,
&service_id_len))
{
return 0;
}
if (vm_id_len != sizeof(GUID)) {
PyErr_Format(PyExc_TypeError,
"%s(): AF_HYPERV address vm_id must have a "
"length of %d",
caller, sizeof(GUID));
return 0;
}
if (service_id_len != sizeof(GUID)) {
PyErr_Format(PyExc_TypeError,
"%s(): AF_HYPERV address service_id must have a "
"length of %d",
caller, sizeof(GUID));
return 0;
}

addr->VmId = *vm_id;
addr->ServiceId = *service_id;

*len_ret = sizeof(*addr);
return 1;
}
default:
PyErr_Format(PyExc_OSError,
"%s(): unsupported AF_HYPERV protocol", caller);
return 0;
}
}
#endif /* AF_HYPERV */

/* More cases here... */

Expand Down Expand Up @@ -2524,6 +2596,13 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
return 1;
}
#endif /* HAVE_SOCKADDR_ALG */
#ifdef AF_HYPERV
case AF_HYPERV:
{
*len_ret = sizeof (SOCKADDR_HV);
return 1;
}
#endif /* AF_HYPERV

/* More cases here... */

Expand Down Expand Up @@ -7351,6 +7430,20 @@ PyInit__socket(void)
/* Linux LLC */
PyModule_AddIntMacro(m, AF_LLC);
#endif
#ifdef AF_HYPERV
/* Hyper-V sockets */
PyModule_AddIntMacro(m, AF_HYPERV);

/* for proto */
PyModule_AddIntMacro(m, HV_PROTOCOL_RAW);

/* for setsockopt() */
PyModule_AddIntMacro(m, HVSOCKET_CONNECT_TIMEOUT);
PyModule_AddIntMacro(m, HVSOCKET_CONNECT_TIMEOUT_MAX);
PyModule_AddIntMacro(m, HVSOCKET_CONTAINER_PASSTHRU);
PyModule_AddIntMacro(m, HVSOCKET_CONNECTED_SUSPEND);
PyModule_AddIntMacro(m, HVSOCKET_ADDRESS_FLAG_PASSTHRU);
#endif /* AF_HYPERV */

#ifdef USE_BLUETOOTH
PyModule_AddIntMacro(m, AF_BLUETOOTH);
Expand Down
11 changes: 11 additions & 0 deletions Modules/socketmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ struct SOCKADDR_BTH_REDEF {
# else
typedef int socklen_t;
# endif /* IPPROTO_IPV6 */

/* Future remove once Py_WINVER has been bumped to >=0x0604 */
# ifndef AF_HYPERV
# define AF_HYPERV 34
# endif

/* FIXME: Should this have some sort of safe guard? */
# include <hvsocket.h>
#endif /* MS_WINDOWS */

#ifdef HAVE_SYS_UN_H
Expand Down Expand Up @@ -288,6 +296,9 @@ typedef union sock_addr {
#ifdef HAVE_LINUX_TIPC_H
struct sockaddr_tipc tipc;
#endif
#ifdef AF_HYPERV
SOCKADDR_HV hv;
#endif
} sock_addr_t;

/* The object holding a socket. It holds some extra information,
Expand Down
0