1 #include "rtnetlink_server.hpp"
2 
3 #include "netlink.hpp"
4 #include "network_manager.hpp"
5 #include "rtnetlink.hpp"
6 
7 #include <linux/netlink.h>
8 #include <linux/rtnetlink.h>
9 
10 #include <phosphor-logging/lg2.hpp>
11 #include <stdplus/fd/create.hpp>
12 #include <stdplus/fd/ops.hpp>
13 
14 namespace phosphor::network::netlink
15 {
16 
17 inline void rthandler(std::string_view data, auto&& cb)
18 {
19     auto ret = gatewayFromRtm(data);
20     if (!ret)
21     {
22         return;
23     }
24     cb(std::get<unsigned>(*ret), std::get<stdplus::InAnyAddr>(*ret));
25 }
26 
27 static unsigned getIfIdx(const nlmsghdr& hdr, std::string_view data)
28 {
29     switch (hdr.nlmsg_type)
30     {
31         case RTM_NEWLINK:
32         case RTM_DELLINK:
33             return extractRtData<ifinfomsg>(data).ifi_index;
34         case RTM_NEWADDR:
35         case RTM_DELADDR:
36             return extractRtData<ifaddrmsg>(data).ifa_index;
37         case RTM_NEWNEIGH:
38         case RTM_DELNEIGH:
39             return extractRtData<ndmsg>(data).ndm_ifindex;
40     }
41     throw std::runtime_error("Unknown nlmsg_type");
42 }
43 
44 static void handler(Manager& m, const nlmsghdr& hdr, std::string_view data)
45 {
46     try
47     {
48         switch (hdr.nlmsg_type)
49         {
50             case RTM_NEWLINK:
51                 m.addInterface(intfFromRtm(data));
52                 break;
53             case RTM_DELLINK:
54                 m.removeInterface(intfFromRtm(data));
55                 break;
56             case RTM_NEWROUTE:
57                 rthandler(data, [&](auto ifidx, auto addr) {
58                     m.addDefGw(ifidx, addr);
59                 });
60                 break;
61             case RTM_DELROUTE:
62                 rthandler(data, [&](auto ifidx, auto addr) {
63                     m.removeDefGw(ifidx, addr);
64                 });
65                 break;
66             case RTM_NEWADDR:
67                 m.addAddress(addrFromRtm(data));
68                 break;
69             case RTM_DELADDR:
70                 m.removeAddress(addrFromRtm(data));
71                 break;
72             case RTM_NEWNEIGH:
73                 m.addNeighbor(neighFromRtm(data));
74                 break;
75             case RTM_DELNEIGH:
76                 m.removeNeighbor(neighFromRtm(data));
77                 break;
78         }
79     }
80     catch (const std::exception& e)
81     {
82         try
83         {
84             if (m.ignoredIntf.contains(getIfIdx(hdr, data)))
85             {
86                 // We don't want to log errors for ignored interfaces
87                 return;
88             }
89         }
90         catch (...)
91         {}
92         lg2::error("Failed handling netlink event: {ERROR}", "ERROR", e);
93     }
94 }
95 
96 static void eventHandler(Manager& m, sdeventplus::source::IO&, int fd, uint32_t)
97 {
98     auto cb = [&](auto&&... args) {
99         return handler(m, std::forward<decltype(args)>(args)...);
100     };
101     while (receive(fd, cb) > 0)
102         ;
103 }
104 
105 static stdplus::ManagedFd makeSock()
106 {
107     using namespace stdplus::fd;
108 
109     auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
110                        static_cast<stdplus::fd::SocketProto>(NETLINK_ROUTE));
111 
112     sock.fcntlSetfl(sock.fcntlGetfl().set(FileFlag::NonBlock));
113 
114     sockaddr_nl local{};
115     local.nl_family = AF_NETLINK;
116     local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
117                       RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
118     bind(sock, local);
119 
120     return sock;
121 }
122 
123 Server::Server(sdeventplus::Event& event, Manager& manager) :
124     sock(makeSock()),
125     io(event, sock.get(), EPOLLIN | EPOLLET, [&](auto&&... args) {
126         return eventHandler(manager, std::forward<decltype(args)>(args)...);
127     })
128 {
129     auto cb = [&](const nlmsghdr& hdr, std::string_view data) {
130         handler(manager, hdr, data);
131     };
132     performRequest(NETLINK_ROUTE, RTM_GETLINK, NLM_F_DUMP, ifinfomsg{}, cb);
133     performRequest(NETLINK_ROUTE, RTM_GETADDR, NLM_F_DUMP, ifaddrmsg{}, cb);
134     performRequest(NETLINK_ROUTE, RTM_GETROUTE, NLM_F_DUMP, rtmsg{}, cb);
135     performRequest(NETLINK_ROUTE, RTM_GETNEIGH, NLM_F_DUMP, ndmsg{}, cb);
136 }
137 
138 } // namespace phosphor::network::netlink
139