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