xref: /openbmc/phosphor-networkd/src/netlink.cpp (revision 1bbe3d1e3bce52044874294e353e80af9ee706a2)
1*1bbe3d1eSWilliam A. Kennington III #include "netlink.hpp"
2*1bbe3d1eSWilliam A. Kennington III 
3*1bbe3d1eSWilliam A. Kennington III #include "util.hpp"
4*1bbe3d1eSWilliam A. Kennington III 
5*1bbe3d1eSWilliam A. Kennington III #include <linux/netlink.h>
6*1bbe3d1eSWilliam A. Kennington III #include <linux/rtnetlink.h>
7*1bbe3d1eSWilliam A. Kennington III #include <sys/socket.h>
8*1bbe3d1eSWilliam A. Kennington III #include <unistd.h>
9*1bbe3d1eSWilliam A. Kennington III 
10*1bbe3d1eSWilliam A. Kennington III #include <array>
11*1bbe3d1eSWilliam A. Kennington III #include <stdexcept>
12*1bbe3d1eSWilliam A. Kennington III #include <stdplus/raw.hpp>
13*1bbe3d1eSWilliam A. Kennington III #include <system_error>
14*1bbe3d1eSWilliam A. Kennington III 
15*1bbe3d1eSWilliam A. Kennington III namespace phosphor
16*1bbe3d1eSWilliam A. Kennington III {
17*1bbe3d1eSWilliam A. Kennington III namespace network
18*1bbe3d1eSWilliam A. Kennington III {
19*1bbe3d1eSWilliam A. Kennington III namespace netlink
20*1bbe3d1eSWilliam A. Kennington III {
21*1bbe3d1eSWilliam A. Kennington III namespace detail
22*1bbe3d1eSWilliam A. Kennington III {
23*1bbe3d1eSWilliam A. Kennington III 
24*1bbe3d1eSWilliam A. Kennington III void processMsg(std::string_view& msgs, bool& done, const ReceiveCallback& cb)
25*1bbe3d1eSWilliam A. Kennington III {
26*1bbe3d1eSWilliam A. Kennington III     // Parse and update the message buffer
27*1bbe3d1eSWilliam A. Kennington III     auto hdr = stdplus::raw::copyFrom<nlmsghdr>(msgs);
28*1bbe3d1eSWilliam A. Kennington III     if (hdr.nlmsg_len < sizeof(hdr))
29*1bbe3d1eSWilliam A. Kennington III     {
30*1bbe3d1eSWilliam A. Kennington III         throw std::runtime_error("Invalid nlmsg length");
31*1bbe3d1eSWilliam A. Kennington III     }
32*1bbe3d1eSWilliam A. Kennington III     if (msgs.size() < hdr.nlmsg_len)
33*1bbe3d1eSWilliam A. Kennington III     {
34*1bbe3d1eSWilliam A. Kennington III         throw std::runtime_error("Bad nlmsg payload");
35*1bbe3d1eSWilliam A. Kennington III     }
36*1bbe3d1eSWilliam A. Kennington III     auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN);
37*1bbe3d1eSWilliam A. Kennington III     msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len));
38*1bbe3d1eSWilliam A. Kennington III 
39*1bbe3d1eSWilliam A. Kennington III     // Figure out how to handle the individual message
40*1bbe3d1eSWilliam A. Kennington III     bool doCallback = true;
41*1bbe3d1eSWilliam A. Kennington III     if (hdr.nlmsg_flags & NLM_F_MULTI)
42*1bbe3d1eSWilliam A. Kennington III     {
43*1bbe3d1eSWilliam A. Kennington III         done = false;
44*1bbe3d1eSWilliam A. Kennington III     }
45*1bbe3d1eSWilliam A. Kennington III     if (hdr.nlmsg_type == NLMSG_NOOP)
46*1bbe3d1eSWilliam A. Kennington III     {
47*1bbe3d1eSWilliam A. Kennington III         doCallback = false;
48*1bbe3d1eSWilliam A. Kennington III     }
49*1bbe3d1eSWilliam A. Kennington III     else if (hdr.nlmsg_type == NLMSG_DONE)
50*1bbe3d1eSWilliam A. Kennington III     {
51*1bbe3d1eSWilliam A. Kennington III         if (done)
52*1bbe3d1eSWilliam A. Kennington III         {
53*1bbe3d1eSWilliam A. Kennington III             throw std::runtime_error("Got done for non-multi msg");
54*1bbe3d1eSWilliam A. Kennington III         }
55*1bbe3d1eSWilliam A. Kennington III         done = true;
56*1bbe3d1eSWilliam A. Kennington III         doCallback = false;
57*1bbe3d1eSWilliam A. Kennington III     }
58*1bbe3d1eSWilliam A. Kennington III     else if (hdr.nlmsg_type == NLMSG_ERROR)
59*1bbe3d1eSWilliam A. Kennington III     {
60*1bbe3d1eSWilliam A. Kennington III         auto err = stdplus::raw::copyFrom<nlmsgerr>(msg);
61*1bbe3d1eSWilliam A. Kennington III         // This is just an ACK so don't do the callback
62*1bbe3d1eSWilliam A. Kennington III         if (err.error <= 0)
63*1bbe3d1eSWilliam A. Kennington III         {
64*1bbe3d1eSWilliam A. Kennington III             doCallback = false;
65*1bbe3d1eSWilliam A. Kennington III         }
66*1bbe3d1eSWilliam A. Kennington III     }
67*1bbe3d1eSWilliam A. Kennington III     // All multi-msg headers must have the multi flag
68*1bbe3d1eSWilliam A. Kennington III     if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI))
69*1bbe3d1eSWilliam A. Kennington III     {
70*1bbe3d1eSWilliam A. Kennington III         throw std::runtime_error("Got non-multi msg before done");
71*1bbe3d1eSWilliam A. Kennington III     }
72*1bbe3d1eSWilliam A. Kennington III     if (doCallback)
73*1bbe3d1eSWilliam A. Kennington III     {
74*1bbe3d1eSWilliam A. Kennington III         cb(hdr, msg);
75*1bbe3d1eSWilliam A. Kennington III     }
76*1bbe3d1eSWilliam A. Kennington III }
77*1bbe3d1eSWilliam A. Kennington III 
78*1bbe3d1eSWilliam A. Kennington III static void receive(int sock, const ReceiveCallback& cb)
79*1bbe3d1eSWilliam A. Kennington III {
80*1bbe3d1eSWilliam A. Kennington III     // We need to make sure we have enough room for an entire packet otherwise
81*1bbe3d1eSWilliam A. Kennington III     // it gets truncated. The netlink docs guarantee packets will not exceed 8K
82*1bbe3d1eSWilliam A. Kennington III     std::array<char, 8192> buf;
83*1bbe3d1eSWilliam A. Kennington III 
84*1bbe3d1eSWilliam A. Kennington III     iovec iov{};
85*1bbe3d1eSWilliam A. Kennington III     iov.iov_base = buf.data();
86*1bbe3d1eSWilliam A. Kennington III     iov.iov_len = buf.size();
87*1bbe3d1eSWilliam A. Kennington III 
88*1bbe3d1eSWilliam A. Kennington III     sockaddr_nl from{};
89*1bbe3d1eSWilliam A. Kennington III     from.nl_family = AF_NETLINK;
90*1bbe3d1eSWilliam A. Kennington III 
91*1bbe3d1eSWilliam A. Kennington III     msghdr hdr{};
92*1bbe3d1eSWilliam A. Kennington III     hdr.msg_name = &from;
93*1bbe3d1eSWilliam A. Kennington III     hdr.msg_namelen = sizeof(from);
94*1bbe3d1eSWilliam A. Kennington III     hdr.msg_iov = &iov;
95*1bbe3d1eSWilliam A. Kennington III     hdr.msg_iovlen = 1;
96*1bbe3d1eSWilliam A. Kennington III 
97*1bbe3d1eSWilliam A. Kennington III     // We only do multiple recvs if we have a MULTI type message
98*1bbe3d1eSWilliam A. Kennington III     bool done = true;
99*1bbe3d1eSWilliam A. Kennington III     do
100*1bbe3d1eSWilliam A. Kennington III     {
101*1bbe3d1eSWilliam A. Kennington III         ssize_t recvd = recvmsg(sock, &hdr, 0);
102*1bbe3d1eSWilliam A. Kennington III         if (recvd < 0)
103*1bbe3d1eSWilliam A. Kennington III         {
104*1bbe3d1eSWilliam A. Kennington III             throw std::system_error(errno, std::generic_category(),
105*1bbe3d1eSWilliam A. Kennington III                                     "netlink recvmsg");
106*1bbe3d1eSWilliam A. Kennington III         }
107*1bbe3d1eSWilliam A. Kennington III         if (recvd == 0)
108*1bbe3d1eSWilliam A. Kennington III         {
109*1bbe3d1eSWilliam A. Kennington III             throw std::runtime_error("netlink recvmsg: Got empty payload");
110*1bbe3d1eSWilliam A. Kennington III         }
111*1bbe3d1eSWilliam A. Kennington III 
112*1bbe3d1eSWilliam A. Kennington III         std::string_view msgs(buf.data(), recvd);
113*1bbe3d1eSWilliam A. Kennington III         do
114*1bbe3d1eSWilliam A. Kennington III         {
115*1bbe3d1eSWilliam A. Kennington III             processMsg(msgs, done, cb);
116*1bbe3d1eSWilliam A. Kennington III         } while (!done && !msgs.empty());
117*1bbe3d1eSWilliam A. Kennington III 
118*1bbe3d1eSWilliam A. Kennington III         if (done && !msgs.empty())
119*1bbe3d1eSWilliam A. Kennington III         {
120*1bbe3d1eSWilliam A. Kennington III             throw std::runtime_error("Extra unprocessed netlink messages");
121*1bbe3d1eSWilliam A. Kennington III         }
122*1bbe3d1eSWilliam A. Kennington III     } while (!done);
123*1bbe3d1eSWilliam A. Kennington III }
124*1bbe3d1eSWilliam A. Kennington III 
125*1bbe3d1eSWilliam A. Kennington III static void requestSend(int sock, void* data, size_t size)
126*1bbe3d1eSWilliam A. Kennington III {
127*1bbe3d1eSWilliam A. Kennington III     sockaddr_nl dst{};
128*1bbe3d1eSWilliam A. Kennington III     dst.nl_family = AF_NETLINK;
129*1bbe3d1eSWilliam A. Kennington III 
130*1bbe3d1eSWilliam A. Kennington III     iovec iov{};
131*1bbe3d1eSWilliam A. Kennington III     iov.iov_base = data;
132*1bbe3d1eSWilliam A. Kennington III     iov.iov_len = size;
133*1bbe3d1eSWilliam A. Kennington III 
134*1bbe3d1eSWilliam A. Kennington III     msghdr hdr{};
135*1bbe3d1eSWilliam A. Kennington III     hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
136*1bbe3d1eSWilliam A. Kennington III     hdr.msg_namelen = sizeof(dst);
137*1bbe3d1eSWilliam A. Kennington III     hdr.msg_iov = &iov;
138*1bbe3d1eSWilliam A. Kennington III     hdr.msg_iovlen = 1;
139*1bbe3d1eSWilliam A. Kennington III 
140*1bbe3d1eSWilliam A. Kennington III     if (sendmsg(sock, &hdr, 0) < 0)
141*1bbe3d1eSWilliam A. Kennington III     {
142*1bbe3d1eSWilliam A. Kennington III         throw std::system_error(errno, std::generic_category(),
143*1bbe3d1eSWilliam A. Kennington III                                 "netlink sendmsg");
144*1bbe3d1eSWilliam A. Kennington III     }
145*1bbe3d1eSWilliam A. Kennington III }
146*1bbe3d1eSWilliam A. Kennington III 
147*1bbe3d1eSWilliam A. Kennington III static int newRequestSocket(int protocol)
148*1bbe3d1eSWilliam A. Kennington III {
149*1bbe3d1eSWilliam A. Kennington III     int sock = socket(AF_NETLINK, SOCK_RAW, protocol);
150*1bbe3d1eSWilliam A. Kennington III     if (sock < 0)
151*1bbe3d1eSWilliam A. Kennington III     {
152*1bbe3d1eSWilliam A. Kennington III         throw std::system_error(errno, std::generic_category(), "netlink open");
153*1bbe3d1eSWilliam A. Kennington III     }
154*1bbe3d1eSWilliam A. Kennington III 
155*1bbe3d1eSWilliam A. Kennington III     sockaddr_nl local{};
156*1bbe3d1eSWilliam A. Kennington III     local.nl_family = AF_NETLINK;
157*1bbe3d1eSWilliam A. Kennington III     int r = bind(sock, reinterpret_cast<sockaddr*>(&local), sizeof(local));
158*1bbe3d1eSWilliam A. Kennington III     if (r < 0)
159*1bbe3d1eSWilliam A. Kennington III     {
160*1bbe3d1eSWilliam A. Kennington III         close(sock);
161*1bbe3d1eSWilliam A. Kennington III         throw std::system_error(errno, std::generic_category(), "netlink bind");
162*1bbe3d1eSWilliam A. Kennington III     }
163*1bbe3d1eSWilliam A. Kennington III 
164*1bbe3d1eSWilliam A. Kennington III     return sock;
165*1bbe3d1eSWilliam A. Kennington III }
166*1bbe3d1eSWilliam A. Kennington III 
167*1bbe3d1eSWilliam A. Kennington III void performRequest(int protocol, void* data, size_t size,
168*1bbe3d1eSWilliam A. Kennington III                     const ReceiveCallback& cb)
169*1bbe3d1eSWilliam A. Kennington III {
170*1bbe3d1eSWilliam A. Kennington III     Descriptor sock(newRequestSocket(protocol));
171*1bbe3d1eSWilliam A. Kennington III     requestSend(sock(), data, size);
172*1bbe3d1eSWilliam A. Kennington III     receive(sock(), cb);
173*1bbe3d1eSWilliam A. Kennington III }
174*1bbe3d1eSWilliam A. Kennington III 
175*1bbe3d1eSWilliam A. Kennington III } // namespace detail
176*1bbe3d1eSWilliam A. Kennington III 
177*1bbe3d1eSWilliam A. Kennington III std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
178*1bbe3d1eSWilliam A. Kennington III {
179*1bbe3d1eSWilliam A. Kennington III     auto hdr = stdplus::raw::copyFrom<rtattr>(data);
180*1bbe3d1eSWilliam A. Kennington III     if (hdr.rta_len < RTA_LENGTH(0))
181*1bbe3d1eSWilliam A. Kennington III     {
182*1bbe3d1eSWilliam A. Kennington III         throw std::runtime_error("Invalid rtattr length");
183*1bbe3d1eSWilliam A. Kennington III     }
184*1bbe3d1eSWilliam A. Kennington III     if (data.size() < hdr.rta_len)
185*1bbe3d1eSWilliam A. Kennington III     {
186*1bbe3d1eSWilliam A. Kennington III         throw std::runtime_error("Not enough data for rtattr");
187*1bbe3d1eSWilliam A. Kennington III     }
188*1bbe3d1eSWilliam A. Kennington III     auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
189*1bbe3d1eSWilliam A. Kennington III     data.remove_prefix(RTA_ALIGN(hdr.rta_len));
190*1bbe3d1eSWilliam A. Kennington III     return {hdr, attr};
191*1bbe3d1eSWilliam A. Kennington III }
192*1bbe3d1eSWilliam A. Kennington III 
193*1bbe3d1eSWilliam A. Kennington III } // namespace netlink
194*1bbe3d1eSWilliam A. Kennington III } // namespace network
195*1bbe3d1eSWilliam A. Kennington III } // namespace phosphor
196