11#include <unordered_map>
16#include <linux/netlink.h>
17#include <linux/neighbour.h>
18#include <linux/rtnetlink.h>
31inline constexpr auto trim_string(std::string_view sv) -> std::string {
32 while (!sv.empty() && sv.back() ==
'\0') {
35 return std::string(sv);
43inline constexpr auto trim_string(
const std::array<char, N>& arr) -> std::string {
44 return trim_string(std::string_view{arr.data(), arr.size()});
56 if (header.nlmsg_len < NLMSG_LENGTH(
sizeof(ifinfomsg))) {
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)));
64 if (attr_length <= 0) {
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) {
74 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(attr));
79 const auto* buffer =
reinterpret_cast<const char*
>(RTA_DATA(attr));
80 if (buffer ==
nullptr) {
96 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(&attr));
101 const auto* buffer =
reinterpret_cast<const char*
>(RTA_DATA(&attr));
102 if (buffer ==
nullptr) {
118 int address_family = 0;
121 case AF_INET: address_family = AF_INET;
break;
122 case AF_INET6: address_family = AF_INET6;
break;
126 const void* data = RTA_DATA(&attr);
127 if (data ==
nullptr) {
131 std::array<char, INET6_ADDRSTRLEN> buffer{};
132 if (::inet_ntop(address_family, data, buffer.data(), buffer.size()) ==
nullptr) {
144 if (RTA_PAYLOAD(&attr) <
sizeof(uint32_t)) {
149 std::memcpy(&value, RTA_DATA(&attr),
sizeof(value));
158 const auto payload =
static_cast<size_t>(RTA_PAYLOAD(&attr));
163 const auto* data =
reinterpret_cast<const uint8_t*
>(RTA_DATA(&attr));
164 if (data ==
nullptr) {
169 value.reserve(payload * 3U);
171 constexpr char kHex[] =
"0123456789abcdef";
172 for (
size_t i = 0; i < payload; ++i) {
174 value.push_back(
':');
176 const auto byte = data[i];
177 value.push_back(kHex[(
byte >> 4U) & 0x0FU]);
178 value.push_back(kHex[
byte & 0x0FU]);
190template<
typename MsgT>
192 if (header.nlmsg_len < NLMSG_LENGTH(
sizeof(MsgT))) {
195 return reinterpret_cast<const MsgT*
>(NLMSG_DATA(&header));
202template<
typename MsgT,
typename Fn>
203inline void for_each_attr(
const nlmsghdr& header,
const MsgT* info, Fn&& fn) {
204 if (info ==
nullptr) {
208 int attr_length =
static_cast<int>(header.nlmsg_len) -
209 static_cast<int>(NLMSG_LENGTH(
sizeof(MsgT)));
210 if (attr_length <= 0) {
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)) {
223 std::is_scoped_enum_v<std::remove_cvref_t<T>>;
236 uint16_t type_value{};
239 type_value =
static_cast<uint16_t
>(std::to_underlying(type));
241 type_value =
static_cast<uint16_t
>(type);
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"},
263 if (type_names.contains(type_value)) {
264 return type_names.at(type_value);
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"},
282 {AF_TIPC,
"AF_TIPC"},
283 {AF_BLUETOOTH,
"AF_BLUETOOTH"},
284 {AF_VSOCK,
"AF_VSOCK"},
288 if (family_names.contains(family)) {
289 return family_names.at(family);
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"},
318 if (protocol_names.contains(protocol)) {
319 return protocol_names.at(protocol);
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"},
342 if (route_type_names.contains(route_type)) {
343 return route_type_names.at(route_type);
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