grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Networking (part 1: Ethernet)


From: Marco Gerards
Subject: Networking (part 1: Ethernet)
Date: Sun, 22 Jul 2007 16:23:38 +0200
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Hi,

Long time ago I mentioned that I was working on networking.  The best
way to continue from now on is to get my code into CVS ASAP.  That way
other people (and quite some people showed their interest) can work on
this.

Here is the first batch of code.  This code adds support for managing
the frames and adds Ethernet interfaces and basis support.  One bonus
is an actual "driver" that uses the TAP device in linux.

Please do NOT look at the code, it will not be applied like this (thus
no ChangeLog entry).  It is not pretty.  I didn't take any time to
clean it up.  Instead of that, I hope you will look at the interfaces
and comment on that.  If I do not get negative feedback on that, I'll
clean up and try to commit this soon, so I can send the other code.

Other code that is left is for IPv4, ARP and UDP.  And perhaps some
other stuff I forgot about :-).

Anyways...  This code adds support for "memstacks".  The name is not
that great, but I'll try to explain what it is.  Frames usually
consist of a lot of headers, stacked on each other.  This code makes it
possible to push a header (IPv4 on UDP, Ethernet on IPv4, etc) on top
of what is already there.  This makes it easier to handle the memory
for the data passed around.  The goals were to keep it easy to manage
the data and to keep memstack.c small and simple.

Ethernet.c uses these memstacks.  Please have a look at ethernet.h for
the headers and tap.c for an actual implementation.

Please do not start working on this until I committed this code and
the UDP/IPv4 code.  The reason for this is that if you do that, it
would be more time consuming for me to get everything in place.  And
doing that is top priority for me so as many people as possible can
work on this.

Thanks,
Marco

Index: conf/i386-pc.rmk
===================================================================
RCS file: /sources/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.82
diff -u -p -u -p -r1.82 i386-pc.rmk
--- conf/i386-pc.rmk    21 Jul 2007 23:34:57 -0000      1.82
+++ conf/i386-pc.rmk    22 Jul 2007 14:11:44 -0000
@@ -108,7 +108,8 @@ grub_emu_SOURCES = commands/boot.c comma
        partmap/acorn.c partmap/gpt.c                                   \
        util/console.c util/grub-emu.c util/misc.c                      \
        util/biosdisk.c util/getroot.c                  \
-       util/i386/pc/misc.c grub_emu_init.c
+       util/i386/pc/misc.c grub_emu_init.c \
+       network/memstack.c network/ethernet.c util/tap.c
 
 grub_emu_LDFLAGS = $(LIBCURSES)
 
