1 #include "rtnetlink_server.hpp" 2 3 #include <linux/netlink.h> 4 #include <linux/rtnetlink.h> 5 #include <netinet/in.h> 6 7 #include <memory> 8 #include <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <stdplus/fd/create.hpp> 11 #include <stdplus/fd/ops.hpp> 12 #include <stdplus/signal.hpp> 13 #include <string_view> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 namespace phosphor 17 { 18 namespace network 19 { 20 21 extern std::unique_ptr<Timer> refreshObjectTimer; 22 23 namespace rtnetlink 24 { 25 26 static bool shouldRefresh(const struct nlmsghdr& hdr, std::string_view data) 27 { 28 switch (hdr.nlmsg_type) 29 { 30 case RTM_NEWADDR: 31 case RTM_DELADDR: 32 case RTM_NEWROUTE: 33 case RTM_DELROUTE: 34 { 35 return true; 36 } 37 case RTM_NEWNEIGH: 38 case RTM_DELNEIGH: 39 { 40 struct ndmsg ndm; 41 if (data.size() < sizeof(ndm)) 42 { 43 return false; 44 } 45 memcpy(&ndm, data.data(), sizeof(ndm)); 46 // We only want to refresh for static neighbors 47 return ndm.ndm_state & NUD_PERMANENT; 48 } 49 } 50 51 return false; 52 } 53 54 /* Call Back for the sd event loop */ 55 static int eventHandler(sd_event_source* /*es*/, int fd, uint32_t /*revents*/, 56 void* /*userdata*/) 57 { 58 std::array<char, phosphor::network::rtnetlink::BUFSIZE> buffer = {}; 59 int len{}; 60 61 auto netLinkHeader = reinterpret_cast<struct nlmsghdr*>(buffer.data()); 62 63 while ((len = recv(fd, netLinkHeader, phosphor::network::rtnetlink::BUFSIZE, 64 0)) > 0) 65 { 66 for (; (NLMSG_OK(netLinkHeader, len)) && 67 (netLinkHeader->nlmsg_type != NLMSG_DONE); 68 netLinkHeader = NLMSG_NEXT(netLinkHeader, len)) 69 { 70 std::string_view data( 71 reinterpret_cast<const char*>(NLMSG_DATA(netLinkHeader)), 72 netLinkHeader->nlmsg_len - NLMSG_HDRLEN); 73 if (shouldRefresh(*netLinkHeader, data)) 74 { 75 // starting the timer here to make sure that we don't want 76 // create the child objects multiple times. 77 if (!refreshObjectTimer->isEnabled()) 78 { 79 // if start timer throws exception then let the application 80 // crash 81 refreshObjectTimer->restartOnce(refreshTimeout); 82 } // end if 83 } // end if 84 85 } // end for 86 87 buffer.fill('\0'); 88 89 netLinkHeader = reinterpret_cast<struct nlmsghdr*>(buffer.data()); 90 } // end while 91 92 return 0; 93 } 94 95 static stdplus::ManagedFd makeSock() 96 { 97 using namespace stdplus::fd; 98 99 auto sock = socket(SocketDomain::Netlink, SocketType::Raw, 100 static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE)); 101 102 sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock)); 103 104 sockaddr_nl local{}; 105 local.nl_family = AF_NETLINK; 106 local.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | 107 RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH; 108 bind(sock, local); 109 110 return sock; 111 } 112 113 Server::Server(EventPtr& eventPtr) : sock(makeSock()) 114 { 115 using namespace phosphor::logging; 116 using InternalFailure = 117 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 118 int r{}; 119 120 /* Let's make use of the default handler and "floating" 121 reference features of sd_event_add_signal() */ 122 123 stdplus::signal::block(SIGTERM); 124 r = sd_event_add_signal(eventPtr.get(), NULL, SIGTERM, NULL, NULL); 125 if (r < 0) 126 { 127 goto finish; 128 } 129 130 stdplus::signal::block(SIGINT); 131 r = sd_event_add_signal(eventPtr.get(), NULL, SIGINT, NULL, NULL); 132 if (r < 0) 133 { 134 goto finish; 135 } 136 137 r = sd_event_add_io(eventPtr.get(), nullptr, sock.get(), EPOLLIN, 138 eventHandler, nullptr); 139 if (r < 0) 140 { 141 goto finish; 142 } 143 144 finish: 145 146 if (r < 0) 147 { 148 log<level::ERR>("Failure Occurred in starting of server:", 149 entry("ERRNO=%d", errno)); 150 elog<InternalFailure>(); 151 } 152 } 153 154 } // namespace rtnetlink 155 } // namespace network 156 } // namespace phosphor 157