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