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