1 #include "rtnetlink_server.hpp"
2 
3 #include "types.hpp"
4 
5 #include <linux/netlink.h>
6 #include <linux/rtnetlink.h>
7 #include <netinet/in.h>
8 
9 #include <memory>
10 #include <stdplus/fd/create.hpp>
11 #include <stdplus/fd/ops.hpp>
12 #include <string_view>
13 
14 namespace phosphor
15 {
16 namespace network
17 {
18 
19 extern std::unique_ptr<Timer> refreshObjectTimer;
20 
21 namespace rtnetlink
22 {
23 
24 static bool shouldRefresh(const struct nlmsghdr& hdr, std::string_view data)
25 {
26     switch (hdr.nlmsg_type)
27     {
28         case RTM_NEWADDR:
29         case RTM_DELADDR:
30         case RTM_NEWROUTE:
31         case RTM_DELROUTE:
32         {
33             return true;
34         }
35         case RTM_NEWNEIGH:
36         case RTM_DELNEIGH:
37         {
38             struct ndmsg ndm;
39             if (data.size() < sizeof(ndm))
40             {
41                 return false;
42             }
43             memcpy(&ndm, data.data(), sizeof(ndm));
44             // We only want to refresh for static neighbors
45             return ndm.ndm_state & NUD_PERMANENT;
46         }
47     }
48 
49     return false;
50 }
51 
52 /* Call Back for the sd event loop */
53 static void eventHandler(sdeventplus::source::IO&, int fd, uint32_t)
54 {
55     std::array<char, BUFSIZE> buffer = {};
56     int len{};
57 
58     auto netLinkHeader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
59 
60     while ((len = recv(fd, netLinkHeader, buffer.size(), 0)) > 0)
61     {
62         for (; (NLMSG_OK(netLinkHeader, len)) &&
63                (netLinkHeader->nlmsg_type != NLMSG_DONE);
64              netLinkHeader = NLMSG_NEXT(netLinkHeader, len))
65         {
66             std::string_view data(
67                 reinterpret_cast<const char*>(NLMSG_DATA(netLinkHeader)),
68                 netLinkHeader->nlmsg_len - NLMSG_HDRLEN);
69             if (shouldRefresh(*netLinkHeader, data))
70             {
71                 // starting the timer here to make sure that we don't want
72                 // create the child objects multiple times.
73                 if (!refreshObjectTimer->isEnabled())
74                 {
75                     // if start timer throws exception then let the application
76                     // crash
77                     refreshObjectTimer->restartOnce(refreshTimeout);
78                 } // end if
79             }     // end if
80 
81         } // end for
82 
83         buffer.fill('\0');
84 
85         netLinkHeader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
86     } // end while
87 }
88 
89 static stdplus::ManagedFd makeSock()
90 {
91     using namespace stdplus::fd;
92 
93     auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
94                        static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE));
95 
96     sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock));
97 
98     sockaddr_nl local{};
99     local.nl_family = AF_NETLINK;
100     local.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
101                       RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
102     bind(sock, local);
103 
104     return sock;
105 }
106 
107 Server::Server(sdeventplus::Event& event) :
108     sock(makeSock()), io(event, sock.get(), EPOLLIN | EPOLLET, eventHandler)
109 {
110 }
111 
112 } // namespace rtnetlink
113 } // namespace network
114 } // namespace phosphor
115