blob: 5a3dc74ffd9edfb414e3d78d9251f950879ebe62 [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
William A. Kennington III0bc39da2022-10-24 16:09:56 -070014using stdplus::raw::Aligned;
15
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070016namespace phosphor
17{
18namespace network
19{
20namespace netlink
21{
22namespace detail
23{
24
William A. Kennington III058f4cf2022-08-25 17:01:48 -070025void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070026{
27 // Parse and update the message buffer
William A. Kennington III0bc39da2022-10-24 16:09:56 -070028 const auto& hdr = stdplus::raw::refFrom<nlmsghdr, Aligned>(msgs);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070029 if (hdr.nlmsg_len < sizeof(hdr))
30 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070031 throw std::runtime_error(
32 fmt::format("nlmsg length shorter than header: {} < {}",
33 hdr.nlmsg_len, sizeof(hdr)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070034 }
35 if (msgs.size() < hdr.nlmsg_len)
36 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070037 throw std::runtime_error(
38 fmt::format("not enough message for nlmsg: {} < {}", msgs.size(),
39 hdr.nlmsg_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070040 }
41 auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN);
42 msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len));
43
44 // Figure out how to handle the individual message
45 bool doCallback = true;
46 if (hdr.nlmsg_flags & NLM_F_MULTI)
47 {
48 done = false;
49 }
50 if (hdr.nlmsg_type == NLMSG_NOOP)
51 {
52 doCallback = false;
53 }
54 else if (hdr.nlmsg_type == NLMSG_DONE)
55 {
56 if (done)
57 {
58 throw std::runtime_error("Got done for non-multi msg");
59 }
60 done = true;
61 doCallback = false;
62 }
63 else if (hdr.nlmsg_type == NLMSG_ERROR)
64 {
William A. Kennington III0bc39da2022-10-24 16:09:56 -070065 const auto& err = stdplus::raw::refFrom<nlmsgerr, Aligned>(msg);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070066 // This is just an ACK so don't do the callback
67 if (err.error <= 0)
68 {
69 doCallback = false;
70 }
71 }
72 // All multi-msg headers must have the multi flag
73 if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI))
74 {
75 throw std::runtime_error("Got non-multi msg before done");
76 }
77 if (doCallback)
78 {
79 cb(hdr, msg);
80 }
81}
82
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070083static void requestSend(int sock, void* data, size_t size)
84{
85 sockaddr_nl dst{};
86 dst.nl_family = AF_NETLINK;
87
88 iovec iov{};
89 iov.iov_base = data;
90 iov.iov_len = size;
91
92 msghdr hdr{};
93 hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
94 hdr.msg_namelen = sizeof(dst);
95 hdr.msg_iov = &iov;
96 hdr.msg_iovlen = 1;
97
98 if (sendmsg(sock, &hdr, 0) < 0)
99 {
100 throw std::system_error(errno, std::generic_category(),
101 "netlink sendmsg");
102 }
103}
104
William A. Kennington III2ac26602022-01-24 14:38:32 -0800105static stdplus::ManagedFd makeSocket(int protocol)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700106{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800107 using namespace stdplus::fd;
108
109 auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
110 static_cast<stdplus::fd::SocketProto>(protocol));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700111
112 sockaddr_nl local{};
113 local.nl_family = AF_NETLINK;
William A. Kennington III2ac26602022-01-24 14:38:32 -0800114 bind(sock, local);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700115
116 return sock;
117}
118
William A. Kennington III058f4cf2022-08-25 17:01:48 -0700119void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700120{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800121 auto sock = makeSocket(protocol);
122 requestSend(sock.get(), data, size);
123 receive(sock.get(), cb);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700124}
125
126} // namespace detail
127
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700128void receive(int sock, ReceiveCallback cb)
129{
130 // We need to make sure we have enough room for an entire packet otherwise
131 // it gets truncated. The netlink docs guarantee packets will not exceed 8K
132 std::array<char, 8192> buf;
133
134 iovec iov{};
135 iov.iov_base = buf.data();
136 iov.iov_len = buf.size();
137
138 sockaddr_nl from{};
139 from.nl_family = AF_NETLINK;
140
141 msghdr hdr{};
142 hdr.msg_name = &from;
143 hdr.msg_namelen = sizeof(from);
144 hdr.msg_iov = &iov;
145 hdr.msg_iovlen = 1;
146
147 // We only do multiple recvs if we have a MULTI type message
148 bool done = true;
149 do
150 {
151 ssize_t recvd = recvmsg(sock, &hdr, 0);
152 if (recvd < 0)
153 {
154 throw std::system_error(errno, std::generic_category(),
155 "netlink recvmsg");
156 }
157 if (recvd == 0)
158 {
159 if (!done)
160 {
161 throw std::runtime_error("netlink recvmsg: Got empty payload");
162 }
163 return;
164 }
165
166 std::string_view msgs(buf.data(), recvd);
167 do
168 {
169 detail::processMsg(msgs, done, cb);
170 } while (!done && !msgs.empty());
171
172 if (done && !msgs.empty())
173 {
174 throw std::runtime_error("Extra unprocessed netlink messages");
175 }
176 } while (!done);
177}
178
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700179std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
180{
William A. Kennington III0bc39da2022-10-24 16:09:56 -0700181 const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700182 if (hdr.rta_len < RTA_LENGTH(0))
183 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700184 throw std::runtime_error(fmt::format(
185 "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700186 }
187 if (data.size() < hdr.rta_len)
188 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700189 throw std::runtime_error(
190 fmt::format("not enough message for rtattr: {} < {}", data.size(),
191 hdr.rta_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700192 }
193 auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
194 data.remove_prefix(RTA_ALIGN(hdr.rta_len));
195 return {hdr, attr};
196}
197
198} // namespace netlink
199} // namespace network
200} // namespace phosphor