[U-Boot] [RFC PATCH v4 21/23] sandbox: eth: Add a bridge to a real network for sandbox

Joe Hershberger joe.hershberger at gmail.com
Mon Mar 2 08:17:39 CET 2015


On Sun, Mar 1, 2015 at 12:07 PM, Simon Glass <sjg at chromium.org> wrote:
>
> On 24 February 2015 at 17:02, Joe Hershberger <joe.hershberger at 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 at ni.com>
>
> Reviewed-by: Simon Glass <sjg at 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 at 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 at 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 at 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 at 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 at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot


More information about the U-Boot mailing list