xref: /openbmc/phosphor-networkd/test/test_netlink.cpp (revision 12beaada9064359498ec6418f104ab599d54a75a)
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     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 
105 TEST(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);
129     EXPECT_TRUE(stdplus::raw::equal(hdr, hdrOut));
130     EXPECT_TRUE(
131         stdplus::raw::equal(err, stdplus::raw::extract<nlmsgerr>(dataOut)));
132     EXPECT_EQ(0, dataOut.size());
133     EXPECT_TRUE(done);
134 }
135 
136 TEST(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 
152 TEST(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 
177 TEST(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 
202 TEST(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 
228 TEST(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 
238 TEST(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 
248 TEST(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 
258 TEST(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 
270 TEST(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 
291 class 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 
311 TEST_F(PerformRequest, NoResponse)
312 {
313     doLinkDump(0);
314 }
315 
316 TEST_F(PerformRequest, SingleResponse)
317 {
318     doLinkDump(1);
319 }
320 
321 TEST_F(PerformRequest, MultiResponse)
322 {
323     doLinkDump(3);
324 }
325 
326 TEST_F(PerformRequest, MultiMsg)
327 {
328     doLinkDump(1000);
329 }
330 
331 } // namespace netlink
332 } // namespace network
333 } // namespace phosphor
334