llmx-rtaco 0.0.1
RTNL-only netlink control-plane library for Linux (C++23).
Loading...
Searching...
No Matches
nl_request_task.hxx
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <concepts>
6#include <optional>
7#include <span>
8#include <system_error>
9#include <expected>
10#include <vector>
11
12#include <linux/netlink.h>
13
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>
20
22
23namespace llmx {
24namespace rtaco {
25
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>>;
31 {
32 derived.process_message(header)
33 } -> std::same_as<std::optional<std::expected<Result, std::error_code>>>;
34 };
35
45template<typename Derived, typename Result>
47 static constexpr size_t MAX_RESPONSE_BYTES = 64U * 1024U;
48 std::array<uint8_t, MAX_RESPONSE_BYTES> receive_buffer_;
49
51 uint16_t ifindex_;
52 uint32_t sequence_;
53
54public:
61 RequestTask(SocketGuard& socket_guard, uint16_t ifindex, uint32_t sequence) noexcept
62 : socket_guard_{socket_guard}
65
67 virtual ~RequestTask() = default;
68
74 auto async_run() -> boost::asio::awaitable<std::expected<Result, std::error_code>> {
75 impl().prepare_request();
76
77 if (auto send_result = co_await send_request(); !send_result) {
78 co_return std::unexpected(send_result.error());
79 }
80
81 co_return co_await read_loop();
82 }
83
84protected:
85 auto socket() noexcept -> Socket& {
86 return socket_guard_.socket();
87 }
88
89 auto sequence() const noexcept -> uint32_t {
90 return sequence_;
91 }
92
93 auto ifindex() const noexcept -> uint16_t {
94 return ifindex_;
95 }
96
97private:
98 auto impl() noexcept -> Derived& {
99 return static_cast<Derived&>(*this);
100 }
101
102 auto impl() const noexcept -> const Derived& {
103 return static_cast<const Derived&>(*this);
104 }
105
106 auto send_request() -> boost::asio::awaitable<std::expected<void, std::error_code>> {
107 const auto payload = impl().request_payload();
108 size_t offset = 0;
109
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));
115
116 if (ec) {
117 co_return std::unexpected(
118 std::error_code{ec.value(), std::generic_category()});
119 }
120
121 offset += sent;
122 }
123
124 co_return std::expected<void, std::error_code>{};
125 }
126
127 auto read_loop() -> boost::asio::awaitable<std::expected<Result, std::error_code>> {
128 while (true) {
129 boost::system::error_code ec{};
130 const auto bytes = co_await socket().async_receive(
131 boost::asio::buffer(receive_buffer_),
132 boost::asio::redirect_error(boost::asio::use_awaitable, ec));
133
134 if (ec) {
135 co_return std::unexpected(
136 std::error_code{ec.value(), std::generic_category()});
137 }
138
139 if (bytes >= receive_buffer_.size()) {
140 }
141
142 auto remaining = static_cast<unsigned int>(bytes);
143 const auto header_size = static_cast<unsigned int>(sizeof(nlmsghdr));
144 const auto* header = reinterpret_cast<const nlmsghdr*>(receive_buffer_
145 .data());
146
147 while (remaining >= header_size && NLMSG_OK(header, remaining)) {
148 if (auto result = impl().process_message(*header)) {
149 co_return std::move(*result);
150 }
151
152 header = NLMSG_NEXT(header, remaining);
153 }
154 }
155 }
156};
157
158} // namespace rtaco
159} // namespace llmx
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