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