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