[U-Boot] [RFC PATCH v4 23/23] sandbox: eth: Add support for using the 'lo' interface

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


Hi Joe,

On 24 February 2015 at 17:02, Joe Hershberger <joe.hershberger at ni.com> wrote:
> The 'lo' interface on Linux doesn't support thinks like ARP or
> link-layer access like we use to talk to a normal network interface.
> A higher-level network API must be used to access localhost.
>
> As written, this interface is limited to not supporting ICMP since the
> API doesn't allow the socket to be opened for all IP traffic and be able
> to receive at the same time. UDP is far more useful to test with, so it
> was selected over ICMP. Ping won't work, but things like TFTP should
> work.
>
> Signed-off-by: Joe Hershberger <joe.hershberger at ni.com>
>
> ---
>
> Changes in v4:
> -Added support for the 'lo' network interface
>
> Changes in v3: None
> Changes in v2: None
>
>  arch/sandbox/cpu/eth-raw-os.c         | 152 +++++++++++++++++++++++++++-------
>  arch/sandbox/dts/sandbox.dts          |  10 +++
>  arch/sandbox/include/asm/eth-raw-os.h |  10 ++-
>  drivers/net/sandbox-raw.c             |  62 +++++++++++++-
>  4 files changed, 203 insertions(+), 31 deletions(-)
>
> diff --git a/arch/sandbox/cpu/eth-raw-os.c b/arch/sandbox/cpu/eth-raw-os.c
> index 9218f94..acb150a 100644
> --- a/arch/sandbox/cpu/eth-raw-os.c
> +++ b/arch/sandbox/cpu/eth-raw-os.c
> @@ -11,6 +11,8 @@
>  #include <errno.h>
>  #include <net/if.h>
>  #include <netinet/in.h>
> +#include <netinet/ip.h>
> +#include <netinet/udp.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> @@ -19,51 +21,139 @@
>  #include <sys/socket.h>
>  #include <unistd.h>
>
> +#include <arpa/inet.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));
> -       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;
> +       if (priv->local) {

Can you put these two blocks of code (if and else) in separate
functions and call them from here? This function is too long.

> +               struct sockaddr_in *device;
> +               int ret;
> +               struct timeval tv;
> +               int one = 1;
> +
> +               /* Prepare device struct */
> +               priv->device = malloc(sizeof(struct sockaddr_in));

return -ENOMEM if NULL.

> +               device = priv->device;
> +               memset(device, 0, sizeof(struct sockaddr_in));
> +               device->sin_family = AF_INET;
> +               ret = inet_pton(AF_INET, "127.0.0.1",

Is this INADDR_LOOPBACK? Maybe you can just assign it here?

> +                         (struct in_addr *)&device->sin_addr.s_addr);
> +               if (ret < 0) {
> +                       printf("Failed to convert address: %d %s\n", errno,
> +                              strerror(errno));
> +                       return -errno;
> +               }
> +
> +               /**
> +                * Open socket
> +                *  Since we specify UDP here, any incoming ICMP packets will
> +                *  not be received, so things like ping will not work on this
> +                *  localhost interface.
> +                */
> +               priv->sd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
> +               if (priv->sd < 0) {
> +                       printf("Failed to open socket: %d %s\n", errno,
> +                              strerror(errno));
> +                       return -errno;
> +               }
> +
> +               /* Allow the receive to timeout after a millisecond */
> +               tv.tv_sec = 0;
> +               tv.tv_usec = 1000;
> +               ret = setsockopt(priv->sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
> +                          sizeof(struct timeval));
> +               if (ret < 0) {
> +                       printf("Failed to set opt: %d %s\n", errno,
> +                              strerror(errno));
> +                       return -errno;
> +               }
> +
> +               /* Include the UDP/IP headers on send and receive */
> +               ret = setsockopt(priv->sd, IPPROTO_IP, IP_HDRINCL, &one,
> +                                sizeof(one));
> +               if (ret < 0) {
> +                       printf("Failed to set opt: %d %s\n", errno,
> +                              strerror(errno));
> +                       return -errno;
> +               }
> +               priv->local_bind_sd = -1;
> +               priv->local_bind_udp_port = 0;
> +       } else {
> +               struct sockaddr_ll *device;
> +               struct packet_mreq mr;
> +
> +               /* Prepare device struct */
> +               priv->device = malloc(sizeof(struct sockaddr_ll));
> +               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));
>         }
> -       /* 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)
> +                           struct eth_sandbox_raw_priv *priv)
>  {
>         int retval;
> +       struct udphdr *udph = packet + sizeof(struct iphdr);
>
>         if (!priv->sd || !priv->device)
>                 return -EINVAL;
>
> +       if (priv->local && (priv->local_bind_sd == -1 ||
> +                           priv->local_bind_udp_port != udph->source)) {

What does this block of code do? Comment?

> +               struct iphdr *iph = packet;
> +               struct sockaddr_in addr;
> +
> +               if (priv->local_bind_sd != -1)
> +                       close(priv->local_bind_sd);
> +
> +               /* A normal UDP socket is required to bind */
> +               priv->local_bind_sd = socket(AF_INET, SOCK_DGRAM, 0);
> +               if (priv->local_bind_sd < 0) {
> +                       printf("Failed to open bind sd: %d %s\n", errno,
> +                              strerror(errno));
> +                       return -errno;
> +               }
> +               priv->local_bind_udp_port = udph->source;
> +
> +               /**
> +                * Bind the UDP port that we intend to use as our source port
> +                * so that the kernel will not send ICMP port unreachable

Do you mean return the 'ICMP port unreachable' error?

> +                */
> +               addr.sin_family = AF_INET;
> +               addr.sin_port = udph->source;
> +               addr.sin_addr.s_addr = iph->saddr;
> +               retval = bind(priv->local_bind_sd, &addr, sizeof(addr));
> +               if (retval < 0)
> +                       printf("Failed to bind: %d %s\n", errno,
> +                              strerror(errno));
> +       }
> +
>         retval = sendto(priv->sd, packet, length, 0,
>                         (struct sockaddr *)priv->device,
>                         sizeof(struct sockaddr_ll));
> @@ -99,4 +189,10 @@ void sandbox_eth_raw_os_halt(struct eth_sandbox_raw_priv *priv)
>         priv->device = NULL;
>         close(priv->sd);
>         priv->sd = -1;
> +       if (priv->local) {
> +               if (priv->local_bind_sd != -1)
> +                       close(priv->local_bind_sd);
> +               priv->local_bind_sd = -1;
> +               priv->local_bind_udp_port = 0;
> +       }
>  }
> diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
> index b6762f4..9851bda 100644
> --- a/arch/sandbox/dts/sandbox.dts
> +++ b/arch/sandbox/dts/sandbox.dts
> @@ -4,6 +4,10 @@
>         #address-cells = <1>;
>         #size-cells = <0>;
>
> +       aliases {
> +               eth5 = "/eth at 90000000";
> +       };
> +
>         chosen {
>                 stdout-path = "/serial";
>         };
> @@ -192,4 +196,10 @@
>                 reg = <0x80000000 0x1000>;
>                 host-raw-interface = "eth0";
>         };
> +
> +       eth at 90000000 {
> +               compatible = "sandbox,eth-raw";
> +               reg = <0x90000000 0x1000>;
> +               host-raw-interface = "lo";
> +       };
>  };
> diff --git a/arch/sandbox/include/asm/eth-raw-os.h b/arch/sandbox/include/asm/eth-raw-os.h
> index d92b72c..44e7050 100644
> --- a/arch/sandbox/include/asm/eth-raw-os.h
> +++ b/arch/sandbox/include/asm/eth-raw-os.h
> @@ -15,16 +15,24 @@
>   *
>   * sd: socket descriptor - the open socket during a session
>   * device: struct sockaddr_ll - the host interface packets move to/from
> + * local: 1 or 0 to select a local interface or not

