From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1YiJOy-0001e4-Hf for mharc-grub-devel@gnu.org; Wed, 15 Apr 2015 05:14:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40869) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YiJOt-0001Vd-MN for grub-devel@gnu.org; Wed, 15 Apr 2015 05:14:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YiJOp-0003mL-E0 for grub-devel@gnu.org; Wed, 15 Apr 2015 05:14:27 -0400 Received: from mail-vn0-x233.google.com ([2607:f8b0:400c:c0f::233]:46020) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YiJOp-0003m6-7L for grub-devel@gnu.org; Wed, 15 Apr 2015 05:14:23 -0400 Received: by vnbg190 with SMTP id g190so12620243vnb.12 for ; Wed, 15 Apr 2015 02:14:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:subject:date:message-id:in-reply-to:references; bh=hdpu+1BkDGRvVhu+SfKiVvT4bdBJsG+CgKHY1q2Gk8M=; b=EXD5kSwLfGD0dRhuYWwK4V+hZSVcils1WYrNxRJV/sgclgS1S0lmwVPCxKF8t9zUXP FEtvf1E5syEmJNvxWqL9sw/Wm66j2gEd/bS1uqzRViJl3gaRzqL1LXuz1FQhtvaNHbU9 S9UFHEdBHj1s8XYrvUCC4l0OPFUmPbrwu77Ogp0/271I1J/w7oyiyxg7tpmwYtwCSTzj BBZWIINxQLIWe80zvSCybMKMu2K4Jz9AHZhGlILXw8UrPIZ0yMSxTkUU0OJ9WYfDW2fW 79DnEGA6704bTFeykNAxdKCGORWV6y7C0kz1yLAAS2Ww0mzpb1o+25hnUQDzLznVgxp5 sA6A== X-Received: by 10.202.187.5 with SMTP id l5mr14569017oif.16.1429089262644; Wed, 15 Apr 2015 02:14:22 -0700 (PDT) Received: from localhost.localdomain ([130.57.30.250]) by mx.google.com with ESMTPSA id os15sm2022411oeb.8.2015.04.15.02.14.20 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 15 Apr 2015 02:14:22 -0700 (PDT) Sender: =?UTF-8?B?5by15paH6I+v?= From: Michael Chang To: grub-devel@gnu.org Subject: [PATCH 1/3] Added net_bootp6 command Date: Wed, 15 Apr 2015 17:05:07 +0800 Message-Id: <1429088709-924-2-git-send-email-mchang@suse.com> X-Mailer: git-send-email 1.7.3.4 In-Reply-To: <1429088709-924-1-git-send-email-mchang@suse.com> References: <1429088709-924-1-git-send-email-mchang@suse.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:400c:c0f::233 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, 15 Apr 2015 09:14:30 -0000 The net_bootp6 is used to configure the ipv6 network interface through the DHCPv6 protocol Solict/Advertise/Request/Reply. --- grub-core/net/bootp.c | 885 +++++++++++++++++++++++++++++++++++++++++++++++- grub-core/net/ip.c | 35 ++ include/grub/efi/api.h | 56 +++- include/grub/net.h | 19 + 4 files changed, 993 insertions(+), 2 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 6136755..477f205 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -24,6 +24,7 @@ #include #include #include +#include static void parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) @@ -256,6 +257,653 @@ grub_net_configure_by_dhcp_ack (const char *name, return inter; } +struct grub_dhcpv6_option { + grub_uint16_t code; + grub_uint16_t len; + grub_uint8_t data[0]; +} GRUB_PACKED; + + +struct grub_dhcpv6_iana_option { + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_dhcpv6_iaaddr_option { + grub_uint8_t addr[16]; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_DUID_LL +{ + grub_uint16_t type; + grub_uint16_t hw_type; + grub_uint8_t hwaddr[6]; +} GRUB_PACKED; + +struct grub_dhcpv6_dns_servers { + grub_uint8_t addr[16]; + grub_uint8_t next_addr[0]; +} GRUB_PACKED; + +#define DHCPv6_REPLY 7 +#define DHCPv6_ADVERTISE 2 +#define DHCPv6_REQUEST 3 +#define OPTION_BOOTFILE_URL 59 +#define OPTION_DNS_SERVERS 23 +#define OPTION_IA_NA 3 +#define OPTION_IAADDR 5 +#define OPTION_CLIENTID 1 +#define OPTION_SERVERID 2 +#define OPTION_ORO 6 +#define OPTION_ELAPSED_TIME 8 + +struct grub_dhcpv6_session +{ + struct grub_dhcpv6_session *next; + struct grub_dhcpv6_session **prev; + grub_uint32_t iaid; + grub_uint32_t transaction_id:24; + grub_uint64_t start_time; + struct grub_net_network_level_interface *ifaces; +}; + +static struct grub_dhcpv6_session *grub_dhcpv6_sessions = NULL; +#define FOR_DHCPV6_SESSIONS(var) \ + for (var = grub_dhcpv6_sessions ; var; var = var->next) + +static void +grub_dhcpv6_session_add (struct grub_dhcpv6_session *session) +{ + struct grub_datetime date; + grub_err_t err; + grub_int32_t t = 0; + + err = grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + t = 0; + } + + session->transaction_id = t; + session->start_time = grub_get_time_ms (); + + session->prev = &grub_dhcpv6_sessions; + session->next = grub_dhcpv6_sessions; + + if (session->next) + session->next->prev = &session->next; + + grub_dhcpv6_sessions = session; + return; +} + +static void +grub_dhcpv6_session_remove (struct grub_dhcpv6_session *session) +{ + *session->prev = session->next; + if (session->next) + session->next->prev = session->prev; + session->next = NULL; + session->prev = NULL; + return; +} + +static const struct grub_dhcpv6_option* +find_dhcpv6_option (const struct grub_net_dhcpv6_packet *packet, + grub_uint16_t option) +{ + grub_uint16_t code, len; + const struct grub_dhcpv6_option *popt; + + popt = (const struct grub_dhcpv6_option *)packet->dhcp_options; + code = grub_be_to_cpu16 (popt->code); + len = grub_be_to_cpu16 (popt->len); + + while (0 != code && option != code) + { + popt = (const struct grub_dhcpv6_option *)((grub_uint8_t *)popt + + len + sizeof(*popt)); + code = grub_be_to_cpu16 (popt->code); + len = grub_be_to_cpu16 (popt->len); + } + + if (option == code) + return popt; + + return NULL; +} + +static const grub_uint8_t* +find_dhcpv6_address (const struct grub_net_dhcpv6_packet *packet) +{ + const struct grub_dhcpv6_option* popt = find_dhcpv6_option (packet, OPTION_IA_NA); + const struct grub_dhcpv6_iana_option *ia_na; + const struct grub_dhcpv6_option *iaaddr_hdr; + const struct grub_dhcpv6_iaaddr_option *iaaddr; + grub_uint16_t ia_na_data_offset, ia_na_data_len, len; + + if (grub_be_to_cpu16 (popt->code) != OPTION_IA_NA) + { + grub_error (GRUB_ERR_IO, N_("not an IA_NA DHCPv6 option")); + return NULL; + } + + ia_na = (const struct grub_dhcpv6_iana_option *)popt->data; + + if (grub_be_to_cpu16(popt->len) <= sizeof (*ia_na)) + { + grub_error (GRUB_ERR_IO, N_("invalid size for IAADDR")); + return NULL; + } + + ia_na_data_len = grub_be_to_cpu16(popt->len) - sizeof (*ia_na); + ia_na_data_offset = 0; + + iaaddr_hdr = (const struct grub_dhcpv6_option *) ia_na->data; + len = grub_be_to_cpu16 (iaaddr_hdr->len); + + while (grub_be_to_cpu16(iaaddr_hdr->code) != OPTION_IAADDR) + { + ia_na_data_offset += (len + sizeof (*iaaddr_hdr)); + + if (ia_na_data_offset < ia_na_data_len) + { + iaaddr_hdr =(const struct grub_dhcpv6_option *)(ia_na->data + + ia_na_data_offset); + len = grub_be_to_cpu16 (iaaddr_hdr->len); + } + else + { + iaaddr_hdr = NULL; + break; + } + } + + if (!iaaddr_hdr) + { + grub_error (GRUB_ERR_IO, N_("IAADDR not found")); + return NULL; + } + + if ((ia_na_data_offset + sizeof (*iaaddr_hdr) + len) > ia_na_data_len) + { + grub_error (GRUB_ERR_IO, N_("IAADDR size check failed")); + return NULL; + } + + iaaddr = (const struct grub_dhcpv6_iaaddr_option *) iaaddr_hdr->data; + + return iaaddr->addr; +} + +static void +get_dhcpv6_dns_address (const struct grub_net_dhcpv6_packet *packet, + grub_net_network_level_address_t **addr, grub_uint16_t *naddr) +{ + const struct grub_dhcpv6_option* popt; + const struct grub_dhcpv6_dns_servers *dns; + grub_uint16_t len; + const grub_uint8_t *pa; + int i, ln; + grub_net_network_level_address_t *la; + + if (addr) + *addr = NULL; + + if (naddr) + *naddr = 0; + + popt = find_dhcpv6_option (packet, OPTION_DNS_SERVERS); + if (!popt) + return; + + len = grub_be_to_cpu16 (popt->len); + if ((len % 16) != 0) + { + grub_error (GRUB_ERR_IO, N_("invalid dns address length")); + return; + } + + dns = (const struct grub_dhcpv6_dns_servers *)popt->data; + + ln = len / 16; + la = grub_zalloc (sizeof (grub_net_network_level_address_t) * ln); + + for (i = 0, pa = dns->addr; i < ln; i++, pa = dns->next_addr) + { + (la + i)->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + (la + i)->ipv6[0] = grub_get_unaligned64 (pa); + (la + i)->ipv6[1] = grub_get_unaligned64 (pa + 8); + (la + i)->option = DNS_OPTION_PREFER_IPV6; + } + + *addr = la; + *naddr = ln; + + return; +} + +static void +find_dhcpv6_bootfile_url (const struct grub_net_dhcpv6_packet *packet, + char **proto, char **server_ip, char **boot_file) +{ + char *bootfile_url; + const struct grub_dhcpv6_option* opt_url; + char *ip_start, *ip_end; + char *path; + grub_size_t ip_len; + grub_uint16_t len; + const char *protos[] = {"tftp://", "http://", NULL}; + const char *pr; + int i; + + if (proto) + *proto = NULL; + + if (server_ip) + *server_ip = NULL; + + if (boot_file) + *boot_file = NULL; + + opt_url = find_dhcpv6_option (packet, OPTION_BOOTFILE_URL); + + if (!opt_url) + { + grub_error (GRUB_ERR_IO, N_("no bootfile-url in DHCPv6 option")); + return; + } + + len = grub_be_to_cpu16 (opt_url->len); + + bootfile_url = grub_malloc (len + 1); + + if (!bootfile_url) + return; + + grub_memcpy (bootfile_url, opt_url->data, len); + bootfile_url[len] = '\0'; + + for (i = 0; (pr = *(protos + i)); ++i) + if (grub_strncmp (bootfile_url, pr, grub_strlen(pr)) == 0) + break; + + if (!pr) + { + grub_error (GRUB_ERR_IO, + N_("unsupported protocol, only tftp and http are supported")); + goto cleanup; + } + + ip_start = ip_end = NULL; + ip_start = bootfile_url + grub_strlen(pr); + + if (*ip_start != '[') + ip_start = NULL; + else + ip_end = grub_strchr (++ip_start, ']'); + + if (!ip_start || !ip_end) + { + grub_error (GRUB_ERR_IO, N_("IPv6-address not in square brackets")); + goto cleanup; + } + + ip_len = ip_end - ip_start; + + if (proto) + { + grub_size_t proto_len = grub_strlen (pr) - 3; + + *proto = grub_malloc (proto_len + 1); + if (!*proto) + goto cleanup; + + grub_memcpy (*proto, pr, proto_len); + *(*proto + proto_len) = '\0'; + } + + if (server_ip) + { + *server_ip = grub_malloc (ip_len + 1); + + if (!*server_ip) + goto cleanup; + + grub_memcpy (*server_ip, ip_start, ip_len); + *(*server_ip + ip_len) = '\0'; + } + + path = ip_end + 1; + + if (boot_file) + { + *boot_file = grub_strdup (path); + + if (!*boot_file) + goto cleanup; + } + +cleanup: + + if (bootfile_url) + grub_free (bootfile_url); + + if (grub_errno) + { + if (proto && *proto) + { + grub_free (proto); + *proto = NULL; + } + + if (server_ip && *server_ip) + { + grub_free (server_ip); + *server_ip = NULL; + } + + if (boot_file && *boot_file) + { + grub_free (boot_file); + *boot_file = NULL; + } + } + + return; +} + + +static grub_err_t +grub_net_configure_by_dhcpv6_adv (const struct grub_net_dhcpv6_packet *v6_adv, + struct grub_dhcpv6_session *session) +{ + struct grub_net_buff *nb; + const struct grub_dhcpv6_option *opt_client, *opt_server, *opt_iana; + struct grub_dhcpv6_option *popt; + struct grub_net_dhcpv6_packet *v6; + struct udphdr *udph; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + struct grub_net_network_level_interface *inf; + grub_err_t err; + grub_uint16_t len; + grub_uint64_t elapsed; + char err_msg[64]; + + if (v6_adv->message_type != DHCPv6_ADVERTISE) + { + grub_error (GRUB_ERR_IO, N_("DHCPv6 info not found")); + return grub_errno; + } + + opt_client = find_dhcpv6_option (v6_adv, OPTION_CLIENTID); + opt_server = find_dhcpv6_option (v6_adv, OPTION_SERVERID); + opt_iana = find_dhcpv6_option (v6_adv, OPTION_IA_NA); + + err_msg[0] = '\0'; + if (!opt_client) + grub_strcpy (err_msg, "client id"); + + if (!opt_server) + { + if (grub_strlen (err_msg)) + grub_strcpy (err_msg + grub_strlen (err_msg), ", server id"); + else + grub_strcpy (err_msg, "server id"); + } + + if (!opt_iana) + { + if (grub_strlen (err_msg)) + grub_strcpy (err_msg + grub_strlen (err_msg), ", iana"); + else + grub_strcpy (err_msg, "iana"); + } + + if (grub_strlen (err_msg)) + { + grub_strcpy (err_msg + grub_strlen (err_msg), " missing"); + grub_error (GRUB_ERR_IO, N_(err_msg)); + return grub_errno; + } + + inf = session->ifaces; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); + if (err) + return err; + + nb = grub_netbuff_alloc (512); + + if (!nb) + { + grub_netbuff_free (nb); + return grub_errno; + } + + err = grub_netbuff_reserve (nb, 512); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + len = grub_cpu_to_be16(opt_client->len); + err = grub_netbuff_push (nb, len + 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + grub_memcpy (nb->data, opt_client, len + 4); + + len = grub_cpu_to_be16(opt_server->len); + err = grub_netbuff_push (nb, len + 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + grub_memcpy (nb->data, opt_server, len + 4); + + len = grub_cpu_to_be16(opt_iana->len); + err = grub_netbuff_push (nb, len + 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + grub_memcpy (nb->data, opt_iana, len + 4); + + err = grub_netbuff_push (nb, 8); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + popt = (struct grub_dhcpv6_option*) nb->data; + popt->code = grub_cpu_to_be16_compile_time (OPTION_ORO); + popt->len = grub_cpu_to_be16_compile_time (4); + grub_set_unaligned16 (popt->data, grub_cpu_to_be16_compile_time (OPTION_BOOTFILE_URL)); + grub_set_unaligned16 (popt->data + 2, grub_cpu_to_be16_compile_time (OPTION_DNS_SERVERS)); + + err = grub_netbuff_push (nb, 6); + if (err) + { + grub_netbuff_free (nb); + return err; + } + popt = (struct grub_dhcpv6_option*) nb->data; + popt->code = grub_cpu_to_be16_compile_time (OPTION_ELAPSED_TIME); + popt->len = grub_cpu_to_be16_compile_time (2); + + // the time is expressed in hundredths of a second + elapsed = grub_divmod64 (grub_get_time_ms () - session->start_time, 10, 0); + + if (elapsed > 0xffff) + elapsed = 0xffff; + + grub_set_unaligned16 (popt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed)); + + err = grub_netbuff_push (nb, 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + v6 = (struct grub_net_dhcpv6_packet *) nb->data; + v6->message_type = DHCPv6_REQUEST; + v6->transaction_id = v6_adv->transaction_id; + + err = grub_netbuff_push (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (546); + udph->dst = grub_cpu_to_be16_compile_time (547); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, + GRUB_NET_IP_UDP); + + grub_netbuff_free (nb); + + if (err) + return err; + + return GRUB_ERR_NONE; +} + + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcpv6_packet *v6, + grub_size_t size __attribute__ ((unused)), + int is_def, + char **device, char **path) +{ + grub_net_network_level_address_t addr; + grub_net_network_level_netaddress_t netaddr; + struct grub_net_network_level_interface *inf; + const grub_uint8_t *your_ip; + char *proto; + char *server_ip; + char *boot_file; + grub_net_network_level_address_t *dns; + grub_uint16_t num_dns; + + if (device) + *device = NULL; + + if (path) + *path = NULL; + + if (v6->message_type != DHCPv6_REPLY) + { + grub_error (GRUB_ERR_IO, N_("DHCPv6 info not found")); + return NULL; + } + + your_ip = find_dhcpv6_address(v6); + + if (!your_ip) + { + grub_error (GRUB_ERR_IO, N_("DHCPv6 address not found")); + return NULL; + } + + get_dhcpv6_dns_address (v6, &dns, &num_dns); + + if (dns && num_dns) + { + int i; + + for (i = 0; i < num_dns; ++i) + grub_net_add_dns_server (dns + i); + + grub_free (dns); + } + else + { + if (grub_errno) + grub_print_error (); + } + + find_dhcpv6_bootfile_url (v6, &proto, &server_ip, &boot_file); + + if (grub_errno) + grub_print_error (); + + addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + addr.ipv6[0] = grub_get_unaligned64 (your_ip); + addr.ipv6[1] = grub_get_unaligned64 (your_ip + 8); + inf = grub_net_add_addr (name, card, &addr, &card->default_address, flags); + + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + netaddr.ipv6.base[0] = grub_get_unaligned64 (your_ip); + netaddr.ipv6.base[1] = 0; + netaddr.ipv6.masksize = 64; + grub_net_add_route (name, netaddr, inf); + + grub_env_set_net_property (name, "boot_file", boot_file, + grub_strlen (boot_file)); + + if (is_def && server_ip) + { + grub_net_default_server = grub_strdup (server_ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } + + if (device && server_ip && proto) + { + *device = grub_xasprintf ("%s,%s", proto, server_ip); + if (!*device) + return NULL; + } + + if (path && boot_file) + { + *path = grub_strdup (boot_file); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + else + return NULL; + } + + return inf; +} + void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_card *card) @@ -288,6 +936,67 @@ grub_net_process_dhcp (struct grub_net_buff *nb, } } +void +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card __attribute__ ((unused))) +{ + const struct grub_net_dhcpv6_packet *v6; + struct grub_dhcpv6_session *session; + const struct grub_dhcpv6_option *opt_iana; + const struct grub_dhcpv6_iana_option *ia_na; + + v6 = (const struct grub_net_dhcpv6_packet *) nb->data; + + opt_iana = find_dhcpv6_option (v6, OPTION_IA_NA); + if (!opt_iana) + return; + + ia_na = (const struct grub_dhcpv6_iana_option *)opt_iana->data; + FOR_DHCPV6_SESSIONS (session) + { + if (session->transaction_id == v6->transaction_id + && session->iaid == grub_cpu_to_be32 (ia_na->iaid)) + break; + } + + if (!session) + return; + + + if (v6->message_type == DHCPv6_ADVERTISE) + { + grub_net_configure_by_dhcpv6_adv ( + (const struct grub_net_dhcpv6_packet*) nb->data, session); + } + else if (v6->message_type == DHCPv6_REPLY) + { + char *name; + struct grub_net_network_level_interface *inf; + + inf = session->ifaces; + name = grub_xasprintf ("%s:dhcp", inf->card->name); + if (!name) + return; + + grub_net_configure_by_dhcpv6_reply (name, inf->card, + 0, (const struct grub_net_dhcpv6_packet *) nb->data, + (nb->tail - nb->data), 0, 0, 0); + + if (!grub_errno) + { + grub_dhcpv6_session_remove (session); + grub_free (session); + } + + grub_free (name); + } + + if (grub_errno) + grub_print_error (); + + return; +} + static char hexdigit (grub_uint8_t val) { @@ -564,7 +1273,177 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd_getdhcp, cmd_bootp; +static grub_err_t +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + grub_size_t ncards = 0; + unsigned j = 0; + int interval; + grub_err_t err; + struct grub_dhcpv6_session *session; + + err = GRUB_ERR_NONE; + + FOR_NET_CARDS (card) + { + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + ncards++; + } + + FOR_NET_CARDS (card) + { + struct grub_net_network_level_interface *ifaces; + + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + + ifaces = grub_net_ipv6_get_link_local (card, &card->default_address); + if (!ifaces) + { + grub_free (ifaces); + return grub_errno; + } + + session = grub_zalloc (sizeof (*session)); + session->ifaces = ifaces; + session->iaid = j; + grub_dhcpv6_session_add (session); + j++; + } + + for (interval = 200; interval < 10000; interval *= 2) + { + int done = 1; + + FOR_DHCPV6_SESSIONS (session) + { + struct grub_net_buff *nb; + struct grub_dhcpv6_option *opt; + struct grub_net_dhcpv6_packet *v6; + struct grub_DUID_LL *duid; + struct grub_dhcpv6_iana_option *ia_na; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + struct udphdr *udph; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (session->ifaces, + &multicast, &ll_multicast); + if (err) + return grub_errno; + nb = grub_netbuff_alloc (512); + if (!nb) + { + grub_netbuff_free (nb); + return grub_errno; + } + + err = grub_netbuff_reserve (nb, 512); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, 6); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_dhcpv6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (2); + grub_set_unaligned16 (opt->data, 0); + + err = grub_netbuff_push (nb, sizeof(*duid) + 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_dhcpv6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (OPTION_CLIENTID); //option_client_id + opt->len = grub_cpu_to_be16 (sizeof(*duid)); + + duid = (struct grub_DUID_LL *) opt->data; + + duid->type = grub_cpu_to_be16_compile_time (3) ; + duid->hw_type = grub_cpu_to_be16_compile_time (1); + grub_memcpy (&duid->hwaddr, &session->ifaces->hwaddress.mac, + sizeof (session->ifaces->hwaddress.mac)); + + err = grub_netbuff_push (nb, sizeof (*ia_na) + 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_dhcpv6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + ia_na = (struct grub_dhcpv6_iana_option *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (session->iaid); + ia_na->t1 = 0; + ia_na->t2 = 0; + + err = grub_netbuff_push (nb, 4); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + v6 = (struct grub_net_dhcpv6_packet *)nb->data; + v6->message_type = 1; + v6->transaction_id = session->transaction_id; + + grub_netbuff_push (nb, sizeof (*udph)); + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (546); + udph->dst = grub_cpu_to_be16_compile_time (547); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &session->ifaces->address, &multicast); + + err = grub_net_send_ip_packet (session->ifaces, &multicast, + &ll_multicast, nb, GRUB_NET_IP_UDP); + done = 0; + grub_netbuff_free (nb); + + if (err) + return err; + } + if (!done) + grub_net_poll_cards (interval, 0); + } + + FOR_DHCPV6_SESSIONS (session) + { + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("couldn't autoconfigure %s"), + session->ifaces->card->name); + grub_dhcpv6_session_remove (session); + grub_free (session); + } + + + return err; +} + +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_bootp6; void grub_bootp_init (void) @@ -575,6 +1454,9 @@ grub_bootp_init (void) cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, N_("VAR INTERFACE NUMBER DESCRIPTION"), N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6, + N_("[CARD]"), + N_("perform a dhcpv6 autoconfiguration")); } void @@ -582,4 +1464,5 @@ grub_bootp_fini (void) { grub_unregister_command (cmd_getdhcp); grub_unregister_command (cmd_bootp); + grub_unregister_command (cmd_bootp6); } diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 8c56baa..8bb56be 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -238,6 +238,41 @@ handle_dgram (struct grub_net_buff *nb, { struct udphdr *udph; udph = (struct udphdr *) nb->data; + + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 546) + { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_UDP, + source, + dest); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + grub_net_process_dhcp6 (nb, card); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { const struct grub_net_bootp_packet *bootp; diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index e5dd543..ff77750 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1340,14 +1340,68 @@ typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; +typedef struct { + grub_uint8_t addr[4]; +} grub_efi_pxe_ipv4_address_t; + +typedef struct { + grub_uint8_t addr[16]; +} grub_efi_pxe_ipv6_address_t; + +typedef struct { + grub_uint8_t addr[32]; +} grub_efi_pxe_mac_address_t; + +typedef union { + grub_uint32_t addr[4]; + grub_efi_pxe_ipv4_address_t v4; + grub_efi_pxe_ipv6_address_t v6; +} grub_efi_pxe_ip_address_t; + +#define EFI_PXE_BASE_CODE_MAX_IPCNT 8 +typedef struct { + grub_uint8_t filters; + grub_uint8_t ip_cnt; + grub_uint16_t reserved; + grub_efi_pxe_ip_address_t ip_list[EFI_PXE_BASE_CODE_MAX_IPCNT]; +} grub_efi_pxe_ip_filter_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_mac_address_t mac_addr; +} grub_efi_pxe_arp_entry_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_ip_address_t subnet_mask; + grub_efi_pxe_ip_address_t gw_addr; +} grub_efi_pxe_route_entry_t; + + +#define EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 +#define EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 + typedef struct grub_efi_pxe_mode { - grub_uint8_t unused[52]; + grub_uint8_t started; + grub_uint8_t ipv6_available; + grub_uint8_t ipv6_supported; + grub_uint8_t using_ipv6; + //grub_uint8_t unused[48]; + grub_uint8_t unused[16]; + grub_efi_pxe_ip_address_t station_ip; + grub_efi_pxe_ip_address_t subnet_mask; grub_efi_pxe_packet_t dhcp_discover; grub_efi_pxe_packet_t dhcp_ack; grub_efi_pxe_packet_t proxy_offer; grub_efi_pxe_packet_t pxe_discover; grub_efi_pxe_packet_t pxe_reply; + grub_efi_pxe_packet_t pxe_bis_reply; + grub_efi_pxe_ip_filter_t ip_filter; + grub_uint32_t arp_cache_entries; + grub_efi_pxe_arp_entry_t arp_cache[EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; + grub_uint32_t route_table_entries; + grub_efi_pxe_route_entry_t route_table[EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; } grub_efi_pxe_mode_t; typedef struct grub_efi_pxe diff --git a/include/grub/net.h b/include/grub/net.h index 538baa3..71dc243 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -418,6 +418,13 @@ struct grub_net_bootp_packet grub_uint8_t vendor[0]; } GRUB_PACKED; +struct grub_net_dhcpv6_packet +{ + grub_uint32_t message_type:8; + grub_uint32_t transaction_id:24; + grub_uint8_t dhcp_options[0]; +} GRUB_PACKED; + #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 @@ -444,6 +451,14 @@ grub_net_configure_by_dhcp_ack (const char *name, grub_size_t size, int is_def, char **device, char **path); +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcpv6_packet *v6, + grub_size_t size, + int is_def, char **device, char **path); + grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, int mask); @@ -452,6 +467,10 @@ void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_card *card); +void +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card); + int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, const grub_net_link_level_address_t *b); -- 1.7.3.4