llmx-rtaco 0.0.1
RTNL-only netlink control-plane library for Linux (C++23).
Loading...
Searching...
No Matches
nl_common.hxx
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <cstring>
7#include <optional>
8#include <string>
9#include <string_view>
10#include <type_traits>
11#include <utility>
12#include <unordered_map>
13
14#include <arpa/inet.h>
15#include <net/if.h>
16
17#include <linux/netlink.h>
18#include <linux/neighbour.h>
19#include <linux/rtnetlink.h>
20
21namespace llmx {
22namespace rtaco {
23
32inline constexpr auto trim_string(std::string_view sv) -> std::string {
33 while (!sv.empty() && sv.back() == '\0') {
34 sv.remove_suffix(1);
35 }
36 return std::string(sv);
37}
38
43template<size_t N>
44inline constexpr auto trim_string(const std::array<char, N>& arr) -> std::string {
45 return trim_string(std::string_view{arr.data(), arr.size()});
46}
47
48template<typename MsgT>
49inline auto checked_attr_begin(const nlmsghdr& header, const MsgT* info, int& attr_length)
50 -> const rtattr*;
51
52inline auto checked_attr_ok(const rtattr* attr, int attr_length) -> bool;
53
54inline auto checked_attr_next(const rtattr* attr, int& attr_length) -> const rtattr*;
55
56template<typename T>
57inline auto checked_payload(const rtattr& attr) -> const T*;
58
59template<typename T>
60inline auto checked_payload_copy(const rtattr& attr) -> std::optional<T>;
61
70inline auto extract_ifname(const nlmsghdr& header) -> std::string {
71 if (header.nlmsg_len < NLMSG_LENGTH(sizeof(ifinfomsg))) {
72 return {};
73 }
74
75 const auto* info = reinterpret_cast<const ifinfomsg*>(NLMSG_DATA(&header));
76 int attr_length = 0;
77 for (auto* attr = checked_attr_begin(header, info, attr_length);
78 checked_attr_ok(attr, attr_length);
79 attr = checked_attr_next(attr, attr_length)) {
80 if (attr->rta_type != IFLA_IFNAME) {
81 continue;
82 }
83
84 const auto payload = static_cast<size_t>(RTA_PAYLOAD(attr));
85 if (payload == 0) {
86 continue;
87 }
88
89 const auto* buffer = reinterpret_cast<const char*>(RTA_DATA(attr));
90 if (buffer == nullptr) {
91 continue;
92 }
93
94 return trim_string(buffer);
95 }
96
97 return {};
98}
99
105inline auto attribute_string(const rtattr& attr) -> std::string {
106 const auto payload = static_cast<size_t>(RTA_PAYLOAD(&attr));
107 if (payload == 0) {
108 return {};
109 }
110
111 const auto* buffer = reinterpret_cast<const char*>(RTA_DATA(&attr));
112 if (buffer == nullptr) {
113 return {};
114 }
115
116 return trim_string(buffer);
117}
118
127inline auto attribute_address(const rtattr& attr, uint8_t family) -> std::string {
128 int address_family = 0;
129
130 switch (family) {
131 case AF_INET: address_family = AF_INET; break;
132 case AF_INET6: address_family = AF_INET6; break;
133 default: return {};
134 }
135
136 const void* data = RTA_DATA(&attr);
137 if (data == nullptr) {
138 return {};
139 }
140
141 std::array<char, INET6_ADDRSTRLEN> buffer{};
142 if (::inet_ntop(address_family, data, buffer.data(), buffer.size()) == nullptr) {
143 return {};
144 }
145
146 return trim_string(buffer);
147}
148
153inline auto attribute_uint32(const rtattr& attr) -> uint32_t {
154 const auto value = checked_payload_copy<uint32_t>(attr);
155 if (!value.has_value()) {
156 return 0;
157 }
158
159 return *value;
160}
161
166inline auto attribute_hwaddr(const rtattr& attr) -> std::string {
167 const auto payload = static_cast<size_t>(RTA_PAYLOAD(&attr));
168 if (payload == 0) {
169 return {};
170 }
171
172 const auto* data = reinterpret_cast<const uint8_t*>(RTA_DATA(&attr));
173 if (data == nullptr) {
174 return {};
175 }
176
177 std::string value;
178 value.reserve(payload * 3U);
179
180 constexpr char kHex[] = "0123456789abcdef";
181 for (size_t i = 0; i < payload; ++i) {
182 if (i != 0) {
183 value.push_back(':');
184 }
185 const auto byte = data[i];
186 value.push_back(kHex[(byte >> 4U) & 0x0FU]);
187 value.push_back(kHex[byte & 0x0FU]);
188 }
189
190 return value;
191}
192
199template<typename MsgT>
200inline const MsgT* get_msg_payload(const nlmsghdr& header) {
201 if (header.nlmsg_len < NLMSG_LENGTH(sizeof(MsgT))) {
202 return nullptr;
203 }
204 return reinterpret_cast<const MsgT*>(NLMSG_DATA(&header));
205}
206
208inline auto checked_nlmsgerr(const nlmsghdr& header) -> const nlmsgerr* {
209 if (header.nlmsg_type != NLMSG_ERROR) {
210 return nullptr;
211 }
212 return get_msg_payload<nlmsgerr>(header);
213}
214
216template<typename MsgT>
217inline auto checked_attr_begin(const nlmsghdr& header, const MsgT* info, int& attr_length)
218 -> const rtattr* {
219 attr_length = 0;
220 if (info == nullptr || header.nlmsg_len < NLMSG_LENGTH(sizeof(MsgT))) {
221 return nullptr;
222 }
223
224 attr_length = static_cast<int>(header.nlmsg_len) -
225 static_cast<int>(NLMSG_LENGTH(sizeof(MsgT)));
226 if (attr_length <= 0) {
227 attr_length = 0;
228 return nullptr;
229 }
230
231 return reinterpret_cast<const rtattr*>(
232 reinterpret_cast<const std::uintptr_t>(info) + NLMSG_ALIGN(sizeof(MsgT)));
233}
234
236inline auto checked_attr_ok(const rtattr* attr, int attr_length) -> bool {
237 if (attr == nullptr) {
238 return false;
239 }
240 return RTA_OK(attr, attr_length);
241}
242
244inline auto checked_attr_next(const rtattr* attr, int& attr_length) -> const rtattr* {
245 if (attr == nullptr) {
246 return nullptr;
247 }
248 return RTA_NEXT(attr, attr_length);
249}
250
252template<typename T>
253inline auto checked_payload(const rtattr& attr) -> const T* {
254 if (RTA_PAYLOAD(&attr) < sizeof(T)) {
255 return nullptr;
256 }
257
258 return reinterpret_cast<const T*>(RTA_DATA(&attr));
259}
260
266template<typename T>
267inline auto checked_payload_copy(const rtattr& attr) -> std::optional<T> {
268 const auto* payload = checked_payload<T>(attr);
269 if (payload == nullptr) {
270 return std::nullopt;
271 }
272
273 T value{};
274 std::memcpy(&value, payload, sizeof(value));
275 return value;
276}
277
282template<typename MsgT, typename Fn>
283inline void for_each_attr(const nlmsghdr& header, const MsgT* info, Fn&& fn) {
284 int attr_length = 0;
285 for (const rtattr* attr = checked_attr_begin(header, info, attr_length);
286 checked_attr_ok(attr, attr_length);
287 attr = checked_attr_next(attr, attr_length)) {
288 fn(attr);
289 }
290}
291
292template<typename T>
293concept IsEnumeration = std::is_enum_v<std::remove_cvref_t<T>> ||
294 std::is_scoped_enum_v<std::remove_cvref_t<T>>;
295
296template<typename T>
297concept IsNetlinkEvent = IsEnumeration<T> || std::integral<std::remove_cvref_t<T>>;
298
306inline auto type_to_string(IsNetlinkEvent auto&& type) noexcept -> std::string_view {
307 uint16_t type_value{};
308
309 if constexpr (IsEnumeration<decltype(type)>) {
310 type_value = static_cast<uint16_t>(std::to_underlying(type));
311 } else {
312 type_value = static_cast<uint16_t>(type);
313 }
314
315 static const std::unordered_map<uint16_t, std::string_view> type_names{
316 {RTM_NEWLINK, "RTM_NEWLINK"},
317 {RTM_GETLINK, "RTM_GETLINK"},
318 {RTM_DELLINK, "RTM_DELLINK"},
319 {RTM_NEWADDR, "RTM_NEWADDR"},
320 {RTM_GETADDR, "RTM_GETADDR"},
321 {RTM_DELADDR, "RTM_DELADDR"},
322 {RTM_NEWROUTE, "RTM_NEWROUTE"},
323 {RTM_GETROUTE, "RTM_GETROUTE"},
324 {RTM_DELROUTE, "RTM_DELROUTE"},
325 {RTM_NEWNEIGH, "RTM_NEWNEIGH"},
326 {RTM_GETNEIGH, "RTM_GETNEIGH"},
327 {RTM_DELNEIGH, "RTM_DELNEIGH"},
328 {NLMSG_NOOP, "NLMSG_NOOP"},
329 {NLMSG_ERROR, "NLMSG_ERROR"},
330 {NLMSG_DONE, "NLMSG_DONE"},
331 {NLMSG_OVERRUN, "NLMSG_OVERRUN"},
332 };
333
334 if (type_names.contains(type_value)) {
335 return type_names.at(type_value);
336 }
337
338 return "UNKNOWN";
339}
340
342inline auto family_to_string(uint8_t family) noexcept -> std::string_view {
343 static const std::unordered_map<uint8_t, std::string_view> family_names{
344 {AF_UNSPEC, "AF_UNSPEC"},
345 {AF_UNIX, "AF_UNIX"},
346 {AF_INET, "AF_INET"},
347 {AF_INET6, "AF_INET6"},
348 {AF_NETLINK, "AF_NETLINK"},
349 {AF_PACKET, "AF_PACKET"},
350 {AF_BRIDGE, "AF_BRIDGE"},
351 {AF_MPLS, "AF_MPLS"},
352 {AF_CAN, "AF_CAN"},
353 {AF_TIPC, "AF_TIPC"},
354 {AF_BLUETOOTH, "AF_BLUETOOTH"},
355 {AF_VSOCK, "AF_VSOCK"},
356 {AF_XDP, "AF_XDP"},
357 };
358
359 if (family_names.contains(family)) {
360 return family_names.at(family);
361 }
362
363 return "UNKNOWN";
364}
365
367inline auto protocol_to_string(uint8_t protocol) noexcept -> std::string_view {
368 static const std::unordered_map<uint8_t, std::string_view> protocol_names{
369 {RTPROT_UNSPEC, "RTPROT_UNSPEC"},
370 {RTPROT_REDIRECT, "RTPROT_REDIRECT"},
371 {RTPROT_KERNEL, "RTPROT_KERNEL"},
372 {RTPROT_BOOT, "RTPROT_BOOT"},
373 {RTPROT_STATIC, "RTPROT_STATIC"},
374 {RTPROT_GATED, "RTPROT_GATED"},
375 {RTPROT_RA, "RTPROT_RA"},
376 {RTPROT_MRT, "RTPROT_MRT"},
377 {RTPROT_ZEBRA, "RTPROT_ZEBRA"},
378 {RTPROT_BIRD, "RTPROT_BIRD"},
379 {RTPROT_DNROUTED, "RTPROT_DNROUTED"},
380 {RTPROT_XORP, "RTPROT_XORP"},
381 {RTPROT_NTK, "RTPROT_NTK"},
382 {RTPROT_DHCP, "RTPROT_DHCP"},
383 {RTPROT_MROUTED, "RTPROT_MROUTED"},
384 {RTPROT_BABEL, "RTPROT_BABEL"},
385 {RTPROT_BGP, "RTPROT_BGP"},
386 {RTPROT_OPENR, "RTPROT_OPENR"},
387 };
388
389 if (protocol_names.contains(protocol)) {
390 return protocol_names.at(protocol);
391 }
392
393 return "UNKNOWN";
394}
395
397inline auto route_type_to_string(uint8_t route_type) noexcept -> std::string_view {
398 static const std::unordered_map<uint8_t, std::string_view> route_type_names{
399 {RTN_UNSPEC, "RTN_UNSPEC"},
400 {RTN_UNICAST, "RTN_UNICAST"},
401 {RTN_LOCAL, "RTN_LOCAL"},
402 {RTN_BROADCAST, "RTN_BROADCAST"},
403 {RTN_ANYCAST, "RTN_ANYCAST"},
404 {RTN_MULTICAST, "RTN_MULTICAST"},
405 {RTN_BLACKHOLE, "RTN_BLACKHOLE"},
406 {RTN_UNREACHABLE, "RTN_UNREACHABLE"},
407 {RTN_PROHIBIT, "RTN_PROHIBIT"},
408 {RTN_THROW, "RTN_THROW"},
409 {RTN_NAT, "RTN_NAT"},
410 {RTN_XRESOLVE, "RTN_XRESOLVE"},
411 };
412
413 if (route_type_names.contains(route_type)) {
414 return route_type_names.at(route_type);
415 }
416
417 return "UNKNOWN";
418}
419
420} // namespace rtaco
421} // namespace llmx
Definition nl_common.hxx:293
Definition nl_common.hxx:297
Definition nl_common.hxx:22
auto checked_payload_copy(const rtattr &attr) -> std::optional< T >
Read a typed rtattr payload by value when the payload is large enough.
Definition nl_common.hxx:267
void for_each_attr(const nlmsghdr &header, const MsgT *info, Fn &&fn)
Iterate over rtattr attributes for a given message payload.
Definition nl_common.hxx:283
constexpr auto trim_string(std::string_view sv) -> std::string
Remove trailing NUL characters and return an owning string.
Definition nl_common.hxx:32
auto protocol_to_string(uint8_t protocol) noexcept -> std::string_view
Convert a protocol identifier to a readable name.
Definition nl_common.hxx:367
auto type_to_string(IsNetlinkEvent auto &&type) noexcept -> std::string_view
Convert a netlink message or event type to a readable name.
Definition nl_common.hxx:306
auto checked_attr_begin(const nlmsghdr &header, const MsgT *info, int &attr_length) -> const rtattr *
Return the aligned first attribute pointer and payload length.
Definition nl_common.hxx:217
auto extract_ifname(const nlmsghdr &header) -> std::string
Extract the interface name from a netlink header.
Definition nl_common.hxx:70
auto family_to_string(uint8_t family) noexcept -> std::string_view
Convert an address family constant to a human-readable name.
Definition nl_common.hxx:342
auto attribute_string(const rtattr &attr) -> std::string
Convert an rtattr payload to a trimmed std::string.
Definition nl_common.hxx:105
auto attribute_address(const rtattr &attr, uint8_t family) -> std::string
Convert an rtattr payload to a text representation of an IP address.
Definition nl_common.hxx:127
const MsgT * get_msg_payload(const nlmsghdr &header)
Get a typed pointer to the message payload in a netlink header.
Definition nl_common.hxx:200
auto checked_payload(const rtattr &attr) -> const T *
Read a typed rtattr payload only when the payload is large enough.
Definition nl_common.hxx:253
auto attribute_hwaddr(const rtattr &attr) -> std::string
Format a hardware address (MAC) from an rtattr payload as a string.
Definition nl_common.hxx:166
auto attribute_uint32(const rtattr &attr) -> uint32_t
Read a uint32_t value from an rtattr payload.
Definition nl_common.hxx:153
auto checked_nlmsgerr(const nlmsghdr &header) -> const nlmsgerr *
Extract a checked nlmsgerr payload from an NLMSG_ERROR message.
Definition nl_common.hxx:208
auto checked_attr_next(const rtattr *attr, int &attr_length) -> const rtattr *
Advance to the next attribute and update remaining length.
Definition nl_common.hxx:244
auto checked_attr_ok(const rtattr *attr, int attr_length) -> bool
Validate the current attribute against remaining message length.
Definition nl_common.hxx:236
auto route_type_to_string(uint8_t route_type) noexcept -> std::string_view
Convert a route type constant to a string.
Definition nl_common.hxx:397
Definition nl_common.hxx:21