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