Blame SOURCES/0087-Add-support-for-non-Ethernet-network-cards.patch

5593c8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
5593c8
From: Andrzej Kacprowski <andrzej.kacprowski@intel.com>
5593c8
Date: Wed, 10 Jul 2019 15:22:29 +0200
5593c8
Subject: [PATCH] Add support for non-Ethernet network cards
5593c8
5593c8
This patch replaces fixed 6-byte link layer address with
5593c8
up to 32-byte variable sized address.
5593c8
This allows supporting Infiniband and Omni-Path fabric
5593c8
which use 20-byte address, but other network card types
5593c8
can also take advantage of this change.
5593c8
The network card driver is responsible for replacing L2
5593c8
header provided by grub2 if needed.
5593c8
This approach is compatible with UEFI network stack which
5593c8
also allows up to 32-byte variable size link address.
5593c8
5593c8
The BOOTP/DHCP packet format is limited to 16 byte client
5593c8
hardware address, if link address is more that 16-bytes
5593c8
then chaddr field in BOOTP it will be set to 0 as per rfc4390.
5593c8
5593c8
Resolves: rhbz#1370642
5593c8
5593c8
Signed-off-by: Andrzej Kacprowski <andrzej.kacprowski@intel.com>
5593c8
[msalter: Fix max string calculation in grub_net_hwaddr_to_str]
5593c8
Signed-off-by: Mark Salter <msalter@redhat.com>
5593c8
---
5593c8
 grub-core/net/arp.c                    | 155 ++++++++++++++++++++++-----------
5593c8
 grub-core/net/bootp.c                  |  15 ++--
5593c8
 grub-core/net/drivers/efi/efinet.c     |   8 +-
5593c8
 grub-core/net/drivers/emu/emunet.c     |   1 +
5593c8
 grub-core/net/drivers/i386/pc/pxe.c    |  13 +--
5593c8
 grub-core/net/drivers/ieee1275/ofnet.c |   2 +
5593c8
 grub-core/net/drivers/uboot/ubootnet.c |   1 +
5593c8
 grub-core/net/ethernet.c               |  88 +++++++++----------
5593c8
 grub-core/net/icmp6.c                  |  15 ++--
5593c8
 grub-core/net/ip.c                     |   4 +-
5593c8
 grub-core/net/net.c                    |  50 ++++++-----
5593c8
 include/grub/net.h                     |  19 ++--
5593c8
 12 files changed, 219 insertions(+), 152 deletions(-)
5593c8
5593c8
diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c
5593c8
index 54306e3b16d..67b409a8acc 100644
5593c8
--- a/grub-core/net/arp.c
5593c8
+++ b/grub-core/net/arp.c
5593c8
@@ -31,22 +31,12 @@ enum
5593c8
     ARP_REPLY = 2
5593c8
   };
5593c8
 
5593c8
-enum
5593c8
-  {
5593c8
-    /* IANA ARP constant to define hardware type as ethernet. */
5593c8
-    GRUB_NET_ARPHRD_ETHERNET = 1
5593c8
-  };
5593c8
-
5593c8
-struct arppkt {
5593c8
+struct arphdr {
5593c8
   grub_uint16_t hrd;
5593c8
   grub_uint16_t pro;
5593c8
   grub_uint8_t hln;
5593c8
   grub_uint8_t pln;
5593c8
   grub_uint16_t op;
5593c8
-  grub_uint8_t sender_mac[6];
5593c8
-  grub_uint32_t sender_ip;
5593c8
-  grub_uint8_t recv_mac[6];
5593c8
-  grub_uint32_t recv_ip;
5593c8
 } GRUB_PACKED;
5593c8
 
5593c8
 static int have_pending;
5593c8
@@ -57,12 +47,16 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf,
5593c8
 			   const grub_net_network_level_address_t *proto_addr)
