1 /*
2 * Copyright (c) 2021 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_virtual, CONFIG_NET_L2_VIRTUAL_LOG_LEVEL);
9
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_l2.h>
12 #include <zephyr/net/net_if.h>
13 #include <zephyr/net/net_mgmt.h>
14 #include <zephyr/net/virtual.h>
15 #include <zephyr/net/virtual_mgmt.h>
16 #include <zephyr/random/random.h>
17
18 #include "net_private.h"
19 #include "net_stats.h"
20
21 #define NET_BUF_TIMEOUT K_MSEC(100)
22
virtual_recv(struct net_if * iface,struct net_pkt * pkt)23 static enum net_verdict virtual_recv(struct net_if *iface,
24 struct net_pkt *pkt)
25 {
26 struct virtual_interface_context *ctx, *tmp;
27 const struct virtual_interface_api *api;
28 enum net_verdict verdict;
29 sys_slist_t *interfaces;
30
31 interfaces = &iface->config.virtual_interfaces;
32
33 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
34 if (ctx->virtual_iface == NULL) {
35 continue;
36 }
37
38 api = net_if_get_device(ctx->virtual_iface)->api;
39 if (!api || api->recv == NULL) {
40 continue;
41 }
42
43 if (!net_if_is_up(ctx->virtual_iface)) {
44 NET_DBG("Interface %d is down.",
45 net_if_get_by_iface(ctx->virtual_iface));
46 continue;
47 }
48
49 verdict = api->recv(ctx->virtual_iface, pkt);
50 if (verdict == NET_CONTINUE) {
51 continue;
52 }
53
54 if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
55 size_t pkt_len;
56
57 pkt_len = net_pkt_get_len(pkt);
58
59 NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
60
61 net_stats_update_bytes_recv(ctx->virtual_iface,
62 pkt_len);
63 }
64
65 if (verdict == NET_DROP) {
66 net_stats_update_processing_error(ctx->virtual_iface);
67 }
68
69 return verdict;
70 }
71
72 NET_DBG("No handler, dropping pkt %p len %zu", pkt, net_pkt_get_len(pkt));
73
74 return NET_DROP;
75 }
76
virtual_send(struct net_if * iface,struct net_pkt * pkt)77 static int virtual_send(struct net_if *iface, struct net_pkt *pkt)
78 {
79 const struct virtual_interface_api *api = net_if_get_device(iface)->api;
80 size_t pkt_len;
81 int ret;
82
83 if (!api) {
84 return -ENOENT;
85 }
86
87 if (!net_if_is_up(iface)) {
88 NET_DBG("Interface %d is down.",
89 net_if_get_by_iface(iface));
90 return -ENETDOWN;
91 }
92
93 if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
94 pkt_len = net_pkt_get_len(pkt);
95 }
96
97 /* As we are just passing data through, the net_pkt is not freed here.
98 */
99 ret = api->send(iface, pkt);
100
101 if (IS_ENABLED(CONFIG_NET_STATISTICS) && ret == 0) {
102 NET_DBG("Sent pkt %p len %zu", pkt, pkt_len);
103 net_stats_update_bytes_sent(iface, pkt_len);
104 }
105
106 return ret;
107 }
108
virtual_enable(struct net_if * iface,bool state)109 static int virtual_enable(struct net_if *iface, bool state)
110 {
111 const struct virtual_interface_api *virt;
112 struct virtual_interface_context *ctx;
113 int ret = 0;
114
115 virt = net_if_get_device(iface)->api;
116 if (!virt) {
117 return -ENOENT;
118 }
119
120 ctx = net_if_l2_data(iface);
121
122 if (state) {
123 /* Take the interfaces below this interface up as
124 * it does not make sense otherwise.
125 */
126
127 while (ctx->iface) {
128 if (net_if_is_up(ctx->iface)) {
129 /* Network interfaces below this must be up too
130 * so we can bail out at this point.
131 */
132 break;
133 }
134
135 if (net_if_l2(ctx->iface) !=
136 &NET_L2_GET_NAME(VIRTUAL)) {
137 net_if_up(ctx->iface);
138 break;
139 }
140
141 NET_DBG("Taking iface %d up", net_if_get_by_iface(ctx->iface));
142
143 net_if_up(ctx->iface);
144 ctx = net_if_l2_data(ctx->iface);
145 }
146
147 if (virt->start) {
148 ret = virt->start(net_if_get_device(iface));
149 }
150
151 return ret;
152 }
153
154 if (virt->stop) {
155 ret = virt->stop(net_if_get_device(iface));
156 }
157
158 return ret;
159 }
160
virtual_flags(struct net_if * iface)161 enum net_l2_flags virtual_flags(struct net_if *iface)
162 {
163 struct virtual_interface_context *ctx = net_if_l2_data(iface);
164
165 return ctx->virtual_l2_flags;
166 }
167
168 #if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN)
169 extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
170 size_t size, uint16_t proto, k_timeout_t timeout);
171
virtual_l2_alloc(struct net_if * iface,struct net_pkt * pkt,size_t size,enum net_ip_protocol proto,k_timeout_t timeout)172 static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
173 size_t size, enum net_ip_protocol proto,
174 k_timeout_t timeout)
175 {
176 return vlan_alloc_buffer(iface, pkt, size, proto, timeout);
177 }
178 #else
179 #define virtual_l2_alloc NULL
180 #endif
181
182 NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable,
183 virtual_flags, virtual_l2_alloc);
184
random_linkaddr(uint8_t * linkaddr,size_t len)185 static void random_linkaddr(uint8_t *linkaddr, size_t len)
186 {
187 sys_rand_get(linkaddr, len);
188
189 linkaddr[0] |= 0x02; /* force LAA bit */
190 }
191
net_virtual_interface_attach(struct net_if * virtual_iface,struct net_if * iface)192 int net_virtual_interface_attach(struct net_if *virtual_iface,
193 struct net_if *iface)
194 {
195 const struct virtual_interface_api *api;
196 struct virtual_interface_context *ctx;
197 bool up = false;
198
199 if (net_if_get_by_iface(virtual_iface) < 0 ||
200 (iface != NULL && net_if_get_by_iface(iface) < 0)) {
201 return -EINVAL;
202 }
203
204 if (virtual_iface == iface) {
205 return -EINVAL;
206 }
207
208 api = net_if_get_device(virtual_iface)->api;
209 if (api->attach == NULL) {
210 return -ENOENT;
211 }
212
213 ctx = net_if_l2_data(virtual_iface);
214
215 if (ctx->iface) {
216 if (iface != NULL) {
217 /* We are already attached */
218 return -EALREADY;
219 }
220
221 /* Detaching, take the interface down */
222 net_if_down(virtual_iface);
223
224 (void)sys_slist_find_and_remove(
225 &ctx->iface->config.virtual_interfaces,
226 &ctx->node);
227
228 NET_DBG("Detaching %d from %d",
229 net_if_get_by_iface(virtual_iface),
230 net_if_get_by_iface(ctx->iface));
231
232 ctx->iface = NULL;
233 } else {
234 if (iface == NULL) {
235 /* We are already detached */
236 return -EALREADY;
237 }
238
239 /* Attaching, take the interface up if auto start is enabled.
240 */
241 ctx->iface = iface;
242 sys_slist_append(&ctx->iface->config.virtual_interfaces,
243 &ctx->node);
244
245 NET_DBG("Attaching %d to %d",
246 net_if_get_by_iface(virtual_iface),
247 net_if_get_by_iface(ctx->iface));
248
249 up = true;
250 }
251
252 /* Figure out the link address for this interface. The actual link
253 * address is randomized. This must be done before attach is called so
254 * that the attach callback can create link local address for the
255 * network interface (if IPv6). The actual link address is typically
256 * not need in tunnels.
257 */
258 if (iface) {
259 random_linkaddr(ctx->lladdr.addr, sizeof(ctx->lladdr.addr));
260
261 ctx->lladdr.len = sizeof(ctx->lladdr.addr);
262 ctx->lladdr.type = NET_LINK_UNKNOWN;
263
264 net_if_set_link_addr(virtual_iface, ctx->lladdr.addr,
265 ctx->lladdr.len, ctx->lladdr.type);
266 }
267
268 api->attach(virtual_iface, iface);
269
270 if (up && !net_if_flag_is_set(virtual_iface,
271 NET_IF_NO_AUTO_START)) {
272 net_if_up(virtual_iface);
273 }
274
275 return 0;
276 }
277
net_virtual_disable(struct net_if * iface)278 void net_virtual_disable(struct net_if *iface)
279 {
280 struct virtual_interface_context *ctx, *tmp;
281 sys_slist_t *interfaces;
282
283 if (net_if_get_by_iface(iface) < 0) {
284 return;
285 }
286
287 interfaces = &iface->config.virtual_interfaces;
288 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
289 NET_DBG("Iface %d down, setting virtual iface %d carrier off",
290 net_if_get_by_iface(iface),
291 net_if_get_by_iface(ctx->virtual_iface));
292 net_if_carrier_off(ctx->virtual_iface);
293 }
294 }
295
net_virtual_enable(struct net_if * iface)296 void net_virtual_enable(struct net_if *iface)
297 {
298 struct virtual_interface_context *ctx, *tmp;
299 sys_slist_t *interfaces;
300
301 if (net_if_get_by_iface(iface) < 0) {
302 return;
303 }
304
305 interfaces = &iface->config.virtual_interfaces;
306 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
307 NET_DBG("Iface %d up, setting virtual iface %d carrier on",
308 net_if_get_by_iface(iface),
309 net_if_get_by_iface(ctx->virtual_iface));
310 net_if_carrier_on(ctx->virtual_iface);
311 }
312 }
313
net_virtual_get_iface(struct net_if * iface)314 struct net_if *net_virtual_get_iface(struct net_if *iface)
315 {
316 struct virtual_interface_context *ctx;
317
318 if (net_if_get_by_iface(iface) < 0) {
319 return NULL;
320 }
321
322 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
323 return NULL;
324 }
325
326 ctx = net_if_l2_data(iface);
327
328 return ctx->iface;
329 }
330
net_virtual_get_name(struct net_if * iface,char * buf,size_t len)331 char *net_virtual_get_name(struct net_if *iface, char *buf, size_t len)
332 {
333 struct virtual_interface_context *ctx;
334
335 if (net_if_get_by_iface(iface) < 0) {
336 return NULL;
337 }
338
339 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
340 return NULL;
341 }
342
343 ctx = net_if_l2_data(iface);
344
345 strncpy(buf, ctx->name, MIN(len, sizeof(ctx->name)));
346 buf[len - 1] = '\0';
347
348 return buf;
349 }
350
net_virtual_set_name(struct net_if * iface,const char * name)351 void net_virtual_set_name(struct net_if *iface, const char *name)
352 {
353 struct virtual_interface_context *ctx;
354
355 if (net_if_get_by_iface(iface) < 0) {
356 return;
357 }
358
359 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
360 return;
361 }
362
363 ctx = net_if_l2_data(iface);
364
365 strncpy(ctx->name, name, CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN);
366 ctx->name[CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN - 1] = '\0';
367 }
368
net_virtual_set_flags(struct net_if * iface,enum net_l2_flags flags)369 enum net_l2_flags net_virtual_set_flags(struct net_if *iface,
370 enum net_l2_flags flags)
371 {
372 struct virtual_interface_context *ctx;
373 enum net_l2_flags old_flags;
374
375 if (net_if_get_by_iface(iface) < 0) {
376 return 0;
377 }
378
379 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
380 return 0;
381 }
382
383 ctx = net_if_l2_data(iface);
384 old_flags = ctx->virtual_l2_flags;
385 ctx->virtual_l2_flags = flags;
386
387 return old_flags;
388 }
389
net_virtual_init(struct net_if * iface)390 void net_virtual_init(struct net_if *iface)
391 {
392 struct virtual_interface_context *ctx;
393
394 sys_slist_init(&iface->config.virtual_interfaces);
395
396 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
397 return;
398 }
399
400 ctx = net_if_l2_data(iface);
401 if (ctx->is_init) {
402 return;
403 }
404
405 NET_DBG("Initializing virtual L2 %p for iface %d (%p)", ctx,
406 net_if_get_by_iface(iface), iface);
407
408 ctx->virtual_iface = iface;
409 ctx->virtual_l2_flags = 0;
410 ctx->is_init = true;
411 }
412