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