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