blob: 5bc876f84aaebf795b1b844f22525118d33a0951 [file] [log] [blame]
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -07001#include "netlink.hpp"
2
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -07003#include <fmt/format.h>
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -07004#include <linux/netlink.h>
5#include <linux/rtnetlink.h>
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -07006
7#include <array>
8#include <stdexcept>
William A. Kennington III2ac26602022-01-24 14:38:32 -08009#include <stdplus/fd/create.hpp>
10#include <stdplus/fd/ops.hpp>
William A. Kennington III12beaad2020-06-13 19:30:41 -070011#include <stdplus/raw.hpp>
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070012#include <system_error>
13
14namespace phosphor
15{
16namespace network
17{
18namespace netlink
19{
20namespace detail
21{
22
William A. Kennington III058f4cf2022-08-25 17:01:48 -070023void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070024{
25 // Parse and update the message buffer
William A. Kennington III12beaad2020-06-13 19:30:41 -070026 auto hdr = stdplus::raw::copyFrom<nlmsghdr>(msgs);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070027 if (hdr.nlmsg_len < sizeof(hdr))
28 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070029 throw std::runtime_error(
30 fmt::format("nlmsg length shorter than header: {} < {}",
31 hdr.nlmsg_len, sizeof(hdr)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070032 }
33 if (msgs.size() < hdr.nlmsg_len)
34 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070035 throw std::runtime_error(
36 fmt::format("not enough message for nlmsg: {} < {}", msgs.size(),
37 hdr.nlmsg_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070038 }
39 auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN);
40 msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len));
41
42 // Figure out how to handle the individual message
43 bool doCallback = true;
44 if (hdr.nlmsg_flags & NLM_F_MULTI)
45 {
46 done = false;
47 }
48 if (hdr.nlmsg_type == NLMSG_NOOP)
49 {
50 doCallback = false;
51 }
52 else if (hdr.nlmsg_type == NLMSG_DONE)
53 {
54 if (done)
55 {
56 throw std::runtime_error("Got done for non-multi msg");
57 }
58 done = true;
59 doCallback = false;
60 }
61 else if (hdr.nlmsg_type == NLMSG_ERROR)
62 {
William A. Kennington III12beaad2020-06-13 19:30:41 -070063 auto err = stdplus::raw::copyFrom<nlmsgerr>(msg);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070064 // This is just an ACK so don't do the callback
65 if (err.error <= 0)
66 {
67 doCallback = false;
68 }
69 }
70 // All multi-msg headers must have the multi flag
71 if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI))
72 {
73 throw std::runtime_error("Got non-multi msg before done");
74 }
75 if (doCallback)
76 {
77 cb(hdr, msg);
78 }
79}
80
William A. Kennington III058f4cf2022-08-25 17:01:48 -070081static void receive(int sock, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070082{
83 // We need to make sure we have enough room for an entire packet otherwise
84 // it gets truncated. The netlink docs guarantee packets will not exceed 8K
85 std::array<char, 8192> buf;
86
87 iovec iov{};
88 iov.iov_base = buf.data();
89 iov.iov_len = buf.size();
90
91 sockaddr_nl from{};
92 from.nl_family = AF_NETLINK;
93
94 msghdr hdr{};
95 hdr.msg_name = &from;
96 hdr.msg_namelen = sizeof(from);
97 hdr.msg_iov = &iov;
98 hdr.msg_iovlen = 1;
99
100 // We only do multiple recvs if we have a MULTI type message
101 bool done = true;
102 do
103 {
104 ssize_t recvd = recvmsg(sock, &hdr, 0);
105 if (recvd < 0)
106 {
107 throw std::system_error(errno, std::generic_category(),
108 "netlink recvmsg");
109 }
110 if (recvd == 0)
111 {
112 throw std::runtime_error("netlink recvmsg: Got empty payload");
113 }
114
115 std::string_view msgs(buf.data(), recvd);
116 do
117 {
118 processMsg(msgs, done, cb);
119 } while (!done && !msgs.empty());
120
121 if (done && !msgs.empty())
122 {
123 throw std::runtime_error("Extra unprocessed netlink messages");
124 }
125 } while (!done);
126}
127
128static void requestSend(int sock, void* data, size_t size)
129{
130 sockaddr_nl dst{};
131 dst.nl_family = AF_NETLINK;
132
133 iovec iov{};
134 iov.iov_base = data;
135 iov.iov_len = size;
136
137 msghdr hdr{};
138 hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
139 hdr.msg_namelen = sizeof(dst);
140 hdr.msg_iov = &iov;
141 hdr.msg_iovlen = 1;
142
143 if (sendmsg(sock, &hdr, 0) < 0)
144 {
145 throw std::system_error(errno, std::generic_category(),
146 "netlink sendmsg");
147 }
148}
149
William A. Kennington III2ac26602022-01-24 14:38:32 -0800150static stdplus::ManagedFd makeSocket(int protocol)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700151{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800152 using namespace stdplus::fd;
153
154 auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
155 static_cast<stdplus::fd::SocketProto>(protocol));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700156
157 sockaddr_nl local{};
158 local.nl_family = AF_NETLINK;
William A. Kennington III2ac26602022-01-24 14:38:32 -0800159 bind(sock, local);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700160
161 return sock;
162}
163
William A. Kennington III058f4cf2022-08-25 17:01:48 -0700164void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700165{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800166 auto sock = makeSocket(protocol);
167 requestSend(sock.get(), data, size);
168 receive(sock.get(), cb);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700169}
170
171} // namespace detail
172
173std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
174{
William A. Kennington III12beaad2020-06-13 19:30:41 -0700175 auto hdr = stdplus::raw::copyFrom<rtattr>(data);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700176 if (hdr.rta_len < RTA_LENGTH(0))
177 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700178 throw std::runtime_error(fmt::format(
179 "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700180 }
181 if (data.size() < hdr.rta_len)
182 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700183 throw std::runtime_error(
184 fmt::format("not enough message for rtattr: {} < {}", data.size(),
185 hdr.rta_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700186 }
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