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/>.
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>
29 parse_dhcp_vendor (const char *name
, const void *vend
, int limit
, int *mask
)
31 const grub_uint8_t
*ptr
, *ptr0
;
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
)
40 ptr
= ptr
+ sizeof (grub_uint32_t
);
41 while (ptr
- ptr0
< limit
)
44 grub_uint8_t taglength
;
49 if (tagtype
== GRUB_NET_BOOTP_PAD
)
53 if (tagtype
== GRUB_NET_BOOTP_END
)
60 case GRUB_NET_BOOTP_NETMASK
:
64 for (i
= 0; i
< 32; i
++)
65 if (!(ptr
[i
/ 8] & (1 << (7 - (i
% 8)))))
71 case GRUB_NET_BOOTP_ROUTER
:
74 grub_net_network_level_netaddress_t target
;
75 grub_net_network_level_address_t gw
;
78 target
.type
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
;
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
);
85 grub_net_add_route_gw (rname
, target
, gw
);
89 case GRUB_NET_BOOTP_DNS
:
92 for (i
= 0; i
< taglength
/ 4; i
++)
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
);
103 case GRUB_NET_BOOTP_HOSTNAME
:
104 grub_env_set_net_property (name
, "hostname", (const char *) ptr
,
108 case GRUB_NET_BOOTP_DOMAIN
:
109 grub_env_set_net_property (name
, "domain", (const char *) ptr
,
113 case GRUB_NET_BOOTP_ROOT_PATH
:
114 grub_env_set_net_property (name
, "rootpath", (const char *) ptr
,
118 case GRUB_NET_BOOTP_EXTENSIONS_PATH
:
119 grub_env_set_net_property (name
, "extensionspath", (const char *) ptr
,
123 /* If you need any other options please contact GRUB
131 #define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
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
,
139 int is_def
, char **device
, char **path
)
141 grub_net_network_level_address_t addr
;
142 grub_net_link_level_address_t hwaddr
;
143 struct grub_net_network_level_interface
*inter
;
146 addr
.type
= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
;
147 addr
.ipv4
= bp
->your_ip
;
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
;
159 inter
= grub_net_add_addr (name
, card
, &addr
, &hwaddr
, flags
);
162 grub_net_network_level_netaddress_t target
;
163 grub_net_network_level_address_t gw
;
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
);
173 grub_net_add_route_gw (rname
, target
, gw
);
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
);
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
));
186 grub_net_default_server
= 0;
187 if (is_def
&& !grub_net_default_server
&& bp
->server_ip
)
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]);
199 grub_env_set ("net_default_interface", name
);
200 grub_env_export ("net_default_interface");
203 if (device
&& !*device
&& bp
->server_ip
)
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]);
212 if (size
> OFFSET_OF (server_name
, bp
)
213 && bp
->server_name
[0])
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
)
219 grub_net_default_server
= grub_strdup (bp
->server_name
);
222 if (device
&& !*device
)
224 *device
= grub_xasprintf ("tftp,%s", bp
->server_name
);
229 if (size
> OFFSET_OF (boot_file
, bp
) && path
)
231 *path
= grub_strndup (bp
->boot_file
, sizeof (bp
->boot_file
));
236 slash
= grub_strrchr (*path
, '/');
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
);
247 inter
->dhcp_ack
= grub_malloc (size
);
250 grub_memcpy (inter
->dhcp_ack
, bp
, size
);
251 inter
->dhcp_acklen
= size
;
254 grub_errno
= GRUB_ERR_NONE
;
260 grub_net_process_dhcp (struct grub_net_buff
*nb
,
261 struct grub_net_card
*card
)
264 struct grub_net_network_level_interface
*inf
;
266 name
= grub_xasprintf ("%s:dhcp", card
->name
);
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);
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)
285 grub_net_network_level_interface_unregister (inf
);
292 hexdigit (grub_uint8_t val
)
296 return val
+ 'a' - 10;
300 grub_cmd_dhcpopt (struct grub_command
*cmd
__attribute__ ((unused
)),
301 int argc
, char **args
)
303 struct grub_net_network_level_interface
*inter
;
306 grub_uint8_t taglength
;
309 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
310 N_("four arguments expected"));
312 FOR_NET_NETWORK_LEVEL_INTERFACES (inter
)
313 if (grub_strcmp (inter
->name
, args
[1]) == 0)
317 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
318 N_("unrecognised network interface `%s'"), args
[1]);
320 if (!inter
->dhcp_ack
)
321 return grub_error (GRUB_ERR_IO
, N_("no DHCP info found"));
323 if (inter
->dhcp_acklen
<= OFFSET_OF (vendor
, inter
->dhcp_ack
))
324 return grub_error (GRUB_ERR_IO
, N_("no DHCP options found"));
326 num
= grub_strtoul (args
[2], 0, 0);
330 ptr
= inter
->dhcp_ack
->vendor
;
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
);
340 grub_uint8_t tagtype
;
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
);
353 return grub_error (GRUB_ERR_IO
, N_("no DHCP option %d found"), num
);
362 if (grub_strcmp (args
[3], "string") == 0)
364 char *val
= grub_malloc (taglength
+ 1);
367 grub_memcpy (val
, ptr
, taglength
);
369 if (args
[0][0] == '-' && args
[0][1] == 0)
370 grub_printf ("%s\n", val
);
372 return grub_env_set (args
[0], val
);
373 return GRUB_ERR_NONE
;
376 if (grub_strcmp (args
[3], "number") == 0)
378 grub_uint64_t val
= 0;
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
);
387 grub_snprintf (valn
, sizeof (valn
), "%lld\n", (unsigned long long) val
);
388 return grub_env_set (args
[0], valn
);
390 return GRUB_ERR_NONE
;
393 if (grub_strcmp (args
[3], "hex") == 0)
395 char *val
= grub_malloc (2 * taglength
+ 1);
399 for (i
= 0; i
< taglength
; i
++)
401 val
[2 * i
] = hexdigit (ptr
[i
] >> 4);
402 val
[2 * i
+ 1] = hexdigit (ptr
[i
] & 0xf);
404 val
[2 * taglength
] = 0;
405 if (args
[0][0] == '-' && args
[0][1] == 0)
406 grub_printf ("%s\n", val
);
408 return grub_env_set (args
[0], val
);
409 return GRUB_ERR_NONE
;
412 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
413 N_("unrecognised DHCP option format specification `%s'"),
417 /* FIXME: allow to specify mac address. */
419 grub_cmd_bootp (struct grub_command
*cmd
__attribute__ ((unused
)),
420 int argc
, char **args
)
422 struct grub_net_card
*card
;
423 struct grub_net_network_level_interface
*ifaces
;
424 grub_size_t ncards
= 0;
431 if (argc
> 0 && grub_strcmp (card
->name
, args
[0]) != 0)
437 return grub_error (GRUB_ERR_NET_NO_CARD
, N_("no network card found"));
439 ifaces
= grub_zalloc (ncards
* sizeof (ifaces
[0]));
446 if (argc
> 0 && grub_strcmp (card
->name
, args
[0]) != 0)
448 ifaces
[j
].card
= card
;
449 ifaces
[j
].next
= &ifaces
[j
+1];
451 ifaces
[j
].prev
= &ifaces
[j
-1].next
;
452 ifaces
[j
].name
= grub_xasprintf ("%s:dhcp_tmp", card
->name
);
457 for (i
= 0; i
< j
; i
++)
458 grub_free (ifaces
[i
].name
);
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
));
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)
475 for (j
= 0; j
< ncards
; j
++)
477 struct grub_net_bootp_packet
*pack
;
478 struct grub_datetime date
;
480 struct grub_net_buff
*nb
;
482 grub_net_network_level_address_t target
;
483 grub_net_link_level_address_t ll_target
;
487 nb
= grub_netbuff_alloc (sizeof (*pack
) + 64 + 128);
490 grub_netbuff_free (nb
);
493 err
= grub_netbuff_reserve (nb
, sizeof (*pack
) + 64 + 128);
496 grub_netbuff_free (nb
);
499 err
= grub_netbuff_push (nb
, sizeof (*pack
) + 64);
502 grub_netbuff_free (nb
);
505 pack
= (void *) nb
->data
;
507 grub_memset (pack
, 0, sizeof (*pack
) + 64);
511 err
= grub_get_datetime (&date
);
512 if (err
|| !grub_datetime2unixtime (&date
, &t
))
514 grub_errno
= GRUB_ERR_NONE
;
517 pack
->ident
= grub_cpu_to_be32 (t
);
518 pack
->seconds
= grub_cpu_to_be16 (t
);
520 grub_memcpy (&pack
->mac_addr
, &ifaces
[j
].hwaddress
.mac
, 6);
522 grub_netbuff_push (nb
, sizeof (*udph
));
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);
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
);
535 udph
->chksum
= grub_net_ip_transport_checksum (nb
, GRUB_NET_IP_UDP
,
539 err
= grub_net_send_ip_packet (&ifaces
[j
], &target
, &ll_target
, nb
,
541 grub_netbuff_free (nb
);
547 grub_net_poll_cards (interval
, 0);
551 for (j
= 0; j
< ncards
; j
++)
553 grub_free (ifaces
[j
].name
);
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
);
567 static grub_command_t cmd_getdhcp
, cmd_bootp
;
570 grub_bootp_init (void)
572 cmd_bootp
= grub_register_command ("net_bootp", grub_cmd_bootp
,
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."));
581 grub_bootp_fini (void)
583 grub_unregister_command (cmd_getdhcp
);
584 grub_unregister_command (cmd_bootp
);