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 <string>
8#include <string_view>
9#include <type_traits>
10#include <utility>
11#include <unordered_map>
12
13#include <arpa/inet.h>
14#include <net/if.h>
15
16#include <linux/netlink.h>
17#include <linux/neighbour.h>
18#include <linux/rtnetlink.h>
19
20namespace llmx {
21namespace rtaco {
22
31inline constexpr auto trim_string(std::string_view sv) -> std::string {
32 while (!sv.empty() && sv.back() == '\0') {
33 sv.remove_suffix(1);
34 }
35 return std::string(sv);
36}
37
42template<size_t N>
43inline constexpr auto trim_string(const std::array<char, N>& arr) -> std::string {
44 return trim_string(std::string_view{arr.data(), arr.size()});
45}
46
55inline auto extract_ifname(const nlmsghdr& header) -> std::string {
56 if (header.nlmsg_len < NLMSG_LENGTH(sizeof(ifinfomsg))) {
57 return {};
58 }
59
60 const auto* info = reinterpret_cast<const ifinfomsg*>(NLMSG_DATA(&header));
61 int attr_length = static_cast<int>(header.nlmsg_len) -
62 static_cast<int>(NLMSG_LENGTH(sizeof(ifinfomsg)));
63
64 if (attr_length <= 0) {
65 return {};
66 }
67
68 for (auto* attr = IFLA_RTA(info); RTA_OK(attr, attr_length);
69 attr = RTA_NEXT(attr, attr_length)) {
70 if (attr->rta_type != IFLA_IFNAME) {
71 continue;
72 }
73
74 const auto payload = static_cast<size_t>(RTA_PAYLOAD(attr));
75 if (payload == 0U) {
76 continue;
77 }
78
79 const auto* buffer = reinterpret_cast<const char*>(RTA_DATA(attr));
80 if (buffer == nullptr) {
81 continue;
82 }
83
84 return trim_string(buffer);
85 }
86
87 return {};
88}
89
95inline auto attribute_string(const rtattr& attr) -> std::string {
96 const auto payload = static_cast<size_t>(RTA_PAYLOAD(&attr));
97 if (payload == 0U) {
98 return {};
99 }
100
101 const auto* buffer = reinterpret_cast<const char*>(RTA_DATA(&attr));
102 if (buffer == nullptr) {
103 return {};
104 }
105
106 return trim_string(buffer);
107}
108
117inline auto attribute_address(const rtattr& attr, uint8_t family) -> std::string {
118 int address_family = 0;
119
120 switch (family) {
121 case AF_INET: address_family = AF_INET; break;
122 case AF_INET6: address_family = AF_INET6; break;
123 default: return {};
124 }
125
126 const void* data = RTA_DATA(&attr);
127 if (data == nullptr) {
128 return {};
129 }
130
131 std::array<char, INET6_ADDRSTRLEN> buffer{};
132 if (::inet_ntop(address_family, data, buffer.data(), buffer.size()) == nullptr) {
133 return {};
134 }
135
136 return trim_string(buffer);
137}
138
143inline auto attribute_uint32(const rtattr& attr) -> uint32_t {
144 if (RTA_PAYLOAD(&attr) < sizeof(uint32_t)) {
145 return 0U;
146 }
147
148 uint32_t value{};
149 std::memcpy(&value, RTA_DATA(&attr), sizeof(value));
150 return value;
151}
152
157inline auto attribute_hwaddr(const rtattr& attr) -> std::string {
158 const auto payload = static_cast<size_t>(RTA_PAYLOAD(&attr));
159 if (payload == 0U) {
160 return {};
161 }
162
163 const auto* data = reinterpret_cast<const uint8_t*>(RTA_DATA(&attr));
164 if (data == nullptr) {
165 return {};
166 }
167
168 std::string value;
169 value.reserve(payload * 3U);
170
171 constexpr char kHex[] = "0123456789abcdef";
172 for (size_t i = 0; i < payload; ++i) {
173 if (i != 0U) {
174 value.push_back(':');
175 }
176 const auto byte = data[i];
177 value.push_back(kHex[(byte >> 4U) & 0x0FU]);
178 value.push_back(kHex[byte & 0x0FU]);
179 }
180
181 return value;
182}
183
190template<typename MsgT>
191inline const MsgT* get_msg_payload(const nlmsghdr& header) {
192 if (header.nlmsg_len < NLMSG_LENGTH(sizeof(MsgT))) {
193 return nullptr;
194 }
195 return reinterpret_cast<const MsgT*>(NLMSG_DATA(&header));
196}
197
202template<typename MsgT, typename Fn>
203inline void for_each_attr(const nlmsghdr& header, const MsgT* info, Fn&& fn) {
204 if (info == nullptr) {
205 return;
206 }
207
208 int attr_length = static_cast<int>(header.nlmsg_len) -
209 static_cast<int>(NLMSG_LENGTH(sizeof(MsgT)));
210 if (attr_length <= 0) {
211 return;
212 }
213
214 const rtattr* attr = reinterpret_cast<const rtattr*>(
215 reinterpret_cast<std::uintptr_t>(info) + NLMSG_ALIGN(sizeof(MsgT)));
216 for (; RTA_OK(attr, attr_length); attr = RTA_NEXT(attr, attr_length)) {
217 fn(attr);
218 }
219}
220
221template<typename T>
222concept IsEnumeration = std::is_enum_v<std::remove_cvref_t<T>> ||
223 std::is_scoped_enum_v<std::remove_cvref_t<T>>;
224
225template<typename T>
226concept IsNetlinkEvent = IsEnumeration<T> || std::integral<std::remove_cvref_t<T>>;
227
235inline auto type_to_string(IsNetlinkEvent auto&& type) noexcept -> std::string_view {
236 uint16_t type_value{};
237
238 if constexpr (IsEnumeration<decltype(type)>) {
239 type_value = static_cast<uint16_t>(std::to_underlying(type));
240 } else {
241 type_value = static_cast<uint16_t>(type);
242 }
243
244 static const std::unordered_map<uint16_t, std::string_view> type_names{
245 {RTM_NEWLINK, "RTM_NEWLINK"},
246 {RTM_GETLINK, "RTM_GETLINK"},
247 {RTM_DELLINK, "RTM_DELLINK"},
248 {RTM_NEWADDR, "RTM_NEWADDR"},
249 {RTM_GETADDR, "RTM_GETADDR"},
250 {RTM_DELADDR, "RTM_DELADDR"},
251 {RTM_NEWROUTE, "RTM_NEWROUTE"},
252 {RTM_GETROUTE, "RTM_GETROUTE"},
253 {RTM_DELROUTE, "RTM_DELROUTE"},
254 {RTM_NEWNEIGH, "RTM_NEWNEIGH"},
255 {RTM_GETNEIGH, "RTM_GETNEIGH"},
256 {RTM_DELNEIGH, "RTM_DELNEIGH"},
257 {NLMSG_NOOP, "NLMSG_NOOP"},
258 {NLMSG_ERROR, "NLMSG_ERROR"},
259 {NLMSG_DONE, "NLMSG_DONE"},
260 {NLMSG_OVERRUN, "NLMSG_OVERRUN"},
261 };
262
263 if (type_names.contains(type_value)) {
264 return type_names.at(type_value);
265 }
266
267 return "UNKNOWN";
268}
269
271inline auto family_to_string(uint8_t family) noexcept -> std::string_view {
272 static const std::unordered_map<uint8_t, std::string_view> family_names{
273 {AF_UNSPEC, "AF_UNSPEC"},
274 {AF_UNIX, "AF_UNIX"},
275 {AF_INET, "AF_INET"},
276 {AF_INET6, "AF_INET6"},
277 {AF_NETLINK, "AF_NETLINK"},
278 {AF_PACKET, "AF_PACKET"},
279 {AF_BRIDGE, "AF_BRIDGE"},
280 {AF_MPLS, "AF_MPLS"},
281 {AF_CAN, "AF_CAN"},
282 {AF_TIPC, "AF_TIPC"},
283 {AF_BLUETOOTH, "AF_BLUETOOTH"},
284 {AF_VSOCK, "AF_VSOCK"},
285 {AF_XDP, "AF_XDP"},
286 };
287
288 if (family_names.contains(family)) {
289 return family_names.at(family);
290 }
291
292 return "UNKNOWN";
293}
294
296inline auto protocol_to_string(uint8_t protocol) noexcept -> std::string_view {
297 static const std::unordered_map<uint8_t, std::string_view> protocol_names{
298 {RTPROT_UNSPEC, "RTPROT_UNSPEC"},
299 {RTPROT_REDIRECT, "RTPROT_REDIRECT"},
300 {RTPROT_KERNEL, "RTPROT_KERNEL"},
301 {RTPROT_BOOT, "RTPROT_BOOT"},
302 {RTPROT_STATIC, "RTPROT_STATIC"},
303 {RTPROT_GATED, "RTPROT_GATED"},
304 {RTPROT_RA, "RTPROT_RA"},
305 {RTPROT_MRT, "RTPROT_MRT"},
306 {RTPROT_ZEBRA, "RTPROT_ZEBRA"},
307 {RTPROT_BIRD, "RTPROT_BIRD"},
308 {RTPROT_DNROUTED, "RTPROT_DNROUTED"},
309 {RTPROT_XORP, "RTPROT_XORP"},
310 {RTPROT_NTK, "RTPROT_NTK"},
311 {RTPROT_DHCP, "RTPROT_DHCP"},
312 {RTPROT_MROUTED, "RTPROT_MROUTED"},
313 {RTPROT_BABEL, "RTPROT_BABEL"},
314 {RTPROT_BGP, "RTPROT_BGP"},
315 {RTPROT_OPENR, "RTPROT_OPENR"},
316 };
317
318 if (protocol_names.contains(protocol)) {
319 return protocol_names.at(protocol);
320 }
321
322 return "UNKNOWN";
323}
324
326inline auto route_type_to_string(uint8_t route_type) noexcept -> std::string_view {
327 static const std::unordered_map<uint8_t, std::string_view> route_type_names{
328 {RTN_UNSPEC, "RTN_UNSPEC"},
329 {RTN_UNICAST, "RTN_UNICAST"},
330 {RTN_LOCAL, "RTN_LOCAL"},
331 {RTN_BROADCAST, "RTN_BROADCAST"},
332 {RTN_ANYCAST, "RTN_ANYCAST"},
333 {RTN_MULTICAST, "RTN_MULTICAST"},
334 {RTN_BLACKHOLE, "RTN_BLACKHOLE"},
335 {RTN_UNREACHABLE, "RTN_UNREACHABLE"},
336 {RTN_PROHIBIT, "RTN_PROHIBIT"},
337 {RTN_THROW, "RTN_THROW"},
338 {RTN_NAT, "RTN_NAT"},
339 {RTN_XRESOLVE, "RTN_XRESOLVE"},
340 };
341
342 if (route_type_names.contains(route_type)) {
343 return route_type_names.at(route_type);
344 }
345
346 return "UNKNOWN";
347}
348
349} // namespace rtaco
350} // namespace llmx
Definition nl_common.hxx:222
Definition nl_common.hxx:226
Definition nl_common.hxx:21
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:203
constexpr auto trim_string(std::string_view sv) -> std::string
Remove trailing NUL characters and return an owning string.
Definition nl_common.hxx:31
auto protocol_to_string(uint8_t protocol) noexcept -> std::string_view
Convert a protocol identifier to a readable name.
Definition nl_common.hxx:296
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:235
auto extract_ifname(const nlmsghdr &header) -> std::string
Extract the interface name from a netlink header.
Definition nl_common.hxx:55
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:271
auto attribute_string(const rtattr &attr) -> std::string
Convert an rtattr payload to a trimmed std::string.
Definition nl_common.hxx:95
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:117
const MsgT * get_msg_payload(const nlmsghdr &header)
Get a typed pointer to the message payload in a netlink header.
Definition nl_common.hxx:191
auto attribute_hwaddr(const rtattr &attr) -> std::string
Format a hardware address (MAC) from an rtattr payload as a string.
Definition nl_common.hxx:157
auto attribute_uint32(const rtattr &attr) -> uint32_t
Read a uint32_t value from an rtattr payload.
Definition nl_common.hxx:143
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:326
Definition nl_common.hxx:20