diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index fc90415f2..2d3b00f0e 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+"); @@ -324,6 +325,221 @@ grub_efinet_findcards (void) grub_free (handles); } +static struct grub_net_buff * +grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) +{ + grub_efi_uint16_t uri_len; + grub_efi_device_path_t *ldp, *ddp; + grub_efi_uri_device_path_t *uri_dp; + struct grub_net_buff *nb; + grub_err_t err; + + ddp = grub_efi_duplicate_device_path (dp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_free (ddp); + return NULL; + } + + uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0; + + if (!uri_len) + { + grub_free (ddp); + return NULL; + } + + uri_dp = (grub_efi_uri_device_path_t *) ldp; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + { + grub_free (ddp); + return NULL; + } + + nb = grub_netbuff_alloc (512); + if (!nb) + return NULL; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; + struct grub_net_bootp_packet *bp; + grub_uint8_t *ptr; + + bp = (struct grub_net_bootp_packet *) nb->tail; + err = grub_netbuff_put (nb, sizeof (*bp) + 4); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + if (sizeof(bp->boot_file) < uri_len) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (bp->boot_file, uri_dp->uri, uri_len); + grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip)); + grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip)); + + bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0; + bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1; + bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2; + bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3; + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_NETMASK; + *ptr++ = sizeof (ipv4->subnet_mask); + grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_ROUTER; + *ptr++ = sizeof (ipv4->gateway_ip_address); + grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER; + *ptr++ = sizeof ("HTTPClient") - 1; + grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr = GRUB_NET_BOOTP_END; + *use_ipv6 = 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + { + grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp; + bp->hw_type = mac->if_type; + bp->hw_len = sizeof (bp->mac_addr); + grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len); + } + } + else + { + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp; + + struct grub_net_dhcp6_packet *d6p; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_option_iana *iana; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + + d6p = (struct grub_net_dhcp6_packet *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*d6p)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + d6p->message_type = GRUB_NET_DHCP6_REPLY; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr)); + + err = grub_netbuff_put (nb, sizeof(*iana)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr)); + + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*iaaddr)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address)); + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + uri_len); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL); + opt->len = grub_cpu_to_be16 (uri_len); + grub_memcpy (opt->data, uri_dp->uri, uri_len); + + *use_ipv6 = 1; + } + + grub_free (ddp); + return nb; +} + static void grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, char **path) @@ -340,6 +556,11 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, grub_efi_device_path_t *cdp; struct grub_efi_pxe *pxe; struct grub_efi_pxe_mode *pxe_mode; + grub_uint8_t *packet_buf; + grub_size_t packet_bufsz ; + int ipv6; + struct grub_net_buff *nb = NULL; + if (card->driver != &efidriver) continue; cdp = grub_efi_get_device_path (card->efi_handle); @@ -359,11 +580,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, ldp = grub_efi_find_last_device_path (dp); if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE - && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) continue; dup_dp = grub_efi_duplicate_device_path (dp); if (!dup_dp) continue; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + dup_ldp = grub_efi_find_last_device_path (dup_dp); dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -375,16 +606,31 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, } pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (! pxe) - continue; - pxe_mode = pxe->mode; + if (!pxe) + { + nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6); + if (!nb) + { + grub_print_error (); + continue; + } + packet_buf = nb->head; + packet_bufsz = nb->tail - nb->head; + } + else + { + pxe_mode = pxe->mode; + packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack; + packet_bufsz = sizeof (pxe_mode->dhcp_ack); + ipv6 = pxe_mode->using_ipv6; + } - if (pxe_mode->using_ipv6) + if (ipv6) { grub_net_configure_by_dhcpv6_reply (card->name, card, 0, (struct grub_net_dhcp6_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); if (grub_errno) grub_print_error (); @@ -393,10 +639,14 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, { grub_net_configure_by_dhcp_ack (card->name, card, 0, (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); } + + if (nb) + grub_netbuff_free (nb); + return; } } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 50ab8e844..4b4652ca5 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -849,6 +849,8 @@ struct grub_efi_ipv4_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_ipv4_address_t gateway_ip_address; + grub_efi_ipv4_address_t subnet_mask; } GRUB_PACKED; typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t; @@ -903,6 +905,15 @@ struct grub_efi_sata_device_path } GRUB_PACKED; typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t; +#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24 + +struct grub_efi_uri_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t uri[0]; +} GRUB_PACKED; +typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 /* Media Device Path. */