1 #include "rtnetlink_server.hpp"
2 
3 #include "netlink.hpp"
4 #include "network_manager.hpp"
5 #include "rtnetlink.hpp"
6 #include "types.hpp"
7 
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10 #include <netinet/in.h>
11 
12 #include <memory>
13 #include <phosphor-logging/log.hpp>
14 #include <stdplus/fd/create.hpp>
15 #include <stdplus/fd/ops.hpp>
16 #include <string_view>
17 
18 namespace phosphor
19 {
20 namespace network
21 {
22 
23 extern std::unique_ptr<Timer> refreshObjectTimer;
24 
25 namespace netlink
26 {
27 
28 using phosphor::logging::entry;
29 using phosphor::logging::level;
30 using phosphor::logging::log;
31 
32 static bool shouldRefresh(const struct nlmsghdr& hdr, std::string_view) noexcept
33 {
34     switch (hdr.nlmsg_type)
35     {
36         case RTM_NEWLINK:
37         case RTM_DELLINK:
38             return true;
39     }
40     return false;
41 }
42 
43 static void rthandler(Manager& m, bool n, std::string_view data)
44 {
45     auto ret = netlink::gatewayFromRtm(data);
46     if (!ret)
47     {
48         return;
49     }
50     auto ifIdx = std::get<unsigned>(*ret);
51     auto it = m.interfacesByIdx.find(ifIdx);
52     if (it == m.interfacesByIdx.end())
53     {
54         auto msg = fmt::format("Interface `{}` not found for route", ifIdx);
55         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifIdx));
56         return;
57     }
58     std::visit(
59         [&](auto addr) {
60             if constexpr (std::is_same_v<in_addr, decltype(addr)>)
61             {
62                 if (n)
63                 {
64                     it->second->EthernetInterfaceIntf::defaultGateway(
65                         std::to_string(addr));
66                 }
67                 else if (it->second->defaultGateway() == std::to_string(addr))
68                 {
69                     it->second->EthernetInterfaceIntf::defaultGateway("");
70                 }
71             }
72             else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
73             {
74                 if (n)
75                 {
76                     it->second->EthernetInterfaceIntf::defaultGateway6(
77                         std::to_string(addr));
78                 }
79                 else if (it->second->defaultGateway6() == std::to_string(addr))
80                 {
81                     it->second->EthernetInterfaceIntf::defaultGateway6("");
82                 }
83             }
84             else
85             {
86                 static_assert(!std::is_same_v<void, decltype(addr)>);
87             }
88         },
89         std::get<InAddrAny>(*ret));
90 }
91 
92 static void addrhandler(Manager& m, bool n, std::string_view data)
93 {
94     auto info = netlink::addrFromRtm(data);
95     auto it = m.interfacesByIdx.find(info.ifidx);
96     if (it == m.interfacesByIdx.end())
97     {
98         auto msg = fmt::format("Interface `{}` not found for addr", info.ifidx);
99         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", info.ifidx));
100         return;
101     }
102     if (n)
103     {
104         it->second->addAddr(info);
105     }
106     else
107     {
108         it->second->addrs.erase(info.ifaddr);
109     }
110 }
111 
112 static void neighhandler(Manager& m, bool n, std::string_view data)
113 {
114     auto info = netlink::neighFromRtm(data);
115     auto it = m.interfacesByIdx.find(info.ifidx);
116     if (it == m.interfacesByIdx.end())
117     {
118         auto msg = fmt::format("Interface `{}` not found for addr", info.ifidx);
119         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", info.ifidx));
120         return;
121     }
122     if (n)
123     {
124         it->second->addStaticNeigh(info);
125     }
126     else if (info.addr)
127     {
128         it->second->staticNeighbors.erase(*info.addr);
129     }
130 }
131 
132 static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data)
133 {
134     if (shouldRefresh(hdr, data) && !refreshObjectTimer->isEnabled())
135     {
136         refreshObjectTimer->restartOnce(refreshTimeout);
137     }
138     try
139     {
140         switch (hdr.nlmsg_type)
141         {
142             case RTM_NEWROUTE:
143                 rthandler(m, true, data);
144                 break;
145             case RTM_DELROUTE:
146                 rthandler(m, false, data);
147                 break;
148             case RTM_NEWADDR:
149                 addrhandler(m, true, data);
150                 break;
151             case RTM_DELADDR:
152                 addrhandler(m, false, data);
153                 break;
154             case RTM_NEWNEIGH:
155                 neighhandler(m, true, data);
156                 break;
157             case RTM_DELNEIGH:
158                 neighhandler(m, false, data);
159                 break;
160         }
161     }
162     catch (const std::exception& e)
163     {
164         auto msg = fmt::format("Failed parsing netlink event: {}", e.what());
165         log<level::ERR>(msg.c_str());
166     }
167 }
168 
169 static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t)
170 {
171     auto cb = [&](auto&&... args) {
172         return handler(m, std::forward<decltype(args)>(args)...);
173     };
174     while (receive(fd, cb) > 0)
175         ;
176 }
177 
178 static stdplus::ManagedFd makeSock()
179 {
180     using namespace stdplus::fd;
181 
182     auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
183                        static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE));
184 
185     sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock));
186 
187     sockaddr_nl local{};
188     local.nl_family = AF_NETLINK;
189     local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
190                       RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
191     bind(sock, local);
192 
193     return sock;
194 }
195 
196 Server::Server(sdeventplus::Event& event, Manager& manager) :
197     sock(makeSock()),
198     io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) {
199         return eventHandler(manager, std::forward<decltype(args)>(args)...);
200     })
201 {
202 }
203 
204 } // namespace netlink
205 } // namespace network
206 } // namespace phosphor
207