blob: 6351b242951d71e14e0733a01c419e3f22e78ea0 [file] [log] [blame]
#pragma once
#include <fmt/core.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <algorithm>
#include <array>
#include <numeric>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <variant>
constexpr bool operator==(ether_addr lhs, ether_addr rhs) noexcept
{
return std::equal(lhs.ether_addr_octet, lhs.ether_addr_octet + 6,
rhs.ether_addr_octet);
}
constexpr bool operator==(in_addr lhs, in_addr rhs) noexcept
{
return lhs.s_addr == rhs.s_addr;
}
constexpr bool operator==(in6_addr lhs, in6_addr rhs) noexcept
{
return std::equal(lhs.s6_addr32, lhs.s6_addr32 + 4, rhs.s6_addr32);
}
namespace phosphor
{
namespace network
{
// Byte representations for common address types in network byte order
using InAddrAny = std::variant<in_addr, in6_addr>;
class IfAddr
{
private:
InAddrAny addr;
uint8_t pfx;
static void invalidPfx(uint8_t pfx);
public:
constexpr IfAddr() : addr({}), pfx(0) {}
constexpr IfAddr(InAddrAny addr, uint8_t pfx) : addr(addr), pfx(pfx)
{
std::visit(
[pfx](auto v) {
if (sizeof(v) * 8 < pfx)
{
invalidPfx(pfx);
}
},
addr);
}
constexpr auto getAddr() const
{
return addr;
}
constexpr auto getPfx() const
{
return pfx;
}
constexpr bool operator==(phosphor::network::IfAddr rhs) const noexcept
{
return addr == rhs.addr && pfx == rhs.pfx;
}
};
/** @class InterfaceInfo
* @brief Information about interfaces from the kernel
*/
struct InterfaceInfo
{
unsigned short type;
unsigned idx;
unsigned flags;
std::optional<std::string> name = std::nullopt;
std::optional<ether_addr> mac = std::nullopt;
std::optional<unsigned> mtu = std::nullopt;
std::optional<unsigned> parent_idx = std::nullopt;
std::optional<std::string> kind = std::nullopt;
std::optional<uint16_t> vlan_id = std::nullopt;
constexpr bool operator==(const InterfaceInfo& rhs) const noexcept
{
return idx == rhs.idx && flags == rhs.flags && name == rhs.name &&
mac == rhs.mac && mtu == rhs.mtu &&
parent_idx == rhs.parent_idx && kind == rhs.kind &&
vlan_id == rhs.vlan_id;
}
};
/** @class AddressInfo
* @brief Information about a addresses from the kernel
*/
struct AddressInfo
{
unsigned ifidx;
IfAddr ifaddr;
uint8_t scope;
uint32_t flags;
constexpr bool operator==(const AddressInfo& rhs) const noexcept
{
return ifidx == rhs.ifidx && ifaddr == rhs.ifaddr &&
scope == rhs.scope && flags == rhs.flags;
}
};
/** @class NeighborInfo
* @brief Information about a neighbor from the kernel
*/
struct NeighborInfo
{
unsigned ifidx;
uint16_t state;
std::optional<InAddrAny> addr;
std::optional<ether_addr> mac;
constexpr bool operator==(const NeighborInfo& rhs) const noexcept
{
return ifidx == rhs.ifidx && state == rhs.state && addr == rhs.addr &&
mac == rhs.mac;
}
};
struct string_hash : public std::hash<std::string_view>
{
using is_transparent = void;
};
template <typename V>
using string_umap =
std::unordered_map<std::string, V, string_hash, std::equal_to<>>;
using string_uset =
std::unordered_set<std::string, string_hash, std::equal_to<>>;
constexpr std::size_t hash_multi() noexcept
{
return 0;
}
template <typename T, typename... Args>
constexpr std::size_t hash_multi(const T& v, const Args&... args) noexcept
{
const std::size_t seed = hash_multi(args...);
return seed ^ (std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
namespace detail
{
template <typename T, uint8_t size = sizeof(T)>
struct BswapAlign
{
using type = T;
};
template <typename T>
struct BswapAlign<T, 2>
{
using type alignas(uint16_t) = T;
};
template <typename T>
struct BswapAlign<T, 4>
{
using type alignas(uint32_t) = T;
};
template <typename T>
struct BswapAlign<T, 8>
{
using type alignas(uint64_t) = T;
};
template <typename T>
constexpr T bswapInt(typename BswapAlign<T>::type n) noexcept
{
static_assert(std::is_trivially_copyable_v<T>);
if constexpr (sizeof(T) == 2)
{
reinterpret_cast<uint16_t&>(n) =
__builtin_bswap16(reinterpret_cast<uint16_t&>(n));
}
else if constexpr (sizeof(T) == 4)
{
reinterpret_cast<uint32_t&>(n) =
__builtin_bswap32(reinterpret_cast<uint32_t&>(n));
}
else if constexpr (sizeof(T) == 8)
{
reinterpret_cast<uint64_t&>(n) =
__builtin_bswap64(reinterpret_cast<uint64_t&>(n));
}
else
{
auto b = reinterpret_cast<std::byte*>(&n);
std::reverse(b, b + sizeof(n));
}
return n;
}
} // namespace detail
template <typename T>
constexpr T bswap(T n) noexcept
{
return detail::bswapInt<T>(n);
}
template <typename T>
constexpr T hton(T n) noexcept
{
if constexpr (std::endian::native == std::endian::big)
{
return n;
}
else if constexpr (std::endian::native == std::endian::little)
{
return bswap(n);
}
else
{
static_assert(std::is_same_v<T, void>);
}
}
template <typename T>
constexpr T ntoh(T n) noexcept
{
return hton(n);
}
namespace detail
{
inline constexpr auto charLookup = []() {
std::array<int8_t, 256> ret;
std::fill(ret.begin(), ret.end(), -1);
for (int8_t i = 0; i < 10; ++i)
{
ret[i + '0'] = i;
}
for (int8_t i = 0; i < 26; ++i)
{
ret[i + 'A'] = i + 10;
ret[i + 'a'] = i + 10;
}
return ret;
}();
inline constexpr auto intLookup = []() {
std::array<char, 36> ret;
for (int8_t i = 0; i < 10; ++i)
{
ret[i] = i + '0';
}
for (int8_t i = 0; i < 26; ++i)
{
ret[i + 10] = i + 'a';
}
return ret;
}();
} // namespace detail
template <typename T, uint8_t base>
struct DecodeInt
{
static_assert(base > 1 && base <= 36);
static_assert(std::is_unsigned_v<T>);
constexpr T operator()(std::string_view str) const
{
if (str.empty())
{
throw std::invalid_argument("Empty Str");
}
constexpr auto max = std::numeric_limits<T>::max();
auto ret = std::accumulate(str.begin(), str.end(), T{},
[&](T r, char c) {
auto v = detail::charLookup[c];
if (v < 0 || v >= base)
{
throw std::invalid_argument("Invalid numeral");
}
if constexpr (std::popcount(base) == 1)
{
constexpr auto shift = std::countr_zero(base);
constexpr auto maxshift = max >> shift;
if (r > maxshift)
{
throw std::overflow_error("Integer Decode");
}
return (r << shift) | v;
}
else
{
constexpr auto maxbase = max / base;
if (r > maxbase)
{
throw std::overflow_error("Integer Decode");
}
r *= base;
if (max - v < r)
{
throw std::overflow_error("Integer Decode");
}
return r + v;
}
});
return ret;
}
};
template <typename T, uint8_t base>
struct EncodeInt
{
static_assert(base > 1 && base <= 36);
static_assert(std::is_unsigned_v<T>);
static constexpr uint8_t buf_size = []() {
T v = std::numeric_limits<T>::max();
uint8_t i = 0;
for (; v != 0; ++i)
{
v /= base;
}
return i;
}();
using buf_type = std::array<char, buf_size>;
constexpr uint8_t reverseFill(char* buf, T v) const noexcept
{
uint8_t i = 0;
do
{
if constexpr (std::popcount(base) == 1)
{
buf[i++] = detail::intLookup[v & 0xf];
v >>= 4;
}
else
{
buf[i++] = detail::intLookup[v % base];
v /= base;
}
} while (v > 0);
return i;
}
constexpr char* operator()(char* buf, T v) const noexcept
{
uint8_t i = reverseFill(buf, v);
std::reverse(buf, buf + i);
return buf + i;
}
constexpr char* operator()(char* buf, T v, uint8_t min_width) const noexcept
{
uint8_t i = reverseFill(buf, v);
auto end = buf + std::max(i, min_width);
std::fill(buf + i, end, '0');
std::reverse(buf, end);
return end;
}
};
template <typename T>
struct ToAddr
{};
template <>
struct ToAddr<ether_addr>
{
constexpr ether_addr operator()(std::string_view str) const
{
constexpr DecodeInt<uint8_t, 16> di;
ether_addr ret;
if (str.size() == 12 && str.find(":") == str.npos)
{
for (size_t i = 0; i < 6; ++i)
{
ret.ether_addr_octet[i] = di(str.substr(i * 2, 2));
}
}
else
{
for (size_t i = 0; i < 5; ++i)
{
auto loc = str.find(":");
ret.ether_addr_octet[i] = di(str.substr(0, loc));
str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
if (str.empty())
{
throw std::invalid_argument("Missing mac data");
}
}
ret.ether_addr_octet[5] = di(str);
}
return ret;
}
};
template <>
struct ToAddr<in_addr>
{
constexpr in_addr operator()(std::string_view str) const
{
constexpr DecodeInt<uint8_t, 10> di;
uint32_t addr = {};
for (size_t i = 0; i < 3; ++i)
{
auto loc = str.find(".");
addr |= di(str.substr(0, loc));
addr <<= 8;
str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
if (str.empty())
{
throw std::invalid_argument("Missing addr data");
}
}
addr |= di(str);
return {hton(addr)};
}
};
template <>
struct ToAddr<in6_addr>
{
constexpr in6_addr operator()(std::string_view str) const
{
constexpr DecodeInt<uint16_t, 16> di;
in6_addr ret = {};
size_t i = 0;
while (i < 8)
{
auto loc = str.find(':');
if (i == 6 && loc == str.npos)
{
ret.s6_addr32[3] = ToAddr<in_addr>{}(str).s_addr;
return ret;
}
if (loc != 0 && !str.empty())
{
ret.s6_addr16[i++] = hton(di(str.substr(0, loc)));
}
if (i < 8 && str.size() > loc + 1 && str[loc + 1] == ':')
{
str.remove_prefix(loc + 2);
break;
}
else if (str.empty())
{
throw std::invalid_argument("IPv6 Data");
}
str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
}
if (str.starts_with(':'))
{
throw std::invalid_argument("Extra separator");
}
size_t j = 7;
if (!str.empty() && i < 6 && str.find('.') != str.npos)
{
auto loc = str.rfind(':');
ret.s6_addr32[3] =
ToAddr<in_addr>{}(str.substr(loc == str.npos ? 0 : loc + 1))
.s_addr;
str.remove_suffix(loc == str.npos ? str.size() : str.size() - loc);
j -= 2;
}
while (!str.empty() && j > i)
{
auto loc = str.rfind(':');
ret.s6_addr16[j--] =
hton(di(str.substr(loc == str.npos ? 0 : loc + 1)));
str.remove_suffix(loc == str.npos ? str.size() : str.size() - loc);
}
if (!str.empty())
{
throw std::invalid_argument("Too much data");
}
return ret;
}
};
template <>
struct ToAddr<InAddrAny>
{
constexpr InAddrAny operator()(std::string_view str) const
{
if (str.find(':') == str.npos)
{
return ToAddr<in_addr>{}(str);
}
return ToAddr<in6_addr>{}(str);
}
};
template <>
struct ToAddr<IfAddr>
{
constexpr IfAddr operator()(std::string_view str) const
{
auto pos = str.rfind('/');
if (pos == str.npos)
{
throw std::invalid_argument("Invalid IfAddr");
}
return {ToAddr<InAddrAny>{}(str.substr(0, pos)),
DecodeInt<uint8_t, 10>{}(str.substr(pos + 1))};
}
};
template <typename T>
struct ToStr
{};
template <>
struct ToStr<char>
{
static constexpr uint8_t buf_size = 1;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, char v) const noexcept
{
buf[0] = v;
return buf + 1;
}
};
template <>
struct ToStr<ether_addr>
{
// 6 octets * 2 hex chars + 5 separators
static constexpr uint8_t buf_size = 17;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, ether_addr v) const noexcept
{
for (char* ptr = buf + 2; ptr < buf + buf_size; ptr += 3)
{
*ptr = ':';
}
for (size_t i = 0; i < 6; ++i)
{
char* tmp = buf + i * 3;
uint8_t byte = v.ether_addr_octet[i];
EncodeInt<uint8_t, 16>{}(tmp, byte, 2);
}
return buf + buf_size;
}
};
template <>
struct ToStr<in_addr>
{
// 4 octets * 3 dec chars + 3 separators
static constexpr uint8_t buf_size = 15;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, in_addr v) const noexcept
{
auto n = bswap(ntoh(v.s_addr));
for (size_t i = 0; i < 3; ++i)
{
buf = ToStr<char>{}(EncodeInt<uint8_t, 10>{}(buf, n & 0xff), '.');
n >>= 8;
}
return EncodeInt<uint8_t, 10>{}(buf, n & 0xff);
}
};
template <>
struct ToStr<in6_addr>
{
// 8 hextets * 4 hex chars + 7 separators
static constexpr uint8_t buf_size = 39;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, in6_addr v) const noexcept
{
// IPv4 in IPv6 Addr
if (v.s6_addr32[0] == 0 && v.s6_addr32[1] == 0 &&
v.s6_addr32[2] == hton(uint32_t(0xffff)))
{
constexpr auto prefix = std::string_view("::ffff:");
return ToStr<in_addr>{}(
std::copy(prefix.begin(), prefix.end(), buf), {v.s6_addr32[3]});
}
size_t skip_start = 0;
size_t skip_size = 0;
{
size_t new_start = 0;
size_t new_size = 0;
for (size_t i = 0; i < 9; ++i)
{
if (i < 8 && v.s6_addr16[i] == 0)
{
if (new_start + new_size == i)
{
new_size++;
}
else
{
new_start = i;
new_size = 1;
}
}
else if (new_start + new_size == i && new_size > skip_size)
{
skip_start = new_start;
skip_size = new_size;
}
}
}
for (size_t i = 0; i < 8; ++i)
{
if (i == skip_start && skip_size > 1)
{
if (i == 0)
{
*(buf++) = ':';
}
*(buf++) = ':';
i += skip_size - 1;
continue;
}
buf = EncodeInt<uint16_t, 16>{}(buf, ntoh(v.s6_addr16[i]));
if (i < 7)
{
*(buf++) = ':';
}
}
return buf;
}
};
template <>
struct ToStr<InAddrAny>
{
// IPv6 is the bigger of the addrs
static constexpr uint8_t buf_size = ToStr<in6_addr>::buf_size;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, InAddrAny v) const noexcept
{
return std::visit([=](auto v) { return ToStr<decltype(v)>{}(buf, v); },
v);
}
};
template <>
struct ToStr<IfAddr>
{
// InAddrAny + sep + 3 prefix chars
static constexpr uint8_t buf_size = ToStr<InAddrAny>::buf_size + 4;
using buf_type = std::array<char, buf_size>;
constexpr char* operator()(char* buf, IfAddr v) const noexcept
{
buf = ToStr<InAddrAny>{}(buf, v.getAddr());
buf = ToStr<char>{}(buf, '/');
return EncodeInt<uint8_t, 10>{}(buf, v.getPfx());
}
};
namespace detail
{
template <typename T>
constexpr bool vcontains() noexcept
{
return false;
}
template <typename T, typename V, typename... Vs>
constexpr bool vcontains() noexcept
{
return vcontains<T, Vs...>() || std::is_same_v<T, V>;
}
template <typename T, typename... Types>
constexpr std::enable_if_t<vcontains<T, Types...>(), bool>
veq(T t, std::variant<Types...> v) noexcept
{
return std::visit(
[t](auto v) {
if constexpr (std::is_same_v<T, decltype(v)>)
{
return v == t;
}
else
{
return false;
}
},
v);
}
template <typename T>
struct ToStrBuf
{
public:
constexpr std::string_view operator()(T v) noexcept
{
return {buf.data(), ToStr<T>{}(buf.data(), v)};
}
private:
typename ToStr<T>::buf_type buf;
};
template <typename T>
struct Format
{
private:
fmt::formatter<std::string_view> formatter;
public:
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx)
{
return ctx.begin();
}
template <typename FormatContext>
auto format(auto v, FormatContext& ctx) const
{
return formatter.format(ToStrBuf<T>{}(v), ctx);
}
};
} // namespace detail
} // namespace network
} // namespace phosphor
template <typename... Ts>
struct std::hash<std::tuple<Ts...>>
{
constexpr auto operator()(const std::tuple<Ts...>& t) const noexcept
{
return std::apply(phosphor::network::hash_multi<Ts...>, t);
}
};
template <>
struct std::hash<in_addr>
{
std::size_t operator()(in_addr addr) const noexcept;
};
template <>
struct std::hash<in6_addr>
{
std::size_t operator()(in6_addr addr) const noexcept;
};
template <>
struct std::hash<phosphor::network::IfAddr>
{
std::size_t operator()(phosphor::network::IfAddr addr) const noexcept;
};
namespace fmt
{
template <>
struct formatter<ether_addr> : phosphor::network::detail::Format<ether_addr>
{};
template <>
struct formatter<in_addr> : phosphor::network::detail::Format<in_addr>
{};
template <>
struct formatter<in6_addr> : phosphor::network::detail::Format<in6_addr>
{};
template <>
struct formatter<phosphor::network::InAddrAny> :
phosphor::network::detail::Format<phosphor::network::InAddrAny>
{};
template <>
struct formatter<phosphor::network::IfAddr> :
phosphor::network::detail::Format<phosphor::network::IfAddr>
{};
} // namespace fmt
namespace std
{
string to_string(ether_addr value);
string to_string(in_addr value);
string to_string(in6_addr value);
string to_string(phosphor::network::InAddrAny value);
string to_string(phosphor::network::IfAddr value);
} // namespace std
template <typename T>
constexpr std::enable_if_t<!std::is_same_v<phosphor::network::InAddrAny, T>,
bool>
operator==(phosphor::network::InAddrAny lhs, T rhs) noexcept
{
return phosphor::network::detail::veq(rhs, lhs);
}
auto& operator<<(auto& os, ether_addr v)
{
return os << phosphor::network::detail::ToStrBuf<ether_addr>{}(v);
}
auto& operator<<(auto& os, in_addr v)
{
return os << phosphor::network::detail::ToStrBuf<in_addr>{}(v);
}
auto& operator<<(auto& os, in6_addr v)
{
return os << phosphor::network::detail::ToStrBuf<in6_addr>{}(v);
}
auto& operator<<(auto& os, phosphor::network::InAddrAny v)
{
phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
return os << tsb(v);
}
auto& operator<<(auto& os, phosphor::network::IfAddr v)
{
phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
return os << tsb(v);
}
namespace phosphor::network
{
/** @brief Contains all of the object information about the interface */
struct AllIntfInfo
{
InterfaceInfo intf;
std::optional<in_addr> defgw4 = std::nullopt;
std::optional<in6_addr> defgw6 = std::nullopt;
std::unordered_map<IfAddr, AddressInfo> addrs = {};
std::unordered_map<InAddrAny, NeighborInfo> staticNeighs = {};
};
} // namespace phosphor::network