From f307c79290132f89fd7b4ecfe66189e439c02faa Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Fri, 20 Jan 2023 13:27:12 +0100 Subject: [PATCH] initial commit for VSOCK sockets This commit introduces support for VSOCK sockets on Linux. TODO: - documentation - support for Windows (address family AF_HYPERV, protocol HV_PROTOCOL_RAW) --- example/cpp03/vm/Jamfile.v2 | 81 ++++++ example/cpp03/vm/datagram_client.cpp | 63 +++++ example/cpp03/vm/datagram_server.cpp | 96 +++++++ example/cpp03/vm/stream_client.cpp | 62 +++++ example/cpp03/vm/stream_server.cpp | 139 ++++++++++ include/boost/asio.hpp | 4 + include/boost/asio/detail/config.hpp | 5 + include/boost/asio/detail/socket_types.hpp | 7 + include/boost/asio/vm/basic_endpoint.hpp | 248 ++++++++++++++++++ include/boost/asio/vm/cid.hpp | 208 +++++++++++++++ include/boost/asio/vm/datagram_protocol.hpp | 80 ++++++ include/boost/asio/vm/detail/endpoint.hpp | 134 ++++++++++ .../boost/asio/vm/detail/impl/endpoint.ipp | 111 ++++++++ include/boost/asio/vm/impl/cid.hpp | 43 +++ include/boost/asio/vm/impl/cid.ipp | 58 ++++ include/boost/asio/vm/stream_protocol.hpp | 91 +++++++ 16 files changed, 1430 insertions(+) create mode 100644 example/cpp03/vm/Jamfile.v2 create mode 100644 example/cpp03/vm/datagram_client.cpp create mode 100644 example/cpp03/vm/datagram_server.cpp create mode 100644 example/cpp03/vm/stream_client.cpp create mode 100644 example/cpp03/vm/stream_server.cpp create mode 100644 include/boost/asio/vm/basic_endpoint.hpp create mode 100644 include/boost/asio/vm/cid.hpp create mode 100644 include/boost/asio/vm/datagram_protocol.hpp create mode 100644 include/boost/asio/vm/detail/endpoint.hpp create mode 100644 include/boost/asio/vm/detail/impl/endpoint.ipp create mode 100644 include/boost/asio/vm/impl/cid.hpp create mode 100644 include/boost/asio/vm/impl/cid.ipp create mode 100644 include/boost/asio/vm/stream_protocol.hpp diff --git a/example/cpp03/vm/Jamfile.v2 b/example/cpp03/vm/Jamfile.v2 new file mode 100644 index 0000000000..f63c43961e --- /dev/null +++ b/example/cpp03/vm/Jamfile.v2 @@ -0,0 +1,81 @@ +# +# Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# + +lib socket ; # SOLARIS +lib nsl ; # SOLARIS +lib ws2_32 ; # NT +lib mswsock ; # NT +lib ipv6 ; # HPUX +lib network ; # HAIKU + +exe stream_client + : stream_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : BOOST_ALL_NO_LIB=1 + multi + solaris:socket + solaris:nsl + windows:_WIN32_WINNT=0x0501 + windows,gcc:ws2_32 + windows,gcc:mswsock + windows,gcc-cygwin:__USE_W32_SOCKETS + hpux,gcc:_XOPEN_SOURCE_EXTENDED + hpux:ipv6 + haiku:network + ; + +exe stream_server + : stream_server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : BOOST_ALL_NO_LIB=1 + multi + solaris:socket + solaris:nsl + windows:_WIN32_WINNT=0x0501 + windows,gcc:ws2_32 + windows,gcc:mswsock + windows,gcc-cygwin:__USE_W32_SOCKETS + hpux,gcc:_XOPEN_SOURCE_EXTENDED + hpux:ipv6 + haiku:network + ; + +exe datagram_client + : datagram_client.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : BOOST_ALL_NO_LIB=1 + multi + solaris:socket + solaris:nsl + windows:_WIN32_WINNT=0x0501 + windows,gcc:ws2_32 + windows,gcc:mswsock + windows,gcc-cygwin:__USE_W32_SOCKETS + hpux,gcc:_XOPEN_SOURCE_EXTENDED + hpux:ipv6 + haiku:network + ; + +exe datagram_server + : datagram_server.cpp + /boost/system//boost_system + /boost/chrono//boost_chrono + : BOOST_ALL_NO_LIB=1 + multi + solaris:socket + solaris:nsl + windows:_WIN32_WINNT=0x0501 + windows,gcc:ws2_32 + windows,gcc:mswsock + windows,gcc-cygwin:__USE_W32_SOCKETS + hpux,gcc:_XOPEN_SOURCE_EXTENDED + hpux:ipv6 + haiku:network + ; diff --git a/example/cpp03/vm/datagram_client.cpp b/example/cpp03/vm/datagram_client.cpp new file mode 100644 index 0000000000..3952d7f7cd --- /dev/null +++ b/example/cpp03/vm/datagram_client.cpp @@ -0,0 +1,63 @@ +// +// blocking_udp_echo_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +using boost::asio::vm::datagram_protocol; +using boost::asio::vm::cid; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: dgram_client \n"; + return 1; + } + + boost::asio::io_context io_context; + + datagram_protocol::socket s(io_context, datagram_protocol::endpoint(cid::local(), 2001)); + datagram_protocol::endpoint endpoint(cid::local(), atoi(argv[1])); + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = strlen(request); + s.send_to(boost::asio::buffer(request, request_length), endpoint); + + char reply[max_length]; + datagram_protocol::endpoint sender_endpoint; + size_t reply_length = s.receive_from( + boost::asio::buffer(reply, max_length), sender_endpoint); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_VM_SOCKETS) +# error VSOCK sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) diff --git a/example/cpp03/vm/datagram_server.cpp b/example/cpp03/vm/datagram_server.cpp new file mode 100644 index 0000000000..c203a9d822 --- /dev/null +++ b/example/cpp03/vm/datagram_server.cpp @@ -0,0 +1,96 @@ +// +// async_udp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +using boost::asio::vm::datagram_protocol; + +class server +{ +public: + server(boost::asio::io_context& io_context, short port) + : socket_(io_context, datagram_protocol::endpoint(port)) + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_receive_from(const boost::system::error_code& error, + size_t bytes_recvd) + { + if (!error && bytes_recvd > 0) + { + socket_.async_send_to( + boost::asio::buffer(data_, bytes_recvd), sender_endpoint_, + boost::bind(&server::handle_send_to, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + else + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + + void handle_send_to(const boost::system::error_code& /*error*/, + size_t /*bytes_sent*/) + { + socket_.async_receive_from( + boost::asio::buffer(data_, max_length), sender_endpoint_, + boost::bind(&server::handle_receive_from, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + +private: + datagram_protocol::socket socket_; + datagram_protocol::endpoint sender_endpoint_; + enum { max_length = 1024 }; + char data_[max_length]; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: dgram_server \n"; + return 1; + } + + boost::asio::io_context io_context; + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_VM_SOCKETS) +# error VSOCK sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) diff --git a/example/cpp03/vm/stream_client.cpp b/example/cpp03/vm/stream_client.cpp new file mode 100644 index 0000000000..1d6b7d7ec5 --- /dev/null +++ b/example/cpp03/vm/stream_client.cpp @@ -0,0 +1,62 @@ +// +// stream_client.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +using boost::asio::vm::stream_protocol; +using boost::asio::vm::cid; + +enum { max_length = 1024 }; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_client \n"; + return 1; + } + + boost::asio::io_context io_context; + + stream_protocol::socket s(io_context); + s.connect(stream_protocol::endpoint(cid::local(), atoi(argv[1]))); + + using namespace std; // For strlen. + std::cout << "Enter message: "; + char request[max_length]; + std::cin.getline(request, max_length); + size_t request_length = strlen(request); + boost::asio::write(s, boost::asio::buffer(request, request_length)); + + char reply[max_length]; + size_t reply_length = boost::asio::read(s, + boost::asio::buffer(reply, request_length)); + std::cout << "Reply is: "; + std::cout.write(reply, reply_length); + std::cout << "\n"; + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_VM_SOCKETS) +# error VSOCK sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) diff --git a/example/cpp03/vm/stream_server.cpp b/example/cpp03/vm/stream_server.cpp new file mode 100644 index 0000000000..dc545da919 --- /dev/null +++ b/example/cpp03/vm/stream_server.cpp @@ -0,0 +1,139 @@ +// +// stream_server.cpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +using boost::asio::vm::stream_protocol; + +class session + : public boost::enable_shared_from_this +{ +public: + session(boost::asio::io_context& io_context) + : socket_(io_context) + { + } + + stream_protocol::socket& socket() + { + return socket_; + } + + void start() + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if (!error) + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + boost::bind(&session::handle_write, + shared_from_this(), + boost::asio::placeholders::error)); + } + } + + void handle_write(const boost::system::error_code& error) + { + if (!error) + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&session::handle_read, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + } + } + +private: + // The socket used to communicate with the client. + stream_protocol::socket socket_; + + // Buffer used to store data received from the client. + boost::array data_; +}; + +typedef boost::shared_ptr session_ptr; + +class server +{ +public: + server(boost::asio::io_context& io_context, unsigned int port) + : io_context_(io_context), + acceptor_(io_context, stream_protocol::endpoint(port)) + { + session_ptr new_session(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + + void handle_accept(session_ptr new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + } + + new_session.reset(new session(io_context_)); + acceptor_.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, new_session, + boost::asio::placeholders::error)); + } + +private: + boost::asio::io_context& io_context_; + stream_protocol::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: stream_server \n"; + return 1; + } + + boost::asio::io_context io_context; + std::remove(argv[1]); + using namespace std; // For atoi. + server s(io_context, atoi(argv[1])); + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(BOOST_ASIO_HAS_VM_SOCKETS) +# error VSOCK sockets not available on this platform. +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) diff --git a/include/boost/asio.hpp b/include/boost/asio.hpp index ee6f0af4cf..3d041ef7a6 100644 --- a/include/boost/asio.hpp +++ b/include/boost/asio.hpp @@ -194,6 +194,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include diff --git a/include/boost/asio/detail/config.hpp b/include/boost/asio/detail/config.hpp index 168793e476..917946d04e 100644 --- a/include/boost/asio/detail/config.hpp +++ b/include/boost/asio/detail/config.hpp @@ -1664,6 +1664,11 @@ # endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) # endif // defined(BOOST_ASIO_HAS_EPOLL) # endif // !defined(BOOST_ASIO_HAS_TIMERFD) +# if !defined(BOOST_ASIO_HAS_VM_SOCKETS) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +# define BOOST_ASIO_HAS_VM_SOCKETS 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +# endif // !defined(BOOST_ASIO_HAS_VM_SOCKETS) #endif // defined(__linux__) // Linux: io_uring is used instead of epoll. diff --git a/include/boost/asio/detail/socket_types.hpp b/include/boost/asio/detail/socket_types.hpp index 4b308741b2..9041f03b51 100644 --- a/include/boost/asio/detail/socket_types.hpp +++ b/include/boost/asio/detail/socket_types.hpp @@ -89,6 +89,10 @@ # include # include # endif +# if defined(__linux__) +# include +# include +# endif #endif #include @@ -314,6 +318,9 @@ typedef ipv6_mreq in6_mreq_type; typedef sockaddr_in6 sockaddr_in6_type; typedef sockaddr_storage sockaddr_storage_type; typedef sockaddr_un sockaddr_un_type; +# if defined(__linux__) +typedef sockaddr_vm sockaddr_vm_type; +# endif typedef addrinfo addrinfo_type; typedef ::linger linger_type; typedef int ioctl_arg_type; diff --git a/include/boost/asio/vm/basic_endpoint.hpp b/include/boost/asio/vm/basic_endpoint.hpp new file mode 100644 index 0000000000..203286380f --- /dev/null +++ b/include/boost/asio/vm/basic_endpoint.hpp @@ -0,0 +1,248 @@ +// +// vm/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_BASIC_ENDPOINT_HPP +#define BOOST_ASIO_VM_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include + +#if !defined(BOOST_ASIO_NO_IOSTREAM) +# include +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) + +#include + +namespace boost { +namespace asio { +namespace vm { + +/// Type used for storing port numbers. +typedef uint_least16_t port_type; + +/// Describes an endpoint for a VSOCK socket. +/** + * The boost::asio::vm::basic_endpoint class template describes an endpoint + * that may be associated with a particular VSOCK socket. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + * + * @par Concepts: + * Endpoint. + */ +template +class basic_endpoint +{ +public: + /// The protocol type associated with the endpoint. + typedef Protocol protocol_type; + + /// The type of the endpoint structure. This type is dependent on the + /// underlying implementation of the socket layer. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined data_type; +#else + typedef boost::asio::detail::socket_addr_type data_type; +#endif + + /// Default constructor. + basic_endpoint() BOOST_ASIO_NOEXCEPT + { + } + + /// Construct an endpoint using the specified port. + basic_endpoint(port_type port_num) BOOST_ASIO_NOEXCEPT + : impl_(port_num) + { + } + + /// Construct an endpoint using the specified context ID and port. + basic_endpoint(const boost::asio::vm::cid& cid, port_type port_num) BOOST_ASIO_NOEXCEPT + : impl_(cid, port_num) + { + } + + /// Copy constructor. + basic_endpoint(const basic_endpoint& other) BOOST_ASIO_NOEXCEPT + : impl_(other.impl_) + { + } + +#if defined(BOOST_ASIO_HAS_MOVE) + /// Move constructor. + basic_endpoint(basic_endpoint&& other) BOOST_ASIO_NOEXCEPT + : impl_(other.impl_) + { + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + /// Assign from another endpoint. + basic_endpoint& operator=(const basic_endpoint& other) + { + impl_ = other.impl_; + return *this; + } + +#if defined(BOOST_ASIO_HAS_MOVE) + /// Move-assign from another endpoint. + basic_endpoint& operator=(basic_endpoint&& other) BOOST_ASIO_NOEXCEPT + { + impl_ = other.impl_; + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + /// The protocol associated with the endpoint. + protocol_type protocol() const BOOST_ASIO_NOEXCEPT + { + return protocol_type(); + } + + /// Get the underlying endpoint in the native type. + data_type* data() BOOST_ASIO_NOEXCEPT + { + return impl_.data(); + } + + /// Get the underlying endpoint in the native type. + const data_type* data() const + { + return impl_.data(); + } + + /// Get the underlying size of the endpoint in the native type. + std::size_t size() const BOOST_ASIO_NOEXCEPT + { + return impl_.size(); + } + + /// Set the underlying size of the endpoint in the native type. + void resize(std::size_t new_size) + { + impl_.resize(new_size); + } + + /// Get the capacity of the endpoint in the native type. + std::size_t capacity() const BOOST_ASIO_NOEXCEPT + { + return impl_.capacity(); + } + + /// Get the port associated with the endpoint. + port_type port() const BOOST_ASIO_NOEXCEPT + { + return impl_.port(); + } + + /// Set the port associated with the endpoint. + void port(port_type port_num) BOOST_ASIO_NOEXCEPT + { + impl_.port(port_num); + } + + /// Get the CID associated with the endpoint. + boost::asio::vm::cid cid() const BOOST_ASIO_NOEXCEPT + { + return impl_.cid(); + } + + /// Set the CID associated with the endpoint. + void cid(const boost::asio::vm::cid& cid) BOOST_ASIO_NOEXCEPT + { + impl_.cid(cid); + } + + /// Compare two endpoints for equality. + friend bool operator==(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return e1.impl_ == e2.impl_; + } + + /// Compare two endpoints for inequality. + friend bool operator!=(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return !(e1.impl_ == e2.impl_); + } + + /// Compare endpoints for ordering. + friend bool operator<(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) BOOST_ASIO_NOEXCEPT + { + return !(e1 < e2); + } + +private: + // The underlying vm endpoint. + boost::asio::vm::detail::endpoint impl_; +}; + +/// Output an endpoint as a string. +/** + * Used to output a human-readable string for a specified endpoint. + * + * @param os The output stream to which the string will be written. + * + * @param endpoint The endpoint to be written. + * + * @return The output stream. + * + * @relates boost::asio::vm::basic_endpoint + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + os << endpoint.cid() << ":"<< endpoint.port(); + return os; +} + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_BASIC_ENDPOINT_HPP diff --git a/include/boost/asio/vm/cid.hpp b/include/boost/asio/vm/cid.hpp new file mode 100644 index 0000000000..521792a4aa --- /dev/null +++ b/include/boost/asio/vm/cid.hpp @@ -0,0 +1,208 @@ +// +// vm/cid.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_CID_HPP +#define BOOST_ASIO_VM_CID_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include + +#if !defined(BOOST_ASIO_NO_IOSTREAM) +# include +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) + +#include + +namespace boost { +namespace asio { +namespace vm { + +/// Implements VSOCK style context ID. +/** + * The boost::asio::vm::cid class provides the ability to use and + * manipulate VSOCK context IDs. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Unsafe. + */ +class cid +{ +public: + /// The type used to represent an address as an unsigned integer. + typedef uint_least32_t uint_type; + + /// Default constructor. + cid() BOOST_ASIO_NOEXCEPT + { + addr_.svm_cid = VMADDR_CID_ANY; + } + + /// Construct an address from an unsigned integer. + BOOST_ASIO_DECL explicit cid(uint_type c); + + /// Copy constructor. + cid(const cid& other) BOOST_ASIO_NOEXCEPT + : addr_(other.addr_) + { + } + +#if defined(BOOST_ASIO_HAS_MOVE) + /// Move constructor. + cid(cid&& other) BOOST_ASIO_NOEXCEPT + : addr_(other.addr_) + { + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + /// Assign from another address. + cid& operator=(const cid& other) BOOST_ASIO_NOEXCEPT + { + addr_ = other.addr_; + return *this; + } + +#if defined(BOOST_ASIO_HAS_MOVE) + /// Move-assign from another address. + cid& operator=(cid&& other) BOOST_ASIO_NOEXCEPT + { + addr_ = other.addr_; + return *this; + } +#endif // defined(BOOST_ASIO_HAS_MOVE) + + /// Get the address as an unsigned integer + BOOST_ASIO_DECL uint_type to_uint() const BOOST_ASIO_NOEXCEPT; + + /// Determine whether the address is a loopback address. + BOOST_ASIO_DECL bool is_local() const BOOST_ASIO_NOEXCEPT; + + /// Determine whether the address is unspecified. + BOOST_ASIO_DECL bool is_hypervisor() const BOOST_ASIO_NOEXCEPT; + + /// Determine whether the address is a any address. + BOOST_ASIO_DECL bool is_any() const BOOST_ASIO_NOEXCEPT; + + /// Compare two addresses for equality. + friend bool operator==(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.addr_.svm_cid == a2.addr_.svm_cid; + } + + /// Compare two addresses for inequality. + friend bool operator!=(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.addr_.svm_cid != a2.addr_.svm_cid; + } + + /// Compare addresses for ordering. + friend bool operator<(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.to_uint() < a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator>(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.to_uint() > a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator<=(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.to_uint() <= a2.to_uint(); + } + + /// Compare addresses for ordering. + friend bool operator>=(const cid& a1, + const cid& a2) BOOST_ASIO_NOEXCEPT + { + return a1.to_uint() >= a2.to_uint(); + } + + /// Obtain an address object that represents any address. + static cid any() BOOST_ASIO_NOEXCEPT + { + return cid(); + } + + /// Obtain an address object that represents the loopback address. + static cid local() BOOST_ASIO_NOEXCEPT + { + return cid(VMADDR_CID_LOCAL); + } + + /// Obtain an address object that represents the broadcast address. + static cid hypervisor() BOOST_ASIO_NOEXCEPT + { + return cid(VMADDR_CID_HYPERVISOR); + } + +private: + // The underlying VSOCK address. + boost::asio::detail::sockaddr_vm_type addr_; +}; + +/// Create an IPv4 address from an unsigned integer in host byte order. +/** + * @relates cid + */ +inline cid make_cid(cid::uint_type id) +{ + return cid(id); +} + +#if !defined(BOOST_ASIO_NO_IOSTREAM) + +/// Output an address as a string. +/** + * Used to output a human-readable string for a specified address. + * + * @param os The output stream to which the string will be written. + * + * @param addr The address to be written. + * + * @return The output stream. + * + * @relates boost::asio::vm::cid + */ +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const cid& addr); + +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#include +#if defined(BOOST_ASIO_HEADER_ONLY) +# include +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_CID_HPP diff --git a/include/boost/asio/vm/datagram_protocol.hpp b/include/boost/asio/vm/datagram_protocol.hpp new file mode 100644 index 0000000000..017fc938b1 --- /dev/null +++ b/include/boost/asio/vm/datagram_protocol.hpp @@ -0,0 +1,80 @@ +// +// vm/datagram_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_DATAGRAM_PROTOCOL_HPP +#define BOOST_ASIO_VM_DATAGRAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace vm { + +/// Encapsulates the flags needed for datagram-oriented VSOCK sockets. +/** + * The boost::asio::vm::datagram_protocol class contains flags necessary for + * datagram-oriented VSOCK sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class datagram_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const BOOST_ASIO_NOEXCEPT + { + return SOCK_DGRAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const BOOST_ASIO_NOEXCEPT + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const BOOST_ASIO_NOEXCEPT + { + return AF_VSOCK; + } + + /// The type of a VSOCK endpoint. + typedef basic_endpoint endpoint; + + /// The VSOCK socket type. + typedef basic_datagram_socket socket; +}; + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_DATAGRAM_PROTOCOL_HPP diff --git a/include/boost/asio/vm/detail/endpoint.hpp b/include/boost/asio/vm/detail/endpoint.hpp new file mode 100644 index 0000000000..26a595be84 --- /dev/null +++ b/include/boost/asio/vm/detail/endpoint.hpp @@ -0,0 +1,134 @@ +// +// vm/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_DETAIL_ENDPOINT_HPP +#define BOOST_ASIO_VM_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include +#include +#include + +namespace boost { +namespace asio { +namespace vm { +namespace detail { + +// Helper class for implementing a VSOCK endpoint. +class endpoint +{ +public: + // Default constructor. + BOOST_ASIO_DECL endpoint() BOOST_ASIO_NOEXCEPT; + + // Construct an endpoint using the specified path name. + BOOST_ASIO_DECL endpoint(unsigned short port_num) BOOST_ASIO_NOEXCEPT; + + // Construct an endpoint using the specified path name. + BOOST_ASIO_DECL endpoint(const boost::asio::vm::cid& cid, unsigned short port_num) BOOST_ASIO_NOEXCEPT; + + // Copy constructor. + endpoint(const endpoint& other) BOOST_ASIO_NOEXCEPT + : data_(other.data_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) BOOST_ASIO_NOEXCEPT + { + data_ = other.data_; + return *this; + } + + // Get the underlying endpoint in the native type. + boost::asio::detail::socket_addr_type* data() BOOST_ASIO_NOEXCEPT + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const boost::asio::detail::socket_addr_type* data() const BOOST_ASIO_NOEXCEPT + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const BOOST_ASIO_NOEXCEPT + { + return sizeof(boost::asio::detail::sockaddr_vm_type); + } + + // Set the underlying size of the endpoint in the native type. + BOOST_ASIO_DECL void resize(std::size_t size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const BOOST_ASIO_NOEXCEPT + { + return sizeof(data_); + } + + // Get the port associated with the endpoint. + BOOST_ASIO_DECL unsigned short port() const BOOST_ASIO_NOEXCEPT; + + // Set the port associated with the endpoint. + BOOST_ASIO_DECL void port(unsigned short port_num) BOOST_ASIO_NOEXCEPT; + + // Get the CID associated with the endpoint. + BOOST_ASIO_DECL boost::asio::vm::cid cid() const BOOST_ASIO_NOEXCEPT; + + // Set the CID associated with the endpoint. + BOOST_ASIO_DECL void cid( + const boost::asio::vm::cid& addr) BOOST_ASIO_NOEXCEPT; + + // Compare two endpoints for equality. + BOOST_ASIO_DECL friend bool operator==(const endpoint& e1, + const endpoint& e2) BOOST_ASIO_NOEXCEPT; + + // Compare endpoints for ordering. + BOOST_ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2) BOOST_ASIO_NOEXCEPT; + +private: + // The underlying VSOCK socket address. + union data_union + { + boost::asio::detail::socket_addr_type base; + boost::asio::detail::sockaddr_vm_type vm; + } data_; + + // The length of the path associated with the endpoint. + std::size_t path_length_; + + // Initialise with a specified path. + BOOST_ASIO_DECL void init(const char* path, std::size_t path_length); +}; + +} // namespace detail +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#if defined(BOOST_ASIO_HEADER_ONLY) +# include +#endif // defined(BOOST_ASIO_HEADER_ONLY) + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_DETAIL_ENDPOINT_HPP diff --git a/include/boost/asio/vm/detail/impl/endpoint.ipp b/include/boost/asio/vm/detail/impl/endpoint.ipp new file mode 100644 index 0000000000..f890858044 --- /dev/null +++ b/include/boost/asio/vm/detail/impl/endpoint.ipp @@ -0,0 +1,111 @@ +// +// vm/detail/impl/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_DETAIL_IMPL_ENDPOINT_IPP +#define BOOST_ASIO_VM_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace vm { +namespace detail { + +endpoint::endpoint() BOOST_ASIO_NOEXCEPT +{ + memset(&data_, 0, sizeof(data_)); + data_.vm.svm_family = AF_VSOCK; + data_.vm.svm_port = 0; + data_.vm.svm_cid = VMADDR_CID_ANY; +} + +endpoint::endpoint(unsigned short port_num) BOOST_ASIO_NOEXCEPT +{ + memset(&data_, 0, sizeof(data_)); + data_.vm.svm_family = AF_VSOCK; + data_.vm.svm_port = port_num; + data_.vm.svm_cid = VMADDR_CID_ANY; +} + +endpoint::endpoint(const boost::asio::vm::cid& cid, unsigned short port_num) BOOST_ASIO_NOEXCEPT +{ + memset(&data_, 0, sizeof(data_)); + data_.vm.svm_family = AF_VSOCK; + data_.vm.svm_port = port_num; + data_.vm.svm_cid = cid.to_uint(); +} + +void endpoint::resize(std::size_t new_size) +{ + if (new_size > sizeof(boost::asio::detail::sockaddr_storage_type)) + { + boost::system::error_code ec(boost::asio::error::invalid_argument); + boost::asio::detail::throw_error(ec); + } +} + +unsigned short endpoint::port() const BOOST_ASIO_NOEXCEPT +{ + return data_.vm.svm_port; +} + +void endpoint::port(unsigned short port_num) BOOST_ASIO_NOEXCEPT +{ + data_.vm.svm_port = port_num; +} + +boost::asio::vm::cid endpoint::cid() const BOOST_ASIO_NOEXCEPT +{ + return boost::asio::vm::cid(data_.vm.svm_cid); +} + +void endpoint::cid(const boost::asio::vm::cid& cid) BOOST_ASIO_NOEXCEPT +{ + endpoint tmp_endpoint(cid, port()); + data_ = tmp_endpoint.data_; +} + +bool operator==(const endpoint& e1, const endpoint& e2) BOOST_ASIO_NOEXCEPT +{ + return e1.cid() == e2.cid() && e1.port() == e2.port(); +} + +bool operator<(const endpoint& e1, const endpoint& e2) BOOST_ASIO_NOEXCEPT +{ + if (e1.cid() < e2.cid()) + return true; + if (e1.cid() != e2.cid()) + return false; + return e1.port() < e2.port(); +} + +} // namespace detail +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_DETAIL_IMPL_ENDPOINT_IPP diff --git a/include/boost/asio/vm/impl/cid.hpp b/include/boost/asio/vm/impl/cid.hpp new file mode 100644 index 0000000000..3028529dbc --- /dev/null +++ b/include/boost/asio/vm/impl/cid.hpp @@ -0,0 +1,43 @@ +// +// vm/impl/cid.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_IMPL_CID_HPP +#define BOOST_ASIO_VM_IMPL_CID_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(BOOST_ASIO_NO_IOSTREAM) + +#include + +#include + +namespace boost { +namespace asio { +namespace vm { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const cid& addr) +{ + return os << addr.to_uint(); +} + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) + +#endif // BOOST_ASIO_VM_IMPL_CID_HPP diff --git a/include/boost/asio/vm/impl/cid.ipp b/include/boost/asio/vm/impl/cid.ipp new file mode 100644 index 0000000000..0d32fa201e --- /dev/null +++ b/include/boost/asio/vm/impl/cid.ipp @@ -0,0 +1,58 @@ +// +// vm/impl/cid.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_IMPL_CID_IPP +#define BOOST_ASIO_VM_IMPL_CID_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include + +#include + +namespace boost { +namespace asio { +namespace vm { + +cid::cid(cid::uint_type c) +{ + addr_.svm_cid = c; +} + +cid::uint_type cid::to_uint() const BOOST_ASIO_NOEXCEPT +{ + return addr_.svm_cid; +} + +bool cid::is_local() const BOOST_ASIO_NOEXCEPT +{ + return VMADDR_CID_LOCAL == to_uint(); +} + +bool cid::is_hypervisor() const BOOST_ASIO_NOEXCEPT +{ + return VMADDR_CID_HYPERVISOR == to_uint(); +} + +bool cid::is_any() const BOOST_ASIO_NOEXCEPT +{ + return VMADDR_CID_ANY == to_uint(); +} + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // BOOST_ASIO_VM_IMPL_CID_IPP diff --git a/include/boost/asio/vm/stream_protocol.hpp b/include/boost/asio/vm/stream_protocol.hpp new file mode 100644 index 0000000000..15cea50621 --- /dev/null +++ b/include/boost/asio/vm/stream_protocol.hpp @@ -0,0 +1,91 @@ +// +// vm/stream_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2023 OLiver Kowalke (oliver dot kowalke at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASIO_VM_STREAM_PROTOCOL_HPP +#define BOOST_ASIO_VM_STREAM_PROTOCOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#if defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace vm { + +/// Encapsulates the flags needed for stream-oriented VSOCK sockets. +/** + * The boost::asio::vm::stream_protocol class contains flags necessary for + * stream-oriented VSOCK sockets. + * + * @par Thread Safety + * @e Distinct @e objects: Safe.@n + * @e Shared @e objects: Safe. + * + * @par Concepts: + * Protocol. + */ +class stream_protocol +{ +public: + /// Obtain an identifier for the type of the protocol. + int type() const BOOST_ASIO_NOEXCEPT + { + return SOCK_STREAM; + } + + /// Obtain an identifier for the protocol. + int protocol() const BOOST_ASIO_NOEXCEPT + { + return 0; + } + + /// Obtain an identifier for the protocol family. + int family() const BOOST_ASIO_NOEXCEPT + { + return AF_VSOCK; + } + + /// The type of a VSOCK endpoint. + typedef basic_endpoint endpoint; + + /// The VSOCK socket type. + typedef basic_stream_socket socket; + + /// The VSOCK acceptor type. + typedef basic_socket_acceptor acceptor; + +#if !defined(BOOST_ASIO_NO_IOSTREAM) + /// The VSOCK iostream type. + typedef basic_socket_iostream iostream; +#endif // !defined(BOOST_ASIO_NO_IOSTREAM) +}; + +} // namespace vm +} // namespace asio +} // namespace boost + +#include + +#endif // defined(BOOST_ASIO_HAS_VM_SOCKETS) + +#endif // BOOST_ASIO_VM_STREAM_PROTOCOL_HPP