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 
28 /* Allow network tests to control the IP addresses swapping */
29 #if defined(CONFIG_NET_TEST)
30 static bool loopback_dont_swap_addresses;
31 
loopback_enable_address_swap(bool swap_addresses)32 void loopback_enable_address_swap(bool swap_addresses)
33 {
34 	loopback_dont_swap_addresses = !swap_addresses;
35 }
36 #endif /* CONFIG_NET_TEST */
37 
loopback_dev_init(const struct device * dev)38 int loopback_dev_init(const struct device *dev)
39 {
40 	ARG_UNUSED(dev);
41 
42 	return 0;
43 }
44 
loopback_init(struct net_if * iface)45 static void loopback_init(struct net_if *iface)
46 {
47 	struct net_if_addr *ifaddr;
48 
49 	/* RFC 7042, s.2.1.1. address to use in documentation */
50 	net_if_set_link_addr(iface, "\x00\x00\x5e\x00\x53\xff", 6,
51 			     NET_LINK_DUMMY);
52 
53 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
54 		struct net_in_addr ipv4_loopback = NET_INADDR_LOOPBACK_INIT;
55 		struct net_in_addr netmask = { { { 255, 0, 0, 0 } } };
56 
57 		ifaddr = net_if_ipv4_addr_add(iface, &ipv4_loopback,
58 					      NET_ADDR_AUTOCONF, 0);
59 		if (!ifaddr) {
60 			LOG_ERR("Failed to register IPv4 loopback address");
61 		}
62 
63 		net_if_ipv4_set_netmask_by_addr(iface, &ipv4_loopback, &netmask);
64 	}
65 
66 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
67 		struct net_in6_addr ipv6_loopback = NET_IN6ADDR_LOOPBACK_INIT;
68 
69 		ifaddr = net_if_ipv6_addr_add(iface, &ipv6_loopback,
70 					      NET_ADDR_AUTOCONF, 0);
71 		if (!ifaddr) {
72 			LOG_ERR("Failed to register IPv6 loopback address");
73 		}
74 	}
75 
76 	if (IS_ENABLED(CONFIG_NET_INTERFACE_NAME)) {
77 		int ret;
78 
79 		ret = net_if_set_name(iface, "lo");
80 		if (ret < 0) {
81 			LOG_ERR("Failed to set loopback interface name (%d)", ret);
82 		}
83 	}
84 }
85 
86 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
87 static float loopback_packet_drop_ratio = 0.0f;
88 static float loopback_packet_drop_state = 0.0f;
89 static int loopback_packet_dropped_count;
90 
loopback_set_packet_drop_ratio(float ratio)91 int loopback_set_packet_drop_ratio(float ratio)
92 {
93 	if (ratio < 0.0f || ratio > 1.0f) {
94 		return -EINVAL;
95 	}
96 	loopback_packet_drop_ratio = ratio;
97 	return 0;
98 }
99 
loopback_get_num_dropped_packets(void)100 int loopback_get_num_dropped_packets(void)
101 {
102 	return loopback_packet_dropped_count;
103 }
104 
105 #endif
106 
loopback_send(const struct device * dev,struct net_pkt * pkt)107 static int loopback_send(const struct device *dev, struct net_pkt *pkt)
108 {
109 	struct net_pkt *cloned;
110 	int res;
111 
112 	ARG_UNUSED(dev);
113 
114 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
115 	/* Drop packets based on the loopback_packet_drop_ratio
116 	 * a ratio of 0.2 will drop one every 5 packets
117 	 */
118 	loopback_packet_drop_state += loopback_packet_drop_ratio;
119 	if (loopback_packet_drop_state >= 1.0f) {
120 		/* Administrate we dropped a packet */
121 		loopback_packet_drop_state -= 1.0f;
122 		loopback_packet_dropped_count++;
123 		return 0;
124 	}
125 #endif
126 
127 	if (!pkt->frags) {
128 		LOG_ERR("No data to send");
129 		return -ENODATA;
130 	}
131 
132 	/* We should simulate normal driver meaning that if the packet is
133 	 * properly sent (which is always in this driver), then the packet
134 	 * must be dropped. This is very much needed for TCP packets where
135 	 * the packet is reference counted in various stages of sending.
136 	 */
137 	cloned = net_pkt_rx_clone(pkt, K_MSEC(100));
138 	if (!cloned) {
139 		res = -ENOMEM;
140 		goto out;
141 	}
142 
143 	/* We need to swap the IP addresses because otherwise
144 	 * the packet will be dropped.
145 	 *
146 	 * Some of the network tests require that addresses are not swapped so allow
147 	 * the test to control this remotely.
148 	 */
149 	if (!COND_CODE_1(CONFIG_NET_TEST, (loopback_dont_swap_addresses), (false))) {
150 		if (net_pkt_family(pkt) == NET_AF_INET6) {
151 			net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->src,
152 					       NET_IPV6_HDR(pkt)->dst);
153 			net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->dst,
154 					       NET_IPV6_HDR(pkt)->src);
155 		} else {
156 			net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->src,
157 					       NET_IPV4_HDR(pkt)->dst);
158 			net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->dst,
159 					       NET_IPV4_HDR(pkt)->src);
160 		}
161 	}
162 
163 	res = net_recv_data(net_pkt_iface(cloned), cloned);
164 	if (res < 0) {
165 		LOG_ERR("Data receive failed.");
166 	}
167 
168 out:
169 	/* Let the receiving thread run now */
170 	k_yield();
171 
172 	return res;
173 }
174 
175 static struct dummy_api loopback_api = {
176 	.iface_api.init = loopback_init,
177 
178 	.send = loopback_send,
179 };
180 
181 NET_DEVICE_INIT(loopback, "lo",
182 		loopback_dev_init, NULL, NULL, NULL,
183 		CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
184 		&loopback_api, DUMMY_L2,
185 		NET_L2_GET_CTX_TYPE(DUMMY_L2), CONFIG_NET_LOOPBACK_MTU);
186