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