net/dhcp: Refactor DHCP packet transmission into separate function

In contrast to BOOTP, DHCP uses a 4-way handshake, so requires to send
packets more often.

Refactor the generation and sending of the BOOTREQUEST packet into
a separate function, so that future code can more easily reuse this.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Andrei Borzenkov 2019-03-07 15:14:10 +00:00 committed by Vincent Batts
parent 6c0969eed5
commit f8da4f6154
1 changed files with 116 additions and 87 deletions

View File

@ -49,6 +49,8 @@ enum
GRUB_DHCP_OPT_OVERLOAD_SNAME = 2,
};
#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
static const void *
find_dhcp_option (const struct grub_net_bootp_packet *bp, grub_size_t size,
grub_uint8_t opt_code, grub_uint8_t *opt_len)
@ -340,6 +342,112 @@ grub_net_configure_by_dhcp_ack (const char *name,
return inter;
}
static grub_err_t
send_dhcp_packet (struct grub_net_network_level_interface *iface)
{
grub_err_t err;
struct grub_net_bootp_packet *pack;
struct grub_datetime date;
grub_int32_t t = 0;
struct grub_net_buff *nb;
struct udphdr *udph;
grub_net_network_level_address_t target;
grub_net_link_level_address_t ll_target;
grub_uint8_t *offset;
nb = grub_netbuff_alloc (sizeof (*pack) + sizeof(dhcp_option_header)
+ sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
if (!nb)
return grub_errno;
err = grub_netbuff_reserve (nb, sizeof (*pack) + sizeof(dhcp_option_header)
+ sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
if (err)
goto out;
err = grub_netbuff_push (nb, sizeof(dhcp_option_header)
+ sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE);
if (err)
goto out;
grub_memset (nb->data, 0, sizeof(dhcp_option_header)
+ sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime)
+ GRUB_BOOTP_MAX_OPTIONS_SIZE);
err = grub_netbuff_push (nb, sizeof (*pack));
if (err)
goto out;
pack = (void *) nb->data;
grub_memset (pack, 0, sizeof (*pack));
pack->opcode = 1;
pack->hw_type = 1;
pack->hw_len = 6;
err = grub_get_datetime (&date);
if (err || !grub_datetime2unixtime (&date, &t))
{
grub_errno = GRUB_ERR_NONE;
t = 0;
}
pack->seconds = grub_cpu_to_be16 (t);
pack->ident = grub_cpu_to_be32 (t);
grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
offset = (grub_uint8_t *)&pack->vendor;
grub_memcpy (offset, dhcp_option_header, sizeof(dhcp_option_header));
offset += sizeof(dhcp_option_header);
grub_memcpy (offset, grub_dhcpdiscover, sizeof(grub_dhcpdiscover));
offset += sizeof(grub_dhcpdiscover);
grub_memcpy (offset, grub_userclass, sizeof(grub_userclass));
offset += sizeof(grub_userclass);
grub_memcpy (offset, grub_dhcptime, sizeof(grub_dhcptime));
/* insert Client System Architecture (option 93) */
offset += sizeof(grub_dhcptime);
offset[0] = 93;
offset[1] = 2;
offset[2] = (GRUB_NET_BOOTP_ARCH >> 8);
offset[3] = (GRUB_NET_BOOTP_ARCH & 0xFF);
/* option terminator */
offset[4] = 255;
grub_netbuff_push (nb, sizeof (*udph));
udph = (struct udphdr *) nb->data;
udph->src = grub_cpu_to_be16_compile_time (68);
udph->dst = grub_cpu_to_be16_compile_time (67);
udph->chksum = 0;
udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
target.ipv4 = 0xffffffff;
err = grub_net_link_layer_resolve (iface, &target, &ll_target);
if (err)
goto out;
udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
&iface->address,
&target);
err = grub_net_send_ip_packet (iface, &target, &ll_target, nb,
GRUB_NET_IP_UDP);
out:
grub_netbuff_free (nb);
return err;
}
void
grub_net_process_dhcp (struct grub_net_buff *nb,
struct grub_net_card *card)
@ -496,6 +604,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
unsigned j = 0;
int interval;
grub_err_t err;
unsigned i;
FOR_NET_CARDS (card)
{
@ -524,7 +633,6 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
card->num_ifaces++;
if (!ifaces[j].name)
{
unsigned i;
for (i = 0; i < j; i++)
grub_free (ifaces[i].name);
grub_free (ifaces);
@ -542,100 +650,21 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
ifaces[0].prev = &grub_net_network_level_interfaces;
for (interval = 200; interval < 10000; interval *= 2)
{
int done = 0;
int need_poll = 0;
for (j = 0; j < ncards; j++)
{
struct grub_net_bootp_packet *pack;
struct grub_datetime date;
grub_int32_t t = 0;
struct grub_net_buff *nb;
struct udphdr *udph;
grub_net_network_level_address_t target;
grub_net_link_level_address_t ll_target;
grub_uint8_t *offset;
if (!ifaces[j].prev)
continue;
nb = grub_netbuff_alloc (sizeof (*pack) + sizeof(dhcp_option_header)
+ sizeof(grub_userclass)
+ sizeof(grub_dhcpdiscover)
+ sizeof(grub_dhcptime) + 64 + 128);
if (!nb)
{
grub_netbuff_free (nb);
return grub_errno;
}
err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
err = send_dhcp_packet (&ifaces[j]);
if (err)
{
grub_netbuff_free (nb);
return err;
grub_print_error ();
continue;
}
err = grub_netbuff_push (nb, sizeof (*pack) + 64);
if (err)
{
grub_netbuff_free (nb);
return err;
}
pack = (void *) nb->data;
done = 1;
grub_memset (pack, 0, sizeof (*pack) + 64);
pack->opcode = 1;
pack->hw_type = 1;
pack->hw_len = 6;
err = grub_get_datetime (&date);
if (err || !grub_datetime2unixtime (&date, &t))
{
grub_errno = GRUB_ERR_NONE;
t = 0;
}
pack->ident = grub_cpu_to_be32 (t);
pack->seconds = grub_cpu_to_be16 (t);
grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6);
offset = (grub_uint8_t *)&pack->vendor;
grub_memcpy (offset, dhcp_option_header, sizeof(dhcp_option_header));
offset += sizeof(dhcp_option_header);
grub_memcpy (offset, grub_dhcpdiscover, sizeof(grub_dhcpdiscover));
offset += sizeof(grub_dhcpdiscover);
grub_memcpy (offset, grub_userclass, sizeof(grub_userclass));
offset += sizeof(grub_userclass);
grub_memcpy (offset, grub_dhcptime, sizeof(grub_dhcptime));
/* insert Client System Architecture (option 93) */
offset += sizeof(grub_dhcptime);
offset[0] = 93;
offset[1] = 2;
offset[2] = (GRUB_NET_BOOTP_ARCH >> 8);
offset[3] = (GRUB_NET_BOOTP_ARCH & 0xFF);
/* option terminator */
offset[4] = 255;
grub_netbuff_push (nb, sizeof (*udph));
udph = (struct udphdr *) nb->data;
udph->src = grub_cpu_to_be16_compile_time (68);
udph->dst = grub_cpu_to_be16_compile_time (67);
udph->chksum = 0;
udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
target.ipv4 = 0xffffffff;
err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
if (err)
return err;
udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
&ifaces[j].address,
&target);
err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
GRUB_NET_IP_UDP);
grub_netbuff_free (nb);
if (err)
return err;
need_poll = 1;
}
if (!done)
if (!need_poll)
break;
grub_net_poll_cards (interval, 0);
}