blob: eccf039a16afda347f754e7c57668e4b9077d273 [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
William A. Kennington III2ac26602022-01-24 14:38:32 -08006#include <stdplus/fd/create.hpp>
7#include <stdplus/fd/ops.hpp>
William A. Kennington III12beaad2020-06-13 19:30:41 -07008#include <stdplus/raw.hpp>
Patrick Williams89d734b2023-05-10 07:50:25 -05009
10#include <array>
William A. Kennington IIIcafc1512023-07-25 02:22:32 -070011#include <format>
Patrick Williams89d734b2023-05-10 07:50:25 -050012#include <stdexcept>
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070013#include <system_error>
14
William A. Kennington III0bc39da2022-10-24 16:09:56 -070015using stdplus::raw::Aligned;
16
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070017namespace phosphor
18{
19namespace network
20{
21namespace netlink
22{
23namespace detail
24{
25
William A. Kennington III058f4cf2022-08-25 17:01:48 -070026void processMsg(std::string_view& msgs, bool& done, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070027{
28 // Parse and update the message buffer
William A. Kennington III0bc39da2022-10-24 16:09:56 -070029 const auto& hdr = stdplus::raw::refFrom<nlmsghdr, Aligned>(msgs);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070030 if (hdr.nlmsg_len < sizeof(hdr))
31 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070032 throw std::runtime_error(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -070033 std::format("nlmsg length shorter than header: {} < {}",
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070034 hdr.nlmsg_len, sizeof(hdr)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070035 }
36 if (msgs.size() < hdr.nlmsg_len)
37 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070038 throw std::runtime_error(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -070039 std::format("not enough message for nlmsg: {} < {}", msgs.size(),
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -070040 hdr.nlmsg_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070041 }
42 auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN);
43 msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len));
44
45 // Figure out how to handle the individual message
46 bool doCallback = true;
47 if (hdr.nlmsg_flags & NLM_F_MULTI)
48 {
49 done = false;
50 }
51 if (hdr.nlmsg_type == NLMSG_NOOP)
52 {
53 doCallback = false;
54 }
55 else if (hdr.nlmsg_type == NLMSG_DONE)
56 {
57 if (done)
58 {
59 throw std::runtime_error("Got done for non-multi msg");
60 }
61 done = true;
62 doCallback = false;
63 }
64 else if (hdr.nlmsg_type == NLMSG_ERROR)
65 {
William A. Kennington III0bc39da2022-10-24 16:09:56 -070066 const auto& err = stdplus::raw::refFrom<nlmsgerr, Aligned>(msg);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070067 // This is just an ACK so don't do the callback
68 if (err.error <= 0)
69 {
70 doCallback = false;
71 }
72 }
73 // All multi-msg headers must have the multi flag
74 if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI))
75 {
76 throw std::runtime_error("Got non-multi msg before done");
77 }
78 if (doCallback)
79 {
80 cb(hdr, msg);
81 }
82}
83
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -070084static void requestSend(int sock, void* data, size_t size)
85{
86 sockaddr_nl dst{};
87 dst.nl_family = AF_NETLINK;
88
89 iovec iov{};
90 iov.iov_base = data;
91 iov.iov_len = size;
92
93 msghdr hdr{};
94 hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
95 hdr.msg_namelen = sizeof(dst);
96 hdr.msg_iov = &iov;
97 hdr.msg_iovlen = 1;
98
99 if (sendmsg(sock, &hdr, 0) < 0)
100 {
101 throw std::system_error(errno, std::generic_category(),
102 "netlink sendmsg");
103 }
104}
105
William A. Kennington III2ac26602022-01-24 14:38:32 -0800106static stdplus::ManagedFd makeSocket(int protocol)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700107{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800108 using namespace stdplus::fd;
109
110 auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
111 static_cast<stdplus::fd::SocketProto>(protocol));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700112
113 sockaddr_nl local{};
114 local.nl_family = AF_NETLINK;
William A. Kennington III2ac26602022-01-24 14:38:32 -0800115 bind(sock, local);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700116
117 return sock;
118}
119
William A. Kennington III058f4cf2022-08-25 17:01:48 -0700120void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700121{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800122 auto sock = makeSocket(protocol);
123 requestSend(sock.get(), data, size);
124 receive(sock.get(), cb);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700125}
126
127} // namespace detail
128
William A. Kennington III29418ef2022-11-07 22:59:47 -0800129size_t receive(int sock, ReceiveCallback cb)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700130{
131 // We need to make sure we have enough room for an entire packet otherwise
132 // it gets truncated. The netlink docs guarantee packets will not exceed 8K
133 std::array<char, 8192> buf;
134
135 iovec iov{};
136 iov.iov_base = buf.data();
137 iov.iov_len = buf.size();
138
139 sockaddr_nl from{};
140 from.nl_family = AF_NETLINK;
141
142 msghdr hdr{};
143 hdr.msg_name = &from;
144 hdr.msg_namelen = sizeof(from);
145 hdr.msg_iov = &iov;
146 hdr.msg_iovlen = 1;
147
148 // We only do multiple recvs if we have a MULTI type message
149 bool done = true;
William A. Kennington III29418ef2022-11-07 22:59:47 -0800150 size_t num_msgs = 0;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700151 do
152 {
153 ssize_t recvd = recvmsg(sock, &hdr, 0);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800154 if (recvd < 0 && errno != EAGAIN)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700155 {
156 throw std::system_error(errno, std::generic_category(),
157 "netlink recvmsg");
158 }
William A. Kennington III29418ef2022-11-07 22:59:47 -0800159 if (recvd <= 0)
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700160 {
161 if (!done)
162 {
163 throw std::runtime_error("netlink recvmsg: Got empty payload");
164 }
William A. Kennington III29418ef2022-11-07 22:59:47 -0800165 return num_msgs;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700166 }
167
168 std::string_view msgs(buf.data(), recvd);
169 do
170 {
171 detail::processMsg(msgs, done, cb);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800172 num_msgs++;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700173 } while (!done && !msgs.empty());
174
175 if (done && !msgs.empty())
176 {
177 throw std::runtime_error("Extra unprocessed netlink messages");
178 }
179 } while (!done);
William A. Kennington III29418ef2022-11-07 22:59:47 -0800180 return num_msgs;
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700181}
182
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700183std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
184{
William A. Kennington III0bc39da2022-10-24 16:09:56 -0700185 const auto& hdr = stdplus::raw::refFrom<rtattr, Aligned>(data);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700186 if (hdr.rta_len < RTA_LENGTH(0))
187 {
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700188 throw std::runtime_error(std::format(
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700189 "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700190 }
191 if (data.size() < hdr.rta_len)
192 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700193 throw std::runtime_error(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700194 std::format("not enough message for rtattr: {} < {}", data.size(),
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700195 hdr.rta_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700196 }
197 auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
198 data.remove_prefix(RTA_ALIGN(hdr.rta_len));
199 return {hdr, attr};
200}
201
202} // namespace netlink
203} // namespace network
204} // namespace phosphor