blob: 15771616996050919107d7232ee8106d3cfe98cb [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 III29418ef2022-11-07 22:59:47 -0800128size_t receive(int sock, ReceiveCallback cb)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700129{
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;
William A. Kennington III29418ef2022-11-07 22:59:47 -0800149 size_t num_msgs = 0;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700150 do
151 {
152 ssize_t recvd = recvmsg(sock, &hdr, 0);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800153 if (recvd < 0 && errno != EAGAIN)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700154 {
155 throw std::system_error(errno, std::generic_category(),
156 "netlink recvmsg");
157 }
William A. Kennington III29418ef2022-11-07 22:59:47 -0800158 if (recvd <= 0)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700159 {
160 if (!done)
161 {
162 throw std::runtime_error("netlink recvmsg: Got empty payload");
163 }
William A. Kennington III29418ef2022-11-07 22:59:47 -0800164 return num_msgs;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700165 }
166
167 std::string_view msgs(buf.data(), recvd);
168 do
169 {
170 detail::processMsg(msgs, done, cb);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800171 num_msgs++;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700172 } while (!done && !msgs.empty());
173
174 if (done && !msgs.empty())
175 {
176 throw std::runtime_error("Extra unprocessed netlink messages");
177 }
178 } while (!done);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800179 return num_msgs;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700180}
181
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700182std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
183{
William A. Kennington III0bc39da2022-10-24 16:09:56 -0700184 const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700185 if (hdr.rta_len < RTA_LENGTH(0))
186 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700187 throw std::runtime_error(fmt::format(
188 "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700189 }
190 if (data.size() < hdr.rta_len)
191 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700192 throw std::runtime_error(
193 fmt::format("not enough message for rtattr: {} < {}", data.size(),
194 hdr.rta_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700195 }
196 auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
197 data.remove_prefix(RTA_ALIGN(hdr.rta_len));
198 return {hdr, attr};
199}
200
201} // namespace netlink
202} // namespace network
203} // namespace phosphor