diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 4fdeac3..4d5df64 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -25,20 +25,89 @@ #include #include -static void -parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) +struct grub_dhcp_discover_options + { + grub_uint8_t magic[4]; + struct + { + grub_uint8_t code; + grub_uint8_t len; + grub_uint8_t data; + } GRUB_PACKED message_type; + grub_uint8_t end; + } GRUB_PACKED; + +struct grub_dhcp_request_options + { + grub_uint8_t magic[4]; + struct + { + grub_uint8_t code; + grub_uint8_t len; + grub_uint8_t data; + } GRUB_PACKED message_type; + struct + { + grub_uint8_t type; + grub_uint8_t len; + grub_uint32_t data; + } GRUB_PACKED server_identifier; + struct + { + grub_uint8_t type; + grub_uint8_t len; + grub_uint32_t data; + } GRUB_PACKED requested_ip; + struct + { + grub_uint8_t type; + grub_uint8_t len; + grub_uint8_t data[7]; + } GRUB_PACKED parameter_request; + grub_uint8_t end; + } GRUB_PACKED; + +enum + { + GRUB_DHCP_MESSAGE_UNKNOWN, + GRUB_DHCP_MESSAGE_DISCOVER, + GRUB_DHCP_MESSAGE_OFFER, + GRUB_DHCP_MESSAGE_REQUEST, + GRUB_DHCP_MESSAGE_DECLINE, + GRUB_DHCP_MESSAGE_ACK, + GRUB_DHCP_MESSAGE_NAK, + GRUB_DHCP_MESSAGE_RELEASE, + GRUB_DHCP_MESSAGE_INFORM, + }; + +#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64 +#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y))) + +static const void * +find_dhcp_option (const struct grub_net_bootp_packet *bp, unsigned size, + grub_uint8_t opt_code, grub_uint8_t *opt_len) { const grub_uint8_t *ptr, *ptr0; + grub_uint8_t overload = 0; + int end = 0; + + /* Magic + message type option */ + if (size < sizeof (*bp) + sizeof (grub_uint32_t)) + goto out; - ptr = ptr0 = vend; + ptr = ptr0 = (grub_uint8_t *) (bp + 1); if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3) - return; + goto out; + ptr = ptr + sizeof (grub_uint32_t); - while (ptr - ptr0 < limit) + size -= (sizeof (*bp) + sizeof (grub_uint32_t)); + +again: + while (ptr - ptr0 < size) { grub_uint8_t tagtype; grub_uint8_t taglength; @@ -51,84 +120,75 @@ parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) /* End tag. */ if (tagtype == GRUB_NET_BOOTP_END) - return; + { + end = 1; + break; + } + + if (ptr - ptr0 >= size) + goto out; taglength = *ptr++; - switch (tagtype) - { - case GRUB_NET_BOOTP_NETMASK: - if (taglength == 4) - { - int i; - for (i = 0; i < 32; i++) - if (!(ptr[i / 8] & (1 << (7 - (i % 8))))) - break; - *mask = i; - } - break; + if (ptr + taglength - ptr0 >= size) + goto out; - case GRUB_NET_BOOTP_ROUTER: - if (taglength == 4) - { - grub_net_network_level_netaddress_t target; - grub_net_network_level_address_t gw; - char *rname; - - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base = 0; - target.ipv4.masksize = 0; - gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4)); - rname = grub_xasprintf ("%s:default", name); - if (rname) - grub_net_add_route_gw (rname, target, gw); - grub_free (rname); - } - break; - case GRUB_NET_BOOTP_DNS: - { - int i; - for (i = 0; i < taglength / 4; i++) - { - struct grub_net_network_level_address s; - s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - s.ipv4 = grub_get_unaligned32 (ptr); - s.option = DNS_OPTION_PREFER_IPV4; - grub_net_add_dns_server (&s); - ptr += 4; - } - } - continue; - case GRUB_NET_BOOTP_HOSTNAME: - grub_env_set_net_property (name, "hostname", (const char *) ptr, - taglength); - break; - - case GRUB_NET_BOOTP_DOMAIN: - grub_env_set_net_property (name, "domain", (const char *) ptr, - taglength); - break; - - case GRUB_NET_BOOTP_ROOT_PATH: - grub_env_set_net_property (name, "rootpath", (const char *) ptr, - taglength); - break; - - case GRUB_NET_BOOTP_EXTENSIONS_PATH: - grub_env_set_net_property (name, "extensionspath", (const char *) ptr, - taglength); - break; - - /* If you need any other options please contact GRUB - development team. */ + if (tagtype == opt_code) + { + if (opt_len) + *opt_len = taglength; + return ptr; } + if (tagtype == GRUB_NET_DHCP_OVERLOAD) + overload = *ptr; + ptr += taglength; } -} -#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y))) + /* RFC2131, 4.1, 23ff: + If the options in a DHCP message extend into the 'sname' and 'file' + fields, the 'option overload' option MUST appear in the 'options' + field, with value 1, 2 or 3, as specified in RFC 1533. If the + 'option overload' option is present in the 'options' field, the + options in the 'options' field MUST be terminated by an 'end' option, + and MAY contain one or more 'pad' options to fill the options field. + The options in the 'sname' and 'file' fields (if in use as indicated + by the 'options overload' option) MUST begin with the first octet of + the field, MUST be terminated by an 'end' option, and MUST be + followed by 'pad' options to fill the remainder of the field. Any + individual option in the 'options', 'sname' and 'file' fields MUST be + entirely contained in that field. The options in the 'options' field + MUST be interpreted first, so that any 'option overload' options may + be interpreted. The 'file' field MUST be interpreted next (if the + 'option overload' option indicates that the 'file' field contains + DHCP options), followed by the 'sname' field. + + FIXME: We do not explicitly check for trailing 'pad' options here. + */ + if (end) + { + end = 0; + if (overload & 1U) + { + overload &= ~1U; + ptr = ptr0 = (grub_uint8_t *) &bp->boot_file[0]; + size = sizeof (bp->boot_file); + goto again; + } + + if (overload & 2U) + { + overload &= ~2U; + ptr = ptr0 = (grub_uint8_t *) &bp->server_name[0]; + size = sizeof (bp->server_name); + goto again; + } + } + +out: + return 0; +} struct grub_net_network_level_interface * grub_net_configure_by_dhcp_ack (const char *name, @@ -142,6 +202,10 @@ grub_net_configure_by_dhcp_ack (const char *name, grub_net_link_level_address_t hwaddr; struct grub_net_network_level_interface *inter; int mask = -1; + const grub_uint8_t *opt; + grub_uint8_t opt_len = 0; + const char *boot_file = 0, *server_name = 0; + grub_size_t boot_file_len, server_name_len; addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; addr.ipv4 = bp->your_ip; @@ -157,38 +221,31 @@ grub_net_configure_by_dhcp_ack (const char *name, hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags); -#if 0 - /* This is likely based on misunderstanding. gateway_ip refers to - address of BOOTP relay and should not be used after BOOTP transaction - is complete. - See RFC1542, 3.4 Interpretation of the 'giaddr' field - */ - if (bp->gateway_ip) + + opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_TFTP_SERVER_NAME, &opt_len); + if (opt && opt_len) { - grub_net_network_level_netaddress_t target; - grub_net_network_level_address_t gw; - char *rname; - - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base = bp->server_ip; - target.ipv4.masksize = 32; - gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - gw.ipv4 = bp->gateway_ip; - rname = grub_xasprintf ("%s:gw", name); - if (rname) - grub_net_add_route_gw (rname, target, gw); - grub_free (rname); + server_name = (const char *) opt; + server_name_len = opt_len; + } + else if (size > OFFSET_OF (server_name, bp) && bp->server_name[0]) + { + server_name = bp->server_name; + server_name_len = sizeof (bp->server_name); + } - target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base = bp->gateway_ip; - target.ipv4.masksize = 32; - grub_net_add_route (name, target, inter); + opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_BOOTFILE_NAME, &opt_len); + if (opt && opt_len) + { + boot_file = (const char *) opt; + boot_file_len = opt_len; + } + else if (size > OFFSET_OF (boot_file, bp) && bp->boot_file[0]) + { + boot_file = bp->boot_file; + boot_file_len = sizeof (bp->boot_file); } -#endif - if (size > OFFSET_OF (boot_file, bp)) - grub_env_set_net_property (name, "boot_file", bp->boot_file, - sizeof (bp->boot_file)); if (is_def) grub_net_default_server = 0; if (is_def && !grub_net_default_server && bp->server_ip) @@ -216,40 +273,104 @@ grub_net_configure_by_dhcp_ack (const char *name, ((grub_uint8_t *) &bp->server_ip)[3]); grub_print_error (); } - if (size > OFFSET_OF (server_name, bp) - && bp->server_name[0]) + + if (server_name) { - grub_env_set_net_property (name, "dhcp_server_name", bp->server_name, - sizeof (bp->server_name)); + /* FIXME this is actually TFTP server name; should not we change it? */ + grub_env_set_net_property (name, "dhcp_server_name", server_name, server_name_len); if (is_def && !grub_net_default_server) { - grub_net_default_server = grub_strdup (bp->server_name); + grub_net_default_server = grub_strdup (server_name); grub_print_error (); } if (device && !*device) { - *device = grub_xasprintf ("tftp,%s", bp->server_name); + *device = grub_xasprintf ("tftp,%s", server_name); grub_print_error (); } } - if (size > OFFSET_OF (boot_file, bp) && path) + if (boot_file) { - *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file)); - grub_print_error (); - if (*path) + grub_env_set_net_property (name, "boot_file", boot_file, boot_file_len); + if (path) { - char *slash; - slash = grub_strrchr (*path, '/'); - if (slash) - *slash = 0; - else - **path = 0; + *path = grub_strndup (boot_file, boot_file_len); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } } } - if (size > OFFSET_OF (vendor, bp)) - parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask); + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_NETMASK, &opt_len); + if (opt && opt_len == 4) + { + int i; + for (i = 0; i < 32; i++) + if (!(opt[i / 8] & (1 << (7 - (i % 8))))) + break; + mask = i; + } grub_net_add_ipv4_local (inter, mask); + + /* We do not implement dead gateway detection and the first entry SHOULD + be preferred one */ + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROUTER, &opt_len); + if (opt && opt_len && !(opt_len & 3)) + { + grub_net_network_level_netaddress_t target; + grub_net_network_level_address_t gw; + char *rname; + + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = 0; + target.ipv4.masksize = 0; + gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + gw.ipv4 = grub_get_unaligned32 (opt); + rname = grub_xasprintf ("%s:default", name); + if (rname) + grub_net_add_route_gw (rname, target, gw); + grub_free (rname); + } + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DNS, &opt_len); + if (opt && opt_len && !(opt_len & 3)) + { + int i; + for (i = 0; i < opt_len / 4; i++) + { + struct grub_net_network_level_address s; + + s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + s.ipv4 = grub_get_unaligned32 (opt); + s.option = DNS_OPTION_PREFER_IPV4; + grub_net_add_dns_server (&s); + opt += 4; + } + } + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_HOSTNAME, &opt_len); + if (opt && opt_len) + grub_env_set_net_property (name, "hostname", (const char *) opt, opt_len); + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DOMAIN, &opt_len); + if (opt && opt_len) + grub_env_set_net_property (name, "domain", (const char *) opt, opt_len); + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROOT_PATH, &opt_len); + if (opt && opt_len) + grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len); + + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len); + if (opt && opt_len) + grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len); inter->dhcp_ack = grub_malloc (size); if (inter->dhcp_ack) @@ -263,36 +384,211 @@ 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; + + static struct grub_dhcp_discover_options discover_options = + { + { + GRUB_NET_BOOTP_RFC1048_MAGIC_0, + GRUB_NET_BOOTP_RFC1048_MAGIC_1, + GRUB_NET_BOOTP_RFC1048_MAGIC_2, + GRUB_NET_BOOTP_RFC1048_MAGIC_3, + }, + { + GRUB_NET_DHCP_MESSAGE_TYPE, + sizeof (discover_options.message_type.data), + GRUB_DHCP_MESSAGE_DISCOVER, + }, + GRUB_NET_BOOTP_END, + }; + + static struct grub_dhcp_request_options request_options = + { + { + GRUB_NET_BOOTP_RFC1048_MAGIC_0, + GRUB_NET_BOOTP_RFC1048_MAGIC_1, + GRUB_NET_BOOTP_RFC1048_MAGIC_2, + GRUB_NET_BOOTP_RFC1048_MAGIC_3, + }, + { + GRUB_NET_DHCP_MESSAGE_TYPE, + sizeof (request_options.message_type.data), + GRUB_DHCP_MESSAGE_REQUEST, + }, + { + GRUB_NET_DHCP_SERVER_IDENTIFIER, + sizeof (request_options.server_identifier.data), + 0, + }, + { + GRUB_NET_DHCP_REQUESTED_IP_ADDRESS, + sizeof (request_options.requested_ip.data), + 0, + }, + { + GRUB_NET_DHCP_PARAMETER_REQUEST_LIST, + sizeof (request_options.parameter_request.data), + { + GRUB_NET_BOOTP_NETMASK, + GRUB_NET_BOOTP_ROUTER, + GRUB_NET_BOOTP_DNS, + GRUB_NET_BOOTP_DOMAIN, + GRUB_NET_BOOTP_HOSTNAME, + GRUB_NET_BOOTP_ROOT_PATH, + GRUB_NET_BOOTP_EXTENSIONS_PATH, + }, + }, + GRUB_NET_BOOTP_END, + }; + + COMPILE_TIME_ASSERT (sizeof (discover_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE); + COMPILE_TIME_ASSERT (sizeof (request_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE); + + nb = grub_netbuff_alloc (sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128); + if (!nb) + return grub_errno; + + err = grub_netbuff_reserve (nb, sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128); + if (err) + goto out; + + err = grub_netbuff_push (nb, GRUB_BOOTP_MAX_OPTIONS_SIZE); + if (err) + goto out; + + grub_memset (nb->data, 0, GRUB_BOOTP_MAX_OPTIONS_SIZE); + if (!iface->srv_id) + { + grub_memcpy (nb->data, &discover_options, sizeof (discover_options)); + } + else + { + struct grub_dhcp_request_options *ro = (struct grub_dhcp_request_options *) nb->data; + + grub_memcpy (nb->data, &request_options, sizeof (request_options)); + grub_set_unaligned32 (&ro->server_identifier.data, iface->srv_id); + grub_set_unaligned32 (&ro->requested_ip.data, iface->my_ip); + } + + 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); + if (!iface->srv_id) + iface->xid = pack->ident = grub_cpu_to_be32 (t); + else + pack->ident = iface->xid; + + grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6); + + 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) + struct grub_net_network_level_interface *iface) { char *name; struct grub_net_network_level_interface *inf; - - name = grub_xasprintf ("%s:dhcp", card->name); - if (!name) + struct grub_net_card *card = iface->card; + const struct grub_net_bootp_packet *bp = (const struct grub_net_bootp_packet *) nb->data; + grub_size_t size = nb->tail - nb->data; + const grub_uint8_t *opt; + grub_uint8_t optlen = 0, type; + grub_uint32_t srv_id = 0; + + opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_MESSAGE_TYPE, &optlen); + if (opt && optlen == 1) + type = *opt; + else + type = GRUB_DHCP_MESSAGE_UNKNOWN; + + opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_SERVER_IDENTIFIER, &optlen); + if (opt && optlen == sizeof (srv_id)) + srv_id = grub_get_unaligned32 (opt); + + /* If we received BOOTP reply or DHCPACK, proceed with configuration. Otherwise + store offered address and server id for later processing of DHCPACK */ + if ((!iface->srv_id && type == GRUB_DHCP_MESSAGE_UNKNOWN) + || (iface->srv_id && type == GRUB_DHCP_MESSAGE_ACK + && bp->ident == iface->xid + && srv_id == iface->srv_id)) { - grub_print_error (); - return; + name = grub_xasprintf ("%s:dhcp", card->name); + if (!name) + { + grub_print_error (); + return; + } + grub_net_configure_by_dhcp_ack (name, card, 0, bp, size, 0, 0, 0); + grub_free (name); + if (grub_errno) + grub_print_error (); + else + { + FOR_NET_NETWORK_LEVEL_INTERFACES(inf) + if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0 + && grub_memcmp (inf->name + grub_strlen (card->name), + ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0) + { + grub_net_network_level_interface_unregister (inf); + break; + } + } } - grub_net_configure_by_dhcp_ack (name, card, - 0, (const struct grub_net_bootp_packet *) nb->data, - (nb->tail - nb->data), 0, 0, 0); - grub_free (name); - if (grub_errno) - grub_print_error (); - else + else if (!iface->srv_id && type == GRUB_DHCP_MESSAGE_OFFER) { - FOR_NET_NETWORK_LEVEL_INTERFACES(inf) - if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0 - && grub_memcmp (inf->name + grub_strlen (card->name), - ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0) - { - grub_net_network_level_interface_unregister (inf); - break; - } + iface->srv_id = srv_id; + iface->my_ip = bp->your_ip; } + else if (iface->srv_id && type == GRUB_DHCP_MESSAGE_NAK + && bp->ident == iface->xid + && srv_id == iface->srv_id) + iface->xid = iface->srv_id = iface->my_ip = 0; } static char @@ -308,8 +604,8 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), int argc, char **args) { struct grub_net_network_level_interface *inter; - int num; - grub_uint8_t *ptr; + unsigned num; + const grub_uint8_t *ptr; grub_uint8_t taglength; if (argc < 4) @@ -327,44 +623,27 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), if (!inter->dhcp_ack) return grub_error (GRUB_ERR_IO, N_("no DHCP info found")); - if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack)) - return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); - - num = grub_strtoul (args[2], 0, 0); - if (grub_errno) - return grub_errno; - ptr = inter->dhcp_ack->vendor; - if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0 + /* This duplicates check in find_dhcp_option to preserve previous error return */ + if (inter->dhcp_acklen < OFFSET_OF (vendor, inter->dhcp_ack) + sizeof (grub_uint32_t) + || ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0 || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1 || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2 || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3) return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); - ptr = ptr + sizeof (grub_uint32_t); - while (1) - { - grub_uint8_t tagtype; - - if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen) - return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); - tagtype = *ptr++; + num = grub_strtoul (args[2], 0, 0); + if (grub_errno) + return grub_errno; - /* Pad tag. */ - if (tagtype == 0) - continue; + /* Exclude PAD (0) and END (255) option codes */ + if (num == 0 || num > 254) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid DHCP option code")); - /* End tag. */ - if (tagtype == 0xff) - return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); - - taglength = *ptr++; - - if (tagtype == num) - break; - ptr += taglength; - } + ptr = find_dhcp_option (inter->dhcp_ack, inter->dhcp_acklen, num, &taglength); + if (!ptr) + return grub_error (GRUB_ERR_IO, N_("no DHCP option %u found"), num); if (grub_strcmp (args[3], "string") == 0) { @@ -481,71 +760,12 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), int done = 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; - if (!ifaces[j].prev) continue; - nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128); - if (!nb) - { - grub_netbuff_free (nb); - return grub_errno; - } - err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128); - if (err) - { - grub_netbuff_free (nb); - return err; - } - 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); - 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); + done = 1; - err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb, - GRUB_NET_IP_UDP); - grub_netbuff_free (nb); + err = send_dhcp_packet (&ifaces[j]); if (err) return err; } diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 8c56baa..162c70d 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -278,7 +278,7 @@ handle_dgram (struct grub_net_buff *nb, && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr, sizeof (inf->hwaddress.mac)) == 0) { - grub_net_process_dhcp (nb, inf->card); + grub_net_process_dhcp (nb, inf); grub_netbuff_free (nb); return GRUB_ERR_NONE; } diff --git a/include/grub/net.h b/include/grub/net.h index 538baa3..073a33b 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -279,6 +279,9 @@ struct grub_net_network_level_interface grub_net_interface_flags_t flags; struct grub_net_bootp_packet *dhcp_ack; grub_size_t dhcp_acklen; + grub_uint32_t xid; /* DHCPv4 transation id */ + grub_uint32_t srv_id; /* DHCPv4 server_identifier */ + grub_uint32_t my_ip; /* DHCPv4 offered IP address */ void *data; }; @@ -433,6 +436,13 @@ enum GRUB_NET_BOOTP_DOMAIN = 0x0f, GRUB_NET_BOOTP_ROOT_PATH = 0x11, GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12, + GRUB_NET_DHCP_REQUESTED_IP_ADDRESS = 50, + GRUB_NET_DHCP_OVERLOAD = 52, + GRUB_NET_DHCP_MESSAGE_TYPE = 53, + GRUB_NET_DHCP_SERVER_IDENTIFIER = 54, + GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55, + GRUB_NET_DHCP_TFTP_SERVER_NAME = 66, + GRUB_NET_DHCP_BOOTFILE_NAME = 67, GRUB_NET_BOOTP_END = 0xff }; @@ -450,7 +460,7 @@ grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, void grub_net_process_dhcp (struct grub_net_buff *nb, - struct grub_net_card *card); + struct grub_net_network_level_interface *iface); int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,