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 in_addr ipv4_loopback = INADDR_LOOPBACK_INIT;
55 struct 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 in6_addr ipv6_loopback = 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
77 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
78 static float loopback_packet_drop_ratio = 0.0f;
79 static float loopback_packet_drop_state = 0.0f;
80 static int loopback_packet_dropped_count;
81
loopback_set_packet_drop_ratio(float ratio)82 int loopback_set_packet_drop_ratio(float ratio)
83 {
84 if (ratio < 0.0f || ratio > 1.0f) {
85 return -EINVAL;
86 }
87 loopback_packet_drop_ratio = ratio;
88 return 0;
89 }
90
loopback_get_num_dropped_packets(void)91 int loopback_get_num_dropped_packets(void)
92 {
93 return loopback_packet_dropped_count;
94 }
95
96 #endif
97
loopback_send(const struct device * dev,struct net_pkt * pkt)98 static int loopback_send(const struct device *dev, struct net_pkt *pkt)
99 {
100 struct net_pkt *cloned;
101 int res;
102
103 ARG_UNUSED(dev);
104
105 #ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP
106 /* Drop packets based on the loopback_packet_drop_ratio
107 * a ratio of 0.2 will drop one every 5 packets
108 */
109 loopback_packet_drop_state += loopback_packet_drop_ratio;
110 if (loopback_packet_drop_state >= 1.0f) {
111 /* Administrate we dropped a packet */
112 loopback_packet_drop_state -= 1.0f;
113 loopback_packet_dropped_count++;
114 return 0;
115 }
116 #endif
117
118 if (!pkt->frags) {
119 LOG_ERR("No data to send");
120 return -ENODATA;
121 }
122
123 /* We should simulate normal driver meaning that if the packet is
124 * properly sent (which is always in this driver), then the packet
125 * must be dropped. This is very much needed for TCP packets where
126 * the packet is reference counted in various stages of sending.
127 */
128 cloned = net_pkt_rx_clone(pkt, K_MSEC(100));
129 if (!cloned) {
130 res = -ENOMEM;
131 goto out;
132 }
133
134 /* We need to swap the IP addresses because otherwise
135 * the packet will be dropped.
136 *
137 * Some of the network tests require that addresses are not swapped so allow
138 * the test to control this remotely.
139 */
140 if (!COND_CODE_1(CONFIG_NET_TEST, (loopback_dont_swap_addresses), (false))) {
141 if (net_pkt_family(pkt) == AF_INET6) {
142 net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->src,
143 NET_IPV6_HDR(pkt)->dst);
144 net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->dst,
145 NET_IPV6_HDR(pkt)->src);
146 } else {
147 net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->src,
148 NET_IPV4_HDR(pkt)->dst);
149 net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->dst,
150 NET_IPV4_HDR(pkt)->src);
151 }
152 }
153
154 res = net_recv_data(net_pkt_iface(cloned), cloned);
155 if (res < 0) {
156 LOG_ERR("Data receive failed.");
157 }
158
159 out:
160 /* Let the receiving thread run now */
161 k_yield();
162
163 return res;
164 }
165
166 static struct dummy_api loopback_api = {
167 .iface_api.init = loopback_init,
168
169 .send = loopback_send,
170 };
171
172 NET_DEVICE_INIT(loopback, "lo",
173 loopback_dev_init, NULL, NULL, NULL,
174 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
175 &loopback_api, DUMMY_L2,
176 NET_L2_GET_CTX_TYPE(DUMMY_L2), CONFIG_NET_LOOPBACK_MTU);
177