1 /*
2  * Copyright (c) 2015 Intel Corporation
3  * Copyright (c) 2017 Linaro Limited
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  *
11  * Network loopback interface implementation.
12  */
13 
14 #define LOG_MODULE_NAME netlo
15 #define LOG_LEVEL CONFIG_NET_LOOPBACK_LOG_LEVEL
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
19 
20 #include <zephyr/net/net_pkt.h>
21 #include <zephyr/net/buf.h>
22 #include <zephyr/net/net_ip.h>
23 #include <zephyr/net/net_if.h>
24 #include <zephyr/net/loopback.h>
25 
26 #include <zephyr/net/dummy.h>
27 
loopback_dev_init(const struct device * dev)28 int loopback_dev_init(const struct device *dev)
29 {
30 	ARG_UNUSED(dev);
31 
32 	return 0;
33 }
34 
loopback_init(struct net_if * iface)35 static void loopback_init(struct net_if *iface)
36 {
37 	struct net_if_addr *ifaddr;
38 
39 	/* RFC 7042, s.2.1.1. address to use in documentation */
40 	net_if_set_link_addr(iface, "\x00\x00\x5e\x00\x53\xff", 6,
41 			     NET_LINK_DUMMY);
42 
43 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
44 		struct in_addr ipv4_loopback = INADDR_LOOPBACK_INIT;
45 
46 		ifaddr = net_if_ipv4_addr_add(iface, &ipv4_loopback,
47 					      NET_ADDR_AUTOCONF, 0);
48 		if (!ifaddr) {
49 			LOG_ERR("Failed to register IPv4 loopback address");
50 		}
51 	}
52 
53 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
54 		struct in6_addr ipv6_loopback = IN6ADDR_LOOPBACK_INIT;
55 
56 		ifaddr = net_if_ipv6_addr_add(iface, &ipv6_loopback,
57 					      NET_ADDR_AUTOCONF, 0);
58 		if (!ifaddr) {
59 			LOG_ERR("Failed to register IPv6 loopback address");
60 		}
61 	}
62 }
63 
64 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
65 static float loopback_packet_drop_ratio = 0.0f;
66 static float loopback_packet_drop_state = 0.0f;
67 static int loopback_packet_dropped_count;
68 
loopback_set_packet_drop_ratio(float ratio)69 int loopback_set_packet_drop_ratio(float ratio)
70 {
71 	if (ratio < 0.0f || ratio > 1.0f) {
72 		return -EINVAL;
73 	}
74 	loopback_packet_drop_ratio = ratio;
75 	return 0;
76 }
77 
loopback_get_num_dropped_packets(void)78 int loopback_get_num_dropped_packets(void)
79 {
80 	return loopback_packet_dropped_count;
81 }
82 
83 #endif
84 
loopback_send(const struct device * dev,struct net_pkt * pkt)85 static int loopback_send(const struct device *dev, struct net_pkt *pkt)
86 {
87 	struct net_pkt *cloned;
88 	int res;
89 
90 	ARG_UNUSED(dev);
91 
92 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
93 	/* Drop packets based on the loopback_packet_drop_ratio
94 	 * a ratio of 0.2 will drop one every 5 packets
95 	 */
96 	loopback_packet_drop_state += loopback_packet_drop_ratio;
97 	if (loopback_packet_drop_state >= 1.0f) {
98 		/* Administrate we dropped a packet */
99 		loopback_packet_drop_state -= 1.0f;
100 		loopback_packet_dropped_count++;
101 		return 0;
102 	}
103 #endif
104 
105 	if (!pkt->frags) {
106 		LOG_ERR("No data to send");
107 		return -ENODATA;
108 	}
109 
110 	/* We need to swap the IP addresses because otherwise
111 	 * the packet will be dropped.
112 	 */
113 
114 	if (net_pkt_family(pkt) == AF_INET6) {
115 		struct in6_addr addr;
116 
117 		net_ipv6_addr_copy_raw((uint8_t *)&addr, NET_IPV6_HDR(pkt)->src);
118 		net_ipv6_addr_copy_raw(NET_IPV6_HDR(pkt)->src,
119 				       NET_IPV6_HDR(pkt)->dst);
120 		net_ipv6_addr_copy_raw(NET_IPV6_HDR(pkt)->dst, (uint8_t *)&addr);
121 	} else {
122 		struct in_addr addr;
123 
124 		net_ipv4_addr_copy_raw((uint8_t *)&addr, NET_IPV4_HDR(pkt)->src);
125 		net_ipv4_addr_copy_raw(NET_IPV4_HDR(pkt)->src,
126 				       NET_IPV4_HDR(pkt)->dst);
127 		net_ipv4_addr_copy_raw(NET_IPV4_HDR(pkt)->dst, (uint8_t *)&addr);
128 	}
129 
130 	/* We should simulate normal driver meaning that if the packet is
131 	 * properly sent (which is always in this driver), then the packet
132 	 * must be dropped. This is very much needed for TCP packets where
133 	 * the packet is reference counted in various stages of sending.
134 	 */
135 	cloned = net_pkt_rx_clone(pkt, K_MSEC(100));
136 	if (!cloned) {
137 		res = -ENOMEM;
138 		goto out;
139 	}
140 
141 	res = net_recv_data(net_pkt_iface(cloned), cloned);
142 	if (res < 0) {
143 		LOG_ERR("Data receive failed.");
144 	}
145 
146 out:
147 	/* Let the receiving thread run now */
148 	k_yield();
149 
150 	return res;
151 }
152 
153 static struct dummy_api loopback_api = {
154 	.iface_api.init = loopback_init,
155 
156 	.send = loopback_send,
157 };
158 
159 NET_DEVICE_INIT(loopback, "lo",
160 		loopback_dev_init, NULL, NULL, NULL,
161 		CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
162 		&loopback_api, DUMMY_L2,
163 		NET_L2_GET_CTX_TYPE(DUMMY_L2), CONFIG_NET_LOOPBACK_MTU);
164