From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1aTcW1-000082-0Y for mharc-grub-devel@gnu.org; Wed, 10 Feb 2016 16:41:37 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51786) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aTcVy-00004v-Dq for grub-devel@gnu.org; Wed, 10 Feb 2016 16:41:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aTcVu-0003BC-CF for grub-devel@gnu.org; Wed, 10 Feb 2016 16:41:34 -0500 Received: from 66-220-144-178.intmgw.facebook.com ([66.220.144.178]:26001 helo=mx-out.facebook.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aTcVu-0003B8-3B for grub-devel@gnu.org; Wed, 10 Feb 2016 16:41:30 -0500 Received: from facebook.com (2401:db00:2040:5012:face:0:9:0) by mx-out.facebook.com (10.102.107.97) with ESMTP id 0d418b1ed03c11e5b7800002c99331b0-ffcef270 for ; Wed, 10 Feb 2016 13:20:04 -0800 Received: by devbig041.ash4.facebook.com (Postfix, from userid 8730) id AC6DB4540BEC; Wed, 10 Feb 2016 13:21:10 -0800 (PST) From: Josef Bacik To: grub-devel@gnu.org, kernel-team@fb.com Subject: [PATCH 08/14] efinet: filter multicast traffic based on addresses Date: Wed, 10 Feb 2016 13:21:02 -0800 Message-Id: <1455139268-3241273-9-git-send-email-jbacik@fb.com> X-Mailer: git-send-email 1.8.1 In-Reply-To: <1455139268-3241273-1-git-send-email-jbacik@fb.com> References: <1455139268-3241273-1-git-send-email-jbacik@fb.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 66.220.144.178 Cc: Josef Bacik X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 10 Feb 2016 21:41:35 -0000 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 --- 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 #include #include +#include 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