
On Sun, Mar 1, 2015 at 12:07 PM, Simon Glass sjg@chromium.org wrote:
On 24 February 2015 at 17:02, 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
A few nits below.
Changes in v4: -Added comments to README.sandbox -Use accessors for platdata and priv -Add comments to priv struct definition -Move os file to arch -Cleanup var definition order -Moved config to Kconfig -Clean up the interface to sandbox's eth-raw-os by passing priv to
raw-os
-Fixed the MAC address limitation (now all traffic uses MAC address
from env)
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 | 102
+++++++++++++++++++++++++++++++++
arch/sandbox/dts/sandbox.dts | 6 ++ arch/sandbox/include/asm/eth-raw-os.h | 32 +++++++++++ board/sandbox/README.sandbox | 13 +++++ drivers/net/Kconfig | 5 ++ drivers/net/Makefile | 1 + drivers/net/sandbox-raw.c | 105
++++++++++++++++++++++++++++++++++
9 files changed, 277 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/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 $@ lt;
+$(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..9218f94 --- /dev/null +++ b/arch/sandbox/cpu/eth-raw-os.c @@ -0,0 +1,102 @@ +/*
- 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 <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_init(const char *ifname, unsigned char *ethmac,
struct eth_sandbox_raw_priv *priv)
+{
struct sockaddr_ll *device;
struct packet_mreq mr;
/* Prepare device struct */
priv->device = malloc(sizeof(struct sockaddr_ll));
This calls U-Boot's malloc() and it can return NULL, so you should return -ENOMEM in that case.
OK
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 */
setsockopt(priv->sd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
strlen(ifname) + 1);
/* Enable promiscuous mode to receive responses meant for us */
mr.mr_ifindex = device->sll_ifindex;
mr.mr_type = PACKET_MR_PROMISC;
setsockopt(priv->sd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
&mr, sizeof(mr));
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 here?
OK
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;
}
return retval;
return -errno here? At present you are returning -1 I think, which is
-EPERM.
OK
+}
+void sandbox_eth_raw_os_halt(struct eth_sandbox_raw_priv *priv) +{
free((struct sockaddr_ll *)priv->device);
Don't need that cast?
Hmm... good point.
priv->device = NULL;
close(priv->sd);
priv->sd = -1;
+} diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 235dcc0..b6762f4 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -186,4 +186,10 @@ reg = <0x10002000 0x1000>; fake-host-hwaddr = <0x00 0x00 0x66 0x44 0x22 0x00>; };
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..d92b72c --- /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_init(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_halt(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..c4c3139 100644 --- a/board/sandbox/README.sandbox +++ b/board/sandbox/README.sandbox @@ -190,6 +190,19 @@ 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.
+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
This is so cool.
:D
Can you give some examples here? For me, ping seems to work, but I can't use bootp. Is that what the raw mode is for? How do I enable it? I tried setting ethact but am not sure what I am doing.
Sorry it's not clear. I'll update it, but if you want to play with it in the mean time, I'll explain here. But first, what were the symptoms where you say you couldn't use bootp?
This patch only supports 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 device tree as added by this patch should be everything you need to use eth0 on the host machine.
To contrast, the patch that adds support for the 'lo' interface cannot use the RAW AF_PACKET API because the lo interface doesn't support Ethernet-level traffic. It is a higher-level interface that is expected only to be used at the AF_INET level of the API. As such, the most raw we can get on that interface is the RAW AF_INET API on UDP. This allows us to set the IP_HDRINCL option to include everything except the Ethernet header in the packets we send and receive.
There is no decision to make. The local code path will only work on the 'lo' interface and the not-local code path will only work on non-'lo' interface. This check is explicit in sb_eth_raw_start().
Useful examples would be:
- ping
- bootp
- tftpboot
I have tested all 3 of these with just the device tree included in this patch. All that you need to do is set the eth1addr to something. Set ethact to "eth1".
You can then use dhcp (typically need to set autoload to no first) or set a static IP. Then you can ping or tftp.
If you set ethact to eth5 (the localhost interface in the device tree in the 'lo' support patch) then don't expect ping to work, but you should be able to tftpboot from the local TFTP server.
and how to use raw/normal device.
I hope this is clear above.
SPI Emulation
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b08746a..dcbfa8a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -20,4 +20,9 @@ config ETH_SANDBOX default y bool "Sandbox: Mocked Ethernet driver"
+config ETH_SANDBOX_RAW
depends on DM_ETH && SANDBOX
default y
bool "Sandbox: Bridge to Linux Raw Sockets"
This needs some help.
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..01b33a9 --- /dev/null +++ b/drivers/net/sandbox-raw.c @@ -0,0 +1,105 @@ +/*
- 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);
int retval;
const char *interface;
debug("eth_sandbox_raw: Start\n");
interface = fdt_getprop(gd->fdt_blob, dev->of_offset,
"host-raw-interface", NULL);
This can return NULL, so you should return -EINVAL in that case.
OK
retval = sandbox_eth_raw_os_init(interface, pdata->enetaddr,
priv);
return retval;
+}
+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) +{
struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
int retval;
uchar buffer[PKTSIZE];
int length;
retval = sandbox_eth_raw_os_recv(buffer, &length, priv);
if (!retval && length) {
debug("eth_sandbox_raw: received packet %d\n",
length);
NetReceive(buffer, length);
}
return 0;
+}
+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_halt(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_remove(struct udevice *dev) +{
return 0;
+}
You can drop this function.
Good point.
+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,
.remove = sb_eth_raw_remove,
.ops = &sb_eth_raw_ops,
.priv_auto_alloc_size = sizeof(struct eth_sandbox_raw_priv),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
1.7.11.5
Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot