blob: b788dd19b65298e8ba0f35b66862c21045301612 [file] [log] [blame]
William A. Kennington IIIc920bdb2019-04-19 14:23:06 -07001#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
13namespace phosphor
14{
15namespace network
16{
17namespace netlink
18{
19namespace detail
20{
21
22TEST(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
37TEST(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
52TEST(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
67TEST(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
83TEST(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
103TEST(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
133TEST(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
149TEST(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
174TEST(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
199TEST(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
225TEST(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
235TEST(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
245TEST(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
255TEST(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
267TEST(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