1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * TAP Ethernet driver for the native_sim board. This is meant for network
11  * connectivity between the host and Zephyr.
12  *
13  * Note this driver is divided in two files. This one, built in the embedded code context,
14  * with whichever libC is used in that context, and eth_native_tap_adapt.c built with the host
15  * libC.
16  */
17 
18 #define LOG_MODULE_NAME eth_tap
19 #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
20 
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
23 
24 #include <stdio.h>
25 
26 #include <zephyr/kernel.h>
27 #include <stdbool.h>
28 #include <errno.h>
29 #include <stddef.h>
30 #include <cmdline.h>
31 #include <posix_native_task.h>
32 
33 #include <zephyr/net/net_pkt.h>
34 #include <zephyr/net/net_core.h>
35 #include <zephyr/net/net_if.h>
36 #include <zephyr/net/ethernet.h>
37 #include <ethernet/eth_stats.h>
38 
39 #include <zephyr/drivers/ptp_clock.h>
40 #include <zephyr/net/gptp.h>
41 #include <zephyr/net/lldp.h>
42 
43 #include "eth_native_tap_priv.h"
44 #include "nsi_host_trampolines.h"
45 #include "eth.h"
46 
47 #define NET_BUF_TIMEOUT K_MSEC(100)
48 
49 #if defined(CONFIG_NET_VLAN)
50 #define ETH_HDR_LEN sizeof(struct net_eth_vlan_hdr)
51 #else
52 #define ETH_HDR_LEN sizeof(struct net_eth_hdr)
53 #endif
54 
55 struct eth_context {
56 	uint8_t recv[NET_ETH_MTU + ETH_HDR_LEN];
57 	uint8_t send[NET_ETH_MTU + ETH_HDR_LEN];
58 	uint8_t mac_addr[6];
59 	struct net_linkaddr ll_addr;
60 	struct net_if *iface;
61 	const char *if_name;
62 	k_tid_t rx_thread;
63 	struct z_thread_stack_element *rx_stack;
64 	size_t rx_stack_size;
65 	int dev_fd;
66 	bool init_done;
67 	bool status;
68 	bool promisc_mode;
69 
70 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
71 	struct net_stats_eth stats;
72 #endif
73 #if defined(CONFIG_ETH_NATIVE_TAP_PTP_CLOCK)
74 	const struct device *ptp_clock;
75 #endif
76 };
77 
78 static const char *if_name_cmd_opt;
79 #ifdef CONFIG_NET_IPV4
80 static const char *ipv4_addr_cmd_opt;
81 static const char *ipv4_nm_cmd_opt;
82 static const char *ipv4_gw_cmd_opt;
83 #endif
84 
85 
86 #define DEFINE_RX_THREAD(x, _)						\
87 	K_KERNEL_STACK_DEFINE(rx_thread_stack_##x,			\
88 			      CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);\
89 	static struct k_thread rx_thread_data_##x
90 
91 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, DEFINE_RX_THREAD, (;), _);
92 
93 #if defined(CONFIG_NET_GPTP)
need_timestamping(struct gptp_hdr * hdr)94 static bool need_timestamping(struct gptp_hdr *hdr)
95 {
96 	switch (hdr->message_type) {
97 	case GPTP_SYNC_MESSAGE:
98 	case GPTP_PATH_DELAY_RESP_MESSAGE:
99 		return true;
100 	default:
101 		return false;
102 	}
103 }
104 
check_gptp_msg(struct net_if * iface,struct net_pkt * pkt,bool is_tx)105 static struct gptp_hdr *check_gptp_msg(struct net_if *iface,
106 				       struct net_pkt *pkt,
107 				       bool is_tx)
108 {
109 	uint8_t *msg_start = net_pkt_data(pkt);
110 	struct gptp_hdr *gptp_hdr;
111 	int eth_hlen;
112 	struct net_eth_hdr *hdr;
113 
114 	hdr = (struct net_eth_hdr *)msg_start;
115 	if (ntohs(hdr->type) != NET_ETH_PTYPE_PTP) {
116 		return NULL;
117 	}
118 
119 	eth_hlen = sizeof(struct net_eth_hdr);
120 
121 	/* In TX, the first net_buf contains the Ethernet header
122 	 * and the actual gPTP header is in the second net_buf.
123 	 * In RX, the Ethernet header + other headers are in the
124 	 * first net_buf.
125 	 */
126 	if (is_tx) {
127 		if (pkt->frags->frags == NULL) {
128 			return false;
129 		}
130 
131 		gptp_hdr = (struct gptp_hdr *)pkt->frags->frags->data;
132 	} else {
133 		gptp_hdr = (struct gptp_hdr *)(pkt->frags->data + eth_hlen);
134 	}
135 
136 	return gptp_hdr;
137 }
138 
update_pkt_priority(struct gptp_hdr * hdr,struct net_pkt * pkt)139 static void update_pkt_priority(struct gptp_hdr *hdr, struct net_pkt *pkt)
140 {
141 	if (GPTP_IS_EVENT_MSG(hdr->message_type)) {
142 		net_pkt_set_priority(pkt, NET_PRIORITY_CA);
143 	} else {
144 		net_pkt_set_priority(pkt, NET_PRIORITY_IC);
145 	}
146 }
147 
update_gptp(struct net_if * iface,struct net_pkt * pkt,bool send)148 static void update_gptp(struct net_if *iface, struct net_pkt *pkt,
149 			bool send)
150 {
151 	struct net_ptp_time timestamp;
152 	struct gptp_hdr *hdr;
153 	int ret;
154 
155 	ret = eth_clock_gettime(&timestamp.second, &timestamp.nanosecond);
156 	if (ret < 0) {
157 		return;
158 	}
159 
160 	net_pkt_set_timestamp(pkt, &timestamp);
161 
162 	hdr = check_gptp_msg(iface, pkt, send);
163 	if (!hdr) {
164 		return;
165 	}
166 
167 	if (send) {
168 		ret = need_timestamping(hdr);
169 		if (ret) {
170 			net_if_add_tx_timestamp(pkt);
171 		}
172 	} else {
173 		update_pkt_priority(hdr, pkt);
174 	}
175 }
176 #else
177 #define update_gptp(iface, pkt, send)
178 #endif /* CONFIG_NET_GPTP */
179 
eth_send(const struct device * dev,struct net_pkt * pkt)180 static int eth_send(const struct device *dev, struct net_pkt *pkt)
181 {
182 	struct eth_context *ctx = dev->data;
183 	int count = net_pkt_get_len(pkt);
184 	int ret;
185 
186 	ret = net_pkt_read(pkt, ctx->send, count);
187 	if (ret) {
188 		return ret;
189 	}
190 
191 	update_gptp(net_pkt_iface(pkt), pkt, true);
192 
193 	LOG_DBG("Send pkt %p len %d", pkt, count);
194 
195 	ret = nsi_host_write(ctx->dev_fd, ctx->send, count);
196 	if (ret < 0) {
197 		LOG_DBG("Cannot send pkt %p (%d)", pkt, ret);
198 	}
199 
200 	return ret < 0 ? ret : 0;
201 }
202 
eth_get_mac(struct eth_context * ctx)203 static struct net_linkaddr *eth_get_mac(struct eth_context *ctx)
204 {
205 	(void)net_linkaddr_set(&ctx->ll_addr, ctx->mac_addr,
206 			       sizeof(ctx->mac_addr));
207 
208 	return &ctx->ll_addr;
209 }
210 
prepare_pkt(struct eth_context * ctx,int count,int * status)211 static struct net_pkt *prepare_pkt(struct eth_context *ctx,
212 				   int count, int *status)
213 {
214 	struct net_pkt *pkt;
215 
216 	pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, count,
217 					   AF_UNSPEC, 0, NET_BUF_TIMEOUT);
218 	if (!pkt) {
219 		*status = -ENOMEM;
220 		return NULL;
221 	}
222 
223 	if (net_pkt_write(pkt, ctx->recv, count)) {
224 		net_pkt_unref(pkt);
225 		*status = -ENOBUFS;
226 		return NULL;
227 	}
228 
229 	*status = 0;
230 
231 	LOG_DBG("Recv pkt %p len %d", pkt, count);
232 
233 	return pkt;
234 }
235 
read_data(struct eth_context * ctx,int fd)236 static int read_data(struct eth_context *ctx, int fd)
237 {
238 	struct net_if *iface = ctx->iface;
239 	struct net_pkt *pkt = NULL;
240 	int status;
241 	int count;
242 
243 	count = nsi_host_read(fd, ctx->recv, sizeof(ctx->recv));
244 	if (count <= 0) {
245 		return 0;
246 	}
247 
248 	pkt = prepare_pkt(ctx, count, &status);
249 	if (!pkt) {
250 		return status;
251 	}
252 
253 	update_gptp(iface, pkt, false);
254 
255 	if (net_recv_data(iface, pkt) < 0) {
256 		net_pkt_unref(pkt);
257 	}
258 
259 	return 0;
260 }
261 
eth_rx(void * p1,void * p2,void * p3)262 static void eth_rx(void *p1, void *p2, void *p3)
263 {
264 	ARG_UNUSED(p2);
265 	ARG_UNUSED(p3);
266 
267 	struct eth_context *ctx = p1;
268 	LOG_DBG("Starting ZETH RX thread");
269 
270 	while (1) {
271 		if (net_if_is_up(ctx->iface)) {
272 			while (!eth_wait_data(ctx->dev_fd)) {
273 				read_data(ctx, ctx->dev_fd);
274 				k_yield();
275 			}
276 		}
277 
278 		k_sleep(K_MSEC(CONFIG_ETH_NATIVE_TAP_RX_TIMEOUT));
279 	}
280 }
281 
282 #if defined(CONFIG_THREAD_MAX_NAME_LEN)
283 #define THREAD_MAX_NAME_LEN CONFIG_THREAD_MAX_NAME_LEN
284 #else
285 #define THREAD_MAX_NAME_LEN 1
286 #endif
287 
create_rx_handler(struct eth_context * ctx)288 static void create_rx_handler(struct eth_context *ctx)
289 {
290 	k_thread_create(ctx->rx_thread,
291 			ctx->rx_stack,
292 			ctx->rx_stack_size,
293 			eth_rx,
294 			ctx, NULL, NULL, K_PRIO_COOP(14),
295 			0, K_NO_WAIT);
296 
297 	if (IS_ENABLED(CONFIG_THREAD_NAME)) {
298 		char name[THREAD_MAX_NAME_LEN];
299 
300 		snprintk(name, sizeof(name), "eth_native_tap_rx-%s",
301 			 ctx->if_name);
302 		k_thread_name_set(ctx->rx_thread, name);
303 	}
304 }
305 
eth_iface_init(struct net_if * iface)306 static void eth_iface_init(struct net_if *iface)
307 {
308 	struct eth_context *ctx = net_if_get_device(iface)->data;
309 	struct net_linkaddr *ll_addr = eth_get_mac(ctx);
310 #ifdef CONFIG_NET_IPV4
311 	struct in_addr addr, netmask;
312 #endif
313 
314 	ctx->iface = iface;
315 
316 	ethernet_init(iface);
317 
318 	if (ctx->init_done) {
319 		return;
320 	}
321 
322 	net_lldp_set_lldpdu(iface);
323 
324 	ctx->init_done = true;
325 
326 #if defined(CONFIG_ETH_NATIVE_TAP_RANDOM_MAC)
327 	/* 00-00-5E-00-53-xx Documentation RFC 7042 */
328 	gen_random_mac(ctx->mac_addr, 0x00, 0x00, 0x5E);
329 
330 	ctx->mac_addr[3] = 0x00;
331 	ctx->mac_addr[4] = 0x53;
332 
333 	/* The TUN/TAP setup script will by default set the MAC address of host
334 	 * interface to 00:00:5E:00:53:FF so do not allow that.
335 	 */
336 	if (ctx->mac_addr[5] == 0xff) {
337 		ctx->mac_addr[5] = 0x01;
338 	}
339 #else
340 	/* Difficult to configure MAC addresses any sane way if we have more
341 	 * than one network interface.
342 	 */
343 	BUILD_ASSERT(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT == 1,
344 		     "Cannot have static MAC if interface count > 1");
345 
346 	if (CONFIG_ETH_NATIVE_TAP_MAC_ADDR[0] != 0) {
347 		if (net_bytes_from_str(ctx->mac_addr, sizeof(ctx->mac_addr),
348 				       CONFIG_ETH_NATIVE_TAP_MAC_ADDR) < 0) {
349 			LOG_ERR("Invalid MAC address %s",
350 				CONFIG_ETH_NATIVE_TAP_MAC_ADDR);
351 		}
352 	}
353 #endif
354 
355 	/* If we have only one network interface, then use the name
356 	 * defined in the Kconfig directly. This way there is no need to
357 	 * change the documentation etc. and break things.
358 	 */
359 	if (CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT == 1) {
360 		ctx->if_name = CONFIG_ETH_NATIVE_TAP_DRV_NAME;
361 	}
362 
363 	if (if_name_cmd_opt != NULL) {
364 		ctx->if_name = if_name_cmd_opt;
365 	}
366 
367 	LOG_DBG("Interface %p using \"%s\"", iface, ctx->if_name);
368 
369 	net_if_set_link_addr(iface, ll_addr->addr, ll_addr->len,
370 			     NET_LINK_ETHERNET);
371 
372 #ifdef CONFIG_NET_IPV4
373 	if (ipv4_addr_cmd_opt != NULL) {
374 		if (net_addr_pton(AF_INET, ipv4_addr_cmd_opt, &addr) == 0) {
375 			net_if_ipv4_addr_add(iface, &addr, NET_ADDR_MANUAL, 0);
376 
377 			if (ipv4_nm_cmd_opt != NULL) {
378 				if (net_addr_pton(AF_INET, ipv4_nm_cmd_opt, &netmask) == 0) {
379 					net_if_ipv4_set_netmask_by_addr(iface, &addr, &netmask);
380 				} else {
381 					NET_ERR("Invalid netmask: %s", ipv4_nm_cmd_opt);
382 				}
383 			}
384 		} else {
385 			NET_ERR("Invalid address: %s", ipv4_addr_cmd_opt);
386 		}
387 	}
388 
389 	if (ipv4_gw_cmd_opt != NULL) {
390 		if (net_addr_pton(AF_INET, ipv4_gw_cmd_opt, &addr) == 0) {
391 			net_if_ipv4_set_gw(iface, &addr);
392 		} else {
393 			NET_ERR("Invalid gateway: %s", ipv4_gw_cmd_opt);
394 		}
395 	}
396 #endif
397 
398 	ctx->dev_fd = eth_iface_create(CONFIG_ETH_NATIVE_POSIX_DEV_NAME, ctx->if_name, false);
399 	if (ctx->dev_fd < 0) {
400 		LOG_ERR("Cannot create %s (%d/%s)", ctx->if_name, ctx->dev_fd,
401 			strerror(-ctx->dev_fd));
402 	} else {
403 		/* Create a thread that will handle incoming data from host */
404 		create_rx_handler(ctx);
405 	}
406 }
407 
eth_native_tap_get_capabilities(const struct device * dev)408 static enum ethernet_hw_caps eth_native_tap_get_capabilities(const struct device *dev)
409 {
410 	ARG_UNUSED(dev);
411 
412 	return ETHERNET_TXTIME
413 #if defined(CONFIG_NET_VLAN)
414 		| ETHERNET_HW_VLAN
415 #endif
416 #if defined(CONFIG_ETH_NATIVE_TAP_VLAN_TAG_STRIP)
417 		| ETHERNET_HW_VLAN_TAG_STRIP
418 #endif
419 #if defined(CONFIG_ETH_NATIVE_TAP_PTP_CLOCK)
420 		| ETHERNET_PTP
421 #endif
422 #if defined(CONFIG_NET_PROMISCUOUS_MODE)
423 		| ETHERNET_PROMISC_MODE
424 #endif
425 #if defined(CONFIG_NET_LLDP)
426 		| ETHERNET_LLDP
427 #endif
428 		;
429 }
430 
431 #if defined(CONFIG_ETH_NATIVE_TAP_PTP_CLOCK)
eth_get_ptp_clock(const struct device * dev)432 static const struct device *eth_get_ptp_clock(const struct device *dev)
433 {
434 	struct eth_context *context = dev->data;
435 
436 	return context->ptp_clock;
437 }
438 #endif
439 
440 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
get_stats(const struct device * dev)441 static struct net_stats_eth *get_stats(const struct device *dev)
442 {
443 	struct eth_context *context = dev->data;
444 
445 	return &(context->stats);
446 }
447 #endif
448 
set_config(const struct device * dev,enum ethernet_config_type type,const struct ethernet_config * config)449 static int set_config(const struct device *dev,
450 		      enum ethernet_config_type type,
451 		      const struct ethernet_config *config)
452 {
453 	int ret = 0;
454 
455 	if (IS_ENABLED(CONFIG_NET_PROMISCUOUS_MODE) &&
456 	    type == ETHERNET_CONFIG_TYPE_PROMISC_MODE) {
457 		struct eth_context *context = dev->data;
458 
459 		if (config->promisc_mode) {
460 			if (context->promisc_mode) {
461 				return -EALREADY;
462 			}
463 
464 			context->promisc_mode = true;
465 		} else {
466 			if (!context->promisc_mode) {
467 				return -EALREADY;
468 			}
469 
470 			context->promisc_mode = false;
471 		}
472 
473 		ret = eth_promisc_mode(context->if_name,
474 				       context->promisc_mode);
475 	} else if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) {
476 		struct eth_context *context = dev->data;
477 
478 		memcpy(context->mac_addr, config->mac_address.addr,
479 		       sizeof(context->mac_addr));
480 	}
481 
482 	return ret;
483 }
484 
485 #if defined(CONFIG_NET_VLAN)
vlan_setup(const struct device * dev,struct net_if * iface,uint16_t tag,bool enable)486 static int vlan_setup(const struct device *dev, struct net_if *iface,
487 		      uint16_t tag, bool enable)
488 {
489 	if (enable) {
490 		net_lldp_set_lldpdu(iface);
491 	} else {
492 		net_lldp_unset_lldpdu(iface);
493 	}
494 
495 	return 0;
496 }
497 #endif /* CONFIG_NET_VLAN */
498 
499 static const struct ethernet_api eth_if_api = {
500 	.iface_api.init = eth_iface_init,
501 
502 	.get_capabilities = eth_native_tap_get_capabilities,
503 	.set_config = set_config,
504 	.send = eth_send,
505 
506 #if defined(CONFIG_NET_VLAN)
507 	.vlan_setup = vlan_setup,
508 #endif
509 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
510 	.get_stats = get_stats,
511 #endif
512 #if defined(CONFIG_ETH_NATIVE_TAP_PTP_CLOCK)
513 	.get_ptp_clock = eth_get_ptp_clock,
514 #endif
515 };
516 
517 #define DEFINE_ETH_DEV_DATA(x, _)					     \
518 	static struct eth_context eth_context_data_##x = {		     \
519 		.if_name = CONFIG_ETH_NATIVE_TAP_DRV_NAME #x,		     \
520 		.rx_thread = &rx_thread_data_##x,			     \
521 		.rx_stack = rx_thread_stack_##x,			     \
522 		.rx_stack_size = K_KERNEL_STACK_SIZEOF(rx_thread_stack_##x), \
523 	}
524 
525 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, DEFINE_ETH_DEV_DATA, (;), _);
526 
527 #define DEFINE_ETH_DEVICE(x, _)						\
528 	ETH_NET_DEVICE_INIT(eth_native_tap_##x,				\
529 			    CONFIG_ETH_NATIVE_TAP_DRV_NAME #x,		\
530 			    NULL, NULL,	&eth_context_data_##x, NULL,	\
531 			    CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,	\
532 			    &eth_if_api,				\
533 			    NET_ETH_MTU)
534 
535 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, DEFINE_ETH_DEVICE, (;), _);
536 
537 #if defined(CONFIG_ETH_NATIVE_TAP_PTP_CLOCK)
538 
539 #if defined(CONFIG_NET_GPTP)
540 BUILD_ASSERT(								\
541 	CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT == CONFIG_NET_GPTP_NUM_PORTS, \
542 	"Number of network interfaces must match gPTP port count");
543 #endif
544 
545 struct ptp_context {
546 	struct eth_context *eth_context;
547 };
548 
549 #define DEFINE_PTP_DEV_DATA(x, _) \
550 	static struct ptp_context ptp_context_##x
551 
552 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, DEFINE_PTP_DEV_DATA, (;), _);
553 
ptp_clock_set_native_tap(const struct device * clk,struct net_ptp_time * tm)554 static int ptp_clock_set_native_tap(const struct device *clk, struct net_ptp_time *tm)
555 {
556 	ARG_UNUSED(clk);
557 	ARG_UNUSED(tm);
558 
559 	/* We cannot set the host device time so this function
560 	 * does nothing.
561 	 */
562 
563 	return 0;
564 }
565 
ptp_clock_get_native_tap(const struct device * clk,struct net_ptp_time * tm)566 static int ptp_clock_get_native_tap(const struct device *clk, struct net_ptp_time *tm)
567 {
568 	ARG_UNUSED(clk);
569 
570 	return eth_clock_gettime(&tm->second, &tm->nanosecond);
571 }
572 
ptp_clock_adjust_native_tap(const struct device * clk,int increment)573 static int ptp_clock_adjust_native_tap(const struct device *clk, int increment)
574 {
575 	ARG_UNUSED(clk);
576 	ARG_UNUSED(increment);
577 
578 	/* We cannot adjust the host device time so this function
579 	 * does nothing.
580 	 */
581 
582 	return 0;
583 }
584 
ptp_clock_rate_adjust_native_tap(const struct device * clk,double ratio)585 static int ptp_clock_rate_adjust_native_tap(const struct device *clk, double ratio)
586 {
587 	ARG_UNUSED(clk);
588 	ARG_UNUSED(ratio);
589 
590 	/* We cannot adjust the host device time so this function
591 	 * does nothing.
592 	 */
593 
594 	return 0;
595 }
596 
597 static DEVICE_API(ptp_clock, api) = {
598 	.set = ptp_clock_set_native_tap,
599 	.get = ptp_clock_get_native_tap,
600 	.adjust = ptp_clock_adjust_native_tap,
601 	.rate_adjust = ptp_clock_rate_adjust_native_tap,
602 };
603 
604 #define PTP_INIT_FUNC(x, _)						\
605 	static int ptp_init_##x(const struct device *port)			\
606 	{								\
607 		const struct device *const eth_dev = DEVICE_GET(eth_native_tap_##x); \
608 		struct eth_context *context = eth_dev->data;	\
609 		struct ptp_context *ptp_context = port->data;	\
610 									\
611 		context->ptp_clock = port;				\
612 		ptp_context->eth_context = context;			\
613 									\
614 		return 0;						\
615 	}
616 
617 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, PTP_INIT_FUNC, (), _)
618 
619 #define DEFINE_PTP_DEVICE(x, _)						\
620 	DEVICE_DEFINE(eth_native_tap_ptp_clock_##x,			\
621 			    PTP_CLOCK_NAME "_" #x,			\
622 			    ptp_init_##x,				\
623 			    NULL,					\
624 			    &ptp_context_##x,				\
625 			    NULL,					\
626 			    POST_KERNEL,				\
627 			    CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,	\
628 			    &api)
629 
630 LISTIFY(CONFIG_ETH_NATIVE_TAP_INTERFACE_COUNT, DEFINE_PTP_DEVICE, (;), _);
631 
632 #endif /* CONFIG_ETH_NATIVE_TAP_PTP_CLOCK */
633 
add_native_tap_options(void)634 static void add_native_tap_options(void)
635 {
636 	static struct args_struct_t eth_native_tap_options[] = {
637 		{
638 			.is_mandatory = false,
639 			.option = "eth-if",
640 			.name = "name",
641 			.type = 's',
642 			.dest = (void *)&if_name_cmd_opt,
643 			.descript = "Name of the eth interface to use",
644 		},
645 #ifdef CONFIG_NET_IPV4
646 		{
647 			.is_mandatory = false,
648 			.option = "ipv4-addr",
649 			.name = "ipv4",
650 			.type = 's',
651 			.dest = (void *)&ipv4_addr_cmd_opt,
652 			.descript = "IPv4 address",
653 		},
654 		{
655 			.is_mandatory = false,
656 			.option = "ipv4-gw",
657 			.name = "ipv4",
658 			.type = 's',
659 			.dest = (void *)&ipv4_gw_cmd_opt,
660 			.descript = "IPv4 gateway",
661 		},
662 		{
663 			.is_mandatory = false,
664 			.option = "ipv4-nm",
665 			.name = "ipv4",
666 			.type = 's',
667 			.dest = (void *)&ipv4_nm_cmd_opt,
668 			.descript = "IPv4 netmask",
669 		},
670 #endif
671 		ARG_TABLE_ENDMARKER,
672 	};
673 
674 	native_add_command_line_opts(eth_native_tap_options);
675 }
676 
677 NATIVE_TASK(add_native_tap_options, PRE_BOOT_1, 10);
678