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

Simon Glass sjg at chromium.org
Sun Mar 1 19:07:55 CET 2015


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 $@ $<
> +
> +$(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.

> +       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?

> +       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.

> +}
> +
> +void sandbox_eth_raw_os_halt(struct eth_sandbox_raw_priv *priv)
> +{
> +       free((struct sockaddr_ll *)priv->device);

Don't need that cast?

> +       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.

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.

Useful examples would be:

- ping
- bootp
- tftpboot

and how to use raw/normal device.

> +
> +
>  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.

> +
> +       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.

> +
> +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


More information about the U-Boot mailing list