12#include <linux/netlink.h>
14#include <boost/asio/async_result.hpp>
15#include <boost/asio/awaitable.hpp>
16#include <boost/asio/buffer.hpp>
17#include <boost/asio/redirect_error.hpp>
18#include <boost/asio/use_awaitable.hpp>
19#include <boost/system/error_code.hpp>
26template<
typename Derived,
typename Result>
28 requires(Derived& derived,
const Derived& const_derived,
const nlmsghdr& header) {
29 { derived.prepare_request() } -> std::same_as<void>;
30 { const_derived.request_payload() } -> std::same_as<std::span<const uint8_t>>;
32 derived.process_message(header)
33 } -> std::same_as<std::optional<std::expected<Result, std::error_code>>>;
45template<
typename Derived,
typename Result>
74 auto async_run() -> boost::asio::awaitable<std::expected<Result, std::error_code>> {
75 impl().prepare_request();
77 if (
auto send_result =
co_await send_request(); !send_result) {
78 co_return std::unexpected(send_result.error());
93 auto ifindex() const noexcept -> uint16_t {
98 auto impl() noexcept -> Derived& {
99 return static_cast<Derived&
>(*this);
102 auto impl() const noexcept -> const Derived& {
103 return static_cast<const Derived&
>(*this);
106 auto send_request() -> boost::asio::awaitable<std::expected<void, std::error_code>> {
107 const auto payload =
impl().request_payload();
110 while (offset < payload.size()) {
111 boost::system::error_code ec{};
112 const auto sent =
co_await socket().async_send(
113 boost::asio::buffer(payload.data() + offset, payload.size() - offset),
114 boost::asio::redirect_error(boost::asio::use_awaitable, ec));
117 co_return std::unexpected(
118 std::error_code{ec.value(), std::generic_category()});
124 co_return std::expected<void, std::error_code>{};
127 auto read_loop() -> boost::asio::awaitable<std::expected<Result, std::error_code>> {
129 boost::system::error_code ec{};
130 const auto bytes =
co_await socket().async_receive(
132 boost::asio::redirect_error(boost::asio::use_awaitable, ec));
135 co_return std::unexpected(
136 std::error_code{ec.value(), std::generic_category()});
142 auto remaining =
static_cast<unsigned int>(bytes);
143 const auto header_size =
static_cast<unsigned int>(
sizeof(nlmsghdr));
147 while (remaining >= header_size && NLMSG_OK(header, remaining)) {
148 if (
auto result =
impl().process_message(*header)) {
149 co_return std::move(*result);
152 header = NLMSG_NEXT(header, remaining);
auto impl() const noexcept -> const Derived &
Definition nl_request_task.hxx:102
uint32_t sequence_
Definition nl_request_task.hxx:52
auto socket() noexcept -> Socket &
Definition nl_request_task.hxx:85
virtual ~RequestTask()=default
Virtual destructor.
SocketGuard & socket_guard_
Definition nl_request_task.hxx:50
auto read_loop() -> boost::asio::awaitable< std::expected< Result, std::error_code > >
Definition nl_request_task.hxx:127
uint16_t ifindex_
Definition nl_request_task.hxx:51
static constexpr size_t MAX_RESPONSE_BYTES
Definition nl_request_task.hxx:47
auto impl() noexcept -> Derived &
Definition nl_request_task.hxx:98
RequestTask(SocketGuard &socket_guard, uint16_t ifindex, uint32_t sequence) noexcept
Construct a RequestTask.
Definition nl_request_task.hxx:61
auto ifindex() const noexcept -> uint16_t
Definition nl_request_task.hxx:93
auto async_run() -> boost::asio::awaitable< std::expected< Result, std::error_code > >
Run the request asynchronously and return the result.
Definition nl_request_task.hxx:74
auto send_request() -> boost::asio::awaitable< std::expected< void, std::error_code > >
Definition nl_request_task.hxx:106
std::array< uint8_t, MAX_RESPONSE_BYTES > receive_buffer_
Definition nl_request_task.hxx:48
auto sequence() const noexcept -> uint32_t
Definition nl_request_task.hxx:89
Thread-safe guard owning a labeled Socket.
Definition nl_socket_guard.hxx:25
RAII wrapper for a Boost.Asio netlink socket.
Definition nl_socket.hxx:46
Definition nl_request_task.hxx:27
Definition nl_common.hxx:21
Definition nl_common.hxx:20