#include "rtnetlink_server.hpp" #include "netlink.hpp" #include "network_manager.hpp" #include "rtnetlink.hpp" #include "types.hpp" #include #include #include #include #include #include #include #include namespace phosphor { namespace network { extern std::unique_ptr refreshObjectTimer; namespace netlink { using phosphor::logging::entry; using phosphor::logging::level; using phosphor::logging::log; static bool shouldRefresh(const struct nlmsghdr& hdr, std::string_view) noexcept { switch (hdr.nlmsg_type) { case RTM_NEWLINK: case RTM_DELLINK: return true; } return false; } static void rthandler(Manager& m, bool n, std::string_view data) { auto ret = netlink::gatewayFromRtm(data); if (!ret) { return; } auto ifIdx = std::get(*ret); auto it = m.interfacesByIdx.find(ifIdx); if (it == m.interfacesByIdx.end()) { auto msg = fmt::format("Interface `{}` not found for route", ifIdx); log(msg.c_str(), entry("IFIDX=%u", ifIdx)); return; } std::visit( [&](auto addr) { if constexpr (std::is_same_v) { 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) { 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); } }, std::get(*ret)); } static void addrhandler(Manager& m, bool n, std::string_view data) { auto info = netlink::addrFromRtm(data); auto it = m.interfacesByIdx.find(info.ifidx); if (it == m.interfacesByIdx.end()) { auto msg = fmt::format("Interface `{}` not found for addr", info.ifidx); log(msg.c_str(), entry("IFIDX=%u", info.ifidx)); return; } if (n) { it->second->addAddr(info); } else { it->second->addrs.erase(info.ifaddr); } } static void neighhandler(Manager& m, bool n, std::string_view data) { auto info = netlink::neighFromRtm(data); auto it = m.interfacesByIdx.find(info.ifidx); if (it == m.interfacesByIdx.end()) { auto msg = fmt::format("Interface `{}` not found for addr", info.ifidx); log(msg.c_str(), entry("IFIDX=%u", info.ifidx)); return; } if (n) { it->second->addStaticNeigh(info); } else if (info.addr) { it->second->staticNeighbors.erase(*info.addr); } } static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data) { if (shouldRefresh(hdr, data) && !refreshObjectTimer->isEnabled()) { refreshObjectTimer->restartOnce(refreshTimeout); } try { switch (hdr.nlmsg_type) { case RTM_NEWROUTE: rthandler(m, true, data); break; case RTM_DELROUTE: rthandler(m, false, data); break; case RTM_NEWADDR: addrhandler(m, true, data); break; case RTM_DELADDR: addrhandler(m, false, data); break; case RTM_NEWNEIGH: neighhandler(m, true, data); break; case RTM_DELNEIGH: neighhandler(m, false, data); break; } } catch (const std::exception& e) { auto msg = fmt::format("Failed parsing netlink event: {}", e.what()); log(msg.c_str()); } } static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t) { auto cb = [&](auto&&... args) { return handler(m, std::forward(args)...); }; while (receive(fd, cb) > 0) ; } static stdplus::ManagedFd makeSock() { using namespace stdplus::fd; auto sock = socket(SocketDomain::Netlink, SocketType::Raw, static_cast(NETLINK_ROUTE)); sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock)); sockaddr_nl local{}; local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH; bind(sock, local); return sock; } Server::Server(sdeventplus::Event& event, Manager& manager) : sock(makeSock()), io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) { return eventHandler(manager, std::forward(args)...); }) { } } // namespace netlink } // namespace network } // namespace phosphor