5593c8
 {
5593c8
   struct grub_net_buff nb;
5593c8
-  struct arppkt *arp_packet;
5593c8
+  struct arphdr *arp_header;
5593c8
   grub_net_link_level_address_t target_mac_addr;
5593c8
   grub_err_t err;
5593c8
   int i;
5593c8
   grub_uint8_t *nbd;
5593c8
   grub_uint8_t arp_data[128];
5593c8
+  grub_uint8_t hln;
5593c8
+  grub_uint8_t pln;
5593c8
+  grub_uint8_t arp_packet_len;
5593c8
+  grub_uint8_t *tmp_ptr;
5593c8
 
5593c8
   if (proto_addr->type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
5593c8
     return grub_error (GRUB_ERR_BUG, "unsupported address family");
5593c8
@@ -73,23 +67,39 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf,
5593c8
   grub_netbuff_clear (&nb);
5593c8
   grub_netbuff_reserve (&nb, 128);
5593c8
 
5593c8
-  err = grub_netbuff_push (&nb, sizeof (*arp_packet));
5593c8
+  hln = inf->card->default_address.len;
5593c8
+  pln = sizeof (proto_addr->ipv4);
5593c8
+  arp_packet_len = sizeof (*arp_header) + 2 * (hln + pln);
5593c8
+
5593c8
+  err = grub_netbuff_push (&nb, arp_packet_len);
5593c8
   if (err)
5593c8
     return err;
5593c8
 
5593c8
-  arp_packet = (struct arppkt *) nb.data;
5593c8
-  arp_packet->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET);
5593c8
-  arp_packet->hln = 6;
5593c8
-  arp_packet->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
5593c8
-  arp_packet->pln = 4;
5593c8
-  arp_packet->op = grub_cpu_to_be16_compile_time (ARP_REQUEST);
5593c8
-  /* Sender hardware address.  */
5593c8
-  grub_memcpy (arp_packet->sender_mac, &inf->hwaddress.mac, 6);
5593c8
-  arp_packet->sender_ip = inf->address.ipv4;
5593c8
-  grub_memset (arp_packet->recv_mac, 0, 6);
5593c8
-  arp_packet->recv_ip = proto_addr->ipv4;
5593c8
-  /* Target protocol address */
5593c8
-  grub_memset (&target_mac_addr.mac, 0xff, 6);
5593c8
+  arp_header = (struct arphdr *) nb.data;
5593c8
+  arp_header->hrd = grub_cpu_to_be16 (inf->card->default_address.type);
5593c8
+  arp_header->hln = hln;
5593c8
+  arp_header->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
5593c8
+  arp_header->pln = pln;
5593c8
+  arp_header->op = grub_cpu_to_be16_compile_time (ARP_REQUEST);
5593c8
+  tmp_ptr = nb.data + sizeof (*arp_header);
5593c8
+
5593c8
+  /* The source hardware address. */
5593c8
+  grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln);
5593c8
+  tmp_ptr += hln;
5593c8
+
5593c8
+  /* The source protocol address. */
5593c8
+  grub_memcpy (tmp_ptr, &inf->address.ipv4, pln);
5593c8
+  tmp_ptr += pln;
5593c8
+
5593c8
+  /* The target hardware address. */
5593c8
+  grub_memset (tmp_ptr, 0, hln);
5593c8
+  tmp_ptr += hln;
5593c8
+
5593c8
+  /* The target protocol address */
5593c8
+  grub_memcpy (tmp_ptr, &proto_addr->ipv4, pln);
5593c8
+  tmp_ptr += pln;
5593c8
+
5593c8
+  grub_memset (&target_mac_addr.mac, 0xff, hln);
5593c8
 
5593c8
   nbd = nb.data;
5593c8
   send_ethernet_packet (inf, &nb, target_mac_addr, GRUB_NET_ETHERTYPE_ARP);
5593c8
@@ -114,28 +124,53 @@ grub_err_t
5593c8
 grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
5593c8
                       grub_uint16_t *vlantag)
5593c8
 {
5593c8
-  struct arppkt *arp_packet = (struct arppkt *) nb->data;
5593c8
+  struct arphdr *arp_header = (struct arphdr *) nb->data;
5593c8
   grub_net_network_level_address_t sender_addr, target_addr;
5593c8
   grub_net_link_level_address_t sender_mac_addr;
5593c8
   struct grub_net_network_level_interface *inf;
5593c8
+  grub_uint16_t hw_type;
5593c8
+  grub_uint8_t hln;
5593c8
+  grub_uint8_t pln;
5593c8
+  grub_uint8_t arp_packet_len;
5593c8
+  grub_uint8_t *tmp_ptr;
5593c8
 
5593c8
-  if (arp_packet->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP)
5593c8
-      || arp_packet->pln != 4 || arp_packet->hln != 6
5593c8
-      || nb->tail - nb->data < (int) sizeof (*arp_packet))
5593c8
+  hw_type = card->default_address.type;
5593c8
+  hln = card->default_address.len;
5593c8
+  pln = sizeof(sender_addr.ipv4);
5593c8
+  arp_packet_len = sizeof (*arp_header) + 2 * (pln + hln);
5593c8
+
5593c8
+  if (arp_header->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP)
5593c8
+      || arp_header->hrd != grub_cpu_to_be16 (hw_type)
5593c8
+      || arp_header->hln != hln || arp_header->pln != pln
5593c8
+      || nb->tail - nb->data < (int) arp_packet_len) {
5593c8
     return GRUB_ERR_NONE;
5593c8
+  }
5593c8
 
5593c8
+  tmp_ptr =  nb->data + sizeof (*arp_header);
5593c8
+
5593c8
+  /* The source hardware address. */
5593c8
+  sender_mac_addr.type = hw_type;
5593c8
+  sender_mac_addr.len = hln;
5593c8
+  grub_memcpy (sender_mac_addr.mac, tmp_ptr, hln);
5593c8
+  tmp_ptr += hln;
5593c8
+
5593c8
+  /* The source protocol address. */
5593c8
   sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
