blob: 6e5a198768d2f6e9786258050e4249a14c4cb6f7 [file] [log] [blame]
William A. Kennington III862275a2019-04-22 20:37:08 -07001#include "mock_syscall.hpp"
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -07002#include "netlink.hpp"
3#include "util.hpp"
4
5#include <linux/netlink.h>
6#include <linux/rtnetlink.h>
7
8#include <cstring>
9#include <stdexcept>
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 <string_view>
12
13#include <gtest/gtest.h>
14
15namespace phosphor
16{
17namespace network
18{
19namespace netlink
20{
21namespace detail
22{
23
24TEST(ExtractMsgs, TooSmall)
25{
26 const char buf[] = {'1'};
27 static_assert(sizeof(buf) < sizeof(nlmsghdr));
28 std::string_view data(buf, sizeof(buf));
29
30 size_t cbCalls = 0;
31 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
32 bool done = true;
33 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error);
34 EXPECT_EQ(1, data.size());
35 EXPECT_EQ(0, cbCalls);
36 EXPECT_TRUE(done);
37}
38
39TEST(ExtractMsgs, SmallAttrLen)
40{
41 nlmsghdr hdr{};
42 hdr.nlmsg_len = NLMSG_LENGTH(0) - 1;
43 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
44
45 size_t cbCalls = 0;
46 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
47 bool done = true;
48 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error);
49 EXPECT_EQ(NLMSG_SPACE(0), data.size());
50 EXPECT_EQ(0, cbCalls);
51 EXPECT_TRUE(done);
52}
53
54TEST(ExtractMsgs, LargeAttrLen)
55{
56 nlmsghdr hdr{};
57 hdr.nlmsg_len = NLMSG_LENGTH(0) + 1;
58 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
59
60 size_t cbCalls = 0;
61 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
62 bool done = true;
63 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error);
64 EXPECT_EQ(NLMSG_SPACE(0), data.size());
65 EXPECT_EQ(0, cbCalls);
66 EXPECT_TRUE(done);
67}
68
69TEST(ExtractMsgs, NoopMsg)
70{
71 nlmsghdr hdr{};
72 hdr.nlmsg_len = NLMSG_LENGTH(0);
73 hdr.nlmsg_type = NLMSG_NOOP;
74 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
75
76 size_t cbCalls = 0;
77 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
78 bool done = true;
79 processMsg(data, done, cb);
80 EXPECT_EQ(0, data.size());
81 EXPECT_EQ(0, cbCalls);
82 EXPECT_TRUE(done);
83}
84
85TEST(ExtractMsgs, AckMsg)
86{
87 nlmsgerr ack{};
88 nlmsghdr hdr{};
89 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
90 hdr.nlmsg_type = NLMSG_ERROR;
91 char buf[NLMSG_ALIGN(hdr.nlmsg_len)];
92 std::memcpy(buf, &hdr, sizeof(hdr));
93 std::memcpy(NLMSG_DATA(buf), &ack, sizeof(ack));
94 std::string_view data(reinterpret_cast<char*>(&buf), sizeof(buf));
95
96 size_t cbCalls = 0;
97 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
98 bool done = true;
99 processMsg(data, done, cb);
100 EXPECT_EQ(0, data.size());
101 EXPECT_EQ(0, cbCalls);
102 EXPECT_TRUE(done);
103}
104
105TEST(ExtractMsgs, ErrMsg)
106{
107 nlmsgerr err{};
108 err.error = EINVAL;
109 nlmsghdr hdr{};
110 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(err));
111 hdr.nlmsg_type = NLMSG_ERROR;
112 char buf[NLMSG_ALIGN(hdr.nlmsg_len)];
113 std::memcpy(buf, &hdr, sizeof(hdr));
114 std::memcpy(NLMSG_DATA(buf), &err, sizeof(err));
115 std::string_view data(reinterpret_cast<char*>(&buf), sizeof(buf));
116
117 size_t cbCalls = 0;
118 nlmsghdr hdrOut;
119 std::string_view dataOut;
120 auto cb = [&](const nlmsghdr& hdr, std::string_view data) {
121 hdrOut = hdr;
122 dataOut = data;
123 cbCalls++;
124 };
125 bool done = true;
126 processMsg(data, done, cb);
127 EXPECT_EQ(0, data.size());
128 EXPECT_EQ(1, cbCalls);
William A. Kennington III12beaad2020-06-13 19:30:41 -0700129 EXPECT_TRUE(stdplus::raw::equal(hdr, hdrOut));
130 EXPECT_TRUE(
131 stdplus::raw::equal(err, stdplus::raw::extract<nlmsgerr>(dataOut)));
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700132 EXPECT_EQ(0, dataOut.size());
133 EXPECT_TRUE(done);
134}
135
136TEST(ExtractMsgs, DoneNoMulti)
137{
138 nlmsghdr hdr{};
139 hdr.nlmsg_len = NLMSG_LENGTH(0);
140 hdr.nlmsg_type = NLMSG_DONE;
141 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
142
143 size_t cbCalls = 0;
144 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
145 bool done = true;
146 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error);
147 EXPECT_EQ(0, data.size());
148 EXPECT_EQ(0, cbCalls);
149 EXPECT_TRUE(done);
150}
151
152TEST(ExtractMsg, TwoMultiMsgs)
153{
154 nlmsghdr hdr{};
155 hdr.nlmsg_len = NLMSG_LENGTH(0);
156 hdr.nlmsg_type = RTM_NEWLINK;
157 hdr.nlmsg_flags = NLM_F_MULTI;
158 std::string buf;
159 buf.append(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
160 buf.append(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
161
162 std::string_view data = buf;
163 size_t cbCalls = 0;
164 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
165 bool done = true;
166 processMsg(data, done, cb);
167 EXPECT_EQ(NLMSG_SPACE(0), data.size());
168 EXPECT_EQ(1, cbCalls);
169 EXPECT_FALSE(done);
170
171 processMsg(data, done, cb);
172 EXPECT_EQ(0, data.size());
173 EXPECT_EQ(2, cbCalls);
174 EXPECT_FALSE(done);
175}
176
177TEST(ExtractMsgs, MultiMsgValid)
178{
179 nlmsghdr hdr{};
180 hdr.nlmsg_len = NLMSG_LENGTH(0);
181 hdr.nlmsg_type = RTM_NEWLINK;
182 hdr.nlmsg_flags = NLM_F_MULTI;
183 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
184
185 size_t cbCalls = 0;
186 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
187 bool done = true;
188 processMsg(data, done, cb);
189 EXPECT_EQ(0, data.size());
190 EXPECT_EQ(1, cbCalls);
191 EXPECT_FALSE(done);
192
193 hdr.nlmsg_type = NLMSG_DONE;
194 hdr.nlmsg_flags = 0;
195 data = std::string_view(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
196 processMsg(data, done, cb);
197 EXPECT_EQ(0, data.size());
198 EXPECT_EQ(1, cbCalls);
199 EXPECT_TRUE(done);
200}
201
202TEST(ExtractMsgs, MultiMsgInvalid)
203{
204 nlmsghdr hdr{};
205 hdr.nlmsg_len = NLMSG_LENGTH(0);
206 hdr.nlmsg_type = RTM_NEWLINK;
207 hdr.nlmsg_flags = NLM_F_MULTI;
208 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
209
210 size_t cbCalls = 0;
211 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
212 bool done = true;
213 processMsg(data, done, cb);
214 EXPECT_EQ(0, data.size());
215 EXPECT_EQ(1, cbCalls);
216 EXPECT_FALSE(done);
217
218 hdr.nlmsg_flags = 0;
219 data = std::string_view(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0));
220 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error);
221 EXPECT_EQ(0, data.size());
222 EXPECT_EQ(1, cbCalls);
223 EXPECT_FALSE(done);
224}
225
226} // namespace detail
227
228TEST(ExtractRtAttr, TooSmall)
229{
230 const char buf[] = {'1'};
231 static_assert(sizeof(buf) < sizeof(rtattr));
232 std::string_view data(buf, sizeof(buf));
233
234 EXPECT_THROW(extractRtAttr(data), std::runtime_error);
235 EXPECT_EQ(1, data.size());
236}
237
238TEST(ExtractRtAttr, SmallAttrLen)
239{
240 rtattr rta{};
241 rta.rta_len = RTA_LENGTH(0) - 1;
242 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0));
243
244 EXPECT_THROW(extractRtAttr(data), std::runtime_error);
245 EXPECT_EQ(RTA_SPACE(0), data.size());
246}
247
248TEST(ExtractRtAttr, LargeAttrLen)
249{
250 rtattr rta{};
251 rta.rta_len = RTA_LENGTH(0) + 1;
252 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0));
253
254 EXPECT_THROW(extractRtAttr(data), std::runtime_error);
255 EXPECT_EQ(RTA_SPACE(0), data.size());
256}
257
258TEST(ExtractRtAttr, NoData)
259{
260 rtattr rta{};
261 rta.rta_len = RTA_LENGTH(0);
262 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0));
263
264 auto [hdr, attr] = extractRtAttr(data);
265 EXPECT_EQ(0, data.size());
266 EXPECT_EQ(0, attr.size());
267 EXPECT_EQ(0, std::memcmp(&rta, &hdr, sizeof(rta)));
268}
269
270TEST(ExtractRtAttr, SomeData)
271{
272 const char attrbuf[] = "abcd";
273 const char nextbuf[] = "efgh";
274 rtattr rta{};
275 rta.rta_len = RTA_LENGTH(sizeof(attrbuf));
276
277 char buf[RTA_SPACE(sizeof(attrbuf)) + sizeof(nextbuf)];
278 memcpy(buf, &rta, sizeof(rta));
279 memcpy(RTA_DATA(buf), &attrbuf, sizeof(attrbuf));
280 memcpy(buf + RTA_SPACE(sizeof(attrbuf)), &nextbuf, sizeof(nextbuf));
281 std::string_view data(buf, sizeof(buf));
282
283 auto [hdr, attr] = extractRtAttr(data);
284 EXPECT_EQ(0, memcmp(&rta, &hdr, sizeof(rta)));
285 EXPECT_EQ(sizeof(attrbuf), attr.size());
286 EXPECT_EQ(0, memcmp(&attrbuf, attr.data(), sizeof(attrbuf)));
287 EXPECT_EQ(sizeof(nextbuf), data.size());
288 EXPECT_EQ(0, memcmp(&nextbuf, data.data(), sizeof(nextbuf)));
289}
290
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700291class PerformRequest : public testing::Test
292{
293 public:
294 void doLinkDump(size_t ifs)
295 {
296 mock_clear();
297 for (size_t i = 0; i < ifs; ++i)
298 {
299 mock_addIF("eth" + std::to_string(i), 1 + i);
300 }
301
302 size_t cbCalls = 0;
303 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; };
304 ifinfomsg msg{};
305 netlink::performRequest(NETLINK_ROUTE, RTM_GETLINK, NLM_F_DUMP, msg,
306 cb);
307 EXPECT_EQ(ifs, cbCalls);
308 }
309};
310
311TEST_F(PerformRequest, NoResponse)
312{
313 doLinkDump(0);
314}
315
316TEST_F(PerformRequest, SingleResponse)
317{
318 doLinkDump(1);
319}
320
321TEST_F(PerformRequest, MultiResponse)
322{
323 doLinkDump(3);
324}
325
326TEST_F(PerformRequest, MultiMsg)
327{
328 doLinkDump(1000);
329}
330
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -0700331} // namespace netlink
332} // namespace network
333} // namespace phosphor