#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include 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; 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 name = std::nullopt; std::optional mac = std::nullopt; std::optional mtu = std::nullopt; std::optional parent_idx = std::nullopt; std::optional kind = std::nullopt; std::optional 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 addr; std::optional 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 { using is_transparent = void; }; template using string_umap = std::unordered_map>; using string_uset = std::unordered_set>; constexpr std::size_t hash_multi() noexcept { return 0; } template 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{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); } namespace detail { template struct BswapAlign { using type = T; }; template struct BswapAlign { using type alignas(uint16_t) = T; }; template struct BswapAlign { using type alignas(uint32_t) = T; }; template struct BswapAlign { using type alignas(uint64_t) = T; }; template constexpr T bswapInt(typename BswapAlign::type n) noexcept { static_assert(std::is_trivially_copyable_v); if constexpr (sizeof(T) == 2) { reinterpret_cast(n) = __builtin_bswap16(reinterpret_cast(n)); } else if constexpr (sizeof(T) == 4) { reinterpret_cast(n) = __builtin_bswap32(reinterpret_cast(n)); } else if constexpr (sizeof(T) == 8) { reinterpret_cast(n) = __builtin_bswap64(reinterpret_cast(n)); } else { auto b = reinterpret_cast(&n); std::reverse(b, b + sizeof(n)); } return n; } } // namespace detail template constexpr T bswap(T n) noexcept { return detail::bswapInt(n); } template 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); } } template constexpr T ntoh(T n) noexcept { return hton(n); } namespace detail { inline constexpr auto charLookup = []() { std::array 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 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 struct DecodeInt { static_assert(base > 1 && base <= 36); static_assert(std::is_unsigned_v); constexpr T operator()(std::string_view str) const { if (str.empty()) { throw std::invalid_argument("Empty Str"); } constexpr auto max = std::numeric_limits::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 struct EncodeInt { static_assert(base > 1 && base <= 36); static_assert(std::is_unsigned_v); static constexpr uint8_t buf_size = []() { T v = std::numeric_limits::max(); uint8_t i = 0; for (; v != 0; ++i) { v /= base; } return i; }(); using buf_type = std::array; 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 struct ToAddr {}; template <> struct ToAddr { constexpr ether_addr operator()(std::string_view str) const { constexpr DecodeInt 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 { constexpr in_addr operator()(std::string_view str) const { constexpr DecodeInt 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 { constexpr in6_addr operator()(std::string_view str) const { constexpr DecodeInt 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{}(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{}(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 { constexpr InAddrAny operator()(std::string_view str) const { if (str.find(':') == str.npos) { return ToAddr{}(str); } return ToAddr{}(str); } }; template <> struct ToAddr { constexpr IfAddr operator()(std::string_view str) const { auto pos = str.rfind('/'); if (pos == str.npos) { throw std::invalid_argument("Invalid IfAddr"); } return {ToAddr{}(str.substr(0, pos)), DecodeInt{}(str.substr(pos + 1))}; } }; template struct ToStr {}; template <> struct ToStr { static constexpr uint8_t buf_size = 1; using buf_type = std::array; constexpr char* operator()(char* buf, char v) const noexcept { buf[0] = v; return buf + 1; } }; template <> struct ToStr { // 6 octets * 2 hex chars + 5 separators static constexpr uint8_t buf_size = 17; using buf_type = std::array; 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{}(tmp, byte, 2); } return buf + buf_size; } }; template <> struct ToStr { // 4 octets * 3 dec chars + 3 separators static constexpr uint8_t buf_size = 15; using buf_type = std::array; 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{}(EncodeInt{}(buf, n & 0xff), '.'); n >>= 8; } return EncodeInt{}(buf, n & 0xff); } }; template <> struct ToStr { // 8 hextets * 4 hex chars + 7 separators static constexpr uint8_t buf_size = 39; using buf_type = std::array; 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{}( 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{}(buf, ntoh(v.s6_addr16[i])); if (i < 7) { *(buf++) = ':'; } } return buf; } }; template <> struct ToStr { // IPv6 is the bigger of the addrs static constexpr uint8_t buf_size = ToStr::buf_size; using buf_type = std::array; constexpr char* operator()(char* buf, InAddrAny v) const noexcept { return std::visit([=](auto v) { return ToStr{}(buf, v); }, v); } }; template <> struct ToStr { // InAddrAny + sep + 3 prefix chars static constexpr uint8_t buf_size = ToStr::buf_size + 4; using buf_type = std::array; constexpr char* operator()(char* buf, IfAddr v) const noexcept { buf = ToStr{}(buf, v.getAddr()); buf = ToStr{}(buf, '/'); return EncodeInt{}(buf, v.getPfx()); } }; namespace detail { template constexpr bool vcontains() noexcept { return false; } template constexpr bool vcontains() noexcept { return vcontains() || std::is_same_v; } template constexpr std::enable_if_t(), bool> veq(T t, std::variant v) noexcept { return std::visit( [t](auto v) { if constexpr (std::is_same_v) { return v == t; } else { return false; } }, v); } template struct ToStrBuf { public: constexpr std::string_view operator()(T v) noexcept { return {buf.data(), ToStr{}(buf.data(), v)}; } private: typename ToStr::buf_type buf; }; template struct Format { private: fmt::formatter formatter; public: template constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(auto v, FormatContext& ctx) const { return formatter.format(ToStrBuf{}(v), ctx); } }; } // namespace detail } // namespace network } // namespace phosphor template struct std::hash> { constexpr auto operator()(const std::tuple& t) const noexcept { return std::apply(phosphor::network::hash_multi, t); } }; template <> struct std::hash { std::size_t operator()(in_addr addr) const noexcept; }; template <> struct std::hash { std::size_t operator()(in6_addr addr) const noexcept; }; template <> struct std::hash { std::size_t operator()(phosphor::network::IfAddr addr) const noexcept; }; namespace fmt { template <> struct formatter : phosphor::network::detail::Format {}; template <> struct formatter : phosphor::network::detail::Format {}; template <> struct formatter : phosphor::network::detail::Format {}; template <> struct formatter : phosphor::network::detail::Format {}; template <> struct formatter : phosphor::network::detail::Format {}; } // 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 constexpr std::enable_if_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{}(v); } auto& operator<<(auto& os, in_addr v) { return os << phosphor::network::detail::ToStrBuf{}(v); } auto& operator<<(auto& os, in6_addr v) { return os << phosphor::network::detail::ToStrBuf{}(v); } auto& operator<<(auto& os, phosphor::network::InAddrAny v) { phosphor::network::detail::ToStrBuf tsb; return os << tsb(v); } auto& operator<<(auto& os, phosphor::network::IfAddr v) { phosphor::network::detail::ToStrBuf tsb; return os << tsb(v); } namespace phosphor::network { /** @brief Contains all of the object information about the interface */ struct AllIntfInfo { InterfaceInfo intf; std::optional defgw4 = std::nullopt; std::optional defgw6 = std::nullopt; std::unordered_map addrs = {}; std::unordered_map staticNeighs = {}; }; } // namespace phosphor::network