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 inline void rthandler(std::string_view data, auto&& cb)
44 {
45     auto ret = gatewayFromRtm(data);
46     if (!ret)
47     {
48         return;
49     }
50     cb(std::get<unsigned>(*ret), std::get<InAddrAny>(*ret));
51 }
52 
53 static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data)
54 {
55     if (shouldRefresh(hdr, data) && !refreshObjectTimer->isEnabled())
56     {
57         refreshObjectTimer->restartOnce(refreshTimeout);
58     }
59     try
60     {
61         switch (hdr.nlmsg_type)
62         {
63             case RTM_NEWROUTE:
64                 rthandler(data, [&](auto ifidx, auto addr) {
65                     m.addDefGw(ifidx, addr);
66                 });
67                 break;
68             case RTM_DELROUTE:
69                 rthandler(data, [&](auto ifidx, auto addr) {
70                     m.removeDefGw(ifidx, addr);
71                 });
72                 break;
73             case RTM_NEWADDR:
74                 m.addAddress(addrFromRtm(data));
75                 break;
76             case RTM_DELADDR:
77                 m.removeAddress(addrFromRtm(data));
78                 break;
79             case RTM_NEWNEIGH:
80                 m.addNeighbor(neighFromRtm(data));
81                 break;
82             case RTM_DELNEIGH:
83                 m.removeNeighbor(neighFromRtm(data));
84                 break;
85         }
86     }
87     catch (const std::exception& e)
88     {
89         auto msg = fmt::format("Failed handling netlink event: {}", e.what());
90         log<level::ERR>(msg.c_str(), entry("ERROR=%s", e.what()));
91     }
92 }
93 
94 static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t)
95 {
96     auto cb = [&](auto&&... args) {
97         return handler(m, std::forward<decltype(args)>(args)...);
98     };
99     while (receive(fd, cb) > 0)
100         ;
101 }
102 
103 static stdplus::ManagedFd makeSock()
104 {
105     using namespace stdplus::fd;
106 
107     auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
108                        static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE));
109 
110     sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock));
111 
112     sockaddr_nl local{};
113     local.nl_family = AF_NETLINK;
114     local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
115                       RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
116     bind(sock, local);
117 
118     return sock;
119 }
120 
121 Server::Server(sdeventplus::Event& event, Manager& manager) :
122     sock(makeSock()),
123     io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) {
124         return eventHandler(manager, std::forward<decltype(args)>(args)...);
125     })
126 {
127 }
128 
129 } // namespace netlink
130 } // namespace network
131 } // namespace phosphor
132