]> git.proxmox.com Git - grub2.git/blob - grub-core/net/bootp.c
cleanup: grub_cpu_to_XXX_compile_time for constants
[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);
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
146 addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
147 addr.ipv4 = bp->your_ip;
148
149 if (device)
150 *device = 0;
151 if (path)
152 *path = 0;
153
154 grub_memcpy (hwaddr.mac, bp->mac_addr,
155 bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
156 : sizeof (hwaddr.mac));
157 hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
158
159 inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
160 if (bp->gateway_ip)
161 {
162 grub_net_network_level_netaddress_t target;
163 grub_net_network_level_address_t gw;
164 char *rname;
165
166 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
167 target.ipv4.base = bp->server_ip;
168 target.ipv4.masksize = 32;
169 gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
170 gw.ipv4 = bp->gateway_ip;
171 rname = grub_xasprintf ("%s:gw", name);
172 if (rname)
173 grub_net_add_route_gw (rname, target, gw);
174 grub_free (rname);
175
176 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
177 target.ipv4.base = bp->gateway_ip;
178 target.ipv4.masksize = 32;
179 grub_net_add_route (name, target, inter);
180 }
181
182 if (size > OFFSET_OF (boot_file, bp))
183 grub_env_set_net_property (name, "boot_file", bp->boot_file,
184 sizeof (bp->boot_file));
185 if (is_def)
186 grub_net_default_server = 0;
187 if (is_def && !grub_net_default_server && bp->server_ip)
188 {
189 grub_net_default_server = grub_xasprintf ("%d.%d.%d.%d",
190 ((grub_uint8_t *) &bp->server_ip)[0],
191 ((grub_uint8_t *) &bp->server_ip)[1],
192 ((grub_uint8_t *) &bp->server_ip)[2],
193 ((grub_uint8_t *) &bp->server_ip)[3]);
194 grub_print_error ();
195 }
196
197 if (is_def)
198 {
199 grub_env_set ("net_default_interface", name);
200 grub_env_export ("net_default_interface");
201 }
202
203 if (device && !*device && bp->server_ip)
204 {
205 *device = grub_xasprintf ("tftp,%d.%d.%d.%d",
206 ((grub_uint8_t *) &bp->server_ip)[0],
207 ((grub_uint8_t *) &bp->server_ip)[1],
208 ((grub_uint8_t *) &bp->server_ip)[2],
209 ((grub_uint8_t *) &bp->server_ip)[3]);
210 grub_print_error ();
211 }
212 if (size > OFFSET_OF (server_name, bp)
213 && bp->server_name[0])
214 {
215 grub_env_set_net_property (name, "dhcp_server_name", bp->server_name,
216 sizeof (bp->server_name));
217 if (is_def && !grub_net_default_server)
218 {
219 grub_net_default_server = grub_strdup (bp->server_name);
220 grub_print_error ();
221 }
222 if (device && !*device)
223 {
224 *device = grub_xasprintf ("tftp,%s", bp->server_name);
225 grub_print_error ();
226 }
227 }
228
229 if (size > OFFSET_OF (boot_file, bp) && path)
230 {
231 *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
232 grub_print_error ();
233 if (*path)
234 {
235 char *slash;
236 slash = grub_strrchr (*path, '/');
237 if (slash)
238 *slash = 0;
239 else
240 **path = 0;
241 }
242 }
243 if (size > OFFSET_OF (vendor, bp))
244 parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask);
245 grub_net_add_ipv4_local (inter, mask);
246
247 inter->dhcp_ack = grub_malloc (size);
248 if (inter->dhcp_ack)
249 {
250 grub_memcpy (inter->dhcp_ack, bp, size);
251 inter->dhcp_acklen = size;
252 }
253 else
254 grub_errno = GRUB_ERR_NONE;
255
256 return inter;
257 }
258
259 void
260 grub_net_process_dhcp (struct grub_net_buff *nb,
261 struct grub_net_card *card)
262 {
263 char *name;
264 struct grub_net_network_level_interface *inf;
265
266 name = grub_xasprintf ("%s:dhcp", card->name);
267 if (!name)
268 {
269 grub_print_error ();
270 return;
271 }
272 grub_net_configure_by_dhcp_ack (name, card,
273 0, (const struct grub_net_bootp_packet *) nb->data,
274 (nb->tail - nb->data), 0, 0, 0);
275 grub_free (name);
276 if (grub_errno)
277 grub_print_error ();
278 else
279 {
280 FOR_NET_NETWORK_LEVEL_INTERFACES(inf)
281 if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0
282 && grub_memcmp (inf->name + grub_strlen (card->name),
283 ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0)
284 {
285 grub_net_network_level_interface_unregister (inf);
286 break;
287 }
288 }
289 }
290
291 static char
292 hexdigit (grub_uint8_t val)
293 {
294 if (val < 10)
295 return val + '0';
296 return val + 'a' - 10;
297 }
298
299 static grub_err_t
300 grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
301 int argc, char **args)
302 {
303 struct grub_net_network_level_interface *inter;
304 int num;
305 grub_uint8_t *ptr;
306 grub_uint8_t taglength;
307
308 if (argc < 4)
309 return grub_error (GRUB_ERR_BAD_ARGUMENT,
310 N_("four arguments expected"));
311
312 FOR_NET_NETWORK_LEVEL_INTERFACES (inter)
313 if (grub_strcmp (inter->name, args[1]) == 0)
314 break;
315
316 if (!inter)
317 return grub_error (GRUB_ERR_BAD_ARGUMENT,
318 N_("unrecognised network interface `%s'"), args[1]);
319
320 if (!inter->dhcp_ack)
321 return grub_error (GRUB_ERR_IO, N_("no DHCP info found"));
322
323 if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack))
324 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
325
326 num = grub_strtoul (args[2], 0, 0);
327 if (grub_errno)
328 return grub_errno;
329
330 ptr = inter->dhcp_ack->vendor;
331
332 if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
333 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
334 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
335 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
336 return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
337 ptr = ptr + sizeof (grub_uint32_t);
338 while (1)
339 {
340 grub_uint8_t tagtype;
341
342 if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen)
343 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
344
345 tagtype = *ptr++;
346
347 /* Pad tag. */
348 if (tagtype == 0)
349 continue;
350
351 /* End tag. */
352 if (tagtype == 0xff)
353 return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
354
355 taglength = *ptr++;
356
357 if (tagtype == num)
358 break;
359 ptr += taglength;
360 }
361
362 if (grub_strcmp (args[3], "string") == 0)
363 {
364 char *val = grub_malloc (taglength + 1);
365 if (!val)
366 return grub_errno;
367 grub_memcpy (val, ptr, taglength);
368 val[taglength] = 0;
369 if (args[0][0] == '-' && args[0][1] == 0)
370 grub_printf ("%s\n", val);
371 else
372 return grub_env_set (args[0], val);
373 return GRUB_ERR_NONE;
374 }
375
376 if (grub_strcmp (args[3], "number") == 0)
377 {
378 grub_uint64_t val = 0;
379 int i;
380 for (i = 0; i < taglength; i++)
381 val = (val << 8) | ptr[i];
382 if (args[0][0] == '-' && args[0][1] == 0)
383 grub_printf ("%llu\n", (unsigned long long) val);
384 else
385 {
386 char valn[64];
387 grub_snprintf (valn, sizeof (valn), "%lld\n", (unsigned long long) val);
388 return grub_env_set (args[0], valn);
389 }
390 return GRUB_ERR_NONE;
391 }
392
393 if (grub_strcmp (args[3], "hex") == 0)
394 {
395 char *val = grub_malloc (2 * taglength + 1);
396 int i;
397 if (!val)
398 return grub_errno;
399 for (i = 0; i < taglength; i++)
400 {
401 val[2 * i] = hexdigit (ptr[i] >> 4);
402 val[2 * i + 1] = hexdigit (ptr[i] & 0xf);
403 }
404 val[2 * taglength] = 0;
405 if (args[0][0] == '-' && args[0][1] == 0)
406 grub_printf ("%s\n", val);
407 else
408 return grub_env_set (args[0], val);
409 return GRUB_ERR_NONE;
410 }
411
412 return grub_error (GRUB_ERR_BAD_ARGUMENT,
413 N_("unrecognised DHCP option format specification `%s'"),
414 args[3]);
415 }
416
417 /* FIXME: allow to specify mac address. */
418 static grub_err_t
419 grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
420 int argc, char **args)
421 {
422 struct grub_net_card *card;
423 struct grub_net_network_level_interface *ifaces;
424 grub_size_t ncards = 0;
425 unsigned j = 0;
426 int interval;
427 grub_err_t err;
428
429 FOR_NET_CARDS (card)
430 {
431 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
432 continue;
433 ncards++;
434 }
435
436 if (ncards == 0)
437 return grub_error (GRUB_ERR_NET_NO_CARD, N_("no network card found"));
438
439 ifaces = grub_zalloc (ncards * sizeof (ifaces[0]));
440 if (!ifaces)
441 return grub_errno;
442
443 j = 0;
444 FOR_NET_CARDS (card)
445 {
446 if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
447 continue;
448 ifaces[j].card = card;
449 ifaces[j].next = &ifaces[j+1];
450 if (j)
451 ifaces[j].prev = &ifaces[j-1].next;
452 ifaces[j].name = grub_xasprintf ("%s:dhcp_tmp", card->name);
453 card->num_ifaces++;
454 if (!ifaces[j].name)
455 {
456 unsigned i;
457 for (i = 0; i < j; i++)
458 grub_free (ifaces[i].name);
459 grub_free (ifaces);
460 return grub_errno;
461 }
462 ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
463 grub_memcpy (&ifaces[j].hwaddress, &card->default_address,
464 sizeof (ifaces[j].hwaddress));
465 j++;
466 }
467 ifaces[ncards - 1].next = grub_net_network_level_interfaces;
468 if (grub_net_network_level_interfaces)
469 grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
470 grub_net_network_level_interfaces = &ifaces[0];
471 ifaces[0].prev = &grub_net_network_level_interfaces;
472 for (interval = 200; interval < 10000; interval *= 2)
473 {
474 int done = 0;
475 for (j = 0; j < ncards; j++)
476 {
477 struct grub_net_bootp_packet *pack;
478 struct grub_datetime date;
479 grub_int32_t t = 0;
480 struct grub_net_buff *nb;
481 struct udphdr *udph;
482 grub_net_network_level_address_t target;
483 grub_net_link_level_address_t ll_target;
484
485 if (!ifaces[j].prev)
486 continue;
487 nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128);
488 if (!nb)
489 {
490 grub_netbuff_free (nb);
491 return grub_errno;
492 }
493 err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
494 if (err)
495 {
496 grub_netbuff_free (nb);
497 return err;
498 }
499 err = grub_netbuff_push (nb, sizeof (*pack) + 64);
500 if (err)
501 {
502 grub_netbuff_free (nb);
503 return err;
504 }
505 pack = (void *) nb->data;
506 done = 1;
507 grub_memset (pack, 0, sizeof (*pack) + 64);
508 pack->opcode = 1;
509 pack->hw_type = 1;
510 pack->hw_len = 6;
511 err = grub_get_datetime (&date);
512 if (err || !grub_datetime2unixtime (&date, &t))
513 {
514 grub_errno = GRUB_ERR_NONE;
515 t = 0;
516 }
517 pack->ident = grub_cpu_to_be32 (t);
518 pack->seconds = grub_cpu_to_be16 (t);
519
520 grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6);
521
522 grub_netbuff_push (nb, sizeof (*udph));
523
524 udph = (struct udphdr *) nb->data;
525 udph->src = grub_cpu_to_be16_compile_time (68);
526 udph->dst = grub_cpu_to_be16_compile_time (67);
527 udph->chksum = 0;
528 udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
529 target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
530 target.ipv4 = 0xffffffff;
531 err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
532 if (err)
533 return err;
534
535 udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
536 &ifaces[j].address,
537 &target);
538
539 err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
540 GRUB_NET_IP_UDP);
541 grub_netbuff_free (nb);
542 if (err)
543 return err;
544 }
545 if (!done)
546 break;
547 grub_net_poll_cards (interval, 0);
548 }
549
550 err = GRUB_ERR_NONE;
551 for (j = 0; j < ncards; j++)
552 {
553 grub_free (ifaces[j].name);
554 if (!ifaces[j].prev)
555 continue;
556 grub_error_push ();
557 grub_net_network_level_interface_unregister (&ifaces[j]);
558 err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
559 N_("couldn't autoconfigure %s"),
560 ifaces[j].card->name);
561 }
562
563 grub_free (ifaces);
564 return err;
565 }
566
567 static grub_command_t cmd_getdhcp, cmd_bootp;
568
569 void
570 grub_bootp_init (void)
571 {
572 cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp,
573 N_("[CARD]"),
574 N_("perform a bootp autoconfiguration"));
575 cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
576 N_("VAR INTERFACE NUMBER DESCRIPTION"),
577 N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
578 }
579
580 void
581 grub_bootp_fini (void)
582 {
583 grub_unregister_command (cmd_getdhcp);
584 grub_unregister_command (cmd_bootp);
585 }