#include <iostream>
#include <boost/pfr/precise.hpp>
#include <linux/netlink.h>
#include <netlink/attr.h>
#include <netlink/msg.h>
#include <map>
#include "/home/sargarass/work/es/garda_shared/bastion_libs/src/include/common/nonstd/expected.hpp"
#include "/home/sargarass/work/es/garda_shared/bastion_libs/src/include/common/nonstd/span.hpp"
#include "/home/sargarass/work/es/garda_shared/bastion_libs/src/include/common/nonstd/byte.hpp"
using nonstd::expected;
using nonstd::make_unexpected;
using nonstd::byte;
using nonstd::span;
enum class NetlinkMessageType : int {
unspec = NLA_UNSPEC, /**< Unspecified type, binary data chunk */
uint8 = NLA_U8, /**< 8 bit integer */
uint16 = NLA_U16, /**< 16 bit integer */
uint32 = NLA_U32, /**< 32 bit integer */
uint64 = NLA_U64, /**< 64 bit integer */
string = NLA_STRING, /**< NUL terminated character string */
binary = NLA_BINARY,
int8 = NLA_S8,
int16 = NLA_S16,
int32 = NLA_S32,
int64 = NLA_S64,
nested = NLA_NESTED,
empty_field = __NLA_TYPE_MAX,
};
struct messages {
int a;
std::string b;
std::map<std::string, std::string> c;
int d;
};
expected<void, std::runtime_error> netlink_put(nl_msg &nm, NetlinkMessageType type, uint32_t size, byte const *bytes) {
if (nla_put(&nm, static_cast<int>(type), size, bytes) < 0) {
return make_unexpected(std::runtime_error { "nla_put" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, int8_t v) {
if (nla_put_s8(&nm, static_cast<int>(NetlinkMessageType::int8), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_s8" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, int16_t v) {
if (nla_put_s16(&nm, static_cast<int>(NetlinkMessageType::int16), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_s16" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, int32_t v) {
if (nla_put_s32(&nm, static_cast<int>(NetlinkMessageType::int32), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_s32" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, int64_t v) {
if (nla_put_s64(&nm, static_cast<int>(NetlinkMessageType::int64), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_s64" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, uint8_t v) {
if (nla_put_u8(&nm, static_cast<int>(NetlinkMessageType::uint8), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_u8" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, uint16_t v) {
if (nla_put_u16(&nm, static_cast<int>(NetlinkMessageType::uint16), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_u16" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, uint32_t v) {
if (nla_put_u32(&nm, static_cast<int>(NetlinkMessageType::uint32), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_u32" });
}
return {};
}
expected<void, std::runtime_error> netlink_put(nl_msg &nm, uint64_t v) {
if (nla_put_u64(&nm, static_cast<int>(NetlinkMessageType::uint64), v) < 0) {
return make_unexpected(std::runtime_error { "nla_put_u64" });
}
return {};
}
expected<nlattr*, std::runtime_error> netlink_nested_start(nl_msg &nm) noexcept {
auto result = nla_nest_start(&nm, static_cast<int>(NetlinkMessageType::nested));
if (!result) {
return make_unexpected(std::runtime_error { "nla_nest_start failed" });
}
return { result };
}
void netlink_nested_end(nl_msg &nm, nlattr *start) {
nla_nest_end(&nm, start);
}
namespace serialization_impl {
template <typename T>
size_t serialize_size(T &&v) = delete;
template <>
size_t serialize_size(int const &) {
return static_cast<size_t>( nla_total_size(sizeof(int)) );
}
template <>
size_t serialize_size(std::string const &s) {
return static_cast<size_t>(nla_total_size(static_cast<int>( s.length())));
}
template <>
size_t serialize_size(size_t const &) {
return static_cast<size_t>( nla_total_size(sizeof(size_t)) );
}
template <typename K, typename V>
size_t serialize_size(std::map<K, V> const &v) {
size_t map_size = static_cast<size_t>(nla_total_size(sizeof(size_t)));
for (auto &&p : v) {
map_size += serialize_size(p.first);
map_size += serialize_size(p.second);
}
return static_cast<size_t>(nla_total_size(static_cast<int>(map_size)));
}
template <typename T>
expected<void, std::runtime_error> serialize(nl_msg &nm, T &&v) = delete;
template <>
expected<void, std::runtime_error> serialize(nl_msg &nm, const int &v) {
return netlink_put(nm, v);
}
template <>
expected<void, std::runtime_error> serialize(nl_msg &nm, std::size_t &&v) {
return netlink_put(nm, v);
}
template <>
expected<void, std::runtime_error> serialize(nl_msg &nm, std::string const &v) {
if (v.size() >= 0xFFFFFFFFFFFFFFFFull) {
return make_unexpected(std::runtime_error {"value is too big"});
}
return netlink_put(nm, NetlinkMessageType::string, v.length(), reinterpret_cast<byte const *>(v.c_str()));
}
template <typename K, typename V>
expected<void, std::runtime_error> serialize(nl_msg &nm, std::map<K, V> const &v) {
if (v.empty()) {
auto result = netlink_put(nm, NetlinkMessageType::empty_field, 0, nullptr);
if (!result) {
return result;
}
return {};
}
auto start_nested = netlink_nested_start(nm);
if (!start_nested) {
return start_nested.get_unexpected();
}
for (auto &&p : v) {
serialization_impl::serialize(nm, p.first);
serialization_impl::serialize(nm, p.second);
}
netlink_nested_end(nm, start_nested.value());
}
template <typename T>
void deserialize(nlattr *attr, T &&v) = delete;
template <>
void deserialize(nlattr *attr, int &v) {
if (nla_type(attr) != NLA_S32) {
throw std::runtime_error("expected NLA_S32");
}
v = nla_get_s32(attr);
}
template <>
void deserialize(nlattr *attr, std::string &v) {
if (nla_type(attr) != NLA_STRING) {
throw std::runtime_error("expected NLA_STRING");
}
v = std::string(nla_get_string(attr), nla_len(attr));
}
template <typename K, typename V>
void deserialize(nlattr *attr, std::map<K, V> &v) {
if (nla_type(attr) == static_cast<int>(NetlinkMessageType::empty_field)) {
return;
}
if (nla_type(attr) != NLA_NESTED) {
throw std::runtime_error("expected NLA_NESTED");
}
nlattr *nested = reinterpret_cast<nlattr *>( nla_data(attr) );
auto nested_size = nla_len(attr);
while(nla_ok(nested, nested_size)) {
K key;
V value;
serialization_impl::deserialize(nested, key);
nested = nla_next(nested, &nested_size);
if (!nla_ok(nested, nested_size)) {
throw std::runtime_error("no value for map");
}
serialization_impl::deserialize(nested, value);
v.insert(make_pair(std::move(key), std::move(value)));
nested = nla_next(nested, &nested_size);
}
}
}
template<typename T>
void serialize(nl_msg &nm, T &&value) {
boost::pfr::for_each_field(value, [&nm](const auto &field) {
auto result = serialization_impl::serialize(nm, field);
if (!result) {
throw result.error();
}
});
}
template<typename T>
T deserialize(nlattr *attr, uint32_t &size) {
T result;
int isize = size;
boost::pfr::for_each_field(result, [&isize, &attr](auto &field) {
if (!nla_ok(attr, isize)) {
throw std::runtime_error("error");
}
serialization_impl::deserialize(attr, field);
attr = nla_next(attr, &isize);
});
size = isize;
return result;
}
int main()
{
std::cout << boost::pfr::tuple_size<messages>::value << std::endl;
messages v { 123, "aaaadf e23", {{"a", "bb"}, {"az", "bb"}}};
v.d = 5;
size_t message_size = 0;
boost::pfr::for_each_field(v, [&message_size](const auto &field) {
message_size += serialization_impl::serialize_size(field);
});
nl_msg *nm = nlmsg_alloc_size(nlmsg_total_size(message_size));
nlmsghdr *hdr = nlmsg_hdr(nm);
serialize(*nm, v);
auto attr = (nlattr *)nlmsg_data(hdr);
uint32_t size = nlmsg_datalen(hdr);
auto v2 = deserialize<messages>(attr, size);
std::cout << v2.a << " " << v2.b << std::endl;
for (auto && q : v2.c) {
std::cout << q.first << " " << q.second << std::endl;
}
std::cout << v2.d << std::endl;
return 0;
}