Commit a346ca79 authored by Joe Hershberger's avatar Joe Hershberger Committed by Simon Glass
Browse files

sandbox: eth: Add a bridge to a real network for sandbox



Implement a bridge between U-Boot's network stack and Linux's raw packet
API allowing the sandbox to send and receive packets using the host
machine's network interface.

This raw Ethernet API requires elevated privileges.  You can either run
as root, or you can add the capability needed like so:

sudo /sbin/setcap "CAP_NET_RAW+ep" /path/to/u-boot
Signed-off-by: default avatarJoe Hershberger <joe.hershberger@ni.com>
Reviewed-by: default avatarSimon Glass <sjg@chromium.org>
parent 7ece1c61
......@@ -50,4 +50,7 @@ config NETDEVICES
config DM_ETH
default y
config ETH_SANDBOX_RAW
default y
endmenu
......@@ -8,6 +8,7 @@
#
obj-y := cpu.o os.o start.o state.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += eth-raw-os.o
obj-$(CONFIG_SANDBOX_SDL) += sdl.o
# os.c is build in the system environment, so needs standard includes
......@@ -20,3 +21,12 @@ $(obj)/os.o: $(src)/os.c FORCE
$(call if_changed_dep,cc_os.o)
$(obj)/sdl.o: $(src)/sdl.c FORCE
$(call if_changed_dep,cc_os.o)
# eth-raw-os.c is built in the system env, so needs standard includes
# CFLAGS_REMOVE_eth-raw-os.o cannot be used to drop header include path
quiet_cmd_cc_eth-raw-os.o = CC $(quiet_modtag) $@
cmd_cc_eth-raw-os.o = $(CC) $(filter-out -nostdinc, \
$(patsubst -I%,-idirafter%,$(c_flags))) -c -o $@ $<
$(obj)/eth-raw-os.o: $(src)/eth-raw-os.c FORCE
$(call if_changed_dep,cc_eth-raw-os.o)
/*
* Copyright (c) 2015 National Instruments
*
* (C) Copyright 2015
* Joe Hershberger <joe.hershberger@ni.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <asm/eth-raw-os.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
int sandbox_eth_raw_os_start(const char *ifname, unsigned char *ethmac,
struct eth_sandbox_raw_priv *priv)
{
struct sockaddr_ll *device;
struct packet_mreq mr;
int ret;
int flags;
/* Prepare device struct */
priv->device = malloc(sizeof(struct sockaddr_ll));
if (priv->device == NULL)
return -ENOMEM;
device = priv->device;
memset(device, 0, sizeof(struct sockaddr_ll));
device->sll_ifindex = if_nametoindex(ifname);
device->sll_family = AF_PACKET;
memcpy(device->sll_addr, ethmac, 6);
device->sll_halen = htons(6);
/* Open socket */
priv->sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (priv->sd < 0) {
printf("Failed to open socket: %d %s\n", errno,
strerror(errno));
return -errno;
}
/* Bind to the specified interface */
ret = setsockopt(priv->sd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
strlen(ifname) + 1);
if (ret < 0) {
printf("Failed to bind to '%s': %d %s\n", ifname, errno,
strerror(errno));
return -errno;
}
/* Make the socket non-blocking */
flags = fcntl(priv->sd, F_GETFL, 0);
fcntl(priv->sd, F_SETFL, flags | O_NONBLOCK);
/* Enable promiscuous mode to receive responses meant for us */
mr.mr_ifindex = device->sll_ifindex;
mr.mr_type = PACKET_MR_PROMISC;
ret = setsockopt(priv->sd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
&mr, sizeof(mr));
if (ret < 0) {
struct ifreq ifr;
printf("Failed to set promiscuous mode: %d %s\n"
"Falling back to the old \"flags\" way...\n",
errno, strerror(errno));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(priv->sd, SIOCGIFFLAGS, &ifr) < 0) {
printf("Failed to read flags: %d %s\n", errno,
strerror(errno));
return -errno;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(priv->sd, SIOCSIFFLAGS, &ifr) < 0) {
printf("Failed to write flags: %d %s\n", errno,
strerror(errno));
return -errno;
}
}
return 0;
}
int sandbox_eth_raw_os_send(void *packet, int length,
const struct eth_sandbox_raw_priv *priv)
{
int retval;
if (!priv->sd || !priv->device)
return -EINVAL;
retval = sendto(priv->sd, packet, length, 0,
(struct sockaddr *)priv->device,
sizeof(struct sockaddr_ll));
if (retval < 0) {
printf("Failed to send packet: %d %s\n", errno,
strerror(errno));
return -errno;
}
return retval;
}
int sandbox_eth_raw_os_recv(void *packet, int *length,
const struct eth_sandbox_raw_priv *priv)
{
int retval;
int saddr_size;
if (!priv->sd || !priv->device)
return -EINVAL;
saddr_size = sizeof(struct sockaddr);
retval = recvfrom(priv->sd, packet, 1536, 0,
(struct sockaddr *)priv->device,
(socklen_t *)&saddr_size);
*length = 0;
if (retval >= 0) {
*length = retval;
return 0;
}
/* The socket is non-blocking, so expect EAGAIN when there is no data */
if (errno == EAGAIN)
return 0;
return -errno;
}
void sandbox_eth_raw_os_stop(struct eth_sandbox_raw_priv *priv)
{
free(priv->device);
priv->device = NULL;
close(priv->sd);
priv->sd = -1;
}
......@@ -206,4 +206,10 @@
reg = <0x10002000 0x1000>;
fake-host-hwaddr = [00 00 66 44 22 00];
};
eth@80000000 {
compatible = "sandbox,eth-raw";
reg = <0x80000000 0x1000>;
host-raw-interface = "eth0";
};
};
/*
* Copyright (c) 2015 National Instruments
*
* (C) Copyright 2015
* Joe Hershberger <joe.hershberger@ni.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __ETH_RAW_OS_H
#define __ETH_RAW_OS_H
/**
* struct eth_sandbox_raw_priv - raw socket session
*
* sd: socket descriptor - the open socket during a session
* device: struct sockaddr_ll - the host interface packets move to/from
*/
struct eth_sandbox_raw_priv {
int sd;
void *device;
};
int sandbox_eth_raw_os_start(const char *ifname, unsigned char *ethmac,
struct eth_sandbox_raw_priv *priv);
int sandbox_eth_raw_os_send(void *packet, int length,
const struct eth_sandbox_raw_priv *priv);
int sandbox_eth_raw_os_recv(void *packet, int *length,
const struct eth_sandbox_raw_priv *priv);
void sandbox_eth_raw_os_stop(struct eth_sandbox_raw_priv *priv);
#endif /* __ETH_RAW_OS_H */
......@@ -190,6 +190,58 @@ Also sandbox uses generic board (CONFIG_SYS_GENERIC_BOARD) and supports
driver model (CONFIG_DM) and associated commands.
Linux RAW Networking Bridge
---------------------------
The sandbox_eth_raw driver bridges traffic between the bottom of the network
stack and the RAW sockets API in Linux. This allows much of the U-Boot network
functionality to be tested in sandbox against real network traffic.
For Ethernet network adapters, the bridge utilizes the RAW AF_PACKET API. This
is needed to get access to the lowest level of the network stack in Linux. This
means that all of the Ethernet frame is included. This allows the U-Boot network
stack to be fully used. In other words, nothing about the Linux network stack is
involved in forming the packets that end up on the wire. To receive the
responses to packets sent from U-Boot the network interface has to be set to
promiscuous mode so that the network card won't filter out packets not destined
for its configured (on Linux) MAC address.
The RAW sockets Ethernet API requires elevated privileges in Linux. You can
either run as root, or you can add the capability needed like so:
sudo /sbin/setcap "CAP_NET_RAW+ep" /path/to/u-boot
The default device tree for sandbox includes an entry for eth0 on the sandbox
host machine whose alias is "eth1". The following are a few examples of network
operations being tested on the eth0 interface.
sudo /path/to/u-boot -D
DHCP
....
set autoload no
set ethact eth1
dhcp
PING
....
set autoload no
set ethact eth1
dhcp
ping $gatewayip
TFTP
....
set autoload no
set ethact eth1
dhcp
set serverip WWW.XXX.YYY.ZZZ
tftpboot u-boot.bin
SPI Emulation
-------------
......
......@@ -29,4 +29,14 @@ config ETH_SANDBOX
This driver is particularly useful in the test/dm/eth.c tests
config ETH_SANDBOX_RAW
depends on DM_ETH && SANDBOX
default y
bool "Sandbox: Bridge to Linux Raw Sockets"
help
This driver is a bridge from the bottom of the network stack
in U-Boot to the RAW AF_PACKET API in Linux. This allows real
network traffic to be tested from within sandbox. See
board/sandbox/README.sandbox for more details.
endif # NETDEVICES
......@@ -52,6 +52,7 @@ obj-$(CONFIG_PCNET) += pcnet.o
obj-$(CONFIG_RTL8139) += rtl8139.o
obj-$(CONFIG_RTL8169) += rtl8169.o
obj-$(CONFIG_ETH_SANDBOX) += sandbox.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o
obj-$(CONFIG_SH_ETHER) += sh_eth.o
obj-$(CONFIG_SMC91111) += smc91111.o
obj-$(CONFIG_SMC911X) += smc911x.o
......
/*
* Copyright (c) 2015 National Instruments
*
* (C) Copyright 2015
* Joe Hershberger <joe.hershberger@ni.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <asm/eth-raw-os.h>
#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <net.h>
DECLARE_GLOBAL_DATA_PTR;
static int sb_eth_raw_start(struct udevice *dev)
{
struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
struct eth_pdata *pdata = dev_get_platdata(dev);
const char *interface;
debug("eth_sandbox_raw: Start\n");
interface = fdt_getprop(gd->fdt_blob, dev->of_offset,
"host-raw-interface", NULL);
if (interface == NULL)
return -EINVAL;
return sandbox_eth_raw_os_start(interface, pdata->enetaddr, priv);
}
static int sb_eth_raw_send(struct udevice *dev, void *packet, int length)
{
struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
debug("eth_sandbox_raw: Send packet %d\n", length);
return sandbox_eth_raw_os_send(packet, length, priv);
}
static int sb_eth_raw_recv(struct udevice *dev, uchar **packetp)
{
struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
int retval;
int length;
retval = sandbox_eth_raw_os_recv(net_rx_packets[0], &length, priv);
if (!retval && length) {
debug("eth_sandbox_raw: received packet %d\n",
length);
*packetp = net_rx_packets[0];
return length;
}
return retval;
}
static void sb_eth_raw_stop(struct udevice *dev)
{
struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
debug("eth_sandbox_raw: Stop\n");
sandbox_eth_raw_os_stop(priv);
}
static const struct eth_ops sb_eth_raw_ops = {
.start = sb_eth_raw_start,
.send = sb_eth_raw_send,
.recv = sb_eth_raw_recv,
.stop = sb_eth_raw_stop,
};
static int sb_eth_raw_ofdata_to_platdata(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
pdata->iobase = dev_get_addr(dev);
return 0;
}
static const struct udevice_id sb_eth_raw_ids[] = {
{ .compatible = "sandbox,eth-raw" },
{ }
};
U_BOOT_DRIVER(eth_sandbox_raw) = {
.name = "eth_sandbox_raw",
.id = UCLASS_ETH,
.of_match = sb_eth_raw_ids,
.ofdata_to_platdata = sb_eth_raw_ofdata_to_platdata,
.ops = &sb_eth_raw_ops,
.priv_auto_alloc_size = sizeof(struct eth_sandbox_raw_priv),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment