sandbox: eth: Add a bridge to a real network for sandbox
authorJoe Hershberger <joe.hershberger@ni.com>
Sun, 22 Mar 2015 22:09:21 +0000 (17:09 -0500)
committerSimon Glass <sjg@chromium.org>
Sat, 18 Apr 2015 17:11:13 +0000 (11:11 -0600)
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: Joe Hershberger <joe.hershberger@ni.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/sandbox/Kconfig
arch/sandbox/cpu/Makefile
arch/sandbox/cpu/eth-raw-os.c [new file with mode: 0644]
arch/sandbox/dts/sandbox.dts
arch/sandbox/include/asm/eth-raw-os.h [new file with mode: 0644]
board/sandbox/README.sandbox
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/sandbox-raw.c [new file with mode: 0644]

index e7f55fa82a5cedff7cf1019447445556a3d7848d..52e59d29e81b169d275de7a3c874590be98ae7e5 100644 (file)
@@ -50,4 +50,7 @@ config NETDEVICES
 config DM_ETH
        default y
 
+config ETH_SANDBOX_RAW
+       default y
+
 endmenu
index 7d4410c42a20a6760babfdf28308a3069cd36fec..1b42fee141277b0ca917ae14c265f72d9c73d2b5 100644 (file)
@@ -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 (file)
index 0000000..601205a
--- /dev/null
@@ -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;
+}
index f5e05178e47c7508865fa9dbc9441d4ecb301b5e..553bfbee32f6868a91a200c1e61a4885346e4f6f 100644 (file)
                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 (file)
index 0000000..df60c4f
--- /dev/null
@@ -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 */
index c1f5f7e21fadef55a6a9be49573b98bbe6438072..9f5d3f71d5f10db54853383178be56f2f02c464b 100644 (file)
@@ -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
 -------------
 
index e46e57bc2eccbf93f7bf2ea14eda021717c65172..5bd66ea9d1fd6ff65199f76d395284897bf538a8 100644 (file)
@@ -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
index b9f5db30bb411568fd86e5497b2fb32617f89d77..2f22151c300a94f795b602312a940e3fdab77134 100644 (file)
@@ -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 (file)
index 0000000..435b874
--- /dev/null
@@ -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),
+};