zephyr/drivers/net/loopback.c
Jukka Rissanen 7bf9f599b1 drivers: net: loopback: Allow tests to control address swapping
Some of the network tests require that source and destination
addresses are not swapped so allow test to control the address
swapping from the test.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
2024-11-18 19:29:58 -05:00

176 lines
4.3 KiB
C

/*
* 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");
}
}
}
#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);