5593c8
+  grub_memcpy(&sender_addr.ipv4, tmp_ptr, pln);
5593c8
+  tmp_ptr += pln;
5593c8
+
5593c8
+  grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1);
5593c8
+
5593c8
+  /* The target hardware address. */
5593c8
+  tmp_ptr += hln;
5593c8
+
5593c8
+  /* The target protocol address. */
5593c8
   target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
5593c8
-  sender_addr.ipv4 = arp_packet->sender_ip;
5593c8
-  target_addr.ipv4 = arp_packet->recv_ip;
5593c8
-  if (arp_packet->sender_ip == pending_req)
5593c8
+  grub_memcpy(&target_addr.ipv4, tmp_ptr, pln);
5593c8
+
5593c8
+  if (sender_addr.ipv4 == pending_req)
5593c8
     have_pending = 1;
5593c8
 
5593c8
-  sender_mac_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-  grub_memcpy (sender_mac_addr.mac, arp_packet->sender_mac,
5593c8
-	       sizeof (sender_mac_addr.mac));
5593c8
-  grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1);
5593c8
-
5593c8
   FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
5593c8
   {
5593c8
     /* Verify vlantag id */
5593c8
@@ -148,11 +183,11 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
5593c8
 
5593c8
     /* Am I the protocol address target? */
5593c8
     if (grub_net_addr_cmp (&inf->address, &target_addr) == 0
5593c8
-	&& arp_packet->op == grub_cpu_to_be16_compile_time (ARP_REQUEST))
5593c8
+	&& arp_header->op == grub_cpu_to_be16_compile_time (ARP_REQUEST))
5593c8
       {
5593c8
 	grub_net_link_level_address_t target;
5593c8
 	struct grub_net_buff nb_reply;
5593c8
-	struct arppkt *arp_reply;
5593c8
+	struct arphdr *arp_reply;
5593c8
 	grub_uint8_t arp_data[128];
5593c8
 	grub_err_t err;
5593c8
 
5593c8
@@ -161,25 +196,39 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card,
5593c8
 	grub_netbuff_clear (&nb_reply);
5593c8
 	grub_netbuff_reserve (&nb_reply, 128);
5593c8
 
5593c8
-	err = grub_netbuff_push (&nb_reply, sizeof (*arp_packet));
5593c8
+	err = grub_netbuff_push (&nb_reply, arp_packet_len);
5593c8
 	if (err)
5593c8
 	  return err;
5593c8
 
5593c8
-	arp_reply = (struct arppkt *) nb_reply.data;
5593c8
+	arp_reply = (struct arphdr *) nb_reply.data;
5593c8
 
5593c8
-	arp_reply->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET);
5593c8
+	arp_reply->hrd = grub_cpu_to_be16 (hw_type);
5593c8
 	arp_reply->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP);
5593c8
-	arp_reply->pln = 4;
5593c8
-	arp_reply->hln = 6;
5593c8
+	arp_reply->pln = pln;
5593c8
+	arp_reply->hln = hln;
5593c8
 	arp_reply->op = grub_cpu_to_be16_compile_time (ARP_REPLY);
5593c8
-	arp_reply->sender_ip = arp_packet->recv_ip;
5593c8
-	arp_reply->recv_ip = arp_packet->sender_ip;
5593c8
-	arp_reply->hln = 6;
5593c8
-
5593c8
-	target.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-	grub_memcpy (target.mac, arp_packet->sender_mac, 6);
5593c8
-	grub_memcpy (arp_reply->sender_mac, inf->hwaddress.mac, 6);
5593c8
-	grub_memcpy (arp_reply->recv_mac, arp_packet->sender_mac, 6);
5593c8
+
5593c8
+	tmp_ptr = nb_reply.data + sizeof (*arp_reply);
5593c8
+
5593c8
+	/* The source hardware address. */
5593c8
+	grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln);
5593c8
+	tmp_ptr += hln;
5593c8
+
5593c8
+	/* The source protocol address. */
5593c8
+	grub_memcpy (tmp_ptr, &target_addr.ipv4, pln);
5593c8
+	tmp_ptr += pln;
5593c8
+
5593c8
+	/* The target hardware address. */
5593c8
+	grub_memcpy (tmp_ptr, sender_mac_addr.mac, hln);
5593c8
+	tmp_ptr += hln;
5593c8
+
5593c8
+	/* The target protocol address */
5593c8
+	grub_memcpy (tmp_ptr, &sender_addr.ipv4, pln);
5593c8
+	tmp_ptr += pln;
5593c8
+
5593c8
+	target.type = hw_type;
5593c8
+	target.len = hln;
5593c8
+	grub_memcpy (target.mac, sender_mac_addr.mac, hln);
5593c8
 
5593c8
 	/* Change operation to REPLY and send packet */
