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