Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / bootp.c
blobbc07d535103f67c52a09f7f94f54f625981236b0
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/net.h>
20 #include <grub/env.h>
21 #include <grub/i18n.h>
22 #include <grub/command.h>
23 #include <grub/net/ip.h>
24 #include <grub/net/netbuff.h>
25 #include <grub/net/udp.h>
26 #include <grub/datetime.h>
28 static char *
29 grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)),
30 const char *val __attribute__ ((unused)))
32 return NULL;
35 static void
36 set_env_limn_ro (const char *intername, const char *suffix,
37 char *value, grub_size_t len)
39 char c;
40 char varname[sizeof ("net_") + grub_strlen (intername) + sizeof ("_")
41 + grub_strlen (suffix)];
42 char *ptr;
43 grub_snprintf (varname, sizeof (varname), "net_%s_%s", intername, suffix);
44 for (ptr = varname; *ptr; ptr++)
45 if (*ptr == ':')
46 *ptr = '_';
47 c = value[len];
48 value[len] = 0;
49 grub_env_set (varname, value);
50 value[len] = c;
51 grub_register_variable_hook (varname, 0, grub_env_write_readonly);
54 static void
55 parse_dhcp_vendor (const char *name, void *vend, int limit, int *mask)
57 grub_uint8_t *ptr, *ptr0;
59 ptr = ptr0 = vend;
61 if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
62 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
63 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
64 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
65 return;
66 ptr = ptr + sizeof (grub_uint32_t);
67 while (ptr - ptr0 < limit)
69 grub_uint8_t tagtype;
70 grub_uint8_t taglength;
72 tagtype = *ptr++;
74 /* Pad tag. */
75 if (tagtype == GRUB_NET_BOOTP_PAD)
76 continue;
78 /* End tag. */
79 if (tagtype == GRUB_NET_BOOTP_END)
80 return;
82 taglength = *ptr++;
84 switch (tagtype)
86 case GRUB_NET_BOOTP_NETMASK:
87 if (taglength == 4)
89 int i;
90 for (i = 0; i < 32; i++)
91 if (!(ptr[i / 8] & (1 << (7 - (i % 8)))))
92 break;
93 *mask = i;
95 break;
97 case GRUB_NET_BOOTP_ROUTER:
98 if (taglength == 4)
100 grub_net_network_level_netaddress_t target;
101 grub_net_network_level_address_t gw;
102 char rname[grub_strlen (name) + sizeof (":default")];
104 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
105 target.ipv4.base = 0;
106 target.ipv4.masksize = 0;
107 gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
108 grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4));
109 grub_snprintf (rname, sizeof (rname), "%s:default", name);
110 grub_net_add_route_gw (rname, target, gw);
112 break;
113 case GRUB_NET_BOOTP_DNS:
115 int i;
116 for (i = 0; i < taglength / 4; i++)
118 struct grub_net_network_level_address s;
119 s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
120 s.ipv4 = grub_get_unaligned32 (ptr);
121 grub_net_add_dns_server (&s);
122 ptr += 4;
125 break;
126 case GRUB_NET_BOOTP_HOSTNAME:
127 set_env_limn_ro (name, "hostname", (char *) ptr, taglength);
128 break;
130 case GRUB_NET_BOOTP_DOMAIN:
131 set_env_limn_ro (name, "domain", (char *) ptr, taglength);
132 break;
134 case GRUB_NET_BOOTP_ROOT_PATH:
135 set_env_limn_ro (name, "rootpath", (char *) ptr, taglength);
136 break;
138 case GRUB_NET_BOOTP_EXTENSIONS_PATH:
139 set_env_limn_ro (name, "extensionspath", (char *) ptr, taglength);
140 break;
142 /* If you need any other options please contact GRUB
143 developpement team. */
146 ptr += taglength;
150 #define OFFSET_OF(x, y) ((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y))
152 struct grub_net_network_level_interface *
153 grub_net_configure_by_dhcp_ack (const char *name,
154 struct grub_net_card *card,
155 grub_net_interface_flags_t flags,
156 const struct grub_net_bootp_packet *bp,
157 grub_size_t size,
158 int is_def, char **device, char **path)
160 grub_net_network_level_address_t addr;
161 grub_net_link_level_address_t hwaddr;
162 struct grub_net_network_level_interface *inter;
163 int mask = -1;
165 addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
166 addr.ipv4 = bp->your_ip;
168 if (device)
169 *device = 0;
170 if (path)
171 *path = 0;
173 grub_memcpy (hwaddr.mac, bp->mac_addr,
174 bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
175 : sizeof (hwaddr.mac));
176 hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
178 inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
179 if (bp->gateway_ip)
181 grub_net_network_level_netaddress_t target;
182 grub_net_network_level_address_t gw;
183 char rname[grub_strlen (name) + sizeof (":gw")];
185 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
186 target.ipv4.base = bp->server_ip;
187 target.ipv4.masksize = 32;
188 gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
189 gw.ipv4 = bp->gateway_ip;
190 grub_snprintf (rname, sizeof (rname), "%s:gw", name);
191 grub_net_add_route_gw (rname, target, gw);
193 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
194 target.ipv4.base = bp->gateway_ip;
195 target.ipv4.masksize = 32;
196 grub_net_add_route (name, target, inter);
199 if (size > OFFSET_OF (boot_file, bp))
200 set_env_limn_ro (name, "boot_file", (char *) bp->boot_file,
201 sizeof (bp->boot_file));
202 if (is_def)
203 grub_net_default_server = 0;
204 if (is_def && !grub_net_default_server && bp->server_ip)
206 grub_net_default_server = grub_xasprintf ("%d.%d.%d.%d",
207 ((grub_uint8_t *) &bp->server_ip)[0],
208 ((grub_uint8_t *) &bp->server_ip)[1],
209 ((grub_uint8_t *) &bp->server_ip)[2],
210 ((grub_uint8_t *) &bp->server_ip)[3]);
211 grub_print_error ();
214 if (device && !*device && bp->server_ip)
216 *device = grub_xasprintf ("tftp,%d.%d.%d.%d",
217 ((grub_uint8_t *) &bp->server_ip)[0],
218 ((grub_uint8_t *) &bp->server_ip)[1],
219 ((grub_uint8_t *) &bp->server_ip)[2],
220 ((grub_uint8_t *) &bp->server_ip)[3]);
221 grub_print_error ();
223 if (size > OFFSET_OF (server_name, bp)
224 && bp->server_name[0])
226 set_env_limn_ro (name, "dhcp_server_name", (char *) bp->server_name,
227 sizeof (bp->server_name));
228 if (is_def && !grub_net_default_server)
230 grub_net_default_server = grub_strdup (bp->server_name);
231 grub_print_error ();
233 if (device && !*device)
235 *device = grub_xasprintf ("tftp,%s", bp->server_name);
236 grub_print_error ();
240 if (size > OFFSET_OF (boot_file, bp) && path)
242 *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
243 grub_print_error ();
244 if (*path)
246 char *slash;
247 slash = grub_strrchr (*path, '/');
248 if (slash)
249 *slash = 0;
250 else
251 **path = 0;
254 if (size > OFFSET_OF (vendor, bp))
255 parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask);
256 grub_net_add_ipv4_local (inter, mask);
258 inter->dhcp_ack = grub_malloc (size);
259 if (inter->dhcp_ack)
261 grub_memcpy (inter->dhcp_ack, bp, size);
262 inter->dhcp_acklen = size;
264 else
265 grub_errno = GRUB_ERR_NONE;
267 return inter;
270 void
271 grub_net_process_dhcp (struct grub_net_buff *nb,
272 struct grub_net_card *card)
274 char *name;
275 struct grub_net_network_level_interface *inf;
277 name = grub_xasprintf ("%s:dhcp", card->name);
278 if (!name)
280 grub_print_error ();
281 return;
283 grub_net_configure_by_dhcp_ack (name, card,
284 0, (const struct grub_net_bootp_packet *) nb->data,
285 (nb->tail - nb->data), 0, 0, 0);
286 grub_free (name);
287 if (grub_errno)
288 grub_print_error ();
289 else
291 FOR_NET_NETWORK_LEVEL_INTERFACES(inf)
292 if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0
293 && grub_memcmp (inf->name + grub_strlen (card->name),
294 ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0)
296 grub_net_network_level_interface_unregister (inf);
297 break;
302 static char
303 hexdigit (grub_uint8_t val)
305 if (val < 10)
306 return val + '0';
307 return val + 'a' - 10;
310 static grub_err_t
311 grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
312 int argc, char **args)
314 struct grub_net_network_level_interface *inter;
315 int num;
316 grub_uint8_t *ptr;
317 grub_uint8_t taglength;
319 if (argc < 4)
320 return grub_error (GRUB_ERR_BAD_ARGUMENT,
321 N_("four arguments expected"));
323 FOR_NET_NETWORK_LEVEL_INTERFACES (inter)
324 if (grub_strcmp (inter->name, args[1]) == 0)
325 break;
327 if (!inter)
328 return grub_error (GRUB_ERR_BAD_ARGUMENT,
329 N_("unrecognised network interface `%s'"), args[1]);
331 if (!inter->dhcp_ack)
332 return grub_error (GRUB_ERR_IO, N_("no DHCP info found"));
334 if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack))
335 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
337 num = grub_strtoul (args[2], 0, 0);
338 if (grub_errno)
339 return grub_errno;
341 ptr = inter->dhcp_ack->vendor;
343 if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
344 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
345 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
346 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
347 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
348 ptr = ptr + sizeof (grub_uint32_t);
349 while (1)
351 grub_uint8_t tagtype;
353 if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen)
354 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
356 tagtype = *ptr++;
358 /* Pad tag. */
359 if (tagtype == 0)
360 continue;
362 /* End tag. */
363 if (tagtype == 0xff)
364 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
366 taglength = *ptr++;
368 if (tagtype == num)
369 break;
370 ptr += taglength;
373 if (grub_strcmp (args[3], "string") == 0)
375 char *val = grub_malloc (taglength + 1);
376 if (!val)
377 return grub_errno;
378 grub_memcpy (val, ptr, taglength);
379 val[taglength] = 0;
380 if (args[0][0] == '-' && args[0][1] == 0)
381 grub_printf ("%s\n", val);
382 else
383 return grub_env_set (args[0], val);
384 return GRUB_ERR_NONE;
387 if (grub_strcmp (args[3], "number") == 0)
389 grub_uint64_t val = 0;
390 int i;
391 for (i = 0; i < taglength; i++)
392 val = (val << 8) | ptr[i];
393 if (args[0][0] == '-' && args[0][1] == 0)
394 grub_printf ("%llu\n", (unsigned long long) val);
395 else
397 char valn[64];
398 grub_printf (valn, sizeof (valn), "%lld\n", (unsigned long long) val);
399 return grub_env_set (args[0], valn);
401 return GRUB_ERR_NONE;
404 if (grub_strcmp (args[3], "hex") == 0)
406 char *val = grub_malloc (2 * taglength + 1);
407 int i;
408 if (!val)
409 return grub_errno;
410 for (i = 0; i < taglength; i++)
412 val[2 * i] = hexdigit (ptr[i] >> 4);
413 val[2 * i + 1] = hexdigit (ptr[i] & 0xf);
415 val[2 * taglength] = 0;
416 if (args[0][0] == '-' && args[0][1] == 0)
417 grub_printf ("%s\n", val);
418 else
419 return grub_env_set (args[0], val);
420 return GRUB_ERR_NONE;
423 return grub_error (GRUB_ERR_BAD_ARGUMENT,
424 N_("unrecognised DHCP option format specification `%s'"),
425 args[3]);
428 /* FIXME: allow to specify mac address. */
429 static grub_err_t
430 grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
431 int argc, char **args)
433 struct grub_net_card *card;
434 struct grub_net_network_level_interface *ifaces;
435 grub_size_t ncards = 0;
436 unsigned j = 0;
437 int interval;
438 grub_err_t err;
440 FOR_NET_CARDS (card)
442 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
443 continue;
444 ncards++;
447 ifaces = grub_zalloc (ncards * sizeof (ifaces[0]));
448 if (!ifaces)
449 return grub_errno;
451 j = 0;
452 FOR_NET_CARDS (card)
454 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
455 continue;
456 ifaces[j].card = card;
457 ifaces[j].next = &ifaces[j+1];
458 if (j)
459 ifaces[j].prev = &ifaces[j-1].next;
460 ifaces[j].name = grub_xasprintf ("%s:dhcp_tmp", card->name);
461 card->num_ifaces++;
462 if (!ifaces[j].name)
464 unsigned i;
465 for (i = 0; i < j; i++)
466 grub_free (ifaces[i].name);
467 grub_free (ifaces);
468 return grub_errno;
470 ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
471 grub_memcpy (&ifaces[j].hwaddress, &card->default_address,
472 sizeof (ifaces[j].hwaddress));
473 j++;
475 ifaces[ncards - 1].next = grub_net_network_level_interfaces;
476 if (grub_net_network_level_interfaces)
477 grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
478 grub_net_network_level_interfaces = &ifaces[0];
479 ifaces[0].prev = &grub_net_network_level_interfaces;
480 for (interval = 200; interval < 10000; interval *= 2)
482 int done = 0;
483 for (j = 0; j < ncards; j++)
485 struct grub_net_bootp_packet *pack;
486 struct grub_datetime date;
487 grub_int32_t t = 0;
488 struct grub_net_buff *nb;
489 struct udphdr *udph;
490 grub_net_network_level_address_t target;
491 grub_net_link_level_address_t ll_target;
493 if (!ifaces[j].prev)
494 continue;
495 nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128);
496 if (!nb)
498 grub_netbuff_free (nb);
499 return grub_errno;
501 err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
502 if (err)
504 grub_netbuff_free (nb);
505 return err;
507 err = grub_netbuff_push (nb, sizeof (*pack) + 64);
508 if (err)
510 grub_netbuff_free (nb);
511 return err;
513 pack = (void *) nb->data;
514 done = 1;
515 grub_memset (pack, 0, sizeof (*pack) + 64);
516 pack->opcode = 1;
517 pack->hw_type = 1;
518 pack->hw_len = 6;
519 err = grub_get_datetime (&date);
520 if (err || !grub_datetime2unixtime (&date, &t))
522 grub_errno = GRUB_ERR_NONE;
523 t = 0;
525 pack->ident = grub_cpu_to_be32 (t);
526 pack->seconds = grub_cpu_to_be16 (t);
528 grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6);
530 grub_netbuff_push (nb, sizeof (*udph));
532 udph = (struct udphdr *) nb->data;
533 udph->src = grub_cpu_to_be16 (68);
534 udph->dst = grub_cpu_to_be16 (67);
535 udph->chksum = 0;
536 udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
537 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
538 target.ipv4 = 0xffffffff;
539 err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
540 if (err)
541 return err;
543 udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
544 &ifaces[j].address,
545 &target);
547 err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
548 GRUB_NET_IP_UDP);
549 grub_netbuff_free (nb);
550 if (err)
551 return err;
553 if (!done)
554 break;
555 grub_net_poll_cards (interval, 0);
558 err = GRUB_ERR_NONE;
559 for (j = 0; j < ncards; j++)
561 grub_free (ifaces[j].name);
562 if (!ifaces[j].prev)
563 continue;
564 grub_error_push ();
565 grub_net_network_level_interface_unregister (&ifaces[j]);
566 err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
567 N_("couldn't autoconfigure %s"),
568 ifaces[j].card->name);
571 grub_free (ifaces);
572 return err;
575 static grub_command_t cmd_getdhcp, cmd_bootp;
577 void
578 grub_bootp_init (void)
580 cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp,
581 N_("[CARD]"),
582 N_("perform a bootp autoconfiguration"));
583 cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
584 N_("VAR INTERFACE NUMBER DESCRIPTION"),
585 N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
588 void
589 grub_bootp_fini (void)
591 grub_unregister_command (cmd_getdhcp);
592 grub_unregister_command (cmd_bootp);