rtnetlink: Separate out gateway parsing

This same logic will get re-used by other code in a future change.

Change-Id: I28218b9defb64c15f24df989f46db255e6ce4045
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/meson.build b/src/meson.build
index 79fa0b7..ce1c142 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -52,6 +52,7 @@
   'ipaddress.cpp',
   'netlink.cpp',
   'network_manager.cpp',
+  'rtnetlink.cpp',
   'system_configuration.cpp',
   'system_queries.cpp',
   'types.cpp',
diff --git a/src/routing_table.cpp b/src/routing_table.cpp
index bed1f70..d6285a7 100644
--- a/src/routing_table.cpp
+++ b/src/routing_table.cpp
@@ -1,6 +1,7 @@
 #include "routing_table.hpp"
 
 #include "netlink.hpp"
+#include "rtnetlink.hpp"
 
 #include <optional>
 #include <phosphor-logging/elog-errors.hpp>
@@ -20,30 +21,6 @@
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
-template <typename Addr>
-static void parse(auto& gws, std::string_view msg)
-{
-    std::optional<unsigned> ifIdx;
-    std::optional<Addr> gw;
-    while (!msg.empty())
-    {
-        auto [hdr, data] = netlink::extractRtAttr(msg);
-        switch (hdr.rta_type)
-        {
-            case RTA_OIF:
-                ifIdx.emplace(stdplus::raw::copyFrom<int>(data));
-                break;
-            case RTA_GATEWAY:
-                gw.emplace(stdplus::raw::copyFrom<Addr>(data));
-                break;
-        }
-    }
-    if (ifIdx && gw)
-    {
-        gws.emplace(*ifIdx, *gw);
-    }
-}
-
 static void parseRoute(auto& gws4, auto& gws6, const nlmsghdr& hdr,
                        std::string_view msg)
 {
@@ -51,19 +28,25 @@
     {
         throw std::runtime_error("Not a route msg");
     }
-    const auto& rtm = netlink::extractRtData<rtmsg>(msg);
 
-    if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
+    if (auto ret = netlink::gatewayFromRtm(msg); ret)
     {
-        return;
-    }
-
-    switch (rtm.rtm_family)
-    {
-        case AF_INET:
-            return parse<in_addr>(gws4, msg);
-        case AF_INET6:
-            return parse<in6_addr>(gws6, msg);
+        std::visit(
+            [&](auto addr) {
+                if constexpr (std::is_same_v<in_addr, decltype(addr)>)
+                {
+                    gws4.emplace(std::get<unsigned>(*ret), addr);
+                }
+                else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
+                {
+                    gws6.emplace(std::get<unsigned>(*ret), addr);
+                }
+                else
+                {
+                    static_assert(!std::is_same_v<void, decltype(addr)>);
+                }
+            },
+            std::get<InAddrAny>(*ret));
     }
 }
 
diff --git a/src/rtnetlink.cpp b/src/rtnetlink.cpp
new file mode 100644
index 0000000..6e0946f
--- /dev/null
+++ b/src/rtnetlink.cpp
@@ -0,0 +1,54 @@
+#include "rtnetlink.hpp"
+
+#include "netlink.hpp"
+
+#include <linux/rtnetlink.h>
+
+namespace phosphor::network::netlink
+{
+
+template <typename Addr>
+static std::optional<std::tuple<unsigned, InAddrAny>>
+    parse(std::string_view msg)
+{
+    std::optional<unsigned> ifIdx;
+    std::optional<InAddrAny> gw;
+    while (!msg.empty())
+    {
+        auto [hdr, data] = extractRtAttr(msg);
+        switch (hdr.rta_type)
+        {
+            case RTA_OIF:
+                ifIdx.emplace(stdplus::raw::copyFrom<int>(data));
+                break;
+            case RTA_GATEWAY:
+                gw.emplace(stdplus::raw::copyFrom<Addr>(data));
+                break;
+        }
+    }
+    if (ifIdx && gw)
+    {
+        return std::make_tuple(*ifIdx, *gw);
+    }
+    return std::nullopt;
+}
+
+std::optional<std::tuple<unsigned, InAddrAny>>
+    gatewayFromRtm(std::string_view msg)
+{
+    const auto& rtm = extractRtData<rtmsg>(msg);
+    if (rtm.rtm_table != RT_TABLE_MAIN || rtm.rtm_dst_len != 0)
+    {
+        return std::nullopt;
+    }
+    switch (rtm.rtm_family)
+    {
+        case AF_INET:
+            return parse<in_addr>(msg);
+        case AF_INET6:
+            return parse<in6_addr>(msg);
+    }
+    return std::nullopt;
+}
+
+} // namespace phosphor::network::netlink
diff --git a/src/rtnetlink.hpp b/src/rtnetlink.hpp
new file mode 100644
index 0000000..41df902
--- /dev/null
+++ b/src/rtnetlink.hpp
@@ -0,0 +1,12 @@
+#pragma once
+#include "types.hpp"
+
+#include <optional>
+#include <string_view>
+#include <tuple>
+
+namespace phosphor::network::netlink
+{
+std::optional<std::tuple<unsigned, InAddrAny>>
+    gatewayFromRtm(std::string_view msg);
+}