12#include <unordered_map>
17#include <linux/netlink.h>
18#include <linux/neighbour.h>
19#include <linux/rtnetlink.h>
32inline constexpr auto trim_string(std::string_view sv) -> std::string {
33 while (!sv.empty() && sv.back() ==
'\0') {
36 return std::string(sv);
44inline constexpr auto trim_string(
const std::array<char, N>& arr) -> std::string {
45 return trim_string(std::string_view{arr.data(), arr.size()});
48template<
typename MsgT>
49inline auto checked_attr_begin(
const nlmsghdr& header,
const MsgT* info,
int& attr_length)
52inline auto checked_attr_ok(
const rtattr* attr,
int attr_length) -> bool;
54inline auto checked_attr_next(
const rtattr* attr,
int& attr_length) ->
const rtattr*;
71 if (header.nlmsg_len < NLMSG_LENGTH(
sizeof(ifinfomsg))) {
75 const auto* info =
reinterpret_cast<const ifinfomsg*
>(NLMSG_DATA(&header));
80 if (attr->rta_type != IFLA_IFNAME) {
84 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(attr));
89 const auto* buffer =
reinterpret_cast<const char*
>(RTA_DATA(attr));
90 if (buffer ==
nullptr) {
106 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(&attr));
111 const auto* buffer =
reinterpret_cast<const char*
>(RTA_DATA(&attr));
112 if (buffer ==
nullptr) {
128 int address_family = 0;
131 case AF_INET: address_family = AF_INET;
break;
132 case AF_INET6: address_family = AF_INET6;
break;
136 const void* data = RTA_DATA(&attr);
137 if (data ==
nullptr) {
141 std::array<char, INET6_ADDRSTRLEN> buffer{};
142 if (::inet_ntop(address_family, data, buffer.data(), buffer.size()) ==
nullptr) {
155 if (!value.has_value()) {
167 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(&attr));
172 const auto* data =
reinterpret_cast<const uint8_t*
>(RTA_DATA(&attr));
173 if (data ==
nullptr) {
178 value.reserve(payload * 3U);
180 constexpr char kHex[] =
"0123456789abcdef";
181 for (
size_t i = 0; i < payload; ++i) {
183 value.push_back(
':');
185 const auto byte = data[i];
186 value.push_back(kHex[(
byte >> 4U) & 0x0FU]);
187 value.push_back(kHex[
byte & 0x0FU]);
199template<
typename MsgT>
201 if (header.nlmsg_len < NLMSG_LENGTH(
sizeof(MsgT))) {
204 return reinterpret_cast<const MsgT*
>(NLMSG_DATA(&header));
209 if (header.nlmsg_type != NLMSG_ERROR) {
216template<
typename MsgT>
220 if (info ==
nullptr || header.nlmsg_len < NLMSG_LENGTH(
sizeof(MsgT))) {
224 attr_length =
static_cast<int>(header.nlmsg_len) -
225 static_cast<int>(NLMSG_LENGTH(
sizeof(MsgT)));
226 if (attr_length <= 0) {
231 return reinterpret_cast<const rtattr*
>(
232 reinterpret_cast<const std::uintptr_t
>(info) + NLMSG_ALIGN(
sizeof(MsgT)));
237 if (attr ==
nullptr) {
240 return RTA_OK(attr, attr_length);
245 if (attr ==
nullptr) {
248 return RTA_NEXT(attr, attr_length);
254 if (RTA_PAYLOAD(&attr) <
sizeof(T)) {
258 return reinterpret_cast<const T*
>(RTA_DATA(&attr));
269 if (payload ==
nullptr) {
274 std::memcpy(&value, payload,
sizeof(value));
282template<
typename MsgT,
typename Fn>
283inline void for_each_attr(
const nlmsghdr& header,
const MsgT* info, Fn&& fn) {
294 std::is_scoped_enum_v<std::remove_cvref_t<T>>;
307 uint16_t type_value{};
310 type_value =
static_cast<uint16_t
>(std::to_underlying(type));
312 type_value =
static_cast<uint16_t
>(type);
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"},
334 if (type_names.contains(type_value)) {
335 return type_names.at(type_value);
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"},
353 {AF_TIPC,
"AF_TIPC"},
354 {AF_BLUETOOTH,
"AF_BLUETOOTH"},
355 {AF_VSOCK,
"AF_VSOCK"},
359 if (family_names.contains(family)) {
360 return family_names.at(family);
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"},
389 if (protocol_names.contains(protocol)) {
390 return protocol_names.at(protocol);
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"},
413 if (route_type_names.contains(route_type)) {
414 return route_type_names.at(route_type);
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