rtnetlink_server: Avoid refreshing for gateway changes

Instead of refreshing all of the objects, we can just update the
ethernet object that has the gateway route.

Change-Id: Ib1922273b625b1400625c6a851330e3642a8c647
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp
index 94a8b60..c2e3e63 100644
--- a/src/ethernet_interface.cpp
+++ b/src/ethernet_interface.cpp
@@ -944,7 +944,7 @@
     EthernetInterfaceIntf::defaultGateway(gateway);
 
     writeConfigurationFile();
-    manager.reloadConfigs();
+    manager.reloadConfigsNoRefresh();
 
     return gateway;
 }
@@ -973,7 +973,7 @@
     EthernetInterfaceIntf::defaultGateway6(gateway);
 
     writeConfigurationFile();
-    manager.reloadConfigs();
+    manager.reloadConfigsNoRefresh();
 
     return gateway;
 }
diff --git a/src/network_manager_main.cpp b/src/network_manager_main.cpp
index 80276b3..1c2e38e 100644
--- a/src/network_manager_main.cpp
+++ b/src/network_manager_main.cpp
@@ -292,7 +292,7 @@
     manager = std::make_unique<Manager>(bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR);
 
     // RTNETLINK event handler
-    netlink::Server svr(event);
+    netlink::Server svr(event, *manager);
 
 #ifdef SYNC_MAC_FROM_INVENTORY
     std::ifstream in(configFile);
diff --git a/src/rtnetlink_server.cpp b/src/rtnetlink_server.cpp
index 185589e..880eba1 100644
--- a/src/rtnetlink_server.cpp
+++ b/src/rtnetlink_server.cpp
@@ -1,6 +1,8 @@
 #include "rtnetlink_server.hpp"
 
 #include "netlink.hpp"
+#include "network_manager.hpp"
+#include "rtnetlink.hpp"
 #include "types.hpp"
 
 #include <linux/netlink.h>
@@ -8,6 +10,7 @@
 #include <netinet/in.h>
 
 #include <memory>
+#include <phosphor-logging/log.hpp>
 #include <stdplus/fd/create.hpp>
 #include <stdplus/fd/ops.hpp>
 #include <string_view>
@@ -22,6 +25,10 @@
 namespace netlink
 {
 
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+
 static bool shouldRefresh(const struct nlmsghdr& hdr,
                           std::string_view data) noexcept
 {
@@ -31,8 +38,6 @@
         case RTM_DELLINK:
         case RTM_NEWADDR:
         case RTM_DELADDR:
-        case RTM_NEWROUTE:
-        case RTM_DELROUTE:
             return true;
         case RTM_NEWNEIGH:
         case RTM_DELNEIGH:
@@ -49,17 +54,78 @@
     return false;
 }
 
-static void handler(const nlmsghdr& hdr, std::string_view data)
+static void rthandler(Manager& m, bool n, std::string_view data)
+{
+    auto ret = netlink::gatewayFromRtm(data);
+    if (!ret)
+    {
+        return;
+    }
+    auto ifIdx = std::get<unsigned>(*ret);
+    auto it = m.interfacesByIdx.find(ifIdx);
+    if (it == m.interfacesByIdx.end())
+    {
+        auto msg = fmt::format("Interface `{}` not found for route", ifIdx);
+        log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifIdx));
+        return;
+    }
+    std::visit(
+        [&](auto addr) {
+            if constexpr (std::is_same_v<in_addr, decltype(addr)>)
+            {
+                if (n)
+                {
+                    it->second->EthernetInterfaceIntf::defaultGateway(
+                        std::to_string(addr));
+                }
+                else if (it->second->defaultGateway() == std::to_string(addr))
+                {
+                    it->second->EthernetInterfaceIntf::defaultGateway("");
+                }
+            }
+            else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
+            {
+                if (n)
+                {
+                    it->second->EthernetInterfaceIntf::defaultGateway6(
+                        std::to_string(addr));
+                }
+                else if (it->second->defaultGateway6() == std::to_string(addr))
+                {
+                    it->second->EthernetInterfaceIntf::defaultGateway6("");
+                }
+            }
+            else
+            {
+                static_assert(!std::is_same_v<void, decltype(addr)>);
+            }
+        },
+        std::get<InAddrAny>(*ret));
+}
+
+static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data)
 {
     if (shouldRefresh(hdr, data) && !refreshObjectTimer->isEnabled())
     {
         refreshObjectTimer->restartOnce(refreshTimeout);
     }
+    switch (hdr.nlmsg_type)
+    {
+        case RTM_NEWROUTE:
+            rthandler(m, true, data);
+            break;
+        case RTM_DELROUTE:
+            rthandler(m, false, data);
+            break;
+    }
 }
 
-static void eventHandler(sdeventplus::source::IO&, int fd, uint32_t)
+static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t)
 {
-    while (receive(fd, handler) > 0)
+    auto cb = [&](auto&&... args) {
+        return handler(m, std::forward<decltype(args)>(args)...);
+    };
+    while (receive(fd, cb) > 0)
         ;
 }
 
@@ -81,8 +147,11 @@
     return sock;
 }
 
-Server::Server(sdeventplus::Event& event) :
-    sock(makeSock()), io(event, sock.get(), EPOLLIN | EPOLLET, eventHandler)
+Server::Server(sdeventplus::Event& event, Manager& manager) :
+    sock(makeSock()),
+    io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) {
+        return eventHandler(manager, std::forward<decltype(args)>(args)...);
+    })
 {
 }
 
diff --git a/src/rtnetlink_server.hpp b/src/rtnetlink_server.hpp
index 066f19c..49e32ea 100644
--- a/src/rtnetlink_server.hpp
+++ b/src/rtnetlink_server.hpp
@@ -7,6 +7,7 @@
 {
 namespace network
 {
+class Manager;
 namespace netlink
 {
 
@@ -26,9 +27,9 @@
      *  @details Sets up the server to handle incoming RTNETLINK events
      *
      *  @param[in] eventPtr - Unique ptr reference to sd_event.
-     *  @param[in] socket - netlink socket.
+     *  @param[in] manager  - The network manager that receives updates
      */
-    Server(sdeventplus::Event& event);
+    Server(sdeventplus::Event& event, Manager& manager);
 
     /** @brief Gets the socket associated with this netlink server */
     inline stdplus::Fd& getSock()