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