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