]> git.proxmox.com Git - grub2.git/blob - debian/patches/bootp_new_net_bootp6_command.patch
grub2 (2.02+dfsg1-20) unstable; urgency=medium
[grub2.git] / debian / patches / bootp_new_net_bootp6_command.patch
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
5
6 Implement new net_bootp6 command for IPv6 network auto configuration via the
7 DHCPv6 protocol (RFC3315).
8
9 Signed-off-by: Michael Chang <mchang@suse.com>
10 Signed-off-by: Ken Lin <ken.lin@hpe.com>
11
12 Patch-Name: bootp_new_net_bootp6_command.patch
13 ---
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(-)
18
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
23 @@ -24,6 +24,98 @@
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>
29 +
30 +static int
31 +dissect_url (const char *url, char **proto, char **host, char **path)
32 +{
33 + const char *p, *ps;
34 + grub_size_t l;
35 +
36 + *proto = *host = *path = NULL;
37 + ps = p = url;
38 +
39 + while ((p = grub_strchr (p, ':')))
40 + {
41 + if (grub_strlen (p) < sizeof ("://") - 1)
42 + break;
43 + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
44 + {
45 + l = p - ps;
46 + *proto = grub_malloc (l + 1);
47 + if (!*proto)
48 + {
49 + grub_print_error ();
50 + return 0;
51 + }
52 +
53 + grub_memcpy (*proto, ps, l);
54 + (*proto)[l] = '\0';
55 + p += sizeof ("://") - 1;
56 + break;
57 + }
58 + ++p;
59 + }
60 +
61 + if (!*proto)
62 + {
63 + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
64 + return 0;
65 + }
66 +
67 + ps = p;
68 + p = grub_strchr (p, '/');
69 +
70 + if (!p)
71 + {
72 + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
73 + grub_free (*proto);
74 + *proto = NULL;
75 + return 0;
76 + }
77 +
78 + l = p - ps;
79 +
80 + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
81 + {
82 + *host = grub_malloc (l - 1);
83 + if (!*host)
84 + {
85 + grub_print_error ();
86 + grub_free (*proto);
87 + *proto = NULL;
88 + return 0;
89 + }
90 + grub_memcpy (*host, ps + 1, l - 2);
91 + (*host)[l - 2] = 0;
92 + }
93 + else
94 + {
95 + *host = grub_malloc (l + 1);
96 + if (!*host)
97 + {
98 + grub_print_error ();
99 + grub_free (*proto);
100 + *proto = NULL;
101 + return 0;
102 + }
103 + grub_memcpy (*host, ps, l);
104 + (*host)[l] = 0;
105 + }
106 +
107 + *path = grub_strdup (p);
108 + if (!*path)
109 + {
110 + grub_print_error ();
111 + grub_free (*host);
112 + grub_free (*proto);
113 + *host = NULL;
114 + *proto = NULL;
115 + return 0;
116 + }
117 + return 1;
118 +}
119
120 static void
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,
123 return inter;
124 }
125
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
129 +
130 +struct grub_dhcp6_options
131 +{
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;
137 + grub_uint32_t t1;
138 + grub_uint32_t t2;
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;
147 +};
148 +
149 +typedef struct grub_dhcp6_options *grub_dhcp6_options_t;
150 +
151 +struct grub_dhcp6_session
152 +{
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;
160 +
161 + /* The associated dhcpv6 options */
162 + grub_dhcp6_options_t adv;
163 + grub_dhcp6_options_t reply;
164 +};
165 +
166 +typedef struct grub_dhcp6_session *grub_dhcp6_session_t;
167 +
168 +typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data);
169 +
170 +static void
171 +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size,
172 + dhcp6_option_hook_fn hook, void *hook_data);
173 +
174 +static void
175 +parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data)
176 +{
177 + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data;
178 +
179 + grub_uint16_t code = grub_be_to_cpu16 (opt->code);
180 + grub_uint16_t len = grub_be_to_cpu16 (opt->len);
181 +
182 + if (code == GRUB_NET_DHCP6_OPTION_IAADDR)
183 + {
184 + const struct grub_net_dhcp6_option_iaaddr *iaaddr;
185 + iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data;
186 +
187 + if (len < sizeof (*iaaddr))
188 + {
189 + grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len);
190 + return;
191 + }
192 + if (!dhcp6->ia_addr)
193 + {
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);
200 + }
201 + }
202 +}
203 +
204 +static void
205 +parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data)
206 +{
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);
210 +
211 + switch (code)
212 + {
213 + case GRUB_NET_DHCP6_OPTION_CLIENTID:
214 +
215 + if (dhcp6->client_duid || !len)
216 + {
217 + grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len);
218 + break;
219 + }
220 + dhcp6->client_duid = grub_malloc (len);
221 + grub_memcpy (dhcp6->client_duid, opt->data, len);
222 + dhcp6->client_duid_len = len;
223 + break;
224 +
225 + case GRUB_NET_DHCP6_OPTION_SERVERID:
226 +
227 + if (dhcp6->server_duid || !len)
228 + {
229 + grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len);
230 + break;
231 + }
232 + dhcp6->server_duid = grub_malloc (len);
233 + grub_memcpy (dhcp6->server_duid, opt->data, len);
234 + dhcp6->server_duid_len = len;
235 + break;
236 +
237 + case GRUB_NET_DHCP6_OPTION_IA_NA:
238 + {
239 + const struct grub_net_dhcp6_option_iana *ia_na;
240 + grub_uint16_t data_len;
241 +
242 + if (dhcp6->iaid || len < sizeof (*ia_na))
243 + {
244 + grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len);
245 + break;
246 + }
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);
251 +
252 + data_len = len - sizeof (*ia_na);
253 + if (data_len)
254 + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6);
255 + }
256 + break;
257 +
258 + case GRUB_NET_DHCP6_OPTION_DNS_SERVERS:
259 + {
260 + const grub_uint8_t *po;
261 + grub_uint16_t ln;
262 + grub_net_network_level_address_t *la;
263 +
264 + if (!len || len & 0xf)
265 + {
266 + grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n");
267 + break;
268 + }
269 + dhcp6->num_dns_server = ln = len >> 4;
270 + dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la));
271 +
272 + for (po = opt->data; ln > 0; po += 0x10, la++, ln--)
273 + {
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;
278 + }
279 + }
280 + break;
281 +
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);
287 + break;
288 +
289 + default:
290 + break;
291 + }
292 +}
293 +
294 +static void
295 +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data)
296 +{
297 + while (size)
298 + {
299 + grub_uint16_t code, len;
300 +
301 + if (size < sizeof (*opt))
302 + {
303 + grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size);
304 + break;
305 + }
306 + size -= sizeof (*opt);
307 + len = grub_be_to_cpu16 (opt->len);
308 + code = grub_be_to_cpu16 (opt->code);
309 + if (size < len)
310 + {
311 + grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code);
312 + break;
313 + }
314 + if (!len)
315 + {
316 + grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code);
317 + break;
318 + }
319 + else
320 + {
321 + if (hook)
322 + hook (opt, hook_data);
323 + size -= len;
324 + opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt));
325 + }
326 + }
327 +}
328 +
329 +static grub_dhcp6_options_t
330 +grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h,
331 + grub_size_t size)
332 +{
333 + grub_dhcp6_options_t options;
334 +
335 + if (size < sizeof (*v6h))
336 + {
337 + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
338 + return NULL;
339 + }
340 +
341 + options = grub_zalloc (sizeof(*options));
342 + if (!options)
343 + return NULL;
344 +
345 + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options,
346 + size - sizeof (*v6h), parse_dhcp6_option, options);
347 +
348 + return options;
349 +}
350 +
351 +static void
352 +grub_dhcp6_options_free (grub_dhcp6_options_t options)
353 +{
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);
368 +
369 + grub_free (options);
370 +}
371 +
372 +static grub_dhcp6_session_t grub_dhcp6_sessions;
373 +#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions)
374 +
375 +static void
376 +grub_net_configure_by_dhcp6_info (const char *name,
377 + struct grub_net_card *card,
378 + grub_dhcp6_options_t dhcp6,
379 + int is_def,
380 + int flags,
381 + struct grub_net_network_level_interface **ret_inf)
382 +{
383 + grub_net_network_level_netaddress_t netaddr;
384 + struct grub_net_network_level_interface *inf;
385 +
386 + if (dhcp6->ia_addr)
387 + {
388 + inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags);
389 +
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);
395 +
396 + if (ret_inf)
397 + *ret_inf = inf;
398 + }
399 +
400 + if (dhcp6->dns_server_addrs)
401 + {
402 + grub_uint16_t i;
403 +
404 + for (i = 0; i < dhcp6->num_dns_server; ++i)
405 + grub_net_add_dns_server (dhcp6->dns_server_addrs + i);
406 + }
407 +
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));
411 +
412 + if (is_def && dhcp6->boot_file_server_ip)
413 + {
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");
417 + }
418 +}
419 +
420 +static void
421 +grub_dhcp6_session_add (struct grub_net_network_level_interface *iface,
422 + grub_uint32_t iaid)
423 +{
424 + grub_dhcp6_session_t se;
425 + struct grub_datetime date;
426 + grub_err_t err;
427 + grub_int32_t t = 0;
428 +
429 + se = grub_malloc (sizeof (*se));
430 +
431 + err = grub_get_datetime (&date);
432 + if (err || !grub_datetime2unixtime (&date, &t))
433 + {
434 + grub_errno = GRUB_ERR_NONE;
435 + t = 0;
436 + }
437 +
438 + se->iface = iface;
439 + se->iaid = iaid;
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));
445 + se->adv = NULL;
446 + se->reply = NULL;
447 + grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se));
448 +}
449 +
450 +static void
451 +grub_dhcp6_session_remove (grub_dhcp6_session_t se)
452 +{
453 + grub_list_remove (GRUB_AS_LIST (se));
454 + if (se->adv)
455 + grub_dhcp6_options_free (se->adv);
456 + if (se->reply)
457 + grub_dhcp6_options_free (se->reply);
458 + grub_free (se);
459 +}
460 +
461 +static void
462 +grub_dhcp6_session_remove_all (void)
463 +{
464 + grub_dhcp6_session_t se;
465 +
466 + FOR_DHCP6_SESSIONS (se)
467 + {
468 + grub_dhcp6_session_remove (se);
469 + se = grub_dhcp6_sessions;
470 + }
471 +}
472 +
473 +static grub_err_t
474 +grub_dhcp6_session_configure_network (grub_dhcp6_session_t se)
475 +{
476 + char *name;
477 +
478 + name = grub_xasprintf ("%s:dhcp6", se->iface->card->name);
479 + if (!name)
480 + return grub_errno;
481 +
482 + grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0);
483 + grub_free (name);
484 +
485 + return GRUB_ERR_NONE;
486 +}
487 +
488 +static grub_err_t
489 +grub_dhcp6_session_send_request (grub_dhcp6_session_t se)
490 +{
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;
503 +
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);
507 +
508 + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
509 + if (err)
510 + return err;
511 +
512 + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
513 +
514 + if (!nb)
515 + return grub_errno;
516 +
517 + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
518 + if (err)
519 + {
520 + grub_netbuff_free (nb);
521 + return err;
522 + }
523 +
524 + err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt));
525 + if (err)
526 + {
527 + grub_netbuff_free (nb);
528 + return err;
529 + }
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);
534 +
535 + err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt));
536 + if (err)
537 + {
538 + grub_netbuff_free (nb);
539 + return err;
540 + }
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);
545 +
546 + err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt));
547 + if (err)
548 + {
549 + grub_netbuff_free (nb);
550 + return err;
551 + }
552 +
553 + if (dhcp6->ia_addr)
554 + {
555 + err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt));
556 + if (err)
557 + {
558 + grub_netbuff_free (nb);
559 + return err;
560 + }
561 + }
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));
567 +
568 + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
569 + ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid);
570 +
571 + ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1);
572 + ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2);
573 +
574 + if (dhcp6->ia_addr)
575 + {
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]);
582 +
583 + iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime);
584 + iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime);
585 + }
586 +
587 + err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t));
588 + if (err)
589 + {
590 + grub_netbuff_free (nb);
591 + return err;
592 + }
593 +
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));
599 +
600 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
601 + if (err)
602 + {
603 + grub_netbuff_free (nb);
604 + return err;
605 + }
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));
609 +
610 + /* the time is expressed in hundredths of a second */
611 + elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0);
612 +
613 + if (elapsed > 0xffff)
614 + elapsed = 0xffff;
615 +
616 + grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed));
617 +
618 + err = grub_netbuff_push (nb, sizeof (*v6h));
619 + if (err)
620 + {
621 + grub_netbuff_free (nb);
622 + return err;
623 + }
624 +
625 + v6h = (struct grub_net_dhcp6_packet *) nb->data;
626 + v6h->message_type = GRUB_NET_DHCP6_REQUEST;
627 + v6h->transaction_id = se->transaction_id;
628 +
629 + err = grub_netbuff_push (nb, sizeof (*udph));
630 + if (err)
631 + {
632 + grub_netbuff_free (nb);
633 + return err;
634 + }
635 +
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);
639 + udph->chksum = 0;
640 + udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
641 +
642 + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
643 + &inf->address,
644 + &multicast);
645 + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
646 + GRUB_NET_IP_UDP);
647 +
648 + grub_netbuff_free (nb);
649 +
650 + return err;
651 +}
652 +
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,
658 + grub_size_t size,
659 + int is_def,
660 + char **device, char **path)
661 +{
662 + struct grub_net_network_level_interface *inf;
663 + grub_dhcp6_options_t dhcp6;
664 +
665 + dhcp6 = grub_dhcp6_options_get (v6h, size);
666 + if (!dhcp6)
667 + {
668 + grub_print_error ();
669 + return NULL;
670 + }
671 +
672 + grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf);
673 +
674 + if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip)
675 + {
676 + *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip);
677 + grub_print_error ();
678 + }
679 + if (path && dhcp6->boot_file_path)
680 + {
681 + *path = grub_strdup (dhcp6->boot_file_path);
682 + grub_print_error ();
683 + if (*path)
684 + {
685 + char *slash;
686 + slash = grub_strrchr (*path, '/');
687 + if (slash)
688 + *slash = 0;
689 + else
690 + **path = 0;
691 + }
692 + }
693 +
694 + grub_dhcp6_options_free (dhcp6);
695 + return inf;
696 +}
697 +
698 void
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,
702 }
703 }
704
705 +grub_err_t
706 +grub_net_process_dhcp6 (struct grub_net_buff *nb,
707 + struct grub_net_card *card __attribute__ ((unused)))
708 +{
709 + const struct grub_net_dhcp6_packet *v6h;
710 + grub_dhcp6_session_t se;
711 + grub_size_t size;
712 + grub_dhcp6_options_t options;
713 +
714 + v6h = (const struct grub_net_dhcp6_packet *) nb->data;
715 + size = nb->tail - nb->data;
716 +
717 + options = grub_dhcp6_options_get (v6h, size);
718 + if (!options)
719 + return grub_errno;
720 +
721 + if (!options->client_duid || !options->server_duid || !options->ia_addr)
722 + {
723 + grub_dhcp6_options_free (options);
724 + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet");
725 + }
726 +
727 + FOR_DHCP6_SESSIONS (se)
728 + {
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)
732 + break;
733 + }
734 +
735 + if (!se)
736 + {
737 + grub_dprintf ("bootp", "DHCPv6 session not found\n");
738 + grub_dhcp6_options_free (options);
739 + return GRUB_ERR_NONE;
740 + }
741 +
742 + if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE)
743 + {
744 + if (se->adv)
745 + {
746 + grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n");
747 + grub_dhcp6_options_free (options);
748 + return GRUB_ERR_NONE;
749 + }
750 +
751 + se->adv = options;
752 + return grub_dhcp6_session_send_request (se);
753 + }
754 + else if (v6h->message_type == GRUB_NET_DHCP6_REPLY)
755 + {
756 + if (!se->adv)
757 + {
758 + grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n");
759 + grub_dhcp6_options_free (options);
760 + return GRUB_ERR_NONE;
761 + }
762 +
763 + se->reply = options;
764 + grub_dhcp6_session_configure_network (se);
765 + grub_dhcp6_session_remove (se);
766 + return GRUB_ERR_NONE;
767 + }
768 + else
769 + {
770 + grub_dhcp6_options_free (options);
771 + }
772 +
773 + return GRUB_ERR_NONE;
774 +}
775 +
776 static char
777 hexdigit (grub_uint8_t val)
778 {
779 @@ -582,7 +1317,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
780 return err;
781 }
782
783 -static grub_command_t cmd_getdhcp, cmd_bootp;
784 +static grub_err_t
785 +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
786 + int argc, char **args)
787 +{
788 + struct grub_net_card *card;
789 + grub_uint32_t iaid = 0;
790 + int interval;
791 + grub_err_t err;
792 + grub_dhcp6_session_t se;
793 +
794 + err = GRUB_ERR_NONE;
795 +
796 + FOR_NET_CARDS (card)
797 + {
798 + struct grub_net_network_level_interface *iface;
799 +
800 + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
801 + continue;
802 +
803 + iface = grub_net_ipv6_get_link_local (card, &card->default_address);
804 + if (!iface)
805 + {
806 + grub_dhcp6_session_remove_all ();
807 + return grub_errno;
808 + }
809 +
810 + grub_dhcp6_session_add (iface, iaid++);
811 + }
812 +
813 + for (interval = 200; interval < 10000; interval *= 2)
814 + {
815 + int done = 1;
816 +
817 + FOR_DHCP6_SESSIONS (se)
818 + {
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;
827 +
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);
831 +
832 + err = grub_net_link_layer_resolve (se->iface,
833 + &multicast, &ll_multicast);
834 + if (err)
835 + {
836 + grub_dhcp6_session_remove_all ();
837 + return err;
838 + }
839 +
840 + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
841 +
842 + if (!nb)
843 + {
844 + grub_dhcp6_session_remove_all ();
845 + return grub_errno;
846 + }
847 +
848 + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
849 + if (err)
850 + {
851 + grub_dhcp6_session_remove_all ();
852 + grub_netbuff_free (nb);
853 + return err;
854 + }
855 +
856 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
857 + if (err)
858 + {
859 + grub_dhcp6_session_remove_all ();
860 + grub_netbuff_free (nb);
861 + return err;
862 + }
863 +
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);
868 +
869 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid));
870 + if (err)
871 + {
872 + grub_dhcp6_session_remove_all ();
873 + grub_netbuff_free (nb);
874 + return err;
875 + }
876 +
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));
880 +
881 + duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data;
882 + grub_memcpy (duid, &se->duid, sizeof (*duid));
883 +
884 + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na));
885 + if (err)
886 + {
887 + grub_dhcp6_session_remove_all ();
888 + grub_netbuff_free (nb);
889 + return err;
890 + }
891 +
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);
897 + ia_na->t1 = 0;
898 + ia_na->t2 = 0;
899 +
900 + err = grub_netbuff_push (nb, sizeof (*v6h));
901 + if (err)
902 + {
903 + grub_dhcp6_session_remove_all ();
904 + grub_netbuff_free (nb);
905 + return err;
906 + }
907 +
908 + v6h = (struct grub_net_dhcp6_packet *)nb->data;
909 + v6h->message_type = GRUB_NET_DHCP6_SOLICIT;
910 + v6h->transaction_id = se->transaction_id;
911 +
912 + grub_netbuff_push (nb, sizeof (*udph));
913 +
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);
917 + udph->chksum = 0;
918 + udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
919 +
920 + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
921 + &se->iface->address, &multicast);
922 +
923 + err = grub_net_send_ip_packet (se->iface, &multicast,
924 + &ll_multicast, nb, GRUB_NET_IP_UDP);
925 + done = 0;
926 + grub_netbuff_free (nb);
927 +
928 + if (err)
929 + {
930 + grub_dhcp6_session_remove_all ();
931 + return err;
932 + }
933 + }
934 + if (!done)
935 + grub_net_poll_cards (interval, 0);
936 + }
937 +
938 + FOR_DHCP6_SESSIONS (se)
939 + {
940 + grub_error_push ();
941 + err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
942 + N_("couldn't autoconfigure %s"),
943 + se->iface->card->name);
944 + }
945 +
946 + grub_dhcp6_session_remove_all ();
947 +
948 + return err;
949 +}
950 +
951 +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_bootp6;
952
953 void
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,
960 + N_("[CARD]"),
961 + N_("perform a DHCPv6 autoconfiguration"));
962 }
963
964 void
965 @@ -600,4 +1505,5 @@ grub_bootp_fini (void)
966 {
967 grub_unregister_command (cmd_getdhcp);
968 grub_unregister_command (cmd_bootp);
969 + grub_unregister_command (cmd_bootp6);
970 }
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,
976 {
977 struct udphdr *udph;
978 udph = (struct udphdr *) nb->data;
979 +
980 + if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT))
981 + {
982 + if (udph->chksum)
983 + {
984 + grub_uint16_t chk, expected;
985 + chk = udph->chksum;
986 + udph->chksum = 0;
987 + expected = grub_net_ip_transport_checksum (nb,
988 + GRUB_NET_IP_UDP,
989 + source,
990 + dest);
991 + if (expected != chk)
992 + {
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;
999 + }
1000 + udph->chksum = chk;
1001 + }
1002 +
1003 + err = grub_netbuff_pull (nb, sizeof (*udph));
1004 + if (err)
1005 + {
1006 + grub_netbuff_free (nb);
1007 + return err;
1008 + }
1009 +
1010 + err = grub_net_process_dhcp6 (nb, card);
1011 + if (err)
1012 + grub_print_error ();
1013 +
1014 + grub_netbuff_free (nb);
1015 + return GRUB_ERR_NONE;
1016 + }
1017 +
1018 if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68)
1019 {
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];
1027 } GRUB_PACKED;
1028
1029 +struct grub_net_dhcp6_packet
1030 +{
1031 + grub_uint32_t message_type:8;
1032 + grub_uint32_t transaction_id:24;
1033 + grub_uint8_t dhcp_options[0];
1034 +} GRUB_PACKED;
1035 +
1036 +struct grub_net_dhcp6_option {
1037 + grub_uint16_t code;
1038 + grub_uint16_t len;
1039 + grub_uint8_t data[0];
1040 +} GRUB_PACKED;
1041 +
1042 +struct grub_net_dhcp6_option_iana {
1043 + grub_uint32_t iaid;
1044 + grub_uint32_t t1;
1045 + grub_uint32_t t2;
1046 + grub_uint8_t data[0];
1047 +} GRUB_PACKED;
1048 +
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];
1054 +} GRUB_PACKED;
1055 +
1056 +struct grub_net_dhcp6_option_duid_ll
1057 +{
1058 + grub_uint16_t type;
1059 + grub_uint16_t hw_type;
1060 + grub_uint8_t hwaddr[6];
1061 +} GRUB_PACKED;
1062 +
1063 +enum
1064 + {
1065 + GRUB_NET_DHCP6_SOLICIT = 1,
1066 + GRUB_NET_DHCP6_ADVERTISE = 2,
1067 + GRUB_NET_DHCP6_REQUEST = 3,
1068 + GRUB_NET_DHCP6_REPLY = 7
1069 + };
1070 +
1071 +enum
1072 + {
1073 + DHCP6_CLIENT_PORT = 546,
1074 + DHCP6_SERVER_PORT = 547
1075 + };
1076 +
1077 +enum
1078 + {
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
1087 + };
1088 +
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,
1093 grub_size_t size,
1094 int is_def, char **device, char **path);
1095
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,
1101 + grub_size_t size,
1102 + int is_def, char **device, char **path);
1103 +
1104 grub_err_t
1105 grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
1106 int mask);
1107 @@ -476,6 +544,10 @@ void
1108 grub_net_process_dhcp (struct grub_net_buff *nb,
1109 struct grub_net_card *card);
1110
1111 +grub_err_t
1112 +grub_net_process_dhcp6 (struct grub_net_buff *nb,
1113 + struct grub_net_card *card);
1114 +
1115 int
1116 grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
1117 const grub_net_link_level_address_t *b);