summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/sandbox/Kconfig3
-rw-r--r--arch/sandbox/cpu/Makefile10
-rw-r--r--arch/sandbox/cpu/eth-raw-os.c140
-rw-r--r--arch/sandbox/dts/sandbox.dts6
-rw-r--r--arch/sandbox/include/asm/eth-raw-os.h32
-rw-r--r--board/sandbox/README.sandbox52
-rw-r--r--drivers/net/Kconfig10
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/sandbox-raw.c98
9 files changed, 352 insertions, 0 deletions
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index e7f55fa82a..52e59d29e8 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -50,4 +50,7 @@ config NETDEVICES
config DM_ETH
default y
+config ETH_SANDBOX_RAW
+ default y
+
endmenu
diff --git a/arch/sandbox/cpu/Makefile b/arch/sandbox/cpu/Makefile
index 7d4410c42a..1b42fee141 100644
--- a/arch/sandbox/cpu/Makefile
+++ b/arch/sandbox/cpu/Makefile
@@ -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)
diff --git a/arch/sandbox/cpu/eth-raw-os.c b/arch/sandbox/cpu/eth-raw-os.c
new file mode 100644
index 0000000000..601205a60b
--- /dev/null
+++ b/arch/sandbox/cpu/eth-raw-os.c
@@ -0,0 +1,140 @@
+/*
+ * 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;
+}
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index f5e05178e4..553bfbee32 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -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";
+ };
};
diff --git a/arch/sandbox/include/asm/eth-raw-os.h b/arch/sandbox/include/asm/eth-raw-os.h
new file mode 100644
index 0000000000..df60c4f3b8
--- /dev/null
+++ b/arch/sandbox/include/asm/eth-raw-os.h
@@ -0,0 +1,32 @@
+/*
+ * 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 */
diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox
index c1f5f7e21f..9f5d3f71d5 100644
--- a/board/sandbox/README.sandbox
+++ b/board/sandbox/README.sandbox
@@ -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
-------------
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e46e57bc2e..5bd66ea9d1 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -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
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b9f5db30bb..2f22151c30 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -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
diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c
new file mode 100644
index 0000000000..435b8745c3
--- /dev/null
+++ b/drivers/net/sandbox-raw.c
@@ -0,0 +1,98 @@
+/*
+ * 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),
+};