1 From 7c754fff55f261e39d25da72413213600b2577c3 Mon Sep 17 00:00:00 2001
2 From: Michael Chang <mchang@suse.com>
3 Date: Thu, 27 Oct 2016 17:41:04 -0400
4 Subject: bootp: New net_bootp6 command
6 Implement new net_bootp6 command for IPv6 network auto configuration via the
7 DHCPv6 protocol (RFC3315).
9 Signed-off-by: Michael Chang <mchang@suse.com>
10 Signed-off-by: Ken Lin <ken.lin@hpe.com>
12 Patch-Name: bootp_new_net_bootp6_command.patch
14 grub-core/net/bootp.c | 908 +++++++++++++++++++++++++++++++++++++++++-
15 grub-core/net/ip.c | 39 ++
16 include/grub/net.h | 72 ++++
17 3 files changed, 1018 insertions(+), 1 deletion(-)
19 diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
20 index 9e2fdb795..172528ee8 100644
21 --- a/grub-core/net/bootp.c
22 +++ b/grub-core/net/bootp.c
24 #include <grub/net/netbuff.h>
25 #include <grub/net/udp.h>
26 #include <grub/datetime.h>
27 +#include <grub/time.h>
28 +#include <grub/list.h>
31 +dissect_url (const char *url, char **proto, char **host, char **path)
36 + *proto = *host = *path = NULL;
39 + while ((p = grub_strchr (p, ':')))
41 + if (grub_strlen (p) < sizeof ("://") - 1)
43 + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
46 + *proto = grub_malloc (l + 1);
49 + grub_print_error ();
53 + grub_memcpy (*proto, ps, l);
55 + p += sizeof ("://") - 1;
63 + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
68 + p = grub_strchr (p, '/');
72 + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
80 + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
82 + *host = grub_malloc (l - 1);
85 + grub_print_error ();
90 + grub_memcpy (*host, ps + 1, l - 2);
95 + *host = grub_malloc (l + 1);
98 + grub_print_error ();
103 + grub_memcpy (*host, ps, l);
107 + *path = grub_strdup (p);
110 + grub_print_error ();
112 + grub_free (*proto);
121 parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
122 @@ -270,6 +362,578 @@ grub_net_configure_by_dhcp_ack (const char *name,
126 +/* The default netbuff size for sending DHCPv6 packets which should be
127 + large enough to hold the information */
128 +#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512
130 +struct grub_dhcp6_options
132 + grub_uint8_t *client_duid;
133 + grub_uint16_t client_duid_len;
134 + grub_uint8_t *server_duid;
135 + grub_uint16_t server_duid_len;
136 + grub_uint32_t iaid;
139 + grub_net_network_level_address_t *ia_addr;
140 + grub_uint32_t preferred_lifetime;
141 + grub_uint32_t valid_lifetime;
142 + grub_net_network_level_address_t *dns_server_addrs;
143 + grub_uint16_t num_dns_server;
144 + char *boot_file_proto;
145 + char *boot_file_server_ip;
146 + char *boot_file_path;
149 +typedef struct grub_dhcp6_options *grub_dhcp6_options_t;
151 +struct grub_dhcp6_session
153 + struct grub_dhcp6_session *next;
154 + struct grub_dhcp6_session **prev;
155 + grub_uint32_t iaid;
156 + grub_uint32_t transaction_id:24;
157 + grub_uint64_t start_time;
158 + struct grub_net_dhcp6_option_duid_ll duid;
159 + struct grub_net_network_level_interface *iface;
161 + /* The associated dhcpv6 options */
162 + grub_dhcp6_options_t adv;
163 + grub_dhcp6_options_t reply;
166 +typedef struct grub_dhcp6_session *grub_dhcp6_session_t;
168 +typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data);
171 +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size,
172 + dhcp6_option_hook_fn hook, void *hook_data);
175 +parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data)
177 + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data;
179 + grub_uint16_t code = grub_be_to_cpu16 (opt->code);
180 + grub_uint16_t len = grub_be_to_cpu16 (opt->len);
182 + if (code == GRUB_NET_DHCP6_OPTION_IAADDR)
184 + const struct grub_net_dhcp6_option_iaaddr *iaaddr;
185 + iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data;
187 + if (len < sizeof (*iaaddr))
189 + grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len);
192 + if (!dhcp6->ia_addr)
194 + dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr));
195 + dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
196 + dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr);
197 + dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8);
198 + dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime);
199 + dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime);
205 +parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data)
207 + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data;
208 + grub_uint16_t code = grub_be_to_cpu16 (opt->code);
209 + grub_uint16_t len = grub_be_to_cpu16 (opt->len);
213 + case GRUB_NET_DHCP6_OPTION_CLIENTID:
215 + if (dhcp6->client_duid || !len)
217 + grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len);
220 + dhcp6->client_duid = grub_malloc (len);
221 + grub_memcpy (dhcp6->client_duid, opt->data, len);
222 + dhcp6->client_duid_len = len;
225 + case GRUB_NET_DHCP6_OPTION_SERVERID:
227 + if (dhcp6->server_duid || !len)
229 + grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len);
232 + dhcp6->server_duid = grub_malloc (len);
233 + grub_memcpy (dhcp6->server_duid, opt->data, len);
234 + dhcp6->server_duid_len = len;
237 + case GRUB_NET_DHCP6_OPTION_IA_NA:
239 + const struct grub_net_dhcp6_option_iana *ia_na;
240 + grub_uint16_t data_len;
242 + if (dhcp6->iaid || len < sizeof (*ia_na))
244 + grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len);
247 + ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data;
248 + dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid);
249 + dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1);
250 + dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2);
252 + data_len = len - sizeof (*ia_na);
254 + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6);
258 + case GRUB_NET_DHCP6_OPTION_DNS_SERVERS:
260 + const grub_uint8_t *po;
262 + grub_net_network_level_address_t *la;
264 + if (!len || len & 0xf)
266 + grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n");
269 + dhcp6->num_dns_server = ln = len >> 4;
270 + dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la));
272 + for (po = opt->data; ln > 0; po += 0x10, la++, ln--)
274 + la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
275 + la->ipv6[0] = grub_get_unaligned64 (po);
276 + la->ipv6[1] = grub_get_unaligned64 (po + 8);
277 + la->option = DNS_OPTION_PREFER_IPV6;
282 + case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL:
283 + dissect_url ((const char *)opt->data,
284 + &dhcp6->boot_file_proto,
285 + &dhcp6->boot_file_server_ip,
286 + &dhcp6->boot_file_path);
295 +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data)
299 + grub_uint16_t code, len;
301 + if (size < sizeof (*opt))
303 + grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size);
306 + size -= sizeof (*opt);
307 + len = grub_be_to_cpu16 (opt->len);
308 + code = grub_be_to_cpu16 (opt->code);
311 + grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code);
316 + grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code);
322 + hook (opt, hook_data);
324 + opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt));
329 +static grub_dhcp6_options_t
330 +grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h,
333 + grub_dhcp6_options_t options;
335 + if (size < sizeof (*v6h))
337 + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
341 + options = grub_zalloc (sizeof(*options));
345 + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options,
346 + size - sizeof (*v6h), parse_dhcp6_option, options);
352 +grub_dhcp6_options_free (grub_dhcp6_options_t options)
354 + if (options->client_duid)
355 + grub_free (options->client_duid);
356 + if (options->server_duid)
357 + grub_free (options->server_duid);
358 + if (options->ia_addr)
359 + grub_free (options->ia_addr);
360 + if (options->dns_server_addrs)
361 + grub_free (options->dns_server_addrs);
362 + if (options->boot_file_proto)
363 + grub_free (options->boot_file_proto);
364 + if (options->boot_file_server_ip)
365 + grub_free (options->boot_file_server_ip);
366 + if (options->boot_file_path)
367 + grub_free (options->boot_file_path);
369 + grub_free (options);
372 +static grub_dhcp6_session_t grub_dhcp6_sessions;
373 +#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions)
376 +grub_net_configure_by_dhcp6_info (const char *name,
377 + struct grub_net_card *card,
378 + grub_dhcp6_options_t dhcp6,
381 + struct grub_net_network_level_interface **ret_inf)
383 + grub_net_network_level_netaddress_t netaddr;
384 + struct grub_net_network_level_interface *inf;
386 + if (dhcp6->ia_addr)
388 + inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags);
390 + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
391 + netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0];
392 + netaddr.ipv6.base[1] = 0;
393 + netaddr.ipv6.masksize = 64;
394 + grub_net_add_route (name, netaddr, inf);
400 + if (dhcp6->dns_server_addrs)
404 + for (i = 0; i < dhcp6->num_dns_server; ++i)
405 + grub_net_add_dns_server (dhcp6->dns_server_addrs + i);
408 + if (dhcp6->boot_file_path)
409 + grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path,
410 + grub_strlen (dhcp6->boot_file_path));
412 + if (is_def && dhcp6->boot_file_server_ip)
414 + grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip);
415 + grub_env_set ("net_default_interface", name);
416 + grub_env_export ("net_default_interface");
421 +grub_dhcp6_session_add (struct grub_net_network_level_interface *iface,
422 + grub_uint32_t iaid)
424 + grub_dhcp6_session_t se;
425 + struct grub_datetime date;
427 + grub_int32_t t = 0;
429 + se = grub_malloc (sizeof (*se));
431 + err = grub_get_datetime (&date);
432 + if (err || !grub_datetime2unixtime (&date, &t))
434 + grub_errno = GRUB_ERR_NONE;
440 + se->transaction_id = t;
441 + se->start_time = grub_get_time_ms ();
442 + se->duid.type = grub_cpu_to_be16_compile_time (3) ;
443 + se->duid.hw_type = grub_cpu_to_be16_compile_time (1);
444 + grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr));
447 + grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se));
451 +grub_dhcp6_session_remove (grub_dhcp6_session_t se)
453 + grub_list_remove (GRUB_AS_LIST (se));
455 + grub_dhcp6_options_free (se->adv);
457 + grub_dhcp6_options_free (se->reply);
462 +grub_dhcp6_session_remove_all (void)
464 + grub_dhcp6_session_t se;
466 + FOR_DHCP6_SESSIONS (se)
468 + grub_dhcp6_session_remove (se);
469 + se = grub_dhcp6_sessions;
474 +grub_dhcp6_session_configure_network (grub_dhcp6_session_t se)
478 + name = grub_xasprintf ("%s:dhcp6", se->iface->card->name);
482 + grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0);
485 + return GRUB_ERR_NONE;
489 +grub_dhcp6_session_send_request (grub_dhcp6_session_t se)
491 + struct grub_net_buff *nb;
492 + struct grub_net_dhcp6_option *opt;
493 + struct grub_net_dhcp6_packet *v6h;
494 + struct grub_net_dhcp6_option_iana *ia_na;
495 + struct grub_net_dhcp6_option_iaaddr *iaaddr;
496 + struct udphdr *udph;
497 + grub_net_network_level_address_t multicast;
498 + grub_net_link_level_address_t ll_multicast;
499 + grub_uint64_t elapsed;
500 + struct grub_net_network_level_interface *inf = se->iface;
501 + grub_dhcp6_options_t dhcp6 = se->adv;
502 + grub_err_t err = GRUB_ERR_NONE;
504 + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
505 + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
506 + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
508 + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
512 + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
517 + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
520 + grub_netbuff_free (nb);
524 + err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt));
527 + grub_netbuff_free (nb);
530 + opt = (struct grub_net_dhcp6_option *)nb->data;
531 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
532 + opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len);
533 + grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len);
535 + err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt));
538 + grub_netbuff_free (nb);
541 + opt = (struct grub_net_dhcp6_option *)nb->data;
542 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID);
543 + opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len);
544 + grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len);
546 + err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt));
549 + grub_netbuff_free (nb);
553 + if (dhcp6->ia_addr)
555 + err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt));
558 + grub_netbuff_free (nb);
562 + opt = (struct grub_net_dhcp6_option *)nb->data;
563 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
564 + opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
565 + if (dhcp6->ia_addr)
566 + opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt));
568 + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
569 + ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid);
571 + ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1);
572 + ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2);
574 + if (dhcp6->ia_addr)
576 + opt = (struct grub_net_dhcp6_option *)ia_na->data;
577 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
578 + opt->len = grub_cpu_to_be16 (sizeof (*iaaddr));
579 + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data;
580 + grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]);
581 + grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]);
583 + iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime);
584 + iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime);
587 + err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t));
590 + grub_netbuff_free (nb);
594 + opt = (struct grub_net_dhcp6_option*) nb->data;
595 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO);
596 + opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t));
597 + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL));
598 + grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS));
600 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
603 + grub_netbuff_free (nb);
606 + opt = (struct grub_net_dhcp6_option*) nb->data;
607 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
608 + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
610 + /* the time is expressed in hundredths of a second */
611 + elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0);
613 + if (elapsed > 0xffff)
616 + grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed));
618 + err = grub_netbuff_push (nb, sizeof (*v6h));
621 + grub_netbuff_free (nb);
625 + v6h = (struct grub_net_dhcp6_packet *) nb->data;
626 + v6h->message_type = GRUB_NET_DHCP6_REQUEST;
627 + v6h->transaction_id = se->transaction_id;
629 + err = grub_netbuff_push (nb, sizeof (*udph));
632 + grub_netbuff_free (nb);
636 + udph = (struct udphdr *) nb->data;
637 + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
638 + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
640 + udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
642 + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
645 + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
648 + grub_netbuff_free (nb);
653 +struct grub_net_network_level_interface *
654 +grub_net_configure_by_dhcpv6_reply (const char *name,
655 + struct grub_net_card *card,
656 + grub_net_interface_flags_t flags,
657 + const struct grub_net_dhcp6_packet *v6h,
660 + char **device, char **path)
662 + struct grub_net_network_level_interface *inf;
663 + grub_dhcp6_options_t dhcp6;
665 + dhcp6 = grub_dhcp6_options_get (v6h, size);
668 + grub_print_error ();
672 + grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf);
674 + if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip)
676 + *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip);
677 + grub_print_error ();
679 + if (path && dhcp6->boot_file_path)
681 + *path = grub_strdup (dhcp6->boot_file_path);
682 + grub_print_error ();
686 + slash = grub_strrchr (*path, '/');
694 + grub_dhcp6_options_free (dhcp6);
699 grub_net_process_dhcp (struct grub_net_buff *nb,
700 struct grub_net_card *card)
701 @@ -302,6 +966,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb,
706 +grub_net_process_dhcp6 (struct grub_net_buff *nb,
707 + struct grub_net_card *card __attribute__ ((unused)))
709 + const struct grub_net_dhcp6_packet *v6h;
710 + grub_dhcp6_session_t se;
712 + grub_dhcp6_options_t options;
714 + v6h = (const struct grub_net_dhcp6_packet *) nb->data;
715 + size = nb->tail - nb->data;
717 + options = grub_dhcp6_options_get (v6h, size);
721 + if (!options->client_duid || !options->server_duid || !options->ia_addr)
723 + grub_dhcp6_options_free (options);
724 + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet");
727 + FOR_DHCP6_SESSIONS (se)
729 + if (se->transaction_id == v6h->transaction_id &&
730 + grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 &&
731 + se->iaid == options->iaid)
737 + grub_dprintf ("bootp", "DHCPv6 session not found\n");
738 + grub_dhcp6_options_free (options);
739 + return GRUB_ERR_NONE;
742 + if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE)
746 + grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n");
747 + grub_dhcp6_options_free (options);
748 + return GRUB_ERR_NONE;
752 + return grub_dhcp6_session_send_request (se);
754 + else if (v6h->message_type == GRUB_NET_DHCP6_REPLY)
758 + grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n");
759 + grub_dhcp6_options_free (options);
760 + return GRUB_ERR_NONE;
763 + se->reply = options;
764 + grub_dhcp6_session_configure_network (se);
765 + grub_dhcp6_session_remove (se);
766 + return GRUB_ERR_NONE;
770 + grub_dhcp6_options_free (options);
773 + return GRUB_ERR_NONE;
777 hexdigit (grub_uint8_t val)
779 @@ -582,7 +1317,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
783 -static grub_command_t cmd_getdhcp, cmd_bootp;
785 +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
786 + int argc, char **args)
788 + struct grub_net_card *card;
789 + grub_uint32_t iaid = 0;
792 + grub_dhcp6_session_t se;
794 + err = GRUB_ERR_NONE;
796 + FOR_NET_CARDS (card)
798 + struct grub_net_network_level_interface *iface;
800 + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
803 + iface = grub_net_ipv6_get_link_local (card, &card->default_address);
806 + grub_dhcp6_session_remove_all ();
810 + grub_dhcp6_session_add (iface, iaid++);
813 + for (interval = 200; interval < 10000; interval *= 2)
817 + FOR_DHCP6_SESSIONS (se)
819 + struct grub_net_buff *nb;
820 + struct grub_net_dhcp6_option *opt;
821 + struct grub_net_dhcp6_packet *v6h;
822 + struct grub_net_dhcp6_option_duid_ll *duid;
823 + struct grub_net_dhcp6_option_iana *ia_na;
824 + grub_net_network_level_address_t multicast;
825 + grub_net_link_level_address_t ll_multicast;
826 + struct udphdr *udph;
828 + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
829 + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
830 + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
832 + err = grub_net_link_layer_resolve (se->iface,
833 + &multicast, &ll_multicast);
836 + grub_dhcp6_session_remove_all ();
840 + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
844 + grub_dhcp6_session_remove_all ();
848 + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
851 + grub_dhcp6_session_remove_all ();
852 + grub_netbuff_free (nb);
856 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
859 + grub_dhcp6_session_remove_all ();
860 + grub_netbuff_free (nb);
864 + opt = (struct grub_net_dhcp6_option *)nb->data;
865 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
866 + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
867 + grub_set_unaligned16 (opt->data, 0);
869 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid));
872 + grub_dhcp6_session_remove_all ();
873 + grub_netbuff_free (nb);
877 + opt = (struct grub_net_dhcp6_option *)nb->data;
878 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
879 + opt->len = grub_cpu_to_be16 (sizeof (*duid));
881 + duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data;
882 + grub_memcpy (duid, &se->duid, sizeof (*duid));
884 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na));
887 + grub_dhcp6_session_remove_all ();
888 + grub_netbuff_free (nb);
892 + opt = (struct grub_net_dhcp6_option *)nb->data;
893 + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
894 + opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
895 + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
896 + ia_na->iaid = grub_cpu_to_be32 (se->iaid);
900 + err = grub_netbuff_push (nb, sizeof (*v6h));
903 + grub_dhcp6_session_remove_all ();
904 + grub_netbuff_free (nb);
908 + v6h = (struct grub_net_dhcp6_packet *)nb->data;
909 + v6h->message_type = GRUB_NET_DHCP6_SOLICIT;
910 + v6h->transaction_id = se->transaction_id;
912 + grub_netbuff_push (nb, sizeof (*udph));
914 + udph = (struct udphdr *) nb->data;
915 + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
916 + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
918 + udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
920 + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
921 + &se->iface->address, &multicast);
923 + err = grub_net_send_ip_packet (se->iface, &multicast,
924 + &ll_multicast, nb, GRUB_NET_IP_UDP);
926 + grub_netbuff_free (nb);
930 + grub_dhcp6_session_remove_all ();
935 + grub_net_poll_cards (interval, 0);
938 + FOR_DHCP6_SESSIONS (se)
940 + grub_error_push ();
941 + err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
942 + N_("couldn't autoconfigure %s"),
943 + se->iface->card->name);
946 + grub_dhcp6_session_remove_all ();
951 +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_bootp6;
954 grub_bootp_init (void)
955 @@ -593,6 +1495,9 @@ grub_bootp_init (void)
956 cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
957 N_("VAR INTERFACE NUMBER DESCRIPTION"),
958 N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
959 + cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6,
961 + N_("perform a DHCPv6 autoconfiguration"));
965 @@ -600,4 +1505,5 @@ grub_bootp_fini (void)
967 grub_unregister_command (cmd_getdhcp);
968 grub_unregister_command (cmd_bootp);
969 + grub_unregister_command (cmd_bootp6);
971 diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
972 index aba4f8908..59970ab74 100644
973 --- a/grub-core/net/ip.c
974 +++ b/grub-core/net/ip.c
975 @@ -238,6 +238,45 @@ handle_dgram (struct grub_net_buff *nb,
978 udph = (struct udphdr *) nb->data;
980 + if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT))
984 + grub_uint16_t chk, expected;
985 + chk = udph->chksum;
987 + expected = grub_net_ip_transport_checksum (nb,
991 + if (expected != chk)
993 + grub_dprintf ("net", "Invalid UDP checksum. "
994 + "Expected %x, got %x\n",
995 + grub_be_to_cpu16 (expected),
996 + grub_be_to_cpu16 (chk));
997 + grub_netbuff_free (nb);
998 + return GRUB_ERR_NONE;
1000 + udph->chksum = chk;
1003 + err = grub_netbuff_pull (nb, sizeof (*udph));
1006 + grub_netbuff_free (nb);
1010 + err = grub_net_process_dhcp6 (nb, card);
1012 + grub_print_error ();
1014 + grub_netbuff_free (nb);
1015 + return GRUB_ERR_NONE;
1018 if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68)
1020 const struct grub_net_bootp_packet *bootp;
1021 diff --git a/include/grub/net.h b/include/grub/net.h
1022 index ccc169c2d..38a3973f3 100644
1023 --- a/include/grub/net.h
1024 +++ b/include/grub/net.h
1025 @@ -442,6 +442,66 @@ struct grub_net_bootp_packet
1026 grub_uint8_t vendor[0];
1029 +struct grub_net_dhcp6_packet
1031 + grub_uint32_t message_type:8;
1032 + grub_uint32_t transaction_id:24;
1033 + grub_uint8_t dhcp_options[0];
1036 +struct grub_net_dhcp6_option {
1037 + grub_uint16_t code;
1038 + grub_uint16_t len;
1039 + grub_uint8_t data[0];
1042 +struct grub_net_dhcp6_option_iana {
1043 + grub_uint32_t iaid;
1046 + grub_uint8_t data[0];
1049 +struct grub_net_dhcp6_option_iaaddr {
1050 + grub_uint8_t addr[16];
1051 + grub_uint32_t preferred_lifetime;
1052 + grub_uint32_t valid_lifetime;
1053 + grub_uint8_t data[0];
1056 +struct grub_net_dhcp6_option_duid_ll
1058 + grub_uint16_t type;
1059 + grub_uint16_t hw_type;
1060 + grub_uint8_t hwaddr[6];
1065 + GRUB_NET_DHCP6_SOLICIT = 1,
1066 + GRUB_NET_DHCP6_ADVERTISE = 2,
1067 + GRUB_NET_DHCP6_REQUEST = 3,
1068 + GRUB_NET_DHCP6_REPLY = 7
1073 + DHCP6_CLIENT_PORT = 546,
1074 + DHCP6_SERVER_PORT = 547
1079 + GRUB_NET_DHCP6_OPTION_CLIENTID = 1,
1080 + GRUB_NET_DHCP6_OPTION_SERVERID = 2,
1081 + GRUB_NET_DHCP6_OPTION_IA_NA = 3,
1082 + GRUB_NET_DHCP6_OPTION_IAADDR = 5,
1083 + GRUB_NET_DHCP6_OPTION_ORO = 6,
1084 + GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8,
1085 + GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23,
1086 + GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59
1089 #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63
1090 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82
1091 #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53
1092 @@ -468,6 +528,14 @@ grub_net_configure_by_dhcp_ack (const char *name,
1094 int is_def, char **device, char **path);
1096 +struct grub_net_network_level_interface *
1097 +grub_net_configure_by_dhcpv6_reply (const char *name,
1098 + struct grub_net_card *card,
1099 + grub_net_interface_flags_t flags,
1100 + const struct grub_net_dhcp6_packet *v6,
1102 + int is_def, char **device, char **path);
1105 grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
1107 @@ -476,6 +544,10 @@ void
1108 grub_net_process_dhcp (struct grub_net_buff *nb,
1109 struct grub_net_card *card);
1112 +grub_net_process_dhcp6 (struct grub_net_buff *nb,
1113 + struct grub_net_card *card);
1116 grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
1117 const grub_net_link_level_address_t *b);