From patchwork Wed Mar 4 02:41:18 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Hershberger X-Patchwork-Id: 446040 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 9CDB214007D for ; Wed, 4 Mar 2015 13:46:52 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id EEA443930; Wed, 4 Mar 2015 03:45:32 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id He66FVyu_7xA; Wed, 4 Mar 2015 03:45:32 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4FC7F4B6DB; Wed, 4 Mar 2015 03:44:21 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 22E8E4B624 for ; Wed, 4 Mar 2015 03:43:23 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cj4eHPF8x36p for ; Wed, 4 Mar 2015 03:43:22 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from ni.com (skprod3.natinst.com [130.164.80.24]) by theia.denx.de (Postfix) with ESMTPS id 023834B627 for ; Wed, 4 Mar 2015 03:43:18 +0100 (CET) Received: from us-aus-mgwout2.amer.corp.natinst.com (nb-snip2-1338.natinst.com [130.164.19.135]) by us-aus-skprod3.natinst.com (8.15.0.59/8.15.0.59) with ESMTP id t242hG7U023020; Tue, 3 Mar 2015 20:43:16 -0600 Received: from linux-xvxi.natinst.com ([130.164.14.198]) by us-aus-mgwout2.amer.corp.natinst.com (Lotus Domino Release 8.5.3FP6) with ESMTP id 2015030320431669-645248 ; Tue, 3 Mar 2015 20:43:16 -0600 From: Joe Hershberger To: u-boot@lists.denx.de Date: Tue, 3 Mar 2015 20:41:18 -0600 Message-Id: <1425436881-10323-25-git-send-email-joe.hershberger@ni.com> X-Mailer: git-send-email 1.7.11.5 In-Reply-To: <1425436881-10323-1-git-send-email-joe.hershberger@ni.com> References: <1424822552-4366-1-git-send-email-joe.hershberger@ni.com> <1425436881-10323-1-git-send-email-joe.hershberger@ni.com> X-MIMETrack: Itemize by SMTP Server on US-AUS-MGWOut2/AUS/H/NIC(Release 8.5.3FP6|November 21, 2013) at 03/03/2015 08:43:16 PM, Serialize by Router on US-AUS-MGWOut2/AUS/H/NIC(Release 8.5.3FP6|November 21, 2013) at 03/03/2015 08:43:16 PM, Serialize complete at 03/03/2015 08:43:16 PM X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2015-03-03_08:, , signatures=0 Cc: Joe Hershberger Subject: [U-Boot] [PATCH v5 24/27] sandbox: eth: Add a bridge to a real network for sandbox X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 Reviewed-by: Simon Glass --- 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/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 + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 + * + * 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 + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include + +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), +};