grub-devel
[Top][All Lists]
Advanced

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

[PATCH 08/14] efinet: filter multicast traffic based on addresses


From: Josef Bacik
Subject: [PATCH 08/14] efinet: filter multicast traffic based on addresses
Date: Wed, 10 Feb 2016 13:21:02 -0800

We have some hardware that claims to support PROMISCUOUS_MULTICAST but doesn't
actually work.  Instead utilize the multicast filters and specifically enable
the multicast traffic we care about.  In reality we only care about ipv6
multicast traffic but enable ipv4 multicast as well just in case.  Whenever we
add a new address to the card we calculate the solicited node multicast address
to the multicast filter.  With this patch my broken hardware is still broken but
functional.  Thanks,

Signed-off-by: Josef Bacik <address@hidden>
---
 grub-core/net/drivers/efi/efinet.c | 84 ++++++++++++++++++++++++++++++++++----
 grub-core/net/net.c                |  2 +
 include/grub/net.h                 | 54 ++++++++++++------------
 3 files changed, 105 insertions(+), 35 deletions(-)

diff --git a/grub-core/net/drivers/efi/efinet.c 
b/grub-core/net/drivers/efi/efinet.c
index c8f80a1..bbbadd2 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -23,6 +23,7 @@
 #include <grub/efi/api.h>
 #include <grub/efi/efi.h>
 #include <grub/i18n.h>
+#include <grub/net/ip.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -183,8 +184,9 @@ open_card (struct grub_net_card *dev)
         We need unicast and broadcast and additionaly all nodes and
         solicited multicast for IPv6. Solicited multicast is per-IPv6
         address and we currently do not have API to do it so simply
-        try to enable receive of all multicast packets or evertyhing in
-        the worst case (i386 PXE driver always enables promiscuous too).
+        enable the all node addresses and the link local address.  We do this
+        because some firmware has been found to not do promiscuous multicast
+        mode properly.
 
         This does trust firmware to do what it claims to do.
        */
@@ -192,14 +194,25 @@ open_card (struct grub_net_card *dev)
        {
          grub_uint32_t filters = GRUB_EFI_SIMPLE_NETWORK_RECEIVE_UNICAST   |
                                  GRUB_EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
-                                 
GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+                                 GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+         grub_efi_status_t st;
+         grub_efi_mac_address_t mac_filter[2] = {
+                 { 0x1, 0, 0x5e, 0, 0, 1, },
+                 { 0x33, 0x33, 0, 0, 0, 1, },};
 
          filters &= net->mode->receive_filter_mask;
-         if (!(filters & 
GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST))
-           filters |= (net->mode->receive_filter_mask &
-                       GRUB_EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS);
-
-         efi_call_6 (net->receive_filters, net, filters, 0, 0, 0, NULL);
+         if (net->mode->max_mcast_filter_count < 2)
+           filters &= ~GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+         if (filters & GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST)
+           st = efi_call_6 (net->receive_filters, net, filters, 0, 0, 2,
+                            mac_filter);
+         else
+           st = efi_call_6 (net->receive_filters, net, filters, 0, 0, 0,
+                            NULL);
+         if (st != GRUB_EFI_SUCCESS)
+           grub_dprintf("efinet", "failed to set receive filters %u, %u\n",
+                        (unsigned)filters, (unsigned)st);
        }
 
       efi_call_4 (grub_efi_system_table->boot_services->close_protocol,
@@ -212,6 +225,58 @@ open_card (struct grub_net_card *dev)
   return GRUB_ERR_NONE;
 }
 
