1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/types.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <string.h>
11 #include <errno.h>
12
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/linker/sections.h>
15
16 #include <zephyr/ztest.h>
17 #include <zephyr/net/offloaded_netdev.h>
18 #include <zephyr/net/net_offload.h>
19 #include <zephyr/net/net_if.h>
20 #include <zephyr/net/net_l2.h>
21
22 static struct in_addr test_addr_ipv4 = { { { 192, 0, 2, 1 } } };
23 static struct in6_addr test_addr_ipv6 = { { {
24 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1
25 } } };
26
27 /* Dummy socket creator for socket-offloaded ifaces */
offload_socket(int family,int type,int proto)28 int offload_socket(int family, int type, int proto)
29 {
30 return -1;
31 }
32
33 /* Dummy offload API for net-offloaded ifaces */
34 struct net_offload net_offload_api;
35
36 /* Dummy init function for socket-offloaded ifaces */
sock_offload_l2_iface_init(struct net_if * iface)37 static void sock_offload_l2_iface_init(struct net_if *iface)
38 {
39 /* This must be called, and the passed-in socket creator cannot be NULL,
40 * or the iface will not be recognized as offloaded
41 */
42 net_if_socket_offload_set(iface, offload_socket);
43 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
44 net_if_flag_set(iface, NET_IF_IPV4);
45 net_if_flag_set(iface, NET_IF_IPV6);
46 }
47
48 /* Dummy init function for net-offloaded ifaces */
net_offload_l2_iface_init(struct net_if * iface)49 static void net_offload_l2_iface_init(struct net_if *iface)
50 {
51 /* Reviewers: Is there a better way to do this?
52 * I couldn't find any actual examples in the source
53 */
54 iface->if_dev->offload = &net_offload_api;
55 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
56 net_if_flag_set(iface, NET_IF_IPV4);
57 net_if_flag_set(iface, NET_IF_IPV6);
58 }
59
60 /* Tracks the total number of ifaces that are up (theoretically). */
61 atomic_t up_count = ATOMIC_INIT(0);
62
63 /* Tracks the total number of times that the offload_impl_enable callback was called. */
64 atomic_t call_count = ATOMIC_INIT(0);
65
66 /* Expected return value from offload_impl_enable */
67 atomic_t retval = ATOMIC_INIT(0);
68
69 /* Functionality under test */
offload_impl_enable(const struct net_if * iface,bool enabled)70 static int offload_impl_enable(const struct net_if *iface, bool enabled)
71 {
72 atomic_inc(&call_count);
73 if (enabled) {
74 atomic_inc(&up_count);
75 } else {
76 atomic_dec(&up_count);
77 }
78 return atomic_get(&retval);
79 }
80
81 /* Net-dev APIs for L2s with offloaded sockets, with and without .enable */
82 static struct offloaded_if_api sock_offloaded_impl_api = {
83 .iface_api.init = sock_offload_l2_iface_init,
84 .enable = offload_impl_enable
85 };
86
87 static struct offloaded_if_api sock_offloaded_no_impl_api = {
88 .iface_api.init = sock_offload_l2_iface_init
89 };
90
91 /* Net-dev APIs for L2s that are net-offloaded, with and without .enable */
92 static struct offloaded_if_api net_offloaded_impl_api = {
93 .iface_api.init = net_offload_l2_iface_init,
94 .enable = offload_impl_enable
95 };
96
97 static struct offloaded_if_api net_offloaded_no_impl_api = {
98 .iface_api.init = net_offload_l2_iface_init
99 };
100
101
102 /* Socket-offloaded netdevs, with and without .enable */
103 NET_DEVICE_OFFLOAD_INIT(sock_offload_test_impl, "sock_offload_test_impl",
104 NULL, NULL, NULL, NULL,
105 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
106 &sock_offloaded_impl_api, 0);
107
108 NET_DEVICE_OFFLOAD_INIT(sock_offload_test_no_impl, "sock_offload_test_no_impl",
109 NULL, NULL, NULL, NULL,
110 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
111 &sock_offloaded_no_impl_api, 0);
112
113 /* Net-offloaded netdevs, with and without .enable */
114 NET_DEVICE_OFFLOAD_INIT(net_offload_test_impl, "net_offload_test_impl",
115 NULL, NULL, NULL, NULL,
116 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
117 &net_offloaded_impl_api, 0);
118
119 NET_DEVICE_OFFLOAD_INIT(net_offload_test_no_impl, "net_offload_test_no_impl",
120 NULL, NULL, NULL, NULL,
121 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
122 &net_offloaded_no_impl_api, 0);
123
net_offloaded_netdev_before(void * fixture)124 static void net_offloaded_netdev_before(void *fixture)
125 {
126 ARG_UNUSED(fixture);
127
128 /* Default to successful return value */
129 atomic_set(&retval, 0);
130
131 /* Reset all ifaces */
132 net_if_down(NET_IF_GET(sock_offload_test_impl, 0));
133 net_if_down(NET_IF_GET(sock_offload_test_no_impl, 0));
134 net_if_down(NET_IF_GET(net_offload_test_impl, 0));
135 net_if_down(NET_IF_GET(net_offload_test_impl, 0));
136
137 /* Reset counters */
138 atomic_set(&call_count, 0);
139 atomic_set(&up_count, 0);
140 }
141
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl)142 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl)
143 {
144 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
145
146 /* Verify iface under test is down before test */
147 zassert_false(net_if_is_admin_up(test_iface),
148 "Iface under test must be admin-down before test");
149
150 /* Bring iface up. */
151 (void)net_if_up(test_iface);
152
153 /* Verify that a single iface went up once (according to the enable callback) */
154 zassert_equal(atomic_get(&call_count), 1,
155 "Bad transition-count, offload_impl_enable not called correctly");
156 zassert_equal(atomic_get(&up_count), 1,
157 "Bad up-count, offload_impl_enable not called correctly");
158 zassert_true(net_if_is_admin_up(test_iface),
159 "Iface under test should be up after net_if_up");
160
161 /* Bring iface down */
162 (void)net_if_down(test_iface);
163
164 /* Verify that a single iface went down once (according to the enable callback)*/
165 zassert_equal(atomic_get(&call_count), 2,
166 "Bad transition-count, offload_impl_enable not called correctly");
167 zassert_equal(atomic_get(&up_count), 0,
168 "Bad up-count, offload_impl_enable not called correctly");
169 zassert_false(net_if_is_admin_up(test_iface),
170 "Iface under test should be down after net_if_down");
171 }
172
ZTEST(net_offloaded_netdev,test_up_down_sock_off_no_impl)173 ZTEST(net_offloaded_netdev, test_up_down_sock_off_no_impl)
174 {
175 struct net_if *test_iface = NET_IF_GET(sock_offload_test_no_impl, 0);
176
177 /* Verify iface under test is down before test */
178 zassert_false(net_if_is_admin_up(test_iface),
179 "Iface under test must be admin-down before test");
180
181 /* Bring iface up */
182 (void)net_if_up(test_iface);
183
184 /* Verify that the iface went up, but callbacks were not fired*/
185 zassert_equal(atomic_get(&call_count), 0,
186 "offload_impl_enable was called unexpectedly");
187 zassert_equal(atomic_get(&up_count), 0,
188 "offload_impl_enable was called unexpectedly");
189 zassert_true(net_if_is_admin_up(test_iface),
190 "Iface under test should be up after net_if_up");
191
192 /* Bring iface down */
193 (void)net_if_down(test_iface);
194
195 /* Verify that the iface went down, but callbacks were not fired*/
196 zassert_equal(atomic_get(&call_count), 0,
197 "offload_impl_enable was called unexpectedly");
198 zassert_equal(atomic_get(&up_count), 0,
199 "offload_impl_enable was called unexpectedly");
200 zassert_false(net_if_is_admin_up(test_iface),
201 "Iface under test should be down after net_if_down");
202 }
203
ZTEST(net_offloaded_netdev,test_up_down_net_off_impl)204 ZTEST(net_offloaded_netdev, test_up_down_net_off_impl)
205 {
206 struct net_if *test_iface = NET_IF_GET(net_offload_test_impl, 0);
207
208 /* Verify iface under test is down before test */
209 zassert_false(net_if_is_admin_up(test_iface),
210 "Iface under test must be admin-down before test");
211
212 /* Bring iface up. */
213 (void)net_if_up(test_iface);
214
215 /* Verify that a single iface went up once (according to the enable callback) */
216 zassert_equal(atomic_get(&call_count), 1,
217 "Bad transition-count, offload_impl_enable not called correctly");
218 zassert_equal(atomic_get(&up_count), 1,
219 "Bad up-count, offload_impl_enable not called correctly");
220 zassert_true(net_if_is_admin_up(test_iface),
221 "Iface under test should be up after net_if_up");
222
223 /* Bring iface down */
224 (void)net_if_down(test_iface);
225
226 /* Verify that a single iface went down once (according to the enable callback)*/
227 zassert_equal(atomic_get(&call_count), 2,
228 "Bad transition-count, offload_impl_enable not called correctly");
229 zassert_equal(atomic_get(&up_count), 0,
230 "Bad up-count, offload_impl_enable not called correctly");
231 zassert_false(net_if_is_admin_up(test_iface),
232 "Iface under test should be down after net_if_down");
233 }
234
ZTEST(net_offloaded_netdev,test_up_down_net_off_no_impl)235 ZTEST(net_offloaded_netdev, test_up_down_net_off_no_impl)
236 {
237 struct net_if *test_iface = NET_IF_GET(net_offload_test_no_impl, 0);
238
239 /* Verify iface under test is down before test */
240 zassert_false(net_if_is_admin_up(test_iface),
241 "Iface under test must be admin-down before test");
242
243 /* Bring iface up */
244 (void)net_if_up(test_iface);
245
246 /* Verify that the iface went up, but callbacks were not fired*/
247 zassert_equal(atomic_get(&call_count), 0,
248 "offload_impl_enable was called unexpectedly");
249 zassert_equal(atomic_get(&up_count), 0,
250 "offload_impl_enable was called unexpectedly");
251 zassert_true(net_if_is_admin_up(test_iface),
252 "Iface under test should be up after net_if_up");
253
254 /* Bring iface down */
255 (void)net_if_down(test_iface);
256
257 /* Verify that the iface went down, but callbacks were not fired*/
258 zassert_equal(atomic_get(&call_count), 0,
259 "offload_impl_enable was called unexpectedly");
260 zassert_equal(atomic_get(&up_count), 0,
261 "offload_impl_enable was called unexpectedly");
262 zassert_false(net_if_is_admin_up(test_iface),
263 "Iface under test should be down after net_if_down");
264 }
265
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_double)266 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_double)
267 {
268 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
269
270 /* Verify iface under test is down before test */
271 zassert_false(net_if_is_admin_up(test_iface),
272 "Iface under test must be admin-down before test");
273
274 /* Bring iface up twice */
275 (void)net_if_up(test_iface);
276 (void)net_if_up(test_iface);
277
278 /* Verify that a single iface went up once (according to the enable callback)*/
279 zassert_equal(atomic_get(&call_count), 1,
280 "Bad transition-count, offload_impl_enable not called correctly");
281 zassert_equal(atomic_get(&up_count), 1,
282 "Bad up-count, offload_impl_enable not called correctly");
283 zassert_true(net_if_is_admin_up(test_iface),
284 "Iface under test should be up after net_if_up");
285
286 /* Verify that a single iface went down once (according to the enable callback)*/
287 (void)net_if_down(test_iface);
288 (void)net_if_down(test_iface);
289
290 /* Verify appropriate calls were made */
291 zassert_equal(atomic_get(&call_count), 2,
292 "Bad transition-count, offload_impl_enable not called correctly");
293 zassert_equal(atomic_get(&up_count), 0,
294 "Bad up-count, offload_impl_enable not called correctly");
295 zassert_false(net_if_is_admin_up(test_iface),
296 "Iface under test should be down after net_if_down");
297 }
298
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_fail_up)299 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_up)
300 {
301 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
302
303 /* Verify iface under test is down before test */
304 zassert_false(net_if_is_admin_up(test_iface),
305 "Iface under test must be admin-down before test");
306
307 /* Instruct the enable callback to fail */
308 atomic_set(&retval, -E2BIG);
309
310 /* Expect net_if_up to fail accordingly */
311 zassert_equal(net_if_up(test_iface), -E2BIG,
312 "net_if_up should forward error returned from offload_impl_enabled");
313
314 /* Verify that the iface failed to go up */
315 zassert_false(net_if_is_admin_up(test_iface),
316 "Iface under test should have failed to go up");
317 }
318
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_fail_down)319 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_down)
320 {
321 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
322
323 /* Bring iface up before test */
324 (void) net_if_up(test_iface);
325
326 /* Instruct the enable callback to fail */
327 atomic_set(&retval, -EADDRINUSE);
328
329
330 /* Expect net_if_down to fail accordingly */
331 zassert_equal(net_if_down(test_iface), -EADDRINUSE,
332 "net_if_down should forward error returned from offload_impl_enabled");
333
334 /* Verify that the iface failed to go down */
335 zassert_true(net_if_is_admin_up(test_iface),
336 "Iface under test should have failed to go up");
337 }
338
test_addr_add_common(struct net_if * test_iface,const char * off_type)339 static void test_addr_add_common(struct net_if *test_iface, const char *off_type)
340 {
341 struct net_if *lookup_iface;
342 struct net_if_addr *ipv4_addr;
343 struct net_if_addr *ipv6_addr;
344
345 /* Bring iface up before test */
346 (void)net_if_up(test_iface);
347
348 ipv4_addr = net_if_ipv4_addr_add(test_iface, &test_addr_ipv4,
349 NET_ADDR_MANUAL, 0);
350 zassert_not_null(ipv4_addr,
351 "Failed to add IPv4 address to a %s offloaded interface",
352 off_type);
353 ipv6_addr = net_if_ipv6_addr_add(test_iface, &test_addr_ipv6,
354 NET_ADDR_MANUAL, 0);
355 zassert_not_null(ipv6_addr,
356 "Failed to add IPv6 address to a socket %s interface",
357 off_type);
358
359 lookup_iface = NULL;
360 zassert_equal_ptr(net_if_ipv4_addr_lookup(&test_addr_ipv4, &lookup_iface),
361 ipv4_addr,
362 "Failed to find IPv4 address on a %s offloaded interface");
363 zassert_equal_ptr(lookup_iface, test_iface, "Wrong interface");
364
365 lookup_iface = NULL;
366 zassert_equal_ptr(net_if_ipv6_addr_lookup(&test_addr_ipv6, &lookup_iface),
367 ipv6_addr,
368 "Failed to find IPv6 address on a %s offloaded interface");
369 zassert_equal_ptr(lookup_iface, test_iface, "Wrong interface");
370
371 zassert_true(net_if_ipv4_addr_rm(test_iface, &test_addr_ipv4),
372 "Failed to remove IPv4 address from a %s offloaded interface",
373 off_type);
374 zassert_true(net_if_ipv6_addr_rm(test_iface, &test_addr_ipv6),
375 "Failed to remove IPv4 address from a %s offloaded interface",
376 off_type);
377 }
378
ZTEST(net_offloaded_netdev,test_addr_add_sock_off_impl)379 ZTEST(net_offloaded_netdev, test_addr_add_sock_off_impl)
380 {
381 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
382
383 test_addr_add_common(test_iface, "offloaded");
384 }
385
ZTEST(net_offloaded_netdev,test_addr_add_net_off_impl)386 ZTEST(net_offloaded_netdev, test_addr_add_net_off_impl)
387 {
388 struct net_if *test_iface = NET_IF_GET(net_offload_test_impl, 0);
389
390 test_addr_add_common(test_iface, "net");
391 }
392
393 ZTEST_SUITE(net_offloaded_netdev, NULL, NULL, net_offloaded_netdev_before, NULL, NULL);
394