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