5593c8
 	send_ethernet_packet (inf, &nb_reply, target, GRUB_NET_ETHERTYPE_ARP);
5593c8
diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
5593c8
index e28fb6a09f9..08b6b2b5d6c 100644
5593c8
--- a/grub-core/net/bootp.c
5593c8
+++ b/grub-core/net/bootp.c
5593c8
@@ -233,7 +233,6 @@ grub_net_configure_by_dhcp_ack (const char *name,
5593c8
 				int is_def, char **device, char **path)
5593c8
 {
5593c8
   grub_net_network_level_address_t addr;
5593c8
-  grub_net_link_level_address_t hwaddr;
5593c8
   struct grub_net_network_level_interface *inter;
5593c8
   int mask = -1;
5593c8
   char server_ip[sizeof ("xxx.xxx.xxx.xxx")];
5593c8
@@ -250,12 +249,8 @@ grub_net_configure_by_dhcp_ack (const char *name,
5593c8
   if (path)
5593c8
     *path = 0;
5593c8
 
5593c8
-  grub_memcpy (hwaddr.mac, bp->mac_addr,
5593c8
-	       bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
5593c8
-	       : sizeof (hwaddr.mac));
5593c8
-  hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-
5593c8
-  inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
5593c8
+  grub_dprintf("dhcp", "configuring dhcp for %s\n", name);
5593c8
+  inter = grub_net_add_addr (name, card, &addr, &card->default_address, flags);
5593c8
   if (!inter)
5593c8
     return 0;
5593c8
 
5593c8
@@ -567,7 +562,9 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
5593c8
   grub_memset (pack, 0, sizeof (*pack));
5593c8
   pack->opcode = 1;
5593c8
   pack->hw_type = 1;
5593c8
-  pack->hw_len = 6;
5593c8
+  pack->hw_len = iface->hwaddress.len > 16 ? 0
5593c8
+                                              : iface->hwaddress.len;
5593c8
+
5593c8
   err = grub_get_datetime (&date);
5593c8
   if (err || !grub_datetime2unixtime (&date, &t))
5593c8
     {
5593c8
@@ -580,7 +577,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
5593c8
   else
5593c8
     pack->ident = iface->xid;
5593c8
 
5593c8
-  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
5593c8
+  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, pack->hw_len);
5593c8
 
5593c8
   grub_netbuff_push (nb, sizeof (*udph));
5593c8
 
5593c8
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
5593c8
index 173fb63153c..a673bea807a 100644
5593c8
--- a/grub-core/net/drivers/efi/efinet.c
5593c8
+++ b/grub-core/net/drivers/efi/efinet.c
5593c8
@@ -279,6 +279,9 @@ grub_efinet_findcards (void)
5593c8
 	/* This should not happen... Why?  */
5593c8
 	continue;
5593c8
 
5593c8
+      if (net->mode->hwaddr_size > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
5593c8
+	continue;
5593c8
+
5593c8
       if (net->mode->state == GRUB_EFI_NETWORK_STOPPED
5593c8
 	  && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS)
5593c8
 	continue;
5593c8
@@ -315,10 +318,11 @@ grub_efinet_findcards (void)
5593c8
       card->name = grub_xasprintf ("efinet%d", i++);
5593c8
       card->driver = &efidriver;
5593c8
       card->flags = 0;
5593c8
-      card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
+      card->default_address.type = net->mode->if_type;
5593c8
+      card->default_address.len = net->mode->hwaddr_size;
5593c8
       grub_memcpy (card->default_address.mac,
5593c8
 		   net->mode->current_address,
5593c8
-		   sizeof (card->default_address.mac));
5593c8
+		   net->mode->hwaddr_size);
5593c8
       card->efi_net = net;
5593c8
       card->efi_handle = *handle;
5593c8
 
5593c8
diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c
5593c8
index b194920861f..5b6c5e16a6d 100644
5593c8
--- a/grub-core/net/drivers/emu/emunet.c
5593c8
+++ b/grub-core/net/drivers/emu/emunet.c
5593c8
@@ -46,6 +46,7 @@ static struct grub_net_card emucard =
5593c8
     .mtu = 1500,
5593c8
     .default_address = {
5593c8
 			 .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET,
5593c8
+			 . len = 6,
5593c8
 			 {.mac = {0, 1, 2, 3, 4, 5}}
5593c8
 		       },
5593c8
     .flags = 0
5593c8
diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c
5593c8
index 3f4152d036c..9f8fb4b6d2b 100644
5593c8
--- a/grub-core/net/drivers/i386/pc/pxe.c
5593c8
+++ b/grub-core/net/drivers/i386/pc/pxe.c
5593c8
@@ -386,20 +386,21 @@ GRUB_MOD_INIT(pxe)
5593c8
   grub_memset (ui, 0, sizeof (*ui));
5593c8
   grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry);
5593c8
 
5593c8
+  grub_pxe_card.default_address.len = 6;
5593c8
   grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr,
5593c8
-	       sizeof (grub_pxe_card.default_address.mac));
5593c8
-  for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
5593c8
+	       grub_pxe_card.default_address.len);
5593c8
+  for (i = 0; i < grub_pxe_card.default_address.len; i++)
5593c8
     if (grub_pxe_card.default_address.mac[i] != 0)
