types: Add a type for interface addresses
This will be used to uniquely identify them.
Change-Id: Iabd43520ae5060e4f5dfe18e549f96f6b910b3c1
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.cpp b/src/types.cpp
index 55c6d10..37eb69e 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -1,8 +1,18 @@
#include "types.hpp"
+#include <fmt/format.h>
+
#include <charconv>
-namespace phosphor::network::detail
+namespace phosphor::network
+{
+
+void IfAddr::invalidPfx(uint8_t pfx)
+{
+ throw std::invalid_argument(fmt::format("Invalid prefix {}", pfx));
+}
+
+namespace detail
{
std::string_view AddrBufMaker<ether_addr>::operator()(ether_addr val) noexcept
@@ -91,7 +101,8 @@
return {buf.data(), ptr};
}
-} // namespace phosphor::network::detail
+} // namespace detail
+} // namespace phosphor::network
std::size_t std::hash<in_addr>::operator()(in_addr addr) const noexcept
{
@@ -104,6 +115,12 @@
addr.s6_addr32[2], addr.s6_addr32[3]);
}
+std::size_t std::hash<phosphor::network::IfAddr>::operator()(
+ phosphor::network::IfAddr addr) const noexcept
+{
+ return phosphor::network::hash_multi(addr.getAddr(), addr.getPfx());
+}
+
std::string std::to_string(ether_addr value)
{
return string(phosphor::network::detail::AddrBufMaker<ether_addr>{}(value));
@@ -120,3 +137,8 @@
{
return std::visit([](auto v) { return std::to_string(v); }, value);
}
+
+std::string std::to_string(phosphor::network::IfAddr value)
+{
+ return fmt::to_string(value);
+}
diff --git a/src/types.hpp b/src/types.hpp
index 75db6a4..f1f2179 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -32,6 +32,46 @@
// 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;
+ }
+};
using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
@@ -254,6 +294,12 @@
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 <>
@@ -299,6 +345,29 @@
v);
}
};
+template <>
+struct formatter<phosphor::network::IfAddr>
+{
+ private:
+ fmt::formatter<phosphor::network::InAddrAny> addrF;
+ fmt::formatter<char> strF;
+ fmt::formatter<uint8_t> numF;
+
+ public:
+ template <typename ParseContext>
+ constexpr auto parse(ParseContext& ctx)
+ {
+ return ctx.begin();
+ }
+
+ template <typename FormatContext>
+ auto format(auto v, FormatContext& ctx) const
+ {
+ addrF.format(v.getAddr(), ctx);
+ strF.format('/', ctx);
+ return numF.format(v.getPfx(), ctx);
+ }
+};
} // namespace fmt
namespace std
@@ -307,6 +376,7 @@
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
constexpr bool operator==(ether_addr lhs, ether_addr rhs) noexcept
@@ -357,3 +427,8 @@
},
v);
}
+
+auto& operator<<(auto& os, phosphor::network::IfAddr v)
+{
+ return os << v.getAddr() << "/" << std::dec << int{v.getPfx()};
+}
diff --git a/test/test_types.cpp b/test/test_types.cpp
index 8150555..4acf409 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -111,12 +111,14 @@
EXPECT_EQ("a 0.0.0.1", fmt::format("a {}", InAddrAny{in_addr{htonl(1)}}));
EXPECT_EQ("a 100::", fmt::format("a {}", in6_addr{1}));
EXPECT_EQ("a 100::", fmt::format("a {}", InAddrAny{in6_addr{1}}));
+ EXPECT_EQ("a 100::/90", fmt::format("a {}", IfAddr{in6_addr{1}, 90}));
EXPECT_EQ("01:00:00:00:00:00", std::to_string(ether_addr{1}));
EXPECT_EQ("0.0.0.1", std::to_string(in_addr{htonl(1)}));
EXPECT_EQ("0.0.0.1", std::to_string(InAddrAny{in_addr{htonl(1)}}));
EXPECT_EQ("100::", std::to_string(in6_addr{1}));
EXPECT_EQ("100::", std::to_string(InAddrAny{in6_addr{1}}));
+ EXPECT_EQ("100::/22", std::to_string(IfAddr{in6_addr{1}, 22}));
EXPECT_EQ("a01:00:00:00:00:00",
(std::stringstream{} << "a" << ether_addr{1}).str());
@@ -128,6 +130,15 @@
EXPECT_EQ("a100::", (std::stringstream{} << "a" << in6_addr{1}).str());
EXPECT_EQ("a100::",
(std::stringstream{} << "a" << InAddrAny{in6_addr{1}}).str());
+ auto ss = std::stringstream{};
+ constexpr auto addr = IfAddr{in6_addr{1}, 30};
+ ss << "a" << addr;
+ EXPECT_EQ("a100::/30", ss.str());
+
+ EXPECT_NO_THROW(IfAddr(in6_addr{}, 128));
+ EXPECT_NO_THROW(IfAddr(in_addr{}, 32));
+ EXPECT_THROW(IfAddr(in6_addr{}, 129), std::invalid_argument);
+ EXPECT_THROW(IfAddr(in_addr{}, 33), std::invalid_argument);
}
TEST(Perf, In6Addr)