blob: ed1278f849e75480d76123d31c63a6c76ec6b457 [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 IIIc920bdb2019-04-19 14:23:06 -070081static void requestSend(int sock, void* data, size_t size)
82{
83 sockaddr_nl dst{};
84 dst.nl_family = AF_NETLINK;
85
86 iovec iov{};
87 iov.iov_base = data;
88 iov.iov_len = size;
89
90 msghdr hdr{};
91 hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
92 hdr.msg_namelen = sizeof(dst);
93 hdr.msg_iov = &iov;
94 hdr.msg_iovlen = 1;
95
96 if (sendmsg(sock, &hdr, 0) < 0)
97 {
98 throw std::system_error(errno, std::generic_category(),
99 "netlink sendmsg");
100 }
101}
102
William A. Kennington III2ac26602022-01-24 14:38:32 -0800103static stdplus::ManagedFd makeSocket(int protocol)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700104{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800105 using namespace stdplus::fd;
106
107 auto sock = socket(SocketDomain::Netlink, SocketType::Raw,
108 static_cast<stdplus::fd::SocketProto>(protocol));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700109
110 sockaddr_nl local{};
111 local.nl_family = AF_NETLINK;
William A. Kennington III2ac26602022-01-24 14:38:32 -0800112 bind(sock, local);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700113
114 return sock;
115}
116
William A. Kennington III058f4cf2022-08-25 17:01:48 -0700117void performRequest(int protocol, void* data, size_t size, ReceiveCallback cb)
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700118{
William A. Kennington III2ac26602022-01-24 14:38:32 -0800119 auto sock = makeSocket(protocol);
120 requestSend(sock.get(), data, size);
121 receive(sock.get(), cb);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700122}
123
124} // namespace detail
125
William A. Kennington III5f165dc2022-10-24 15:58:55 -0700126void receive(int sock, ReceiveCallback cb)
127{
128 // We need to make sure we have enough room for an entire packet otherwise
129 // it gets truncated. The netlink docs guarantee packets will not exceed 8K
130 std::array<char, 8192> buf;
131
132 iovec iov{};
133 iov.iov_base = buf.data();
134 iov.iov_len = buf.size();
135
136 sockaddr_nl from{};
137 from.nl_family = AF_NETLINK;
138
139 msghdr hdr{};
140 hdr.msg_name = &from;
141 hdr.msg_namelen = sizeof(from);
142 hdr.msg_iov = &iov;
143 hdr.msg_iovlen = 1;
144
145 // We only do multiple recvs if we have a MULTI type message
146 bool done = true;
147 do
148 {
149 ssize_t recvd = recvmsg(sock, &hdr, 0);
150 if (recvd < 0)
151 {
152 throw std::system_error(errno, std::generic_category(),
153 "netlink recvmsg");
154 }
155 if (recvd == 0)
156 {
157 if (!done)
158 {
159 throw std::runtime_error("netlink recvmsg: Got empty payload");
160 }
161 return;
162 }
163
164 std::string_view msgs(buf.data(), recvd);
165 do
166 {
167 detail::processMsg(msgs, done, cb);
168 } while (!done && !msgs.empty());
169
170 if (done && !msgs.empty())
171 {
172 throw std::runtime_error("Extra unprocessed netlink messages");
173 }
174 } while (!done);
175}
176
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700177std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
178{
William A. Kennington III12beaad2020-06-13 19:30:41 -0700179 auto hdr = stdplus::raw::copyFrom<rtattr>(data);
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700180 if (hdr.rta_len < RTA_LENGTH(0))
181 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700182 throw std::runtime_error(fmt::format(
183 "rtattr shorter than header: {} < {}", hdr.rta_len, RTA_LENGTH(0)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700184 }
185 if (data.size() < hdr.rta_len)
186 {
William A. Kennington IIIf174bcc2022-10-23 14:29:42 -0700187 throw std::runtime_error(
188 fmt::format("not enough message for rtattr: {} < {}", data.size(),
189 hdr.rta_len));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700190 }
191 auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
192 data.remove_prefix(RTA_ALIGN(hdr.rta_len));
193 return {hdr, attr};
194}
195
196} // namespace netlink
197} // namespace network
198} // namespace phosphor