5593c8
       break;
5593c8
-  if (i != sizeof (grub_pxe_card.default_address.mac))
5593c8
+  if (i != grub_pxe_card.default_address.len)
5593c8
     {
5593c8
-      for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++)
5593c8
+      for (i = 0; i < grub_pxe_card.default_address.len; i++)
5593c8
 	if (grub_pxe_card.default_address.mac[i] != 0xff)
5593c8
 	  break;
5593c8
     }
5593c8
-  if (i == sizeof (grub_pxe_card.default_address.mac))
5593c8
+  if (i == grub_pxe_card.default_address.len)
5593c8
     grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr,
5593c8
-		 sizeof (grub_pxe_card.default_address.mac));
5593c8
+		 grub_pxe_card.default_address.len);
5593c8
   grub_pxe_card.mtu = ui->mtu;
5593c8
 
5593c8
   grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c
5593c8
index 3860b6f78d8..bcb3f9ea02d 100644
5593c8
--- a/grub-core/net/drivers/ieee1275/ofnet.c
5593c8
+++ b/grub-core/net/drivers/ieee1275/ofnet.c
5593c8
@@ -160,6 +160,7 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath,
5593c8
   grub_uint16_t vlantag = 0;
5593c8
 
5593c8
   hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
+  hw_addr.len = 6;
5593c8
 
5593c8
   args = bootpath + grub_strlen (devpath) + 1;
5593c8
   do
5593c8
@@ -503,6 +504,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias)
5593c8
     grub_memcpy (&lla.mac, pprop, 6);
5593c8
 
5593c8
   lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
+  lla.len = 6;
5593c8
   card->default_address = lla;
5593c8
 
5593c8
   card->txbufsize = ALIGN_UP (card->mtu, 64) + 256;
5593c8
diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c
5593c8
index 056052e40d5..22ebcbf211e 100644
5593c8
--- a/grub-core/net/drivers/uboot/ubootnet.c
5593c8
+++ b/grub-core/net/drivers/uboot/ubootnet.c
5593c8
@@ -131,6 +131,7 @@ GRUB_MOD_INIT (ubootnet)
5593c8
 
5593c8
       grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6);
5593c8
       card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
+      card->default_address.len = 6;
5593c8
 
5593c8
       card->txbufsize = ALIGN_UP (card->mtu, 64) + 256;
5593c8
       card->txbuf = grub_zalloc (card->txbufsize);
5593c8
diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c
5593c8
index 4d7ceed6f93..9aae83a5eb4 100644
5593c8
--- a/grub-core/net/ethernet.c
5593c8
+++ b/grub-core/net/ethernet.c
5593c8
@@ -29,13 +29,6 @@
5593c8
 
5593c8
 #define LLCADDRMASK 0x7f
5593c8
 
5593c8
-struct etherhdr
5593c8
-{
5593c8
-  grub_uint8_t dst[6];
5593c8
-  grub_uint8_t src[6];
5593c8
-  grub_uint16_t type;
5593c8
-} GRUB_PACKED;
5593c8
-
5593c8
 struct llchdr
5593c8
 {
5593c8
   grub_uint8_t dsap;
5593c8
@@ -55,13 +48,15 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
5593c8
 		      grub_net_link_level_address_t target_addr,
5593c8
 		      grub_net_ethertype_t ethertype)