+/* We only need the lower 24 bits of the address, so just take the bottom part
+   of the address and convert it over.
+ */
+static void
+solicited_node_mcast_addr_to_mac (grub_uint64_t addr,
+                                 grub_efi_mac_address_t mac)
+{
+  grub_uint64_t cpu_addr = grub_be_to_cpu64(addr);
+  int i, c = 0;
+
+  /* The format is 33:33:xx:xx:xx:xx, where xx is the last 32 bits of the
+     multicast address.
+
+     The solicited node mcast addr is in the format ff02:0:0:0:0:1:ffxx:xxxx,
+     where xx is the last 24 bits of the ipv6 address.
+   */
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+  mac[2] = 0xff;
+  for (i = 3; i < 6; i++, c++)
+    mac[i] = (grub_uint8_t)((cpu_addr >> (16 - 8 * c)) & 0xff);
+}
+
+static void
+add_addr (struct grub_net_card *dev,
+         const grub_net_network_level_address_t *address)
+{
+  grub_efi_simple_network_t *net = dev->efi_net;
+  grub_efi_mac_address_t mac_filters[16];
+  grub_efi_status_t st;
+  unsigned slot = net->mode->mcast_filter_count;
+
+  /* We don't need to add anything for ipv4 addresses. */
+  if (address->type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
+    return;
+
+  if ((slot >= net->mode->max_mcast_filter_count)
+      || !(GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST &
+          net->mode->receive_filter_mask))
+    return;
+
+  grub_memcpy(mac_filters, net->mode->mcast_filter,
+             sizeof (grub_efi_mac_address_t) * slot);
+  solicited_node_mcast_addr_to_mac (address->ipv6[1], mac_filters[slot++]);
+  st = efi_call_6 (net->receive_filters, net,
+                  GRUB_EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST, 0, 0, slot,
+                  mac_filters);
+  if (st != GRUB_EFI_SUCCESS)
+    grub_dprintf("efinet", "failed to add new receive filter %u\n",
+                (unsigned)st);
+}
+
 static void
 close_card (struct grub_net_card *dev)
 {
@@ -228,7 +293,8 @@ static struct grub_net_card_driver efidriver =
     .open = open_card,
     .close = close_card,
     .send = send_card_buffer,
-    .recv = get_card_packet
+    .recv = get_card_packet,
+    .add_addr = add_addr,
   };
 
 grub_efi_handle_t
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index f49631f..e04a35b 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -252,6 +252,8 @@ grub_net_add_addr_real (char *name,
   inter->dhcp_ack = NULL;
   inter->dhcp_acklen = 0;
 
+  if (card->driver->add_addr)
+    card->driver->add_addr(card, addr);
   grub_net_network_level_interface_register (inter);
 
   return inter;
diff --git a/include/grub/net.h b/include/grub/net.h
index b5d4546..080a2d9 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -67,6 +67,32 @@ typedef enum grub_net_card_flags
     GRUB_NET_CARD_NO_MANUAL_INTERFACES = 2
   } grub_net_card_flags_t;
 
+typedef enum grub_network_level_protocol_id 
+{
+  GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV,
+  GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4,
+  GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
+} grub_network_level_protocol_id_t;
+
+typedef enum
+{
+  DNS_OPTION_IPV4,
+  DNS_OPTION_IPV6,
+  DNS_OPTION_PREFER_IPV4,
+  DNS_OPTION_PREFER_IPV6
+} grub_dns_option_t;
+
+typedef struct grub_net_network_level_address
+{
+  grub_network_level_protocol_id_t type;
+  union
+  {
+    grub_uint32_t ipv4;
+    grub_uint64_t ipv6[2];
+  };
+  grub_dns_option_t option;
+} grub_net_network_level_address_t;
+
 struct grub_net_card;
 
 struct grub_net_card_driver
@@ -79,6 +105,8 @@ struct grub_net_card_driver
   grub_err_t (*send) (struct grub_net_card *dev,
                      struct grub_net_buff *buf);
   struct grub_net_buff * (*recv) (struct grub_net_card *dev);
+  void (*add_addr) (struct grub_net_card *dev,
+                   const grub_net_network_level_address_t *address);
 };
 
 typedef struct grub_net_packet
@@ -150,32 +178,6 @@ struct grub_net_card
 
 struct grub_net_network_level_interface;
 
-typedef enum grub_network_level_protocol_id 
-{
-  GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV,
-  GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4,
-  GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
-} grub_network_level_protocol_id_t;
-
-typedef enum
-{
-  DNS_OPTION_IPV4,
-  DNS_OPTION_IPV6,
-  DNS_OPTION_PREFER_IPV4,
-  DNS_OPTION_PREFER_IPV6
-} grub_dns_option_t;
-
-typedef struct grub_net_network_level_address
-{
-  grub_network_level_protocol_id_t type;
-  union
-  {
-    grub_uint32_t ipv4;
-    grub_uint64_t ipv6[2];
-  };
-  grub_dns_option_t option;
-} grub_net_network_level_address_t;
-
 typedef struct grub_net_network_level_netaddress
 {
   grub_network_level_protocol_id_t type;
-- 
1.8.1




reply via email to

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