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/socket_offload.h>
20 #include <zephyr/net/net_if.h>
21 #include <zephyr/net/net_l2.h>
22
23 static struct net_in_addr test_addr_ipv4 = { { { 192, 0, 2, 1 } } };
24 static struct net_in6_addr test_addr_ipv6 = { { {
25 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1
26 } } };
27
28 /* Dummy socket creator for socket-offloaded ifaces */
offload_socket(int family,int type,int proto)29 int offload_socket(int family, int type, int proto)
30 {
31 return -1;
32 }
33
34 /* Dummy offload API for net-offloaded ifaces */
35 struct net_offload net_offload_api;
36
37 /* Dummy init function for socket-offloaded ifaces */
sock_offload_l2_iface_init(struct net_if * iface)38 static void sock_offload_l2_iface_init(struct net_if *iface)
39 {
40 /* This must be called, and the passed-in socket creator cannot be NULL,
41 * or the iface will not be recognized as offloaded
42 */
43 net_if_socket_offload_set(iface, offload_socket);
44 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
45 net_if_flag_set(iface, NET_IF_IPV4);
46 net_if_flag_set(iface, NET_IF_IPV6);
47 }
48
49 /* Dummy init function for net-offloaded ifaces */
net_offload_l2_iface_init(struct net_if * iface)50 static void net_offload_l2_iface_init(struct net_if *iface)
51 {
52 /* Reviewers: Is there a better way to do this?
53 * I couldn't find any actual examples in the source
54 */
55 iface->if_dev->offload = &net_offload_api;
56 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
57 net_if_flag_set(iface, NET_IF_IPV4);
58 net_if_flag_set(iface, NET_IF_IPV6);
59 }
60
61 /* Tracks the total number of ifaces that are up (theoretically). */
62 atomic_t up_count = ATOMIC_INIT(0);
63
64 /* Tracks the total number of times that the offload_impl_enable callback was called. */
65 atomic_t call_count = ATOMIC_INIT(0);
66
67 /* Expected return value from offload_impl_enable */
68 atomic_t retval = ATOMIC_INIT(0);
69
70 /* Functionality under test */
offload_impl_enable(const struct net_if * iface,bool enabled)71 static int offload_impl_enable(const struct net_if *iface, bool enabled)
72 {
73 atomic_inc(&call_count);
74 if (enabled) {
75 atomic_inc(&up_count);
76 } else {
77 atomic_dec(&up_count);
78 }
79 return atomic_get(&retval);
80 }
81
82 /* Net-dev APIs for L2s with offloaded sockets, with and without .enable */
83 static struct offloaded_if_api sock_offloaded_impl_api = {
84 .iface_api.init = sock_offload_l2_iface_init,
85 .enable = offload_impl_enable
86 };
87
88 static struct offloaded_if_api sock_offloaded_no_impl_api = {
89 .iface_api.init = sock_offload_l2_iface_init
90 };
91
92 /* Net-dev APIs for L2s that are net-offloaded, with and without .enable */
93 static struct offloaded_if_api net_offloaded_impl_api = {
94 .iface_api.init = net_offload_l2_iface_init,
95 .enable = offload_impl_enable
96 };
97
98 static struct offloaded_if_api net_offloaded_no_impl_api = {
99 .iface_api.init = net_offload_l2_iface_init
100 };
101
102
103 /* Socket-offloaded netdevs, with and without .enable */
104 NET_DEVICE_OFFLOAD_INIT(sock_offload_test_impl, "sock_offload_test_impl",
105 NULL, NULL, NULL, NULL,
106 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
107 &sock_offloaded_impl_api, 0);
108
109 NET_DEVICE_OFFLOAD_INIT(sock_offload_test_no_impl, "sock_offload_test_no_impl",
110 NULL, NULL, NULL, NULL,
111 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
112 &sock_offloaded_no_impl_api, 0);
113
114 /* Net-offloaded netdevs, with and without .enable */
115 NET_DEVICE_OFFLOAD_INIT(net_offload_test_impl, "net_offload_test_impl",
116 NULL, NULL, NULL, NULL,
117 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
118 &net_offloaded_impl_api, 0);
119
120 NET_DEVICE_OFFLOAD_INIT(net_offload_test_no_impl, "net_offload_test_no_impl",
121 NULL, NULL, NULL, NULL,
122 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
123 &net_offloaded_no_impl_api, 0);
124
net_offloaded_netdev_before(void * fixture)125 static void net_offloaded_netdev_before(void *fixture)
126 {
127 ARG_UNUSED(fixture);
128
129 /* Default to successful return value */
130 atomic_set(&retval, 0);
131
132 /* Reset all ifaces */
133 net_if_down(NET_IF_GET(sock_offload_test_impl, 0));
134 net_if_down(NET_IF_GET(sock_offload_test_no_impl, 0));
135 net_if_down(NET_IF_GET(net_offload_test_impl, 0));
136 net_if_down(NET_IF_GET(net_offload_test_impl, 0));
137
138 /* Reset counters */
139 atomic_set(&call_count, 0);
140 atomic_set(&up_count, 0);
141 }
142
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl)143 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl)
144 {
145 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
146
147 /* Verify iface under test is down before test */
148 zassert_false(net_if_is_admin_up(test_iface),
149 "Iface under test must be admin-down before test");
150
151 /* Bring iface up. */
152 (void)net_if_up(test_iface);
153
154 /* Verify that a single iface went up once (according to the enable callback) */
155 zassert_equal(atomic_get(&call_count), 1,
156 "Bad transition-count, offload_impl_enable not called correctly");
157 zassert_equal(atomic_get(&up_count), 1,
158 "Bad up-count, offload_impl_enable not called correctly");
159 zassert_true(net_if_is_admin_up(test_iface),
160 "Iface under test should be up after net_if_up");
161
162 /* Bring iface down */
163 (void)net_if_down(test_iface);
164
165 /* Verify that a single iface went down once (according to the enable callback)*/
166 zassert_equal(atomic_get(&call_count), 2,
167 "Bad transition-count, offload_impl_enable not called correctly");
168 zassert_equal(atomic_get(&up_count), 0,
169 "Bad up-count, offload_impl_enable not called correctly");
170 zassert_false(net_if_is_admin_up(test_iface),
171 "Iface under test should be down after net_if_down");
172 }
173
ZTEST(net_offloaded_netdev,test_up_down_sock_off_no_impl)174 ZTEST(net_offloaded_netdev, test_up_down_sock_off_no_impl)
175 {
176 struct net_if *test_iface = NET_IF_GET(sock_offload_test_no_impl, 0);
177
178 /* Verify iface under test is down before test */
179 zassert_false(net_if_is_admin_up(test_iface),
180 "Iface under test must be admin-down before test");
181
182 /* Bring iface up */
183 (void)net_if_up(test_iface);
184
185 /* Verify that the iface went up, but callbacks were not fired*/
186 zassert_equal(atomic_get(&call_count), 0,
187 "offload_impl_enable was called unexpectedly");
188 zassert_equal(atomic_get(&up_count), 0,
189 "offload_impl_enable was called unexpectedly");
190 zassert_true(net_if_is_admin_up(test_iface),
191 "Iface under test should be up after net_if_up");
192
193 /* Bring iface down */
194 (void)net_if_down(test_iface);
195
196 /* Verify that the iface went down, but callbacks were not fired*/
197 zassert_equal(atomic_get(&call_count), 0,
198 "offload_impl_enable was called unexpectedly");
199 zassert_equal(atomic_get(&up_count), 0,
200 "offload_impl_enable was called unexpectedly");
201 zassert_false(net_if_is_admin_up(test_iface),
202 "Iface under test should be down after net_if_down");
203 }
204
ZTEST(net_offloaded_netdev,test_up_down_net_off_impl)205 ZTEST(net_offloaded_netdev, test_up_down_net_off_impl)
206 {
207 struct net_if *test_iface = NET_IF_GET(net_offload_test_impl, 0);
208
209 /* Verify iface under test is down before test */
210 zassert_false(net_if_is_admin_up(test_iface),
211 "Iface under test must be admin-down before test");
212
213 /* Bring iface up. */
214 (void)net_if_up(test_iface);
215
216 /* Verify that a single iface went up once (according to the enable callback) */
217 zassert_equal(atomic_get(&call_count), 1,
218 "Bad transition-count, offload_impl_enable not called correctly");
219 zassert_equal(atomic_get(&up_count), 1,
220 "Bad up-count, offload_impl_enable not called correctly");
221 zassert_true(net_if_is_admin_up(test_iface),
222 "Iface under test should be up after net_if_up");
223
224 /* Bring iface down */
225 (void)net_if_down(test_iface);
226
227 /* Verify that a single iface went down once (according to the enable callback)*/
228 zassert_equal(atomic_get(&call_count), 2,
229 "Bad transition-count, offload_impl_enable not called correctly");
230 zassert_equal(atomic_get(&up_count), 0,
231 "Bad up-count, offload_impl_enable not called correctly");
232 zassert_false(net_if_is_admin_up(test_iface),
233 "Iface under test should be down after net_if_down");
234 }
235
ZTEST(net_offloaded_netdev,test_up_down_net_off_no_impl)236 ZTEST(net_offloaded_netdev, test_up_down_net_off_no_impl)
237 {
238 struct net_if *test_iface = NET_IF_GET(net_offload_test_no_impl, 0);
239
240 /* Verify iface under test is down before test */
241 zassert_false(net_if_is_admin_up(test_iface),
242 "Iface under test must be admin-down before test");
243
244 /* Bring iface up */
245 (void)net_if_up(test_iface);
246
247 /* Verify that the iface went up, but callbacks were not fired*/
248 zassert_equal(atomic_get(&call_count), 0,
249 "offload_impl_enable was called unexpectedly");
250 zassert_equal(atomic_get(&up_count), 0,
251 "offload_impl_enable was called unexpectedly");
252 zassert_true(net_if_is_admin_up(test_iface),
253 "Iface under test should be up after net_if_up");
254
255 /* Bring iface down */
256 (void)net_if_down(test_iface);
257
258 /* Verify that the iface went down, but callbacks were not fired*/
259 zassert_equal(atomic_get(&call_count), 0,
260 "offload_impl_enable was called unexpectedly");
261 zassert_equal(atomic_get(&up_count), 0,
262 "offload_impl_enable was called unexpectedly");
263 zassert_false(net_if_is_admin_up(test_iface),
264 "Iface under test should be down after net_if_down");
265 }
266
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_double)267 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_double)
268 {
269 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
270
271 /* Verify iface under test is down before test */
272 zassert_false(net_if_is_admin_up(test_iface),
273 "Iface under test must be admin-down before test");
274
275 /* Bring iface up twice */
276 (void)net_if_up(test_iface);
277 (void)net_if_up(test_iface);
278
279 /* Verify that a single iface went up once (according to the enable callback)*/
280 zassert_equal(atomic_get(&call_count), 1,
281 "Bad transition-count, offload_impl_enable not called correctly");
282 zassert_equal(atomic_get(&up_count), 1,
283 "Bad up-count, offload_impl_enable not called correctly");
284 zassert_true(net_if_is_admin_up(test_iface),
285 "Iface under test should be up after net_if_up");
286
287 /* Verify that a single iface went down once (according to the enable callback)*/
288 (void)net_if_down(test_iface);
289 (void)net_if_down(test_iface);
290
291 /* Verify appropriate calls were made */
292 zassert_equal(atomic_get(&call_count), 2,
293 "Bad transition-count, offload_impl_enable not called correctly");
294 zassert_equal(atomic_get(&up_count), 0,
295 "Bad up-count, offload_impl_enable not called correctly");
296 zassert_false(net_if_is_admin_up(test_iface),
297 "Iface under test should be down after net_if_down");
298 }
299
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_fail_up)300 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_up)
301 {
302 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
303
304 /* Verify iface under test is down before test */
305 zassert_false(net_if_is_admin_up(test_iface),
306 "Iface under test must be admin-down before test");
307
308 /* Instruct the enable callback to fail */
309 atomic_set(&retval, -E2BIG);
310
311 /* Expect net_if_up to fail accordingly */
312 zassert_equal(net_if_up(test_iface), -E2BIG,
313 "net_if_up should forward error returned from offload_impl_enabled");
314
315 /* Verify that the iface failed to go up */
316 zassert_false(net_if_is_admin_up(test_iface),
317 "Iface under test should have failed to go up");
318 }
319
ZTEST(net_offloaded_netdev,test_up_down_sock_off_impl_fail_down)320 ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_down)
321 {
322 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
323
324 /* Bring iface up before test */
325 (void) net_if_up(test_iface);
326
327 /* Instruct the enable callback to fail */
328 atomic_set(&retval, -EADDRINUSE);
329
330
331 /* Expect net_if_down to fail accordingly */
332 zassert_equal(net_if_down(test_iface), -EADDRINUSE,
333 "net_if_down should forward error returned from offload_impl_enabled");
334
335 /* Verify that the iface failed to go down */
336 zassert_true(net_if_is_admin_up(test_iface),
337 "Iface under test should have failed to go up");
338 }
339
test_addr_add_common(struct net_if * test_iface,const char * off_type)340 static void test_addr_add_common(struct net_if *test_iface, const char *off_type)
341 {
342 struct net_if *lookup_iface;
343 struct net_if_addr *ipv4_addr;
344 struct net_if_addr *ipv6_addr;
345
346 /* Bring iface up before test */
347 (void)net_if_up(test_iface);
348
349 ipv4_addr = net_if_ipv4_addr_add(test_iface, &test_addr_ipv4,
350 NET_ADDR_MANUAL, 0);
351 zassert_not_null(ipv4_addr,
352 "Failed to add IPv4 address to a %s offloaded interface",
353 off_type);
354 ipv6_addr = net_if_ipv6_addr_add(test_iface, &test_addr_ipv6,
355 NET_ADDR_MANUAL, 0);
356 zassert_not_null(ipv6_addr,
357 "Failed to add IPv6 address to a socket %s interface",
358 off_type);
359
360 lookup_iface = NULL;
361 zassert_equal_ptr(net_if_ipv4_addr_lookup(&test_addr_ipv4, &lookup_iface),
362 ipv4_addr,
363 "Failed to find IPv4 address on a %s offloaded interface", off_type);
364 zassert_equal_ptr(lookup_iface, test_iface, "Wrong interface");
365
366 lookup_iface = NULL;
367 zassert_equal_ptr(net_if_ipv6_addr_lookup(&test_addr_ipv6, &lookup_iface),
368 ipv6_addr,
369 "Failed to find IPv6 address on a %s offloaded interface", off_type);
370 zassert_equal_ptr(lookup_iface, test_iface, "Wrong interface");
371
372 zassert_true(net_if_ipv4_addr_rm(test_iface, &test_addr_ipv4),
373 "Failed to remove IPv4 address from a %s offloaded interface",
374 off_type);
375 zassert_true(net_if_ipv6_addr_rm(test_iface, &test_addr_ipv6),
376 "Failed to remove IPv4 address from a %s offloaded interface",
377 off_type);
378 }
379
ZTEST(net_offloaded_netdev,test_addr_add_sock_off_impl)380 ZTEST(net_offloaded_netdev, test_addr_add_sock_off_impl)
381 {
382 struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
383
384 test_addr_add_common(test_iface, "offloaded");
385 }
386
ZTEST(net_offloaded_netdev,test_addr_add_net_off_impl)387 ZTEST(net_offloaded_netdev, test_addr_add_net_off_impl)
388 {
389 struct net_if *test_iface = NET_IF_GET(net_offload_test_impl, 0);
390
391 test_addr_add_common(test_iface, "net");
392 }
393
394 static bool offload_getaddrinfo_called;
395
test_offload_getaddrinfo(const char * node,const char * service,const struct zsock_addrinfo * hints,struct zsock_addrinfo ** res)396 static int test_offload_getaddrinfo(const char *node,
397 const char *service,
398 const struct zsock_addrinfo *hints,
399 struct zsock_addrinfo **res)
400 {
401 ARG_UNUSED(node);
402 ARG_UNUSED(service);
403 ARG_UNUSED(hints);
404 ARG_UNUSED(res);
405
406 offload_getaddrinfo_called = true;
407
408 return 0;
409 }
410
test_offload_freeaddrinfo(struct zsock_addrinfo * res)411 static void test_offload_freeaddrinfo(struct zsock_addrinfo *res)
412 {
413 ARG_UNUSED(res);
414 }
415
416 static const struct socket_dns_offload test_dns_offload_ops = {
417 .getaddrinfo = test_offload_getaddrinfo,
418 .freeaddrinfo = test_offload_freeaddrinfo,
419 };
420
ZTEST(net_offloaded_netdev,test_dns_offload)421 ZTEST(net_offloaded_netdev, test_dns_offload)
422 {
423 struct zsock_addrinfo *ai;
424
425 /* Register offloaded DNS */
426 offload_getaddrinfo_called = false;
427 socket_offload_dns_register(&test_dns_offload_ops);
428 zassert_true(socket_offload_dns_is_enabled(),
429 "DNS offloading should be enabled");
430 zassert_ok(zsock_getaddrinfo("127.0.0.1", NULL, NULL, &ai),
431 "getaddrinfo() failed");
432 zassert_true(offload_getaddrinfo_called,
433 "Offloaded implementation should be called");
434 zsock_freeaddrinfo(ai);
435
436 /* Disable offloaded DNS */
437 offload_getaddrinfo_called = false;
438 socket_offload_dns_enable(false);
439 zassert_false(socket_offload_dns_is_enabled(),
440 "DNS offloading should be disabled");
441 zassert_ok(zsock_getaddrinfo("127.0.0.1", NULL, NULL, &ai),
442 "getaddrinfo() failed");
443 zassert_false(offload_getaddrinfo_called,
444 "Offloaded implementation shouldn't be called");
445 zsock_freeaddrinfo(ai);
446
447 /* Reenable offloaded DNS */
448 offload_getaddrinfo_called = false;
449 socket_offload_dns_enable(true);
450 zassert_true(socket_offload_dns_is_enabled(),
451 "DNS offloading should be enabled");
452 zassert_ok(zsock_getaddrinfo("127.0.0.1", NULL, NULL, &ai),
453 "getaddrinfo() failed");
454 zassert_true(offload_getaddrinfo_called,
455 "Offloaded implementation should be called");
456 zsock_freeaddrinfo(ai);
457
458 /* Deregister offloaded DNS */
459 offload_getaddrinfo_called = false;
460 socket_offload_dns_deregister(&test_dns_offload_ops);
461 zassert_false(socket_offload_dns_is_enabled(),
462 "DNS offloading should be disabled");
463 zassert_ok(zsock_getaddrinfo("127.0.0.1", NULL, NULL, &ai),
464 "getaddrinfo() failed");
465 zassert_false(offload_getaddrinfo_called,
466 "Offloaded implementation shouldn't be called");
467 zsock_freeaddrinfo(ai);
468 }
469
470 ZTEST_SUITE(net_offloaded_netdev, NULL, NULL, net_offloaded_netdev_before, NULL, NULL);
471