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