Should expand this a bit. The local interface is the loopback
device....why do you want one over the other?

> + * local_bindsd: socket descriptor to prevent the kernel from sending
> + *              a message to the server claiming the port is
> + *              unreachable
> + * local_bind_udp_port: The UDP port number that we bound to
>   */
>  struct eth_sandbox_raw_priv {
>         int sd;
>         void *device;
> +       int local;
> +       int local_bind_sd;
> +       unsigned short local_bind_udp_port;
>  };
>
>  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);
> +                           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);
> diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c
> index 01b33a9..4b0f836 100644
> --- a/drivers/net/sandbox-raw.c
> +++ b/drivers/net/sandbox-raw.c
> @@ -15,6 +15,8 @@
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> +static int reply_arp;
> +static IPaddr_t arp_ip;
>
>  static int sb_eth_raw_start(struct udevice *dev)
>  {
> @@ -28,6 +30,12 @@ static int sb_eth_raw_start(struct udevice *dev)
>         interface = fdt_getprop(gd->fdt_blob, dev->of_offset,
>                                             "host-raw-interface", NULL);
>
> +       if (strcmp(interface, "lo") == 0) {
> +               priv->local = 1;
> +               setenv("ipaddr", "127.0.0.1");
> +               setenv("serverip", "127.0.0.1");
> +       }
> +
>         retval = sandbox_eth_raw_os_init(interface, pdata->enetaddr, priv);
>
>         return retval;
> @@ -39,19 +47,69 @@ static int sb_eth_raw_send(struct udevice *dev, void *packet, int length)
>
>         debug("eth_sandbox_raw: Send packet %d\n", length);
>
> +       if (priv->local) {
> +               struct ethernet_hdr *eth = packet;
> +
> +               if (ntohs(eth->et_protlen) == PROT_ARP) {
> +                       struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
> +
> +                       /**
> +                        * localhost works on a higher level API in Linux than

nit: higher-level

> +                        * ARP packets, so fake it

fake what?

> +                        */
> +                       arp_ip = NetReadIP(&arp->ar_tpa);
> +                       reply_arp = 1;
> +                       return 0;
> +               }
> +               packet += ETHER_HDR_SIZE;
> +               length -= ETHER_HDR_SIZE;
> +       }
>         return sandbox_eth_raw_os_send(packet, length, priv);
>  }
>
>  static int sb_eth_raw_recv(struct udevice *dev)
>  {
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
>         struct eth_sandbox_raw_priv *priv = dev_get_priv(dev);
> -       int retval;
> +       int retval = 0;
>         uchar buffer[PKTSIZE];
>         int length;
>
> -       retval = sandbox_eth_raw_os_recv(buffer, &length, priv);
> +       if (reply_arp) {
> +               struct arp_hdr *arp = (void *)buffer + ETHER_HDR_SIZE;
> +
> +               /* Formulate a fake ARP */
> +               arp->ar_hrd = htons(ARP_ETHER);
> +               arp->ar_pro = htons(PROT_IP);
> +               arp->ar_hln = ARP_HLEN;
> +               arp->ar_pln = ARP_PLEN;
> +               arp->ar_op = htons(ARPOP_REPLY);
> +               /* Any non-zero MAC address will work */
> +               memset(&arp->ar_sha, 0x01, ARP_HLEN);
> +               /* Use whatever IP we were looking for (always 127.0.0.1?) */
> +               NetWriteIP(&arp->ar_spa, arp_ip);
> +               memcpy(&arp->ar_tha, pdata->enetaddr, ARP_HLEN);
> +               NetWriteIP(&arp->ar_tpa, NetOurIP);
> +               length = ARP_HDR_SIZE;
> +       } else {
> +               /* If local, the Ethernet header won't be included; skip it */
> +               uchar *pktptr = priv->local ? buffer + ETHER_HDR_SIZE : buffer;
> +
> +               retval = sandbox_eth_raw_os_recv(pktptr, &length, priv);
> +       }
>
>         if (!retval && length) {
> +               if (priv->local) {
> +                       struct ethernet_hdr *eth = (void *)buffer;
> +
> +                       /* Fill in enough of the missing Ethernet header */
> +                       memcpy(eth->et_dest, pdata->enetaddr, ARP_HLEN);
> +                       memset(eth->et_src, 0x01, ARP_HLEN);
> +                       eth->et_protlen = htons(reply_arp ? PROT_ARP : PROT_IP);
> +                       reply_arp = 0;
> +                       length += ETHER_HDR_SIZE;
> +               }
> +
>                 debug("eth_sandbox_raw: received packet %d\n",
>                       length);
>                 NetReceive(buffer, length);
> --
> 1.7.11.5
>

Regards,
Simon


More information about the U-Boot mailing list