1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/net/net_if.h>
8 #include <zephyr/net/conn_mgr_connectivity.h>
9 #include "test_conn_impl.h"
10 
11 /* Event simulation */
12 static int simulated_event;
13 static struct net_if *simulated_event_iface;
14 static K_MUTEX_DEFINE(simulated_event_mutex);
15 
16 /* Static storage for fatal error info */
17 static int fatal_error;
18 
simulate_event_handler(struct k_work * work)19 void simulate_event_handler(struct k_work *work)
20 {
21 	ARG_UNUSED(*work);
22 
23 	k_mutex_lock(&simulated_event_mutex, K_FOREVER);
24 
25 	if (simulated_event == 0) {
26 		net_mgmt_event_notify(
27 			NET_EVENT_CONN_IF_TIMEOUT,
28 			simulated_event_iface
29 		);
30 	} else {
31 		fatal_error = simulated_event;
32 		net_mgmt_event_notify_with_info(
33 			NET_EVENT_CONN_IF_FATAL_ERROR,
34 			simulated_event_iface, &fatal_error, sizeof(fatal_error)
35 		);
36 	}
37 
38 	k_mutex_unlock(&simulated_event_mutex);
39 }
40 
41 static K_WORK_DELAYABLE_DEFINE(simulate_event_work, simulate_event_handler);
42 
43 /**
44  * @brief Simulates an event on the target iface.
45  *
46  * Do not attempt to simulate multiple events simultaneously -- only the last event requested
47  * will be fired.
48  *
49  * @param target - iface to simulate the event on.
50  * @param event - Event to simulate.
51  *		  If 0, simulate a timeout.
52  *		  Otherwise, simulate a fatal error with this value as the reason/info.
53  */
simulate_event(struct net_if * target,int event)54 static void simulate_event(struct net_if *target, int event)
55 {
56 	k_mutex_lock(&simulated_event_mutex, K_FOREVER);
57 
58 	simulated_event = event;
59 	simulated_event_iface = target;
60 	k_work_reschedule(&simulate_event_work, SIMULATED_EVENT_DELAY_TIME);
61 
62 	k_mutex_unlock(&simulated_event_mutex);
63 }
64 
simulate_timeout(struct net_if * target)65 static void simulate_timeout(struct net_if *target)
66 {
67 	simulate_event(target, 0);
68 }
69 
simulate_fatal_error(struct net_if * target,int reason)70 void simulate_fatal_error(struct net_if *target, int reason)
71 {
72 	simulate_event(target, reason);
73 }
74 
simulate_connection_loss(struct net_if * target)75 void simulate_connection_loss(struct net_if *target)
76 {
77 	net_if_dormant_on(target);
78 }
79 
80 /* Connectivity implementations */
81 
inc_call_count(struct test_conn_data * data,bool a)82 static void inc_call_count(struct test_conn_data *data, bool a)
83 {
84 	if (a) {
85 		data->call_cnt_a += 1;
86 	} else {
87 		data->call_cnt_b += 1;
88 	}
89 }
90 
test_connect(struct conn_mgr_conn_binding * const binding,bool a)91 static int test_connect(struct conn_mgr_conn_binding *const binding, bool a)
92 {
93 	struct test_conn_data *data = binding->ctx;
94 
95 	inc_call_count(data, a);
96 
97 	/* Fail immediately if requested */
98 	if (data->api_err != 0) {
99 		return data->api_err;
100 	}
101 
102 	/* Fail after a delay if requested */
103 	if (data->fatal_error) {
104 		simulate_fatal_error(binding->iface, data->fatal_error);
105 		return 0;
106 	}
107 
108 	if (data->timeout) {
109 		simulate_timeout(binding->iface);
110 		return 0;
111 	}
112 
113 	/* Succeed otherwise */
114 
115 	data->conn_bal += 1;
116 
117 	/* Mark iface as connected */
118 	net_if_dormant_off(binding->iface);
119 	return 0;
120 }
121 
test_disconnect(struct conn_mgr_conn_binding * const binding,bool a)122 static int test_disconnect(struct conn_mgr_conn_binding *const binding, bool a)
123 {
124 	struct test_conn_data *data = binding->ctx;
125 
126 	inc_call_count(data, a);
127 
128 	if (data->api_err != 0) {
129 		return data->api_err;
130 	}
131 
132 	data->conn_bal -= 1;
133 
134 	/* Mark iface as dormant (disconnected) */
135 	net_if_dormant_on(binding->iface);
136 	return 0;
137 }
138 
opt_pointer(struct test_conn_data * data,int optname)139 char *opt_pointer(struct test_conn_data *data, int optname)
140 {
141 	switch (optname) {
142 	case TEST_CONN_OPT_X:
143 		return data->data_x;
144 	case TEST_CONN_OPT_Y:
145 		return data->data_y;
146 	}
147 	return NULL;
148 }
149 
test_set_opt_a(struct conn_mgr_conn_binding * const binding,int optname,const void * optval,size_t optlen)150 int test_set_opt_a(struct conn_mgr_conn_binding *const binding, int optname,
151 		   const void *optval, size_t optlen)
152 {
153 	struct test_conn_data *data = binding->ctx;
154 	char *target = opt_pointer(data, optname);
155 	int len = MIN(optlen, TEST_CONN_DATA_LEN);
156 
157 	/* get/set opt are only implemented for implementation A */
158 	inc_call_count(data, true);
159 
160 	if (target == NULL) {
161 		return -ENOPROTOOPT;
162 	}
163 
164 	if (data->api_err) {
165 		return data->api_err;
166 	}
167 
168 	(void)memset(target, 0, TEST_CONN_DATA_LEN);
169 	(void)memcpy(target, optval, len);
170 
171 	return 0;
172 }
173 
test_get_opt_a(struct conn_mgr_conn_binding * const binding,int optname,void * optval,size_t * optlen)174 int test_get_opt_a(struct conn_mgr_conn_binding *const binding, int optname,
175 		   void *optval, size_t *optlen)
176 {
177 	struct test_conn_data *data = binding->ctx;
178 	char *target = opt_pointer(data, optname);
179 	int len;
180 
181 	/* get/set opt are only implemented for implementation A */
182 	inc_call_count(data, true);
183 
184 	if (target == NULL) {
185 		*optlen = 0;
186 		return -ENOPROTOOPT;
187 	}
188 
189 	len = MIN(strlen(target) + 1, *optlen);
190 
191 	if (data->api_err) {
192 		*optlen = 0;
193 		return data->api_err;
194 	}
195 	*optlen = len;
196 	(void)memset(optval, 0, len);
197 	(void)memcpy(optval, target, len-1);
198 
199 	return 0;
200 }
201 
test_init(struct conn_mgr_conn_binding * const binding,bool a)202 static void test_init(struct conn_mgr_conn_binding *const binding, bool a)
203 {
204 	struct test_conn_data *data = binding->ctx;
205 
206 	if (a) {
207 		data->init_calls_a += 1;
208 	} else {
209 		data->init_calls_b += 1;
210 	}
211 
212 	/* Mark the iface dormant (disconnected) on initialization */
213 	net_if_dormant_on(binding->iface);
214 }
215 
test_init_a(struct conn_mgr_conn_binding * const binding)216 static void test_init_a(struct conn_mgr_conn_binding *const binding)
217 {
218 	test_init(binding, true);
219 }
220 
test_init_b(struct conn_mgr_conn_binding * const binding)221 static void test_init_b(struct conn_mgr_conn_binding *const binding)
222 {
223 	test_init(binding, false);
224 }
225 
test_connect_a(struct conn_mgr_conn_binding * const binding)226 static int test_connect_a(struct conn_mgr_conn_binding *const binding)
227 {
228 	return test_connect(binding, true);
229 }
230 
test_connect_b(struct conn_mgr_conn_binding * const binding)231 static int test_connect_b(struct conn_mgr_conn_binding *const binding)
232 {
233 	return test_connect(binding, false);
234 }
235 
test_disconnect_a(struct conn_mgr_conn_binding * const binding)236 static int test_disconnect_a(struct conn_mgr_conn_binding *const binding)
237 {
238 	return test_disconnect(binding, true);
239 }
240 
test_disconnect_b(struct conn_mgr_conn_binding * const binding)241 static int test_disconnect_b(struct conn_mgr_conn_binding *const binding)
242 {
243 	return test_disconnect(binding, false);
244 }
245 
246 static struct conn_mgr_conn_api test_conn_api_a = {
247 	.connect = test_connect_a,
248 	.disconnect = test_disconnect_a,
249 	.init = test_init_a,
250 	.get_opt = test_get_opt_a,
251 	.set_opt = test_set_opt_a
252 };
253 
254 static struct conn_mgr_conn_api test_conn_api_b = {
255 	.connect = test_connect_b,
256 	.disconnect = test_disconnect_b,
257 	.init = test_init_b,
258 };
259 
260 static struct conn_mgr_conn_api test_conn_api_ni = {
261 	.connect = test_connect_a,
262 	.disconnect = test_disconnect_a,
263 };
264 
265 /* Equivalent but distinct implementations */
266 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_A, &test_conn_api_a);
267 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_B, &test_conn_api_b);
268 
269 /* Implementation without init */
270 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_NI, &test_conn_api_ni);
271 
272 /* Bad implementation, should be handled gracefully */
273 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_N, NULL);
274