Index: include/grub/ethernet.h
===================================================================
RCS file: include/grub/ethernet.h
diff -N include/grub/ethernet.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ include/grub/ethernet.h     22 Jul 2007 14:11:44 -0000
@@ -0,0 +1,102 @@
+/* ethernet.h - prototypes for ethernet operations.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_ETHERNET_HEADER
+#define GRUB_ETHERNET_HEADER   1
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/memstack.h>
+
+struct grub_ethernet;
+struct grub_ethernet_frame;
+
+struct grub_ethernet_dev
+{
+  /* The device driver name.  */
+  const char *name;
+
+  /* Call HOOK with each device name, until HOOK returns non-zero.  */
+  int (*iterate) (int (*hook) (const char *name));
+
+    /* Open the device named NAME, and set up ETH.  */
+  grub_err_t (*open) (const char *name, struct grub_ethernet *eth);
+
+  /* Close the device ETH.  */
+  void (*close) (struct grub_ethernet *eth);
+
+  grub_ssize_t (*receive) (struct grub_ethernet *eth,
+                          struct grub_ethernet_frame *frame, int nonblock);
+
+  grub_err_t (*send) (struct grub_ethernet *eth, grub_memstack_t stack);
+
+  /* The next ethernet device.  */
+  struct grub_ethernet_dev *next;
+};
+
+typedef struct grub_ethernet_dev *grub_ethernet_dev_t;
+
+struct grub_ethernet
+{
+  /* The ethernet device name.  */
+  const char *name;
+
+  /* The underlying ethernet device.  */
+  grub_ethernet_dev_t dev;
+
+  grub_uint8_t hwaddress[8];
+
+  /* Device-specific data.  */
+  char *data;
+};
+typedef struct grub_ethernet *grub_ethernet_t;
+
+void grub_ethernet_dev_register (grub_ethernet_dev_t dev);
+void grub_ethernet_dev_unregister (grub_ethernet_dev_t dev);
+int grub_ethernet_dev_iterate (int (*hook) (const char *name));
+grub_ethernet_t grub_ethernet_open (const char *name);
+void grub_ethernet_close (grub_ethernet_t eth);
+grub_ssize_t grub_ethernet_dev_receive (grub_ethernet_t eth,
+                                       struct grub_ethernet_frame *buf,
+                                       int nonblock);
+grub_err_t grub_ethernet_dev_send (grub_ethernet_t eth,
+                                  grub_uint8_t destination[8],
+                                  int type, grub_memstack_t stack);
+
+
+
+#define GRUB_ETHERNET_MTU      1500
+
+struct grub_ethernet_frame
+{
+  grub_uint8_t destination[6];
+  grub_uint8_t source[6];
+  grub_uint16_t type;
+  grub_uint8_t data[0];
+  /* 32 bits CRC.  */
+} __attribute__((packed));
+
+typedef enum
+  {
+    GRUB_ETHERNET_TYPE_ARP = 0x806,
+    GRUB_ETHERNET_TYPE_IPV4 = 0x800,
+    GRUB_ETHERNET_TYPE_IPV6 = 0x86DD
+  } grub_ethernet_type_t;
+
+#endif /* ! GRUB_ETHERNET_HEADER */
Index: include/grub/memstack.h
===================================================================
RCS file: include/grub/memstack.h
diff -N include/grub/memstack.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ include/grub/memstack.h     22 Jul 2007 14:11:44 -0000
@@ -0,0 +1,67 @@
+/* memstack.h - Store stacked (or nested) headers and data.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_MEMSTACK_HEADER
+#define GRUB_MEMSTACK_HEADER   1
+
+#include <grub/types.h>
+#include <grub/err.h>
+
+enum grub_memstack_type
+  {
+    GRUB_MEMSTACK_TYPE_ETHERNET,
+    GRUB_MEMSTACK_TYPE_IPV4,
+    GRUB_MEMSTACK_TYPE_ARP,
+    GRUB_MEMSTACK_TYPE_UDP,
+    GRUB_MEMSTACK_TYPE_DATA /* XXX */
+  };
+typedef enum grub_memstack_type grub_memstack_type_t;
+
+struct grub_memstack_data
+{
+  void *data;
+  grub_size_t size;
+  grub_memstack_type_t type;
+
+  struct grub_memstack_data *next;
+};
+
+struct grub_memstack
+{
+  int size;
+  struct grub_memstack_data *data;
+};
+typedef struct grub_memstack *grub_memstack_t;
+
+
+
+grub_memstack_t grub_memstack_new (void);
+
+void grub_memstack_free (grub_memstack_t stack);
+
+grub_err_t grub_memstack_push (grub_memstack_t stack,
+                              grub_memstack_type_t type,
+                              void *data, grub_size_t size);
+
+void *grub_memstack_pop (grub_memstack_t stack, grub_size_t *size);
+
+grub_err_t grub_memstack_popall (grub_memstack_t stack,
+                                char *buf, grub_size_t *size);
+
+#endif /* ! GRUB_MEMSTACK_HEADER */
Index: network/ethernet.c
===================================================================
RCS file: network/ethernet.c
diff -N network/ethernet.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ network/ethernet.c  22 Jul 2007 14:11:44 -0000
@@ -0,0 +1,161 @@
+/* ethernet.c - Process ethernet frames.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/ethernet.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+static grub_ethernet_dev_t grub_ethernet_dev_list;
+
+/* If a driver does not have an unique identifier, it can request one.
+   The next id determines the next `ethx' identifier that will be
+   assigned.  */
+static int grub_ethernet_next_id = 0;
+
+void
+grub_ethernet_dev_register (grub_ethernet_dev_t dev)
+{
+  dev->next = grub_ethernet_dev_list;
+  grub_ethernet_dev_list = dev;
+}
+
+void
+grub_ethernet_dev_unregister (grub_ethernet_dev_t dev)
+{
+  grub_ethernet_dev_t *p, q;
+  
+  for (p = &grub_ethernet_dev_list, q = *p; q; p = &(q->next), q = q->next)
+    if (q == dev)
+      {
+        *p = q->next;
+       break;
+      }
+}
+
+int
+grub_ethernet_dev_iterate (int (*hook) (const char *name))
+{
+  grub_ethernet_dev_t p;
+
+  for (p = grub_ethernet_dev_list; p; p = p->next)
+    if ((p->iterate) (hook))
+      return 1;
+
+  return 0;
+}
+
+
+grub_ethernet_t
+grub_ethernet_open (const char *name)
+{
+  grub_ethernet_t eth;
+  grub_ethernet_dev_t dev;
+  char *raw = (char *) name;
+  
+  eth = (grub_ethernet_t) grub_malloc (sizeof (*eth));
+  if (! eth)
+    return 0;
+
+  eth->dev = 0;
+  eth->data = 0;
+  eth->name = grub_strdup (name);
+  if (! eth->name)
+    goto fail;
+
+  for (dev = grub_ethernet_dev_list; dev; dev = dev->next)
+    {
+      if ((dev->open) (raw, eth) == GRUB_ERR_NONE)
+       break;
+      else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
+       grub_errno = GRUB_ERR_NONE;
+      else
+       goto fail;
+    }
+
+  if (! dev)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ethernet card");
+      goto fail;
+    }
+
+  eth->dev = dev;
+  
+ fail:
+  
+  if (raw && raw != name)
+    grub_free (raw);
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_ethernet_close (eth);
+      return 0;
+    }
+
+  return eth;
+}
+
+void
+grub_ethernet_close (grub_ethernet_t eth)
+{
+  if (eth->dev && eth->dev->close)
+    (eth->dev->close) (eth);
+
+  grub_free ((void *) eth->name);
+  grub_free (eth);
+}
+
+grub_ssize_t
+grub_ethernet_dev_receive (grub_ethernet_t eth,
+                          struct grub_ethernet_frame *buf, int nonblock)
+{
+  return eth->dev->receive  (eth, buf, nonblock);
+}
+
+grub_err_t
+grub_ethernet_dev_send (grub_ethernet_t eth, grub_uint8_t destination[8],
+                       int type, grub_memstack_t stack)
+{
+  struct grub_ethernet_frame *frame;
+
+  frame = grub_malloc (sizeof (*frame));
+  if (! frame)
+    {
+      goto fail;
+      return 0;
+    }
+
+  /* Set up the ethernet headers.  */
+  grub_memcpy (frame->destination, destination, 6);
+  grub_memcpy (frame->source, eth->hwaddress, 6);
+  frame->type = grub_cpu_to_be16 (type);
+
+  /* Put the ethernet header in the frame.  */
+  if (grub_memstack_push (stack, GRUB_MEMSTACK_TYPE_ETHERNET,
+                         frame, sizeof (*frame)))
+    goto fail;
+
+
+  return eth->dev->send  (eth, stack);
+
+ fail:
+  /* If the frame can't be sent, just drop it.  */
+  grub_memstack_free (stack);
+
+  return grub_errno;
+}
Index: network/memstack.c
===================================================================
RCS file: network/memstack.c
diff -N network/memstack.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ network/memstack.c  22 Jul 2007 14:11:44 -0000
@@ -0,0 +1,164 @@
+/* memstack.c - Store stacked (or nested) headers and data.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/memstack.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+grub_memstack_t
+grub_memstack_new (void)
+{
+  grub_memstack_t stack;
+
+  stack = grub_malloc (sizeof (*stack));
+  stack->size = 0;
+  stack->data = 0;
+
+  return stack;
+}
+
+grub_memstack_t
+grub_memstack_dummy_new (const char *data, int size)
+{
+  grub_memstack_t stack;
+
+  stack = grub_malloc (sizeof (*stack));
+  stack->size = size;
+  stack->data = data;
+
+  return stack;
+}
+
+void
+grub_memstack_free (grub_memstack_t stack)
+{
+  struct grub_memstack_data *stdata;
+  struct grub_memstack_data *next;
+
+  if (! stack)
+    return;
+
+  stdata = stack->data;
+  while (stdata)
+    {
+      next = stdata->next;
+      if (stdata->data)
+       grub_free (stdata->data);
+      grub_free (stdata);
+      stdata = next;
+    }
+  grub_free (stack);
+}
+
+grub_err_t
+grub_memstack_push (grub_memstack_t stack,
+                   grub_memstack_type_t type, void *data, grub_size_t size)
+{
+  struct grub_memstack_data *stdata;
+
+  stdata = grub_malloc (sizeof (*stdata));
+  if (! stdata)
+    return grub_errno;
+
+  stdata->data = data;
+  stdata->size = size;
+  stdata->type = type;
+
+  stdata->next = stack->data;
+  stack->data = stdata;
+  stack->size += size;
+
+  return 0;
+}
+
+void *
+grub_memstack_pop (grub_memstack_t stack, grub_size_t *size)
+{
+  struct grub_memstack_data *stdata = stack->data;
+  void *data;
+
+  if (! stdata)
+    return 0;
+
+  data = stdata->data;
+
+  stack->size -= stdata->size;
+  if (size)
+    *size = stdata->size;
+
+  stack->data = stdata->next;
+  grub_free (stdata);
+
+  return data;
+}
+
+grub_err_t
+grub_memstack_popall (grub_memstack_t stack, char *buf, grub_size_t *size)
+{
+  char *bufp = buf;
+
+  void *data;
+  grub_size_t dsize;
+
+  *size = stack->size;
+  while (1)
+    {
+      if (stack->data)
+       grub_printf ("TYPE=%d\n", stack->data->type);
+      data = grub_memstack_pop (stack, &dsize);
+      if (! data)
+       break;
+
+      grub_memcpy (bufp, data, dsize);
+      bufp += dsize;
+    }
+  return 0;
+}
+
+int
+grub_memstack_checksum (grub_memstack_t stack, int first)
+{
+  grub_uint32_t checksum = 0;
+  unsigned int i;
+  struct grub_memstack_data *stdata;
+
+  for (stdata  = stack->data; stdata; stdata = stdata->next)
+    {
+      if (stdata->data)
+       {
+         grub_printf ("Add chk\n");
+         grub_uint16_t *data = (grub_uint16_t *) stdata->data;
+         for (i = 0; i < stdata->size / 2; i++)
+           checksum += (grub_uint32_t) data[i];
+
+         /* Special case for the last byte.  */
+         if (stdata->size % 2)
+           checksum += (grub_uint32_t) (/* grub_be_to_cpu16 */ 
(data[stdata->size - 1] << 16));
+       }
+      if (first)
+       break;
+    }
+  grub_printf ("CS: %x\n", checksum & 0xFFFF);
+
+  while (checksum >> 16)
+    checksum = (checksum & 0xFFFF) + (checksum >> 16);
+  grub_printf (">CS: %x (%x)\n", checksum, ~checksum);
+
+  return ~checksum;
+}
Index: util/tap.c
===================================================================
RCS file: util/tap.c
diff -N util/tap.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ util/tap.c  22 Jul 2007 14:11:44 -0000
@@ -0,0 +1,167 @@
+/* tap.c - Send and receive ethernet frames over the TAP device.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2007  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/misc.h>
+#include <grub/ethernet.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+
+/* XXX: GNU/Linux only*/
+static int grub_ethernet_tap = 0;
+
+grub_err_t
+grub_ethtap_open (const char *name, grub_ethernet_t eth)
+{
+  struct ifreq ifr;
+
+  if (grub_ethernet_tap && grub_strcmp (name, "tap"))
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device");
+
+  /* Get the MAC address.  */
+  if (ioctl (grub_ethernet_tap, SIOCGIFHWADDR, &ifr))
+    {
+      close (grub_ethernet_tap);
+      return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                        "Can't get hardware address");
+    }
+  grub_memcpy (eth->hwaddress, ifr.ifr_hwaddr.sa_data, 6);
+
+  return 0;
+}
+
+static int
+grub_ethtap_iterate (int (*hook) (const char *name))
+{
+  if (grub_ethernet_tap)
+    return hook ("tap");
+  return 0;
+}
+
+grub_ssize_t
+grub_ethtap_receive (struct grub_ethernet *eth __attribute__((unused)),
+                    struct grub_ethernet_frame *frame, int nonblock)
+{
+  int len;
+  char buf[1504];
+  int flags;
+
+  if (nonblock)
+    {
+      flags = fcntl (grub_ethernet_tap, F_GETFL, 0);
+
+      /* On an error, just say the queue is empty, this is better than
+        just blocking.  */
+      if (flags == -1)
+       return 1;
+
+      fcntl (grub_ethernet_tap, F_SETFL, O_NONBLOCK);
+      
+    }
+
+  /* Read a single ethernet frame.  XXX: how to get the size right?  */
+  len = read (grub_ethernet_tap, buf, sizeof (buf));
+
+  if (nonblock)
+    {
+      fcntl (grub_ethernet_tap, F_SETFL, flags);
+      if (len <= 0)
+       return 0;
+    }
+
+  grub_memcpy ((char *) frame, buf + 4, sizeof (buf) - 4);
+  grub_printf ("Got: %d\n", len - 4);
+  return len - 4;
+}
+
+grub_err_t
+grub_ethtap_send (struct grub_ethernet *eth __attribute__((unused)),
+                 grub_memstack_t stack)
+{
+  grub_size_t size;
+  grub_size_t len;
+  char buf[1504];
+  struct tun_header
+  {
+    grub_uint16_t flags;
+    grub_uint16_t proto;
+  } __attribute__((packed));
+  struct tun_header *hdr = (struct tun_header *) buf;
+
+  grub_memstack_popall (stack, buf + 4, &size);
+
+  hdr->flags = 0;
+  hdr->proto = /* ETH_P_802_3 */1; /* XXX */
+
+  /* Read a single ethernet frame.  XXX: how to get the size right?  */
+  len = write (grub_ethernet_tap, buf, size + 4);
+
+  return len;
+}
+
+
+static struct grub_ethernet_dev grub_ethtap_dev =
+  {
+    .name = "TAP",
+    .iterate = grub_ethtap_iterate,
+    .open = grub_ethtap_open,
+    .close = 0,
+    .receive = grub_ethtap_receive,
+    .send = grub_ethtap_send,
+    .next = 0
+  };
+
+
+void
+grub_ethtap_init (void)
+{
+  int tap;
+  struct ifreq ifr;
+
+  tap = open ("/dev/net/tun", O_RDWR);
+  if (! tap)
+    {
+      printf ("Can't open...\n"); /* XXX */
+      return;
+    }
+
+  ifr.ifr_flags = IFF_TAP;
+
+  if (ioctl (tap, TUNSETIFF, (void *) &ifr))
+    {
+      close (tap);
+      return;
+    }
+
+  grub_ethernet_tap = tap;
+
+  grub_ethernet_dev_register (&grub_ethtap_dev);
+}
+
+void
+grub_ethtap_fini (void)
+{
+  close (grub_ethernet_tap);
+  grub_ethernet_tap = 0;
+}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]