5593c8
 {
5593c8
-  struct etherhdr *eth;
5593c8
+  grub_uint8_t *eth;
5593c8
   grub_err_t err;
5593c8
-  grub_uint8_t etherhdr_size;
5593c8
-  grub_uint16_t vlantag_id = VLANTAG_IDENTIFIER;
5593c8
+  grub_uint32_t vlantag = 0;
5593c8
+  grub_uint8_t hw_addr_len = inf->card->default_address.len;
5593c8
+  grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2;
5593c8
 
5593c8
-  etherhdr_size = sizeof (*eth);
5593c8
-  COMPILE_TIME_ASSERT (sizeof (*eth) + 4 < GRUB_NET_MAX_LINK_HEADER_SIZE);
5593c8
+  /* Source and destination link addresses + ethertype + vlan tag */
5593c8
+  COMPILE_TIME_ASSERT ((GRUB_NET_MAX_LINK_ADDRESS_SIZE * 2 + 2 + 4) <
5593c8
+		       GRUB_NET_MAX_LINK_HEADER_SIZE);
5593c8
 
5593c8
   /* Increase ethernet header in case of vlantag */
5593c8
   if (inf->vlantag != 0)
5593c8
@@ -70,11 +65,22 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
5593c8
   err = grub_netbuff_push (nb, etherhdr_size);
5593c8
   if (err)
5593c8
     return err;
5593c8
-  eth = (struct etherhdr *) nb->data;
5593c8
-  grub_memcpy (eth->dst, target_addr.mac, 6);
5593c8
-  grub_memcpy (eth->src, inf->hwaddress.mac, 6);
5593c8
+  eth = nb->data;
5593c8
+  grub_memcpy (eth, target_addr.mac, hw_addr_len);
5593c8
+  eth += hw_addr_len;
5593c8
+  grub_memcpy (eth, inf->hwaddress.mac, hw_addr_len);
5593c8
+  eth += hw_addr_len;
5593c8
+
5593c8
+  /* Check if a vlan-tag is present. */
5593c8
+  if (vlantag != 0)
5593c8
+    {
5593c8
+      *((grub_uint32_t *)eth) = grub_cpu_to_be32 (vlantag);
5593c8
+      eth += sizeof (vlantag);
5593c8
+    }
5593c8
+
5593c8
+  /* Write ethertype */
5593c8
+  *((grub_uint16_t*) eth) = grub_cpu_to_be16 (ethertype);
5593c8
 
5593c8
-  eth->type = grub_cpu_to_be16 (ethertype);
5593c8
   if (!inf->card->opened)
5593c8
     {
5593c8
       err = GRUB_ERR_NONE;
5593c8
@@ -85,18 +91,6 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf,
5593c8
       inf->card->opened = 1;
5593c8
     }
5593c8
 
5593c8
-  /* Check and add a vlan-tag if needed. */
5593c8
-  if (inf->vlantag != 0)
5593c8
-    {
5593c8
-      /* Move eth type to the right */
5593c8
-      grub_memcpy ((char *) nb->data + etherhdr_size - 2,
5593c8
-                   (char *) nb->data + etherhdr_size - 6, 2);
5593c8
-
5593c8
-      /* Add the tag in the middle */
5593c8
-      grub_memcpy ((char *) nb->data + etherhdr_size - 6, &vlantag_id, 2);
5593c8
-      grub_memcpy ((char *) nb->data + etherhdr_size - 4, (char *) &(inf->vlantag), 2);
5593c8
-    }
5593c8
-
5593c8
   return inf->card->driver->send (inf->card, nb);
5593c8
 }
5593c8
 
5593c8
@@ -104,31 +98,40 @@ grub_err_t
5593c8
 grub_net_recv_ethernet_packet (struct grub_net_buff *nb,
5593c8
 			       struct grub_net_card *card)
5593c8
 {
5593c8
-  struct etherhdr *eth;
5593c8
+  grub_uint8_t *eth;
5593c8
   struct llchdr *llch;
5593c8
   struct snaphdr *snaph;
5593c8
   grub_net_ethertype_t type;
5593c8
   grub_net_link_level_address_t hwaddress;
5593c8
   grub_net_link_level_address_t src_hwaddress;
5593c8
   grub_err_t err;
5593c8
-  grub_uint8_t etherhdr_size = sizeof (*eth);
5593c8
+  grub_uint8_t hw_addr_len = card->default_address.len;
5593c8
+  grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2;
5593c8
   grub_uint16_t vlantag = 0;
5593c8
 
5593c8
+  eth = nb->data;
5593c8
 
5593c8
-  /* Check if a vlan-tag is present. If so, the ethernet header is 4 bytes */
5593c8
-  /* longer than the original one. The vlantag id is extracted and the header */
5593c8
-  /* is reseted to the original size. */
5593c8
-  if (grub_get_unaligned16 (nb->data + etherhdr_size - 2) == VLANTAG_IDENTIFIER)
5593c8
+  hwaddress.type = card->default_address.type;
5593c8
+  hwaddress.len = hw_addr_len;
5593c8
+  grub_memcpy (hwaddress.mac, eth, hw_addr_len);
5593c8
+  eth += hw_addr_len;
5593c8
+
5593c8
+  src_hwaddress.type = card->default_address.type;
5593c8
+  src_hwaddress.len = hw_addr_len;
5593c8
+  grub_memcpy (src_hwaddress.mac, eth, hw_addr_len);
5593c8
+  eth += hw_addr_len;
5593c8
+
5593c8
+  type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
5593c8
+  if (type == VLANTAG_IDENTIFIER)
5593c8
     {
5593c8
-      vlantag = grub_get_unaligned16 (nb->data + etherhdr_size);
5593c8
+      /* Skip vlan tag */
5593c8
+      eth += 2;
5593c8
+      vlantag = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
5593c8
       etherhdr_size += 4;
5593c8
-      /* Move eth type to the original position */
5593c8
-      grub_memcpy((char *) nb->data + etherhdr_size - 6,
5593c8
-                  (char *) nb->data + etherhdr_size - 2, 2);
5593c8
+      eth += 2;
5593c8
+      type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth));
5593c8
     }
