xref: /openbmc/phosphor-networkd/src/rtnetlink_server.cpp (revision a84269022678260112d508424f5aeb617ceb3c88)
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,
33                           std::string_view data) noexcept
34 {
35     switch (hdr.nlmsg_type)
36     {
37         case RTM_NEWLINK:
38         case RTM_DELLINK:
39             return true;
40         case RTM_NEWNEIGH:
41         case RTM_DELNEIGH:
42         {
43             if (data.size() < sizeof(ndmsg))
44             {
45                 return false;
46             }
47             const auto& ndm = *reinterpret_cast<const ndmsg*>(data.data());
48             // We only want to refresh for static neighbors
49             return ndm.ndm_state & NUD_PERMANENT;
50         }
51     }
52     return false;
53 }
54 
55 static void rthandler(Manager& m, bool n, std::string_view data)
56 {
57     auto ret = netlink::gatewayFromRtm(data);
58     if (!ret)
59     {
60         return;
61     }
62     auto ifIdx = std::get<unsigned>(*ret);
63     auto it = m.interfacesByIdx.find(ifIdx);
64     if (it == m.interfacesByIdx.end())
65     {
66         auto msg = fmt::format("Interface `{}` not found for route", ifIdx);
67         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", ifIdx));
68         return;
69     }
70     std::visit(
71         [&](auto addr) {
72             if constexpr (std::is_same_v<in_addr, decltype(addr)>)
73             {
74                 if (n)
75                 {
76                     it->second->EthernetInterfaceIntf::defaultGateway(
77                         std::to_string(addr));
78                 }
79                 else if (it->second->defaultGateway() == std::to_string(addr))
80                 {
81                     it->second->EthernetInterfaceIntf::defaultGateway("");
82                 }
83             }
84             else if constexpr (std::is_same_v<in6_addr, decltype(addr)>)
85             {
86                 if (n)
87                 {
88                     it->second->EthernetInterfaceIntf::defaultGateway6(
89                         std::to_string(addr));
90                 }
91                 else if (it->second->defaultGateway6() == std::to_string(addr))
92                 {
93                     it->second->EthernetInterfaceIntf::defaultGateway6("");
94                 }
95             }
96             else
97             {
98                 static_assert(!std::is_same_v<void, decltype(addr)>);
99             }
100         },
101         std::get<InAddrAny>(*ret));
102 }
103 
104 static void addrhandler(Manager& m, bool n, std::string_view data)
105 {
106     auto info = netlink::addrFromRtm(data);
107     auto it = m.interfacesByIdx.find(info.ifidx);
108     if (it == m.interfacesByIdx.end())
109     {
110         auto msg = fmt::format("Interface `{}` not found for addr", info.ifidx);
111         log<level::ERR>(msg.c_str(), entry("IFIDX=%u", info.ifidx));
112         return;
113     }
114     if (n)
115     {
116         it->second->addAddr(info);
117     }
118     else
119     {
120         it->second->addrs.erase(info.ifaddr);
121     }
122 }
123 
124 static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data)
125 {
126     if (shouldRefresh(hdr, data) && !refreshObjectTimer->isEnabled())
127     {
128         refreshObjectTimer->restartOnce(refreshTimeout);
129     }
130     switch (hdr.nlmsg_type)
131     {
132         case RTM_NEWROUTE:
133             rthandler(m, true, data);
134             break;
135         case RTM_DELROUTE:
136             rthandler(m, false, data);
137             break;
138         case RTM_NEWADDR:
139             addrhandler(m, true, data);
140             break;
141         case RTM_DELADDR:
142             addrhandler(m, false, data);
143             break;
144     }
145 }
146 
147 static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t)
148 {
149     auto cb = [&](auto&&... args) {
150         return handler(m, std::forward<decltype(args)>(args)...);
151     };
152     while (receive(fd, cb) > 0)
153         ;
154 }
155 
156 static stdplus::ManagedFd makeSock()
157 {
158     using namespace stdplus::fd;
159 
160     auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
161                        static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE));
162 
163     sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock));
164 
165     sockaddr_nl local{};
166     local.nl_family = AF_NETLINK;
167     local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
168                       RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
169     bind(sock, local);
170 
171     return sock;
172 }
173 
174 Server::Server(sdeventplus::Event& event, Manager& manager) :
175     sock(makeSock()),
176     io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) {
177         return eventHandler(manager, std::forward<decltype(args)>(args)...);
178     })
179 {
180 }
181 
182 } // namespace netlink
183 } // namespace network
184 } // namespace phosphor
185