zephyr/drivers/net/loopback.c
Jukka Rissanen 0436ad9e78 drivers: net: loopback: Simulate real hw device
Instead of passing net_pkt as is to the receiving side of the
interface, clone the sent packet and drop the sent one.
This is needed mainly in TCP where passing the same packet from
sending to receiving side is causing havoc.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
2017-11-15 12:27:14 +02:00

103 lines
2.2 KiB
C

/*
* Copyright (c) 2015 Intel Corporation
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Network loopback interface implementation.
*/
#define SYS_LOG_DOMAIN "netlo"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_NETLO_LEVEL
#include <logging/sys_log.h>
#include <misc/printk.h>
#include <net/net_pkt.h>
#include <net/buf.h>
#include <net/net_ip.h>
#include <net/net_if.h>
int loopback_dev_init(struct device *dev)
{
return 0;
}
static void loopback_init(struct net_if *iface)
{
/* 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);
}
static int loopback_send(struct net_if *iface, struct net_pkt *pkt)
{
struct net_pkt *cloned;
int res;
if (!pkt->frags) {
SYS_LOG_ERR("No data to send");
return -ENODATA;
}
/* We need to swap the IP addresses because otherwise
* the packet will be dropped.
*/
if (net_pkt_family(pkt) == AF_INET6) {
struct in6_addr addr;
net_ipaddr_copy(&addr, &NET_IPV6_HDR(pkt)->src);
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src,
&NET_IPV6_HDR(pkt)->dst);
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, &addr);
} else {
struct in_addr addr;
net_ipaddr_copy(&addr, &NET_IPV4_HDR(pkt)->src);
net_ipaddr_copy(&NET_IPV4_HDR(pkt)->src,
&NET_IPV4_HDR(pkt)->dst);
net_ipaddr_copy(&NET_IPV4_HDR(pkt)->dst, &addr);
}
/* 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_clone(pkt, MSEC(100));
if (!cloned) {
res = -ENOMEM;
goto out;
}
res = net_recv_data(iface, cloned);
if (res < 0) {
SYS_LOG_ERR("Data receive failed.");
goto out;
}
out:
net_pkt_unref(pkt);
/* Let the receiving thread run now */
k_yield();
return res;
}
static struct net_if_api loopback_if_api = {
.init = loopback_init,
.send = loopback_send,
};
NET_DEVICE_INIT(loopback, "lo",
loopback_dev_init, NULL, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&loopback_if_api, DUMMY_L2,
NET_L2_GET_CTX_TYPE(DUMMY_L2), 536);