5593c8
 
5593c8
-  eth = (struct etherhdr *) nb->data;
5593c8
-  type = grub_be_to_cpu16 (eth->type);
5593c8
   err = grub_netbuff_pull (nb, etherhdr_size);
5593c8
   if (err)
5593c8
     return err;
5593c8
@@ -148,11 +151,6 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb,
5593c8
 	}
5593c8
     }
5593c8
 
5593c8
-  hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-  grub_memcpy (hwaddress.mac, eth->dst, sizeof (hwaddress.mac));
5593c8
-  src_hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-  grub_memcpy (src_hwaddress.mac, eth->src, sizeof (src_hwaddress.mac));
5593c8
-
5593c8
   switch (type)
5593c8
     {
5593c8
       /* ARP packet. */
5593c8
diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c
5593c8
index 2cbd95dce25..56a3ec5c8e8 100644
5593c8
--- a/grub-core/net/icmp6.c
5593c8
+++ b/grub-core/net/icmp6.c
5593c8
@@ -231,8 +231,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
5593c8
 		&& ohdr->len == 1)
5593c8
 	      {
5593c8
 		grub_net_link_level_address_t ll_address;
5593c8
-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
5593c8
+		ll_address.type = card->default_address.type;
5593c8
+		ll_address.len = card->default_address.len;
5593c8
+		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
5593c8
 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
5593c8
 	      }
5593c8
 	  }
5593c8
@@ -335,8 +336,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
5593c8
 		&& ohdr->len == 1)
5593c8
 	      {
5593c8
 		grub_net_link_level_address_t ll_address;
5593c8
-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
5593c8
+		ll_address.type = card->default_address.type;
5593c8
+		ll_address.len = card->default_address.len;
5593c8
+		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
5593c8
 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
5593c8
 	      }
5593c8
 	  }
5593c8
@@ -384,8 +386,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
5593c8
 		&& ohdr->len == 1)
5593c8
 	      {
5593c8
 		grub_net_link_level_address_t ll_address;
5593c8
-		ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-		grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
5593c8
+		ll_address.type = card->default_address.type;
5593c8
+		ll_address.len = card->default_address.len;
5593c8
+		grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len);
5593c8
 		grub_net_link_layer_add_address (card, source, &ll_address, 0);
5593c8
 	      }
5593c8
 	    if (ohdr->type == OPTION_PREFIX && ohdr->len == 4)
5593c8
diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
5593c8
index ea5edf8f1f6..a5896f6dc26 100644
5593c8
--- a/grub-core/net/ip.c
5593c8
+++ b/grub-core/net/ip.c
5593c8
@@ -276,8 +276,8 @@ handle_dgram (struct grub_net_buff *nb,
5593c8
 	  if (inf->card == card
5593c8
 	      && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV
5593c8
 	      && inf->hwaddress.type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
5593c8
-	      && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
5593c8
-			      sizeof (inf->hwaddress.mac)) == 0)
5593c8
+	      && (grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
5593c8
+			       bootp->hw_len) == 0 || bootp->hw_len == 0))
5593c8
 	    {
5593c8
 	      grub_net_process_dhcp (nb, inf);
5593c8
 	      grub_netbuff_free (nb);
5593c8
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
5593c8
index 22f2689aaeb..a46f82362ed 100644
5593c8
--- a/grub-core/net/net.c
5593c8
+++ b/grub-core/net/net.c
5593c8
@@ -133,8 +133,9 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf,
5593c8
 								   << 48)
5593c8
 	  && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1))))
5593c8
     {
5593c8
-      hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
-      grub_memset (hw_addr->mac, -1, 6);
5593c8
+      hw_addr->type = inf->card->default_address.type;
5593c8
+      hw_addr->len = inf->card->default_address.len;
5593c8
+      grub_memset (hw_addr->mac, -1, hw_addr->len);
5593c8
       return GRUB_ERR_NONE;
5593c8
     }
5593c8
 
5593c8
@@ -142,6 +143,7 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf,
5593c8
       && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff))
5593c8
     {
5593c8
       hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
5593c8
+      hw_addr->len = inf->card->default_address.len;
5593c8
       hw_addr->mac[0] = 0x33;
5593c8
       hw_addr->mac[1] = 0x33;
5593c8
       hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff);
5593c8
@@ -762,23 +764,23 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf)
5593c8
 void
5593c8
 grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str)
