diff mbox

[U-Boot,v5,24/27] sandbox: eth: Add a bridge to a real network for sandbox

Message ID 1425436881-10323-25-git-send-email-joe.hershberger@ni.com
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Joe Hershberger March 4, 2015, 2:41 a.m. UTC
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" u-boot

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>

---

Changes in v5:
-Added fallback for setting promiscuous mode
-Added help to Kconfig
-Added more details and examples in the README
-Check for NULL when reading fdt for host interface
-Check for malloc failure
-Remove cast of pointer passed to free
-Remove the empty sb_eth_raw_remove function
-Return -errno in from send and recv
-Return errno from recv
-Set the socket to non-blocking
-Use net_rx_packets instead of a stack buffer

Changes in v4:
-Add comments to priv struct definition
-Added comments to README.sandbox
-Clean up the interface to sandbox's eth-raw-os by passing priv to raw-os
-Cleanup var definition order
-Fixed the MAC address limitation (now all traffic uses MAC address from env)
-Move os file to arch
-Moved config to Kconfig
-Use accessors for platdata and priv

Changes in v3:
-Made the os raw packet support for sandbox eth build and work.

Changes in v2:
-Added the raw packet proof-of-concept patch.

 arch/sandbox/Kconfig                  |   3 +
 arch/sandbox/cpu/Makefile             |  10 +++
 arch/sandbox/cpu/eth-raw-os.c         | 140 ++++++++++++++++++++++++++++++++++
 arch/sandbox/dts/sandbox.dts          |   6 ++
 arch/sandbox/include/asm/eth-raw-os.h |  32 ++++++++
 board/sandbox/README.sandbox          |  52 +++++++++++++
 drivers/net/Kconfig                   |  10 +++
 drivers/net/Makefile                  |   1 +
 drivers/net/sandbox-raw.c             |  98 ++++++++++++++++++++++++
 9 files changed, 352 insertions(+)
 create mode 100644 arch/sandbox/cpu/eth-raw-os.c
 create mode 100644 arch/sandbox/include/asm/eth-raw-os.h
 create mode 100644 drivers/net/sandbox-raw.c

Comments

Simon Glass March 4, 2015, 6:35 p.m. UTC | #1
Hi Joe,

On 3 March 2015 at 19:41, Joe Hershberger <joe.hershberger@ni.com> wrote:
> 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" u-boot
>
> Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>

Reviewed-by: Simon Glass <sjg@chromium.org>

Nits below.

>
> ---
>
> Changes in v5:
> -Added fallback for setting promiscuous mode
> -Added help to Kconfig
> -Added more details and examples in the README
> -Check for NULL when reading fdt for host interface
> -Check for malloc failure
> -Remove cast of pointer passed to free
> -Remove the empty sb_eth_raw_remove function
> -Return -errno in from send and recv
> -Return errno from recv
> -Set the socket to non-blocking
> -Use net_rx_packets instead of a stack buffer
>
> Changes in v4:
> -Add comments to priv struct definition
> -Added comments to README.sandbox
> -Clean up the interface to sandbox's eth-raw-os by passing priv to raw-os
> -Cleanup var definition order
> -Fixed the MAC address limitation (now all traffic uses MAC address from env)
> -Move os file to arch
> -Moved config to Kconfig
> -Use accessors for platdata and priv
>
> Changes in v3:
> -Made the os raw packet support for sandbox eth build and work.
>
> Changes in v2:
> -Added the raw packet proof-of-concept patch.
>
>  arch/sandbox/Kconfig                  |   3 +
>  arch/sandbox/cpu/Makefile             |  10 +++
>  arch/sandbox/cpu/eth-raw-os.c         | 140 ++++++++++++++++++++++++++++++++++
>  arch/sandbox/dts/sandbox.dts          |   6 ++
>  arch/sandbox/include/asm/eth-raw-os.h |  32 ++++++++
>  board/sandbox/README.sandbox          |  52 +++++++++++++
>  drivers/net/Kconfig                   |  10 +++
>  drivers/net/Makefile                  |   1 +
>  drivers/net/sandbox-raw.c             |  98 ++++++++++++++++++++++++
>  9 files changed, 352 insertions(+)
>  create mode 100644 arch/sandbox/cpu/eth-raw-os.c
>  create mode 100644 arch/sandbox/include/asm/eth-raw-os.h
>  create mode 100644 drivers/net/sandbox-raw.c
>


> diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox
> index c1f5f7e..aedf05a 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

s/u-boot/U-Boot/g

(there's one in the Kconfig also)

> +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" u-boot

I think the last param is the filename, is that right? Would be good
to clarify if this is a full path, etc.

> +
> +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 u-boot -d u-boot.dtb

or sudo 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 e46e57b..601366f 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

Regards,
Simon
diff mbox

Patch

diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig
index 186b58d..f84b3fc 100644
--- a/arch/sandbox/Kconfig
+++ b/arch/sandbox/Kconfig
@@ -43,4 +43,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 7d4410c..1b42fee 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 0000000..601205a
--- /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 c2a3304..8002196 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -186,4 +186,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 0000000..df60c4f
--- /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 c1f5f7e..aedf05a 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" 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 u-boot -d u-boot.dtb
+
+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 e46e57b..601366f 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 15dc431..2659a8a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -51,6 +51,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 0000000..435b874
--- /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),
+};