You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
4.4 KiB
185 lines
4.4 KiB
/* |
|
* Copyright (c) 2015 Intel Corporation |
|
* Copyright (c) 2017 Linaro Limited |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/** |
|
* @file |
|
* |
|
* Network loopback interface implementation. |
|
*/ |
|
|
|
#define LOG_MODULE_NAME netlo |
|
#define LOG_LEVEL CONFIG_NET_LOOPBACK_LOG_LEVEL |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
|
|
|
#include <zephyr/net/net_pkt.h> |
|
#include <zephyr/net_buf.h> |
|
#include <zephyr/net/net_ip.h> |
|
#include <zephyr/net/net_if.h> |
|
#include <zephyr/net/loopback.h> |
|
|
|
#include <zephyr/net/dummy.h> |
|
|
|
/* Allow network tests to control the IP addresses swapping */ |
|
#if defined(CONFIG_NET_TEST) |
|
static bool loopback_dont_swap_addresses; |
|
|
|
void loopback_enable_address_swap(bool swap_addresses) |
|
{ |
|
loopback_dont_swap_addresses = !swap_addresses; |
|
} |
|
#endif /* CONFIG_NET_TEST */ |
|
|
|
int loopback_dev_init(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
|
|
return 0; |
|
} |
|
|
|
static void loopback_init(struct net_if *iface) |
|
{ |
|
struct net_if_addr *ifaddr; |
|
|
|
/* RFC 7042, s.2.1.1. address to use in documentation */ |
|
net_if_set_link_addr(iface, "\x00\x00\x5e\x00\x53\xff", 6, |
|
NET_LINK_DUMMY); |
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4)) { |
|
struct in_addr ipv4_loopback = INADDR_LOOPBACK_INIT; |
|
struct in_addr netmask = { { { 255, 0, 0, 0 } } }; |
|
|
|
ifaddr = net_if_ipv4_addr_add(iface, &ipv4_loopback, |
|
NET_ADDR_AUTOCONF, 0); |
|
if (!ifaddr) { |
|
LOG_ERR("Failed to register IPv4 loopback address"); |
|
} |
|
|
|
net_if_ipv4_set_netmask_by_addr(iface, &ipv4_loopback, &netmask); |
|
} |
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6)) { |
|
struct in6_addr ipv6_loopback = IN6ADDR_LOOPBACK_INIT; |
|
|
|
ifaddr = net_if_ipv6_addr_add(iface, &ipv6_loopback, |
|
NET_ADDR_AUTOCONF, 0); |
|
if (!ifaddr) { |
|
LOG_ERR("Failed to register IPv6 loopback address"); |
|
} |
|
} |
|
|
|
if (IS_ENABLED(CONFIG_NET_INTERFACE_NAME)) { |
|
int ret; |
|
|
|
ret = net_if_set_name(iface, "lo"); |
|
if (ret < 0) { |
|
LOG_ERR("Failed to set loopback interface name (%d)", ret); |
|
} |
|
} |
|
} |
|
|
|
#ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP |
|
static float loopback_packet_drop_ratio = 0.0f; |
|
static float loopback_packet_drop_state = 0.0f; |
|
static int loopback_packet_dropped_count; |
|
|
|
int loopback_set_packet_drop_ratio(float ratio) |
|
{ |
|
if (ratio < 0.0f || ratio > 1.0f) { |
|
return -EINVAL; |
|
} |
|
loopback_packet_drop_ratio = ratio; |
|
return 0; |
|
} |
|
|
|
int loopback_get_num_dropped_packets(void) |
|
{ |
|
return loopback_packet_dropped_count; |
|
} |
|
|
|
#endif |
|
|
|
static int loopback_send(const struct device *dev, struct net_pkt *pkt) |
|
{ |
|
struct net_pkt *cloned; |
|
int res; |
|
|
|
ARG_UNUSED(dev); |
|
|
|
#ifdef CONFIG_NET_LOOPBACK_SIMULATE_PACKET_DROP |
|
/* Drop packets based on the loopback_packet_drop_ratio |
|
* a ratio of 0.2 will drop one every 5 packets |
|
*/ |
|
loopback_packet_drop_state += loopback_packet_drop_ratio; |
|
if (loopback_packet_drop_state >= 1.0f) { |
|
/* Administrate we dropped a packet */ |
|
loopback_packet_drop_state -= 1.0f; |
|
loopback_packet_dropped_count++; |
|
return 0; |
|
} |
|
#endif |
|
|
|
if (!pkt->frags) { |
|
LOG_ERR("No data to send"); |
|
return -ENODATA; |
|
} |
|
|
|
/* We should simulate normal driver meaning that if the packet is |
|
* properly sent (which is always in this driver), then the packet |
|
* must be dropped. This is very much needed for TCP packets where |
|
* the packet is reference counted in various stages of sending. |
|
*/ |
|
cloned = net_pkt_rx_clone(pkt, K_MSEC(100)); |
|
if (!cloned) { |
|
res = -ENOMEM; |
|
goto out; |
|
} |
|
|
|
/* We need to swap the IP addresses because otherwise |
|
* the packet will be dropped. |
|
* |
|
* Some of the network tests require that addresses are not swapped so allow |
|
* the test to control this remotely. |
|
*/ |
|
if (!COND_CODE_1(CONFIG_NET_TEST, (loopback_dont_swap_addresses), (false))) { |
|
if (net_pkt_family(pkt) == AF_INET6) { |
|
net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->src, |
|
NET_IPV6_HDR(pkt)->dst); |
|
net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->dst, |
|
NET_IPV6_HDR(pkt)->src); |
|
} else { |
|
net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->src, |
|
NET_IPV4_HDR(pkt)->dst); |
|
net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->dst, |
|
NET_IPV4_HDR(pkt)->src); |
|
} |
|
} |
|
|
|
res = net_recv_data(net_pkt_iface(cloned), cloned); |
|
if (res < 0) { |
|
LOG_ERR("Data receive failed."); |
|
} |
|
|
|
out: |
|
/* Let the receiving thread run now */ |
|
k_yield(); |
|
|
|
return res; |
|
} |
|
|
|
static struct dummy_api loopback_api = { |
|
.iface_api.init = loopback_init, |
|
|
|
.send = loopback_send, |
|
}; |
|
|
|
NET_DEVICE_INIT(loopback, "lo", |
|
loopback_dev_init, NULL, NULL, NULL, |
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, |
|
&loopback_api, DUMMY_L2, |
|
NET_L2_GET_CTX_TYPE(DUMMY_L2), CONFIG_NET_LOOPBACK_MTU);
|
|
|