5593c8
 {
5593c8
-  str[0] = 0;
5593c8
-  switch (addr->type)
5593c8
+  char *ptr;
5593c8
+  unsigned i;
5593c8
+  int maxstr;
5593c8
+
5593c8
+  if (addr->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
5593c8
     {
5593c8
-    case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET:
5593c8
-      {
5593c8
-	char *ptr;
5593c8
-	unsigned i;
5593c8
-	for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++)
5593c8
-	  {
5593c8
-	    grub_snprintf (ptr, GRUB_NET_MAX_STR_HWADDR_LEN - (ptr - str),
5593c8
-			   "%02x:", addr->mac[i] & 0xff);
5593c8
-	    ptr += (sizeof ("XX:") - 1);
5593c8
-	  }
5593c8
-      return;
5593c8
-      }
5593c8
+       str[0] = 0;
5593c8
+       grub_printf (_("Unsupported hw address type %d len %d\n"),
5593c8
+		    addr->type, addr->len);
5593c8
+       return;
5593c8
+    }
5593c8
+  maxstr = addr->len * grub_strlen ("XX:");
5593c8
+  for (ptr = str, i = 0; i < addr->len; i++)
5593c8
+    {
5593c8
+      ptr += grub_snprintf (ptr, maxstr - (ptr - str),
5593c8
+		     "%02x:", addr->mac[i] & 0xff);
5593c8
     }
5593c8
-  grub_printf (_("Unsupported hw address type %d\n"), addr->type);
5593c8
 }
5593c8
 
5593c8
 int
5593c8
@@ -789,13 +791,17 @@ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
5593c8
     return -1;
5593c8
   if (a->type > b->type)
5593c8
     return +1;
5593c8
-  switch (a->type)
5593c8
+  if (a->len < b->len)
5593c8
+    return -1;
5593c8
+  if (a->len > b->len)
5593c8
+    return +1;
5593c8
+  if (a->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE)
5593c8
     {
5593c8
-    case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET:
5593c8
-      return grub_memcmp (a->mac, b->mac, sizeof (a->mac));
5593c8
+      grub_printf (_("Unsupported hw address type %d len %d\n"),
5593c8
+		   a->type, a->len);
5593c8
+      return + 1;
5593c8
     }
5593c8
-  grub_printf (_("Unsupported hw address type %d\n"), a->type);
5593c8
-  return 1;
5593c8
+  return grub_memcmp (a->mac, b->mac, a->len);
5593c8
 }
5593c8
 
5593c8
 int
5593c8
diff --git a/include/grub/net.h b/include/grub/net.h
5593c8
index 8a05ec4fe7a..af0404db7e3 100644
5593c8
--- a/include/grub/net.h
5593c8
+++ b/include/grub/net.h
5593c8
@@ -29,7 +29,8 @@
5593c8
 
5593c8
 enum
5593c8
   {
5593c8
-    GRUB_NET_MAX_LINK_HEADER_SIZE = 64,
5593c8
+    GRUB_NET_MAX_LINK_HEADER_SIZE = 96,
5593c8
+    GRUB_NET_MAX_LINK_ADDRESS_SIZE = 32,
5593c8
     GRUB_NET_UDP_HEADER_SIZE = 8,
5593c8
     GRUB_NET_TCP_HEADER_SIZE = 20,
5593c8
     GRUB_NET_OUR_IPV4_HEADER_SIZE = 20,
5593c8
@@ -42,15 +43,17 @@ enum
5593c8
 
5593c8
 typedef enum grub_link_level_protocol_id 
5593c8
 {
5593c8
-  GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET
5593c8
+  /* IANA ARP constant to define hardware type. */
5593c8
+  GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET = 1,
5593c8
 } grub_link_level_protocol_id_t;
5593c8
 
5593c8
 typedef struct grub_net_link_level_address
5593c8
 {
5593c8
   grub_link_level_protocol_id_t type;
5593c8
+  grub_uint8_t len;
5593c8
   union
5593c8
   {
5593c8
-    grub_uint8_t mac[6];
5593c8
+    grub_uint8_t mac[GRUB_NET_MAX_LINK_ADDRESS_SIZE];
5593c8
   };
5593c8
 } grub_net_link_level_address_t;
5593c8
 
5593c8
@@ -566,11 +569,13 @@ grub_net_addr_cmp (const grub_net_network_level_address_t *a,
5593c8
 #define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")
5593c8
 
5593c8
 /*
5593c8
-  Currently suppoerted adresses:
5593c8
-  ethernet:   XX:XX:XX:XX:XX:XX
5593c8
+  Up to 32 byte hardware address supported, see GRUB_NET_MAX_LINK_ADDRESS_SIZE
5593c8
  */
5593c8
-
5593c8
-#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX"))
5593c8
+#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof (\
5593c8
+	"XX:XX:XX:XX:XX:XX:XX:XX:"\
5593c8
+	"XX:XX:XX:XX:XX:XX:XX:XX:"\
5593c8
+	"XX:XX:XX:XX:XX:XX:XX:XX:"\
5593c8
+	"XX:XX:XX:XX:XX:XX:XX:XX"))
5593c8
 
5593c8
 void
5593c8
 grub_net_addr_to_str (const grub_net_network_level_address_t *target,