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