[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] bootp: add native DHCPv4 support
From: |
Andre Przywara |
Subject: |
Re: [PATCH] bootp: add native DHCPv4 support |
Date: |
Mon, 21 Jan 2019 14:10:48 +0000 |
On Mon, 21 Jan 2019 13:02:08 +0100
Daniel Kiper <address@hidden> wrote:
Hi Daniel,
thanks very much for your reply!
> On Fri, Jan 11, 2019 at 03:59:34PM +0000, Andre Przywara wrote:
> > From: Andrei Borzenkov <address@hidden>
> >
> > This patch adds support for native DHCPv4 and removes requirement
> > for BOOTP compatibility support in DHCP server.
> >
> > There is no provision for selecting preferred server. We take the
> > first OFFER and try to REQUEST configuration from it. If NAK was
> > received, transaction is restarted, but if we hit timeout,
> > configuration fails.
> >
> > It also handles pure BOOTP reply (detected by lack of DHCP message
> > type option) and proceeds with configuration immedately. I could
> > not test it because I do not have pure BOOTP server.
> >
> > Because we need access to DHCP options in multiple places now, it
> > also factors out DHCP option processing, adding support for option
> > overload.
> >
> > Timeout handling is now per-interface, with independent timeouts for
> > both DISCOVER and REQUEST. It should make it more responsive if
> > answer was delayed initially. Total timeout remains the same as
> > originally, as well as backoff algorithm, but we poll in fixed
> > 200ms ticks so notice (successful) reply earlier.
> >
> > Failure to send packet to interface now does not terminate command
> > (and leaks memory) but rather simply ignores this interface and
> > continues with remaining ones if present.
> >
> > Finally it adds net_dhcp alias with intention to deprecate net_bootp
> > completely (it would be possible to make net_bootp to actually send
> > BOOTP packet if someone thinks it is required).
> >
> > Features not implements:
> >
> > - 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 and it rather complicates implementation, so let's wait
> > for actual use case.
> >
> > - client identifier (RFC6842). So far we expect valid MAC address,
> > which should be enough to uniquely identify client. It is also not
> > clear how to generate unique client identifier if we ever need one.
> >
> > v2: change find_dhcp_option to use subscripts to avoid
> > signed/unsigned comparison warning.
> >
> > Should fix "may be used uninitialized" warning (although I
> > could not reproduce it).
> >
>
> I think that we should put Andrei's credits here. Probably we cannot
> add SOB without his approval. However, I am happy with anything else
> which does something in the similar fashion.
Definitely it's his patch, and the From: line above should make this
clear and the patch would end up with his authorship in git.
Happy to take any other tags, but as you mentioned, I can't just put
his SoB here.
> > Signed-off-by: Andre Przywara <address@hidden>
> >
> > ---
> > Hi,
> >
> > this patch is a rebased version of Andrei's work from 2016
> > [1][2][3]. We have still this issue here where our company DHCP
> > server does not support the BOOTP protocol, so grub doesn't get a
> > valid IP address.
> >
> > I took v2 from Andrei of the list, and fixed the minor conflicts
> > during the rebase. I can confirm that this patch makes the
> > difference between DHCP working and not:
> > grub-master> net_bootp
> > error: couldn't autoconfigure efinet0.
> > ...
> > grub-patched> net_bootp
> > grub-patched> net_ls_addr
> > efinet0:dhcp 00:11:22:33:44:55 10.1.23.42
> >
> > Also back in the days there were concerns about regressing
> > BOOTP-only servers. So I took the CMU bootpd[4], disabled the DHCP
> > extensions at compile time and still got IP addresses in both cases
> > (patched/unpatched). dhclient on Linux on the other hand was not
> > happy with that server and
>
> It seems to me that two sentences are glued here...
>
> > ignored the reply. I guess this is as close to a "BOOTP only
> > server" as we can get in 2019.
>
> I am OK with that.
>
> > I would be very happy if we can get this merged now.
> >
> > Please let me know if you need more information or any help on
> > this.
>
> Could you merge your comment with the commit message above? Please
> just refer to the current situation. If something historic should be
> referred please say clearly that it does not apply for today.
Sure.
> Is it possible to split this patch to smaller pieces?
Possible: yes, though I am not sure I understand enough of it and grub
to do it properly. Will try my best, though, as soon as I find some
spare cycles to understand the patch a bit better.
> Some nitpicks below...
>
> > Thanks!
> > Andre.
> >
> > P.S. The original patch didn't have a Signed-off-by:, so I kept it
> > that way. Added mine for legal reason, feel free to drop it.
> >
> > [1]
> > http://lists.gnu.org/archive/html/grub-devel/2016-01/msg00035.html
> > [2]
> > http://lists.gnu.org/archive/html/grub-devel/2016-03/msg00276.html
> > [3]
> > http://lists.gnu.org/archive/html/grub-devel/2016-04/msg00066.html
> > [4] https://packages.debian.org/jessie/bootp
> >
> > grub-core/net/bootp.c | 759
> > ++++++++++++++++++++++++++++-------------- grub-core/net/ip.c
> > | 2 +- include/grub/net.h | 14 +-
> > 3 files changed, 528 insertions(+), 247 deletions(-)
> >
> > diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
> > index 9e2fdb795..890df715b 100644
> > --- a/grub-core/net/bootp.c
> > +++ b/grub-core/net/bootp.c
>
> If we add DHCP support here then I think we should change file name in
> the following patch.
Yeah, makes some sense.
> > @@ -25,25 +25,107 @@
> > #include <grub/net/udp.h>
> > #include <grub/datetime.h>
> >
> > -static void
> > -parse_dhcp_vendor (const char *name, const void *vend, int limit,
> > int *mask) +struct grub_dhcp_discover_options
> > + {
>
> Drop spaces before "{".
Yes, I saw this, fixed it, then reverted it because I couldn't find an
explicit coding style and didn't want to mess with Andrei's original
work needlessly.
Consider it fixed.
The rest of the comments make sense, I will address them eventually,
unless someone (Andrei?) screams...
Cheers,
Andre.
> > + grub_uint8_t magic[4];
> > + struct
> > + {
>
> Drop two spaces starting from above line...
>
> > + grub_uint8_t code;
> > + grub_uint8_t len;
> > + grub_uint8_t data;
> > + } GRUB_PACKED message_type;
> > + grub_uint8_t end;
> > + } GRUB_PACKED;
>
> ...up to here. Same thing for structs below.
>
> > +
> > +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
> > + {
>
> Drop two spaces starting from above line...
>
> > + 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,
> > + };
>
> ...up to here.
>
> > +enum
> > + {
> > + GRUB_DHCP_OPT_OVERLOAD_FILE = 1,
> > + GRUB_DHCP_OPT_OVERLOAD_SNAME = 2,
> > + };
>
> Ditto.
>
> > +
> > +#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
> > +#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) -
> > (grub_uint8_t *)(y)))
>
> Please define this macro behind GRUB_DHCP_MAX_PACKET_TIMEOUT.
>
> > +
> > +/* Max timeout when waiting for BOOTP/DHCP reply */
> > +#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
> > +
> > +static const void *
> > +find_dhcp_option (const struct grub_net_bootp_packet *bp,
> > grub_size_t size,
>
> s/grub_net_bootp_packet/grub_net_dhcp_packet/?
>
> And in general should not we change bootp with dhcp everywhere after
> this patch?
>
> > + grub_uint8_t opt_code, grub_uint8_t *opt_len)
> > {
> > - const grub_uint8_t *ptr, *ptr0;
> > + const grub_uint8_t *ptr;
> > + grub_uint8_t overload = 0;
> > + int end = 0;
> > + grub_size_t i;
> > +
> > + if (opt_len)
> > + *opt_len = 0;
> > +
> > + /* Magic */
> > + if (size < sizeof (*bp) + sizeof (grub_uint32_t))
> > + goto out;
> >
> > - ptr = ptr0 = vend;
> > + ptr = (grub_uint8_t *) (bp + 1);
>
> Please add one line comment where (bp + 1) points to.
>
> > 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;
> > - ptr = ptr + sizeof (grub_uint32_t);
> > - while (ptr - ptr0 < limit)
> > + goto out;
> > +
> > + size -= sizeof (*bp);
> > + i = sizeof (grub_uint32_t);
> > +
> > +again:
> > + while (i < size)
> > {
> > grub_uint8_t tagtype;
> > grub_uint8_t taglength;
> >
> > - tagtype = *ptr++;
> > + tagtype = ptr[i++];
> >
> > /* Pad tag. */
> > if (tagtype == GRUB_NET_BOOTP_PAD)
> > @@ -51,84 +133,77 @@ 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 (i >= size)
> > + goto out;
> >
> > - taglength = *ptr++;
> > + taglength = ptr[i++];
> > + if (i + taglength >= size)
> > + goto out;
> >
> > - switch (tagtype)
> > + /* FIXME RFC 3396 options concatentation */
> > + if (tagtype == opt_code)
> > {
> > - 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 (opt_len)
> > + *opt_len = taglength;
> > + return &ptr[i];
> > + }
> >
> > - 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, NULL);
> > - grub_free (rname);
> > - }
> > - break;
> > - case GRUB_NET_BOOTP_DNS:
> > + if (tagtype == GRUB_NET_DHCP_OVERLOAD && taglength == 1)
> > + overload = ptr[i];
> > +
> > + i += taglength;
> > + }
> > +
> > + /* 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.
> > + */
>
> Could you convert all multiline comments to the following format?
>
> /*
> * 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'
> ...
> *
> * FIXME: We do not explicitly check for trailing 'pad' options here.
> */
>
> > + if (end)
> > + {
> > + end = 0;
> > + if (overload & GRUB_DHCP_OPT_OVERLOAD_FILE)
> > {
> > - 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;
> > - }
> > + overload &= ~GRUB_DHCP_OPT_OVERLOAD_FILE;
> > + ptr = (grub_uint8_t *) &bp->boot_file[0];
> > + size = sizeof (bp->boot_file);
> > + i = 0;
> > + goto again;
> > }
> > - 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. */
> > - }
> >
> > - ptr += taglength;
> > - }
> > -}
> > + if (overload & GRUB_DHCP_OPT_OVERLOAD_SNAME)
> > + {
> > + overload &= ~GRUB_DHCP_OPT_OVERLOAD_SNAME;
> > + ptr = (grub_uint8_t *) &bp->server_name[0];
> > + size = sizeof (bp->server_name);
> > + i = 0;
> > + goto again;
> > + }
> > + }
> >
> > -#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) -
> > (grub_uint8_t *)(y)))
>
> This is the move. I think that this should be in separate patch.
>
> > +out:
> > + return 0;
> > +}
>
> [...]
>
> > +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);
>
> Please define 128 as a constant.
>
> > + 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));
> > + /* my_ip and srv_id are stored in network order so do not
> > need conversion. */
> > + 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;
>
> Constants instead of plain numbers? Or at least comments what these
> numbers mean.
>
> > + 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);
>
> OK, these are port numbers but same as above.
>
> > + udph->chksum = 0;
> > + udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
> > + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
> > + target.ipv4 = 0xffffffff;
>
> Ditto.
>
> Daniel