From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1aKpSv-0005ER-S7 for mharc-grub-devel@gnu.org; Sun, 17 Jan 2016 10:42:05 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47892) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aKpSq-0005ED-TW for grub-devel@gnu.org; Sun, 17 Jan 2016 10:42:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aKpSn-00083A-G2 for grub-devel@gnu.org; Sun, 17 Jan 2016 10:42:00 -0500 Received: from mail-lb0-x233.google.com ([2a00:1450:4010:c04::233]:33358) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aKpSm-00082x-TU for grub-devel@gnu.org; Sun, 17 Jan 2016 10:41:57 -0500 Received: by mail-lb0-x233.google.com with SMTP id x4so79435618lbm.0 for ; Sun, 17 Jan 2016 07:41:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-type; bh=DIvABxsgaduSupPFidSvRbpSKDqjnWkufiYwgGdWCu0=; b=n3PG8ltS0EQnvSmEEGW6bvnqtUMjZz1t4ZXXs58E8yh1DksCh/tmCIy5g3pyUpE0b/ E/mwGAPQsF9MTvMJ8xBwwEvtS1z4t+bkaeXewjJjFYnoHIA9OAhg/YrU8ULfAdM5BZae nawXBDvQTi2ZgnleId3IVFoZh/OPHQEFcD+S5I5fqtR+vDsS6540fScUHglk0m1rQw5+ PQiPCfpR1P8vc0WMPDNOlyfQmymNgcmYA+H/bHfXqGIhE7pBt/zsbL0TngPwTaSAKZXu ie4GpeQ1xBeDSOyCowHfFF7P6/1ToDHKLY1SnGb2TrIOPUGpJqFfq5rbIQZQoGCrFQlq KLMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-type; bh=DIvABxsgaduSupPFidSvRbpSKDqjnWkufiYwgGdWCu0=; b=E8XLdpT/3ZSmFvtInS2U4oHqKZ9RHkr84SSwqDLBq95mLeaC3UvYhgdpdfTV4xcYWo lBnp2vXPmwnARPuX6TcoaZ60Xjx7WTMgWk7fbQDv7G/Q3ZYTa21jq/xaLwH9gWGt2Zft 2aX5xcsU3Zsqy1bT4TxWnmaHUqZE4dm7Fs6wA0PgrQYizR06qsocusgXg9XiUwEw80AU d1slIvBqOo4SJSSGf5ple7F3/m8HpKfHA7ug9a3hgRcpvXrw/bwK/hwedCxPDd4znVZQ XGJS5rxu6yIpoyMZe4cl0l3oOzSA/egSGS19AF149WEwPx2AXKaajjsakPNadrx/e57y fwRw== X-Gm-Message-State: ALoCoQmS6m+9l48H4yiHHVDdQFtN0qd9Wz0iyr1lxYhzVvD62fP36kLQiyxiLqh/5BlGwL6RftJFGenRJ8q+0BRxSIkX7fWtMw== X-Received: by 10.112.126.164 with SMTP id mz4mr6656068lbb.70.1453045315791; Sun, 17 Jan 2016 07:41:55 -0800 (PST) Received: from [192.168.1.41] (ppp91-76-25-247.pppoe.mtu-net.ru. [91.76.25.247]) by smtp.gmail.com with ESMTPSA id h187sm2637519lfg.18.2016.01.17.07.41.54 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 17 Jan 2016 07:41:54 -0800 (PST) Subject: [PATCH][WiP] native DHCPv4 support in net_bootp To: "Rivard, Matthew T" , The development of GNU GRUB References: <55FB8A5F.6040404@gmail.com> <561DF29D.70702@gmail.com> <561E990D.90205@gmail.com> <561EB696.5070909@gmail.com> From: Andrei Borzenkov X-Enigmail-Draft-Status: N1110 Message-ID: <569BB641.4040609@gmail.com> Date: Sun, 17 Jan 2016 18:41:53 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.5.1 MIME-Version: 1.0 In-Reply-To: <561EB696.5070909@gmail.com> Content-Type: multipart/mixed; boundary="------------090107090906080502010409" X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:4010:c04::233 X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 17 Jan 2016 15:42:04 -0000 This is a multi-part message in MIME format. --------------090107090906080502010409 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit 14.10.2015 23:09, Andrei Borzenkov пишет: > 14.10.2015 21:09, Rivard, Matthew T пишет: >> Just going by these definitions of bootp and dhcp here: >> https://technet.microsoft.com/en-us/library/cc781243%28v=ws.10%29.aspx >> >> The net_bootp only works with a bootp enabled scope on the dhcp server >> after chaining from iPXE to grub2. Without a "bootp" scope setup on >> the dhcp, calling net_bootp on the adapter fails to get an IP address. >> >> So, I guess I could more clearly word that the bootp protocol works >> when calling net_bootp, but standard dhcp isn't. >> > > Hmm ... you are right; we are actually doing BOOTP here, not DHCP. > This patch adds support for native DHCPv4 and removes requirement for BOOTP compatibility support in DHCP server. This is work in progress, but this works for me in test environment. Patch changes net_bootp to implement full DHCP transaction. it still /should/ work with BOOTP pure server (untested, I do not have one). It also re-implements option processing to support overloaded fields and consolidates it in one place. What is currently not implemented - per interface, per transaction stage retransmit timer. Will be done. - DHCP server selection. We take first DHCPOFFER or BOOTPREPLY. No plans to implement. - DHCP option concatenation (RFC3396). I do not expect to hit it in real life; could be implemented relatively easy if needed. - client identifier (RFC6842). I do not expect to hit it in real life; could be added easily if needed. --------------090107090906080502010409 Content-Type: text/x-patch; name="grub-dhcp.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="grub-dhcp.diff" 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 =20 -static void -parse_dhcp_vendor (const char *name, const void *vend, int limit, int *m= ask) +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 =3D 0; + int end =3D 0; + + /* Magic + message type option */ + if (size < sizeof (*bp) + sizeof (grub_uint32_t)) + goto out; =20 - ptr =3D ptr0 =3D vend; + ptr =3D ptr0 =3D (grub_uint8_t *) (bp + 1); =20 if (ptr[0] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_0 || ptr[1] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_1 || ptr[2] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_2 || ptr[3] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_3) - return; + goto out; + ptr =3D ptr + sizeof (grub_uint32_t); - while (ptr - ptr0 < limit) + size -=3D (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 *ven= d, int limit, int *mask) =20 /* End tag. */ if (tagtype =3D=3D GRUB_NET_BOOTP_END) - return; + { + end =3D 1; + break; + } + + if (ptr - ptr0 >=3D size) + goto out; =20 taglength =3D *ptr++; =20 - switch (tagtype) - { - case GRUB_NET_BOOTP_NETMASK: - if (taglength =3D=3D 4) - { - int i; - for (i =3D 0; i < 32; i++) - if (!(ptr[i / 8] & (1 << (7 - (i % 8))))) - break; - *mask =3D i; - } - break; + if (ptr + taglength - ptr0 >=3D size) + goto out; =20 - case GRUB_NET_BOOTP_ROUTER: - if (taglength =3D=3D 4) - { - grub_net_network_level_netaddress_t target; - grub_net_network_level_address_t gw; - char *rname; - =20 - target.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base =3D 0; - target.ipv4.masksize =3D 0; - gw.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4)); - rname =3D 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 =3D 0; i < taglength / 4; i++) - { - struct grub_net_network_level_address s; - s.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - s.ipv4 =3D grub_get_unaligned32 (ptr); - s.option =3D DNS_OPTION_PREFER_IPV4; - grub_net_add_dns_server (&s); - ptr +=3D 4; - } - } - continue; - case GRUB_NET_BOOTP_HOSTNAME: - grub_env_set_net_property (name, "hostname", (const char *) pt= r, - 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 *) pt= r, - 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 =3D=3D opt_code) + { + if (opt_len) + *opt_len =3D taglength; + return ptr; } =20 + if (tagtype =3D=3D GRUB_NET_DHCP_OVERLOAD) + overload =3D *ptr; + ptr +=3D taglength; } -} =20 -#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 'fil= e' + 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' opt= ion, + and MAY contain one or more 'pad' options to fill the options fie= ld. + The options in the 'sname' and 'file' fields (if in use as indica= ted + 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. An= y + individual option in the 'options', 'sname' and 'file' fields MUS= T be + entirely contained in that field. The options in the 'options' f= ield + MUST be interpreted first, so that any 'option overload' options = may + be interpreted. The 'file' field MUST be interpreted next (if th= e + '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= =2E + */ + if (end) + { + end =3D 0; + if (overload & 1U) + { + overload &=3D ~1U; + ptr =3D ptr0 =3D (grub_uint8_t *) &bp->boot_file[0]; + size =3D sizeof (bp->boot_file); + goto again; + } + + if (overload & 2U) + { + overload &=3D ~2U; + ptr =3D ptr0 =3D (grub_uint8_t *) &bp->server_name[0]; + size =3D sizeof (bp->server_name); + goto again; + } + } + +out: + return 0; +} =20 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 =3D -1; + const grub_uint8_t *opt; + grub_uint8_t opt_len =3D 0; + const char *boot_file =3D 0, *server_name =3D 0; + grub_size_t boot_file_len, server_name_len; =20 addr.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; addr.ipv4 =3D bp->your_ip; @@ -157,38 +221,31 @@ grub_net_configure_by_dhcp_ack (const char *name, hwaddr.type =3D GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; =20 inter =3D 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 transacti= on - is complete. - See RFC1542, 3.4 Interpretation of the 'giaddr' field - */ - if (bp->gateway_ip) + + opt =3D find_dhcp_option (bp, size, GRUB_NET_DHCP_TFTP_SERVER_NAME, &o= pt_len); + if (opt && opt_len) { - grub_net_network_level_netaddress_t target; - grub_net_network_level_address_t gw; - char *rname; - =20 - target.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base =3D bp->server_ip; - target.ipv4.masksize =3D 32; - gw.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - gw.ipv4 =3D bp->gateway_ip; - rname =3D grub_xasprintf ("%s:gw", name); - if (rname) - grub_net_add_route_gw (rname, target, gw); - grub_free (rname); + server_name =3D (const char *) opt; + server_name_len =3D opt_len; + } + else if (size > OFFSET_OF (server_name, bp) && bp->server_name[0]) + { + server_name =3D bp->server_name; + server_name_len =3D sizeof (bp->server_name); + } =20 - target.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4.base =3D bp->gateway_ip; - target.ipv4.masksize =3D 32; - grub_net_add_route (name, target, inter); + opt =3D find_dhcp_option (bp, size, GRUB_NET_DHCP_BOOTFILE_NAME, &opt_= len); + if (opt && opt_len) + { + boot_file =3D (const char *) opt; + boot_file_len =3D opt_len; + } + else if (size > OFFSET_OF (boot_file, bp) && bp->boot_file[0]) + { + boot_file =3D bp->boot_file; + boot_file_len =3D sizeof (bp->boot_file); } -#endif =20 - 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 =3D 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_na= me, - sizeof (bp->server_name)); + /* FIXME this is actually TFTP server name; should not we change i= t? */ + 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 =3D grub_strdup (bp->server_name); + grub_net_default_server =3D grub_strdup (server_name); grub_print_error (); } if (device && !*device) { - *device =3D grub_xasprintf ("tftp,%s", bp->server_name); + *device =3D grub_xasprintf ("tftp,%s", server_name); grub_print_error (); } } =20 - if (size > OFFSET_OF (boot_file, bp) && path) + if (boot_file) { - *path =3D 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 =3D grub_strrchr (*path, '/'); - if (slash) - *slash =3D 0; - else - **path =3D 0; + *path =3D grub_strndup (boot_file, boot_file_len); + grub_print_error (); + if (*path) + { + char *slash; + slash =3D grub_strrchr (*path, '/'); + if (slash) + *slash =3D 0; + else + **path =3D 0; + } } } - if (size > OFFSET_OF (vendor, bp)) - parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp),= &mask); + + opt =3D find_dhcp_option (bp, size, GRUB_NET_BOOTP_NETMASK, &opt_len);= + if (opt && opt_len =3D=3D 4) + { + int i; + for (i =3D 0; i < 32; i++) + if (!(opt[i / 8] & (1 << (7 - (i % 8))))) + break; + mask =3D i; + } grub_net_add_ipv4_local (inter, mask); + + /* We do not implement dead gateway detection and the first entry SHOU= LD + be preferred one */ + opt =3D 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 =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base =3D 0; + target.ipv4.masksize =3D 0; + gw.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + gw.ipv4 =3D grub_get_unaligned32 (opt); + rname =3D grub_xasprintf ("%s:default", name); + if (rname) + grub_net_add_route_gw (rname, target, gw); + grub_free (rname); + } + + opt =3D find_dhcp_option (bp, size, GRUB_NET_BOOTP_DNS, &opt_len); + if (opt && opt_len && !(opt_len & 3)) + { + int i; + for (i =3D 0; i < opt_len / 4; i++) + { + struct grub_net_network_level_address s; + + s.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + s.ipv4 =3D grub_get_unaligned32 (opt); + s.option =3D DNS_OPTION_PREFER_IPV4; + grub_net_add_dns_server (&s); + opt +=3D 4; + } + } + + opt =3D 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 =3D 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_l= en); + + opt =3D 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 =3D find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &o= pt_len); + if (opt && opt_len) + grub_env_set_net_property (name, "extensionspath", (const char *) op= t, opt_len); =20 inter->dhcp_ack =3D grub_malloc (size); if (inter->dhcp_ack) @@ -263,36 +384,211 @@ grub_net_configure_by_dhcp_ack (const char *name, return inter; } =20 +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 =3D 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 =3D + { + { + 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 =3D + { + { + 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) <=3D GRUB_BOOTP_MAX_OPT= IONS_SIZE); + COMPILE_TIME_ASSERT (sizeof (request_options) <=3D GRUB_BOOTP_MAX_OPTI= ONS_SIZE); + + nb =3D grub_netbuff_alloc (sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZ= E + 128); + if (!nb) + return grub_errno; + + err =3D grub_netbuff_reserve (nb, sizeof (*pack) + GRUB_BOOTP_MAX_OPTI= ONS_SIZE + 128); + if (err) + goto out; + + err =3D 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 =3D (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 =3D grub_netbuff_push (nb, sizeof (*pack)); + if (err) + goto out; + + pack =3D (void *) nb->data; + grub_memset (pack, 0, sizeof (*pack)); + pack->opcode =3D 1; + pack->hw_type =3D 1; + pack->hw_len =3D 6; + err =3D grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno =3D GRUB_ERR_NONE; + t =3D 0; + } + pack->seconds =3D grub_cpu_to_be16 (t); + if (!iface->srv_id) + iface->xid =3D pack->ident =3D grub_cpu_to_be32 (t); + else + pack->ident =3D iface->xid; + + grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6); + + grub_netbuff_push (nb, sizeof (*udph)); + + udph =3D (struct udphdr *) nb->data; + udph->src =3D grub_cpu_to_be16_compile_time (68); + udph->dst =3D grub_cpu_to_be16_compile_time (67); + udph->chksum =3D 0; + udph->len =3D grub_cpu_to_be16 (nb->tail - nb->data); + target.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4 =3D 0xffffffff; + err =3D grub_net_link_layer_resolve (iface, &target, &ll_target); + if (err) + goto out; + + udph->chksum =3D grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &iface->address, + &target); + + err =3D 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 =3D grub_xasprintf ("%s:dhcp", card->name); - if (!name) + struct grub_net_card *card =3D iface->card; + const struct grub_net_bootp_packet *bp =3D (const struct grub_net_boot= p_packet *) nb->data; + grub_size_t size =3D nb->tail - nb->data; + const grub_uint8_t *opt; + grub_uint8_t optlen =3D 0, type; + grub_uint32_t srv_id =3D 0; + + opt =3D find_dhcp_option (bp, size, GRUB_NET_DHCP_MESSAGE_TYPE, &optle= n); + if (opt && optlen =3D=3D 1) + type =3D *opt; + else + type =3D GRUB_DHCP_MESSAGE_UNKNOWN; + + opt =3D find_dhcp_option (bp, size, GRUB_NET_DHCP_SERVER_IDENTIFIER, &= optlen); + if (opt && optlen =3D=3D sizeof (srv_id)) + srv_id =3D 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 =3D=3D GRUB_DHCP_MESSAGE_UNKNOWN) + || (iface->srv_id && type =3D=3D GRUB_DHCP_MESSAGE_ACK + && bp->ident =3D=3D iface->xid + && srv_id =3D=3D iface->srv_id)) { - grub_print_error (); - return; + name =3D 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)) =3D= =3D 0 + && grub_memcmp (inf->name + grub_strlen (card->name), + ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) =3D=3D 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 =3D=3D GRUB_DHCP_MESSAGE_OFFER) { - FOR_NET_NETWORK_LEVEL_INTERFACES(inf) - if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) =3D=3D= 0 - && grub_memcmp (inf->name + grub_strlen (card->name), - ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) =3D=3D 0) - { - grub_net_network_level_interface_unregister (inf); - break; - } + iface->srv_id =3D srv_id; + iface->my_ip =3D bp->your_ip; } + else if (iface->srv_id && type =3D=3D GRUB_DHCP_MESSAGE_NAK + && bp->ident =3D=3D iface->xid + && srv_id =3D=3D iface->srv_id) + iface->xid =3D iface->srv_id =3D iface->my_ip =3D 0; } =20 static char @@ -308,8 +604,8 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribut= e__ ((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; =20 if (argc < 4) @@ -327,44 +623,27 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attrib= ute__ ((unused)), if (!inter->dhcp_ack) return grub_error (GRUB_ERR_IO, N_("no DHCP info found")); =20 - if (inter->dhcp_acklen <=3D OFFSET_OF (vendor, inter->dhcp_ack)) - return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); - - num =3D grub_strtoul (args[2], 0, 0); - if (grub_errno) - return grub_errno; - ptr =3D inter->dhcp_ack->vendor; =20 - if (ptr[0] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_0 + /* This duplicates check in find_dhcp_option to preserve previous erro= r return */ + if (inter->dhcp_acklen < OFFSET_OF (vendor, inter->dhcp_ack) + sizeof = (grub_uint32_t) + || ptr[0] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_0 || ptr[1] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_1 || ptr[2] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_2 || ptr[3] !=3D GRUB_NET_BOOTP_RFC1048_MAGIC_3) return grub_error (GRUB_ERR_IO, N_("no DHCP options found")); - ptr =3D ptr + sizeof (grub_uint32_t); - while (1) - { - grub_uint8_t tagtype; - - if (ptr >=3D ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_ackl= en) - return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); =20 - tagtype =3D *ptr++; + num =3D grub_strtoul (args[2], 0, 0); + if (grub_errno) + return grub_errno; =20 - /* Pad tag. */ - if (tagtype =3D=3D 0) - continue; + /* Exclude PAD (0) and END (255) option codes */ + if (num =3D=3D 0 || num > 254) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid DHCP option co= de")); =20 - /* End tag. */ - if (tagtype =3D=3D 0xff) - return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num); - - taglength =3D *ptr++; -=09 - if (tagtype =3D=3D num) - break; - ptr +=3D taglength; - } + ptr =3D find_dhcp_option (inter->dhcp_ack, inter->dhcp_acklen, num, &t= aglength); + if (!ptr) + return grub_error (GRUB_ERR_IO, N_("no DHCP option %u found"), num);= =20 if (grub_strcmp (args[3], "string") =3D=3D 0) { @@ -481,71 +760,12 @@ grub_cmd_bootp (struct grub_command *cmd __attribut= e__ ((unused)), int done =3D 0; for (j =3D 0; j < ncards; j++) { - struct grub_net_bootp_packet *pack; - struct grub_datetime date; - grub_int32_t t =3D 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 =3D grub_netbuff_alloc (sizeof (*pack) + 64 + 128); - if (!nb) - { - grub_netbuff_free (nb); - return grub_errno; - } - err =3D grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128); - if (err) - { - grub_netbuff_free (nb); - return err; - } - err =3D grub_netbuff_push (nb, sizeof (*pack) + 64); - if (err) - { - grub_netbuff_free (nb); - return err; - } - pack =3D (void *) nb->data; - done =3D 1; - grub_memset (pack, 0, sizeof (*pack) + 64); - pack->opcode =3D 1; - pack->hw_type =3D 1; - pack->hw_len =3D 6; - err =3D grub_get_datetime (&date); - if (err || !grub_datetime2unixtime (&date, &t)) - { - grub_errno =3D GRUB_ERR_NONE; - t =3D 0; - } - pack->ident =3D grub_cpu_to_be32 (t); - pack->seconds =3D grub_cpu_to_be16 (t); - - grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6);=20 =20 - grub_netbuff_push (nb, sizeof (*udph)); - - udph =3D (struct udphdr *) nb->data; - udph->src =3D grub_cpu_to_be16_compile_time (68); - udph->dst =3D grub_cpu_to_be16_compile_time (67); - udph->chksum =3D 0; - udph->len =3D grub_cpu_to_be16 (nb->tail - nb->data); - target.type =3D GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target.ipv4 =3D 0xffffffff; - err =3D grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target)= ; - if (err) - return err; - - udph->chksum =3D grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,= - &ifaces[j].address, - &target); + done =3D 1; =20 - err =3D grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,= - GRUB_NET_IP_UDP); - grub_netbuff_free (nb); + err =3D 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)) =3D=3D 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; }; =20 @@ -433,6 +436,13 @@ enum GRUB_NET_BOOTP_DOMAIN =3D 0x0f, GRUB_NET_BOOTP_ROOT_PATH =3D 0x11, GRUB_NET_BOOTP_EXTENSIONS_PATH =3D 0x12, + GRUB_NET_DHCP_REQUESTED_IP_ADDRESS =3D 50, + GRUB_NET_DHCP_OVERLOAD =3D 52, + GRUB_NET_DHCP_MESSAGE_TYPE =3D 53, + GRUB_NET_DHCP_SERVER_IDENTIFIER =3D 54, + GRUB_NET_DHCP_PARAMETER_REQUEST_LIST =3D 55, + GRUB_NET_DHCP_TFTP_SERVER_NAME =3D 66, + GRUB_NET_DHCP_BOOTFILE_NAME =3D 67, GRUB_NET_BOOTP_END =3D 0xff }; =20 @@ -450,7 +460,7 @@ grub_net_add_ipv4_local (struct grub_net_network_leve= l_interface *inf, =20 void grub_net_process_dhcp (struct grub_net_buff *nb, - struct grub_net_card *card); + struct grub_net_network_level_interface *iface); =20 int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, --------------090107090906080502010409--