]> git.proxmox.com Git - grub2.git/blob - grub-core/net/bootp.c
Import grub2_2.02+dfsg1.orig.tar.xz
[grub2.git] / grub-core / net / bootp.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
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>
27
28 static void
29 parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
30 {
31 const grub_uint8_t *ptr, *ptr0;
32
33 ptr = ptr0 = vend;
34
35 if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
36 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
37 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
38 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
39 return;
40 ptr = ptr + sizeof (grub_uint32_t);
41 while (ptr - ptr0 < limit)
42 {
43 grub_uint8_t tagtype;
44 grub_uint8_t taglength;
45
46 tagtype = *ptr++;
47
48 /* Pad tag. */
49 if (tagtype == GRUB_NET_BOOTP_PAD)
50 continue;
51
52 /* End tag. */
53 if (tagtype == GRUB_NET_BOOTP_END)
54 return;
55
56 taglength = *ptr++;
57
58 switch (tagtype)
59 {
60 case GRUB_NET_BOOTP_NETMASK:
61 if (taglength == 4)
62 {
63 int i;
64 for (i = 0; i < 32; i++)
65 if (!(ptr[i / 8] & (1 << (7 - (i % 8)))))
66 break;
67 *mask = i;
68 }
69 break;
70
71 case GRUB_NET_BOOTP_ROUTER:
72 if (taglength == 4)
73 {
74 grub_net_network_level_netaddress_t target;
75 grub_net_network_level_address_t gw;
76 char *rname;
77
78 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
79 target.ipv4.base = 0;
80 target.ipv4.masksize = 0;
81 gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
82 grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4));
83 rname = grub_xasprintf ("%s:default", name);
84 if (rname)
85 grub_net_add_route_gw (rname, target, gw, NULL);
86 grub_free (rname);
87 }
88 break;
89 case GRUB_NET_BOOTP_DNS:
90 {
91 int i;
92 for (i = 0; i < taglength / 4; i++)
93 {
94 struct grub_net_network_level_address s;
95 s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
96 s.ipv4 = grub_get_unaligned32 (ptr);
97 s.option = DNS_OPTION_PREFER_IPV4;
98 grub_net_add_dns_server (&s);
99 ptr += 4;
100 }
101 }
102 continue;
103 case GRUB_NET_BOOTP_HOSTNAME:
104 grub_env_set_net_property (name, "hostname", (const char *) ptr,
105 taglength);
106 break;
107
108 case GRUB_NET_BOOTP_DOMAIN:
109 grub_env_set_net_property (name, "domain", (const char *) ptr,
110 taglength);
111 break;
112
113 case GRUB_NET_BOOTP_ROOT_PATH:
114 grub_env_set_net_property (name, "rootpath", (const char *) ptr,
115 taglength);
116 break;
117
118 case GRUB_NET_BOOTP_EXTENSIONS_PATH:
119 grub_env_set_net_property (name, "extensionspath", (const char *) ptr,
120 taglength);
121 break;
122
123 /* If you need any other options please contact GRUB
124 development team. */
125 }
126
127 ptr += taglength;
128 }
129 }
130
131 #define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
132
133 struct grub_net_network_level_interface *
134 grub_net_configure_by_dhcp_ack (const char *name,
135 struct grub_net_card *card,
136 grub_net_interface_flags_t flags,
137 const struct grub_net_bootp_packet *bp,
138 grub_size_t size,
139 int is_def, char **device, char **path)
140 {
141 grub_net_network_level_address_t addr;
142 grub_net_link_level_address_t hwaddr;
143 struct grub_net_network_level_interface *inter;
144 int mask = -1;
145 char server_ip[sizeof ("xxx.xxx.xxx.xxx")];
146
147 addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
148 addr.ipv4 = bp->your_ip;
149
150 if (device)
151 *device = 0;
152 if (path)
153 *path = 0;
154
155 grub_memcpy (hwaddr.mac, bp->mac_addr,
156 bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
157 : sizeof (hwaddr.mac));
158 hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
159
160 inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
161 if (!inter)
162 return 0;
163
164 #if 0
165 /* This is likely based on misunderstanding. gateway_ip refers to
166 address of BOOTP relay and should not be used after BOOTP transaction
167 is complete.
168 See RFC1542, 3.4 Interpretation of the 'giaddr' field
169 */
170 if (bp->gateway_ip)
171 {
172 grub_net_network_level_netaddress_t target;
173 grub_net_network_level_address_t gw;
174 char *rname;
175
176 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
177 target.ipv4.base = bp->server_ip;
178 target.ipv4.masksize = 32;
179 gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
180 gw.ipv4 = bp->gateway_ip;
181 rname = grub_xasprintf ("%s:gw", name);
182 if (rname)
183 grub_net_add_route_gw (rname, target, gw);
184 grub_free (rname);
185
186 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
187 target.ipv4.base = bp->gateway_ip;
188 target.ipv4.masksize = 32;
189 grub_net_add_route (name, target, inter);
190 }
191 #endif
192
193 if (size > OFFSET_OF (boot_file, bp))
194 grub_env_set_net_property (name, "boot_file", bp->boot_file,
195 sizeof (bp->boot_file));
196 if (bp->server_ip)
197 {
198 grub_snprintf (server_ip, sizeof (server_ip), "%d.%d.%d.%d",
199 ((grub_uint8_t *) &bp->server_ip)[0],
200 ((grub_uint8_t *) &bp->server_ip)[1],
201 ((grub_uint8_t *) &bp->server_ip)[2],
202 ((grub_uint8_t *) &bp->server_ip)[3]);
203 grub_env_set_net_property (name, "next_server", server_ip, sizeof (server_ip));
204 grub_print_error ();
205 }
206
207 if (is_def)
208 grub_net_default_server = 0;
209 if (is_def && !grub_net_default_server && bp->server_ip)
210 {
211 grub_net_default_server = grub_strdup (server_ip);
212 grub_print_error ();
213 }
214
215 if (is_def)
216 {
217 grub_env_set ("net_default_interface", name);
218 grub_env_export ("net_default_interface");
219 }
220
221 if (device && !*device && bp->server_ip)
222 {
223 *device = grub_xasprintf ("tftp,%s", server_ip);
224 grub_print_error ();
225 }
226 if (size > OFFSET_OF (server_name, bp)
227 && bp->server_name[0])
228 {
229 grub_env_set_net_property (name, "dhcp_server_name", bp->server_name,
230 sizeof (bp->server_name));
231 if (is_def && !grub_net_default_server)
232 {
233 grub_net_default_server = grub_strdup (bp->server_name);
234 grub_print_error ();
235 }
236 if (device && !*device)
237 {
238 *device = grub_xasprintf ("tftp,%s", bp->server_name);
239 grub_print_error ();
240 }
241 }
242
243 if (size > OFFSET_OF (boot_file, bp) && path)
244 {
245 *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
246 grub_print_error ();
247 if (*path)
248 {
249 char *slash;
250 slash = grub_strrchr (*path, '/');
251 if (slash)
252 *slash = 0;
253 else
254 **path = 0;
255 }
256 }
257 if (size > OFFSET_OF (vendor, bp))
258 parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask);
259 grub_net_add_ipv4_local (inter, mask);
260
261 inter->dhcp_ack = grub_malloc (size);
262 if (inter->dhcp_ack)
263 {
264 grub_memcpy (inter->dhcp_ack, bp, size);
265 inter->dhcp_acklen = size;
266 }
267 else
268 grub_errno = GRUB_ERR_NONE;
269
270 return inter;
271 }
272
273 void
274 grub_net_process_dhcp (struct grub_net_buff *nb,
275 struct grub_net_card *card)
276 {
277 char *name;
278 struct grub_net_network_level_interface *inf;
279
280 name = grub_xasprintf ("%s:dhcp", card->name);
281 if (!name)
282 {
283 grub_print_error ();
284 return;
285 }
286 grub_net_configure_by_dhcp_ack (name, card,
287 0, (const struct grub_net_bootp_packet *) nb->data,
288 (nb->tail - nb->data), 0, 0, 0);
289 grub_free (name);
290 if (grub_errno)
291 grub_print_error ();
292 else
293 {
294 FOR_NET_NETWORK_LEVEL_INTERFACES(inf)
295 if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0
296 && grub_memcmp (inf->name + grub_strlen (card->name),
297 ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0)
298 {
299 grub_net_network_level_interface_unregister (inf);
300 break;
301 }
302 }
303 }
304
305 static char
306 hexdigit (grub_uint8_t val)
307 {
308 if (val < 10)
309 return val + '0';
310 return val + 'a' - 10;
311 }
312
313 static grub_err_t
314 grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
315 int argc, char **args)
316 {
317 struct grub_net_network_level_interface *inter;
318 int num;
319 grub_uint8_t *ptr;
320 grub_uint8_t taglength;
321
322 if (argc < 4)
323 return grub_error (GRUB_ERR_BAD_ARGUMENT,
324 N_("four arguments expected"));
325
326 FOR_NET_NETWORK_LEVEL_INTERFACES (inter)
327 if (grub_strcmp (inter->name, args[1]) == 0)
328 break;
329
330 if (!inter)
331 return grub_error (GRUB_ERR_BAD_ARGUMENT,
332 N_("unrecognised network interface `%s'"), args[1]);
333
334 if (!inter->dhcp_ack)
335 return grub_error (GRUB_ERR_IO, N_("no DHCP info found"));
336
337 if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack))
338 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
339
340 num = grub_strtoul (args[2], 0, 0);
341 if (grub_errno)
342 return grub_errno;
343
344 ptr = inter->dhcp_ack->vendor;
345
346 if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
347 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
348 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
349 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
350 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
351 ptr = ptr + sizeof (grub_uint32_t);
352 while (1)
353 {
354 grub_uint8_t tagtype;
355
356 if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen)
357 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
358
359 tagtype = *ptr++;
360
361 /* Pad tag. */
362 if (tagtype == 0)
363 continue;
364
365 /* End tag. */
366 if (tagtype == 0xff)
367 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
368
369 taglength = *ptr++;
370
371 if (tagtype == num)
372 break;
373 ptr += taglength;
374 }
375
376 if (grub_strcmp (args[3], "string") == 0)
377 {
378 grub_err_t err = GRUB_ERR_NONE;
379 char *val = grub_malloc (taglength + 1);
380 if (!val)
381 return grub_errno;
382 grub_memcpy (val, ptr, taglength);
383 val[taglength] = 0;
384 if (args[0][0] == '-' && args[0][1] == 0)
385 grub_printf ("%s\n", val);
386 else
387 err = grub_env_set (args[0], val);
388 grub_free (val);
389 return err;
390 }
391
392 if (grub_strcmp (args[3], "number") == 0)
393 {
394 grub_uint64_t val = 0;
395 int i;
396 for (i = 0; i < taglength; i++)
397 val = (val << 8) | ptr[i];
398 if (args[0][0] == '-' && args[0][1] == 0)
399 grub_printf ("%llu\n", (unsigned long long) val);
400 else
401 {
402 char valn[64];
403 grub_snprintf (valn, sizeof (valn), "%lld\n", (unsigned long long) val);
404 return grub_env_set (args[0], valn);
405 }
406 return GRUB_ERR_NONE;
407 }
408
409 if (grub_strcmp (args[3], "hex") == 0)
410 {
411 grub_err_t err = GRUB_ERR_NONE;
412 char *val = grub_malloc (2 * taglength + 1);
413 int i;
414 if (!val)
415 return grub_errno;
416 for (i = 0; i < taglength; i++)
417 {
418 val[2 * i] = hexdigit (ptr[i] >> 4);
419 val[2 * i + 1] = hexdigit (ptr[i] & 0xf);
420 }
421 val[2 * taglength] = 0;
422 if (args[0][0] == '-' && args[0][1] == 0)
423 grub_printf ("%s\n", val);
424 else
425 err = grub_env_set (args[0], val);
426 grub_free (val);
427 return err;
428 }
429
430 return grub_error (GRUB_ERR_BAD_ARGUMENT,
431 N_("unrecognised DHCP option format specification `%s'"),
432 args[3]);
433 }
434
435 /* FIXME: allow to specify mac address. */
436 static grub_err_t
437 grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
438 int argc, char **args)
439 {
440 struct grub_net_card *card;
441 struct grub_net_network_level_interface *ifaces;
442 grub_size_t ncards = 0;
443 unsigned j = 0;
444 int interval;
445 grub_err_t err;
446
447 FOR_NET_CARDS (card)
448 {
449 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
450 continue;
451 ncards++;
452 }
453
454 if (ncards == 0)
455 return grub_error (GRUB_ERR_NET_NO_CARD, N_("no network card found"));
456
457 ifaces = grub_zalloc (ncards * sizeof (ifaces[0]));
458 if (!ifaces)
459 return grub_errno;
460
461 j = 0;
462 FOR_NET_CARDS (card)
463 {
464 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
465 continue;
466 ifaces[j].card = card;
467 ifaces[j].next = &ifaces[j+1];
468 if (j)
469 ifaces[j].prev = &ifaces[j-1].next;
470 ifaces[j].name = grub_xasprintf ("%s:dhcp_tmp", card->name);
471 card->num_ifaces++;
472 if (!ifaces[j].name)
473 {
474 unsigned i;
475 for (i = 0; i < j; i++)
476 grub_free (ifaces[i].name);
477 grub_free (ifaces);
478 return grub_errno;
479 }
480 ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
481 grub_memcpy (&ifaces[j].hwaddress, &card->default_address,
482 sizeof (ifaces[j].hwaddress));
483 j++;
484 }
485 ifaces[ncards - 1].next = grub_net_network_level_interfaces;
486 if (grub_net_network_level_interfaces)
487 grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
488 grub_net_network_level_interfaces = &ifaces[0];
489 ifaces[0].prev = &grub_net_network_level_interfaces;
490 for (interval = 200; interval < 10000; interval *= 2)
491 {
492 int done = 0;
493 for (j = 0; j < ncards; j++)
494 {
495 struct grub_net_bootp_packet *pack;
496 struct grub_datetime date;
497 grub_int32_t t = 0;
498 struct grub_net_buff *nb;
499 struct udphdr *udph;
500 grub_net_network_level_address_t target;
501 grub_net_link_level_address_t ll_target;
502
503 if (!ifaces[j].prev)
504 continue;
505 nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128);
506 if (!nb)
507 {
508 grub_netbuff_free (nb);
509 return grub_errno;
510 }
511 err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
512 if (err)
513 {
514 grub_netbuff_free (nb);
515 return err;
516 }
517 err = grub_netbuff_push (nb, sizeof (*pack) + 64);
518 if (err)
519 {
520 grub_netbuff_free (nb);
521 return err;
522 }
523 pack = (void *) nb->data;
524 done = 1;
525 grub_memset (pack, 0, sizeof (*pack) + 64);
526 pack->opcode = 1;
527 pack->hw_type = 1;
528 pack->hw_len = 6;
529 err = grub_get_datetime (&date);
530 if (err || !grub_datetime2unixtime (&date, &t))
531 {
532 grub_errno = GRUB_ERR_NONE;
533 t = 0;
534 }
535 pack->ident = grub_cpu_to_be32 (t);
536 pack->seconds = grub_cpu_to_be16 (t);
537
538 grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6);
539
540 grub_netbuff_push (nb, sizeof (*udph));
541
542 udph = (struct udphdr *) nb->data;
543 udph->src = grub_cpu_to_be16_compile_time (68);
544 udph->dst = grub_cpu_to_be16_compile_time (67);
545 udph->chksum = 0;
546 udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
547 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
548 target.ipv4 = 0xffffffff;
549 err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
550 if (err)
551 return err;
552
553 udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
554 &ifaces[j].address,
555 &target);
556
557 err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
558 GRUB_NET_IP_UDP);
559 grub_netbuff_free (nb);
560 if (err)
561 return err;
562 }
563 if (!done)
564 break;
565 grub_net_poll_cards (interval, 0);
566 }
567
568 err = GRUB_ERR_NONE;
569 for (j = 0; j < ncards; j++)
570 {
571 grub_free (ifaces[j].name);
572 if (!ifaces[j].prev)
573 continue;
574 grub_error_push ();
575 grub_net_network_level_interface_unregister (&ifaces[j]);
576 err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
577 N_("couldn't autoconfigure %s"),
578 ifaces[j].card->name);
579 }
580
581 grub_free (ifaces);
582 return err;
583 }
584
585 static grub_command_t cmd_getdhcp, cmd_bootp;
586
587 void
588 grub_bootp_init (void)
589 {
590 cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp,
591 N_("[CARD]"),
592 N_("perform a bootp autoconfiguration"));
593 cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
594 N_("VAR INTERFACE NUMBER DESCRIPTION"),
595 N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
596 }
597
598 void
599 grub_bootp_fini (void)
600 {
601 grub_unregister_command (cmd_getdhcp);
602 grub_unregister_command (cmd_bootp);
603 }