[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    Re: [PATCH] udhcpc: add support for long options
From:       Martin Lewis <martin.lewis.x84 () gmail ! com>
Date:       2020-08-15 8:35:40
Message-ID: CA+nwWBvMO2ZH-Ln6K-TnLez3wLOjc6onEz4TRHKyR+8jOtBKiA () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Tested both regular options and long options against ISC's dhcpd, looks
good.

Martin

On Thu, 13 Aug 2020 at 10:00, Denys Vlasenko <vda.linux@googlemail.com>
wrote:

> On Tue, Aug 4, 2020 at 5:25 PM Martin Lewis <martin.lewis.x84@gmail.com>
> wrote:
> > Duplicate options are currently overridden (only the last option is
> kept).
> > This leads to unexpected behavior when using long options.
> >
> > The patch adds support for long options in compliance with RFC 3396.
> >
> > Fixes #13136.
> >
> > function                                             old     new   delta
> > udhcp_run_script                                     704     765     +61
> > optitem_unset_env_and_free                             -      31     +31
> > static.xmalloc_optname_optval                        837     833      -4
> > putenvp                                               60      52      -8
> >
> ------------------------------------------------------------------------------
> > (add/remove: 1/0 grow/shrink: 1/2 up/down: 92/-12)             Total: 80
> bytes
> >    text    data     bss     dec     hex filename
> >  994091   16939    1872 1012902   f74a6 busybox_old
> >  994171   16939    1872 1012982   f74f6 busybox_unstripped
> >
> > Signed-off-by: Martin Lewis <martin.lewis.x84@gmail.com>
> > ---
> >  networking/udhcp/dhcpc.c | 125
> ++++++++++++++++++++++++++++++++++-------------
> >  networking/udhcp/dhcpc.h |   1 +
> >  2 files changed, 93 insertions(+), 33 deletions(-)
> >
> > diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
> > index 50dfead63..d4a7a825e 100644
> > --- a/networking/udhcp/dhcpc.c
> > +++ b/networking/udhcp/dhcpc.c
> > @@ -115,6 +115,13 @@ enum {
> >
> >
> >  /*** Script execution code ***/
> > +struct dhcp_optitem
> > +{
> > +       unsigned len;
> > +       uint8_t *data;
> > +       char *env;
> > +       uint8_t code;
> > +};
> >
> >  /* get a rough idea of how long an option will be (rounding up...) */
> >  static const uint8_t len_of_option_as_string[] ALIGN1 = {
> > @@ -186,15 +193,15 @@ static int good_hostname(const char *name)
> >  #endif
> >
> >  /* Create "opt_name=opt_value" string */
> > -static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const
> struct dhcp_optflag *optflag, const char *opt_name)
> > +static NOINLINE char *xmalloc_optname_optval(const struct dhcp_optitem
> *opt_item, const struct dhcp_optflag *optflag, const char *opt_name)
> >  {
> >         unsigned upper_length;
> >         int len, type, optlen;
> >         char *dest, *ret;
> > +       uint8_t *option;
> >
> > -       /* option points to OPT_DATA, need to go back to get OPT_LEN */
> > -       len = option[-OPT_DATA + OPT_LEN];
> > -
> > +       option = opt_item->data;
> > +       len = opt_item->len;
> >         type = optflag->flags & OPTION_TYPE_MASK;
> >         optlen = dhcp_option_lengths[type];
> >         upper_length = len_of_option_as_string[type]
> > @@ -386,11 +393,50 @@ static NOINLINE char
> *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
> >         return ret;
> >  }
> >
> > -static void putenvp(llist_t **envp, char *new_opt)
> > +/* Used by static options (interface, siaddr, etc) */
> > +static void putenvp(char *new_opt)
> >  {
> > +       struct dhcp_optitem *opt_item;
> > +       opt_item = xzalloc(sizeof(struct dhcp_optitem));
> > +       /* Set opt_item->code = 0, so it won't appear in concat_option's
> lookup. */
> > +       opt_item->env = new_opt;
> >         putenv(new_opt);
> > -       log2(" %s", new_opt);
> > -       llist_add_to(envp, new_opt);
> > +       llist_add_to(&client_data.envp, opt_item);
> > +}
>
> This removed logging of environment
>
>
> > +/* Support RFC3396 Long Encoded Options */
> > +static struct dhcp_optitem *concat_option(uint8_t *data, uint8_t len,
> uint8_t code)
> > +{
> > +       llist_t *item;
> > +       struct dhcp_optitem *opt_item;
> > +       unsigned new_len = len;
> > +
> > +       /* Check if an option with the code already exists.
> > +        * A possible optimization is to create a bitmap of all existing
> options in the packet,
> > +        * and iterate over the option list only if they exist.
> > +        * This will result in bigger code, and because dhcp packets
> don't have too many options it
> > +        * shouldn't have a big impact on performance.
> > +        */
> > +       for (item = client_data.envp; item != NULL; item = item->link) {
> > +               opt_item = (struct dhcp_optitem *)item->data;
> > +               if (opt_item->code == code)
> > +                       break;
> > +       }
> > +
> > +       if (item) {
> > +               /* This is a duplicate, concatenate data according to
> RFC 3396 */
> > +               new_len += opt_item->len;
> > +       } else {
> > +               /* This is a new option, add a new dhcp_optitem to the
> list */
> > +               opt_item = xzalloc(sizeof(struct dhcp_optitem));
> > +               opt_item->code = code;
> > +               llist_add_to(&client_data.envp, opt_item);
> > +       }
> > +
> > +       opt_item->data = xrealloc(opt_item->data, new_len); /* xrealloc
> on the first occurrence (NULL) will call malloc */
> > +       memcpy(opt_item->data + opt_item->len, data, len);
> > +       opt_item->len = new_len;
> > +       return opt_item;
> >  }
>
> This will create a copy for every option, duplicated or not.
> Most options are not dups, I propose to track this status and only
> make copies as needed:
> most of the time, we won't make any:
>
> +static struct dhcp_optitem *concat_option(uint8_t *data, uint8_t len,
> uint8_t code)
> +{
> +       llist_t *item;
> +       struct dhcp_optitem *opt_item;
> +
> +       /* Check if an option with the code already exists.
> +        * A possible optimization is to create a bitmap of all
> existing options in the packet,
> +        * and iterate over the option list only if they exist.
> +        * This will result in bigger code, and because dhcp packets
> don't have too many options it
> +        * shouldn't have a big impact on performance.
> +        */
> +       for (item = client_data.envp; item != NULL; item = item->link) {
> +               opt_item = (struct dhcp_optitem *)item->data;
> +               if (opt_item->code == code) {
> +                       /* This option was seen already, concatenate */
> +                       uint8_t *new_data;
> +
> +                       new_data = xmalloc(len + opt_item->len);
> +                       memcpy(
> +                               mempcpy(new_data, opt_item->data,
> opt_item->len),
> +                               data, len
> +                       );
> +                       opt_item->len += len;
> +                       if (opt_item->malloced)
> +                               free(opt_item->data);
> +                       opt_item->malloced = 1;
> +                       opt_item->data = new_data;
> +                       return opt_item;
> +               }
> +       }
> +
> +       /* This is a new option, add a new dhcp_optitem to the list */
> +       opt_item = xzalloc(sizeof(*opt_item));
> +       opt_item->code = code;
> +       /* opt_item->malloced = 0 */
> +       opt_item->data = data;
> +       opt_item->len = len;
> +       llist_add_to(&client_data.envp, opt_item);
> +       return opt_item;
>  }
>
> Applying with this change. Please test current git.
>

[Attachment #5 (text/html)]

<div dir="ltr">Tested both regular options and long options against ISC&#39;s dhcpd, \
looks good.<div><br></div><div>Martin</div></div><br><div class="gmail_quote"><div \
dir="ltr" class="gmail_attr">On Thu, 13 Aug 2020 at 10:00, Denys Vlasenko &lt;<a \
href="mailto:vda.linux@googlemail.com" \
target="_blank">vda.linux@googlemail.com</a>&gt; wrote:<br></div><blockquote \
class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid \
rgb(204,204,204);padding-left:1ex">On Tue, Aug 4, 2020 at 5:25 PM Martin Lewis &lt;<a \
href="mailto:martin.lewis.x84@gmail.com" \
target="_blank">martin.lewis.x84@gmail.com</a>&gt; wrote:<br> &gt; Duplicate options \
are currently overridden (only the last option is kept).<br> &gt; This leads to \
unexpected behavior when using long options.<br> &gt;<br>
&gt; The patch adds support for long options in compliance with RFC 3396.<br>
&gt;<br>
&gt; Fixes #13136.<br>
&gt;<br>
&gt; function                                                                    old  \
new     delta<br> &gt; udhcp_run_script                                               \
704        765        +61<br> &gt; optitem_unset_env_and_free                         \
-         31        +31<br> &gt; static.xmalloc_optname_optval                        \
837        833         -4<br> &gt; putenvp                                            \
60         52         -8<br> &gt; \
------------------------------------------------------------------------------<br> \
&gt; (add/remove: 1/0 grow/shrink: 1/2 up/down: 92/-12)                    Total: 80 \
bytes<br> &gt;      text      data        bss        dec        hex filename<br>
&gt;   994091     16939      1872 1012902     f74a6 busybox_old<br>
&gt;   994171     16939      1872 1012982     f74f6 busybox_unstripped<br>
&gt;<br>
&gt; Signed-off-by: Martin Lewis &lt;<a href="mailto:martin.lewis.x84@gmail.com" \
target="_blank">martin.lewis.x84@gmail.com</a>&gt;<br> &gt; ---<br>
&gt;   networking/udhcp/dhcpc.c | 125 \
++++++++++++++++++++++++++++++++++-------------<br> &gt;   networking/udhcp/dhcpc.h | \
1 +<br> &gt;   2 files changed, 93 insertions(+), 33 deletions(-)<br>
&gt;<br>
&gt; diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c<br>
&gt; index 50dfead63..d4a7a825e 100644<br>
&gt; --- a/networking/udhcp/dhcpc.c<br>
&gt; +++ b/networking/udhcp/dhcpc.c<br>
&gt; @@ -115,6 +115,13 @@ enum {<br>
&gt;<br>
&gt;<br>
&gt;   /*** Script execution code ***/<br>
&gt; +struct dhcp_optitem<br>
&gt; +{<br>
&gt; +           unsigned len;<br>
&gt; +           uint8_t *data;<br>
&gt; +           char *env;<br>
&gt; +           uint8_t code;<br>
&gt; +};<br>
&gt;<br>
&gt;   /* get a rough idea of how long an option will be (rounding up...) */<br>
&gt;   static const uint8_t len_of_option_as_string[] ALIGN1 = {<br>
&gt; @@ -186,15 +193,15 @@ static int good_hostname(const char *name)<br>
&gt;   #endif<br>
&gt;<br>
&gt;   /* Create &quot;opt_name=opt_value&quot; string */<br>
&gt; -static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct \
dhcp_optflag *optflag, const char *opt_name)<br> &gt; +static NOINLINE char \
*xmalloc_optname_optval(const struct dhcp_optitem *opt_item, const struct \
dhcp_optflag *optflag, const char *opt_name)<br> &gt;   {<br>
&gt;              unsigned upper_length;<br>
&gt;              int len, type, optlen;<br>
&gt;              char *dest, *ret;<br>
&gt; +           uint8_t *option;<br>
&gt;<br>
&gt; -           /* option points to OPT_DATA, need to go back to get OPT_LEN */<br>
&gt; -           len = option[-OPT_DATA + OPT_LEN];<br>
&gt; -<br>
&gt; +           option = opt_item-&gt;data;<br>
&gt; +           len = opt_item-&gt;len;<br>
&gt;              type = optflag-&gt;flags &amp; OPTION_TYPE_MASK;<br>
&gt;              optlen = dhcp_option_lengths[type];<br>
&gt;              upper_length = len_of_option_as_string[type]<br>
&gt; @@ -386,11 +393,50 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t \
*option, const struct dhcp_<br> &gt;              return ret;<br>
&gt;   }<br>
&gt;<br>
&gt; -static void putenvp(llist_t **envp, char *new_opt)<br>
&gt; +/* Used by static options (interface, siaddr, etc) */<br>
&gt; +static void putenvp(char *new_opt)<br>
&gt;   {<br>
&gt; +           struct dhcp_optitem *opt_item;<br>
&gt; +           opt_item = xzalloc(sizeof(struct dhcp_optitem));<br>
&gt; +           /* Set opt_item-&gt;code = 0, so it won&#39;t appear in \
concat_option&#39;s lookup. */<br> &gt; +           opt_item-&gt;env = new_opt;<br>
&gt;              putenv(new_opt);<br>
&gt; -           log2(&quot; %s&quot;, new_opt);<br>
&gt; -           llist_add_to(envp, new_opt);<br>
&gt; +           llist_add_to(&amp;client_data.envp, opt_item);<br>
&gt; +}<br>
<br>
This removed logging of environment<br>
<br>
<br>
&gt; +/* Support RFC3396 Long Encoded Options */<br>
&gt; +static struct dhcp_optitem *concat_option(uint8_t *data, uint8_t len, uint8_t \
code)<br> &gt; +{<br>
&gt; +           llist_t *item;<br>
&gt; +           struct dhcp_optitem *opt_item;<br>
&gt; +           unsigned new_len = len;<br>
&gt; +<br>
&gt; +           /* Check if an option with the code already exists.<br>
&gt; +            * A possible optimization is to create a bitmap of all existing \
options in the packet,<br> &gt; +            * and iterate over the option list only \
if they exist.<br> &gt; +            * This will result in bigger code, and because \
dhcp packets don&#39;t have too many options it<br> &gt; +            * shouldn&#39;t \
have a big impact on performance.<br> &gt; +            */<br>
&gt; +           for (item = client_data.envp; item != NULL; item = item-&gt;link) \
{<br> &gt; +                       opt_item = (struct dhcp_optitem \
*)item-&gt;data;<br> &gt; +                       if (opt_item-&gt;code == code)<br>
&gt; +                                   break;<br>
&gt; +           }<br>
&gt; +<br>
&gt; +           if (item) {<br>
&gt; +                       /* This is a duplicate, concatenate data according to \
RFC 3396 */<br> &gt; +                       new_len += opt_item-&gt;len;<br>
&gt; +           } else {<br>
&gt; +                       /* This is a new option, add a new dhcp_optitem to the \
list */<br> &gt; +                       opt_item = xzalloc(sizeof(struct \
dhcp_optitem));<br> &gt; +                       opt_item-&gt;code = code;<br>
&gt; +                       llist_add_to(&amp;client_data.envp, opt_item);<br>
&gt; +           }<br>
&gt; +<br>
&gt; +           opt_item-&gt;data = xrealloc(opt_item-&gt;data, new_len); /* \
xrealloc on the first occurrence (NULL) will call malloc */<br> &gt; +           \
memcpy(opt_item-&gt;data + opt_item-&gt;len, data, len);<br> &gt; +           \
opt_item-&gt;len = new_len;<br> &gt; +           return opt_item;<br>
&gt;   }<br>
<br>
This will create a copy for every option, duplicated or not.<br>
Most options are not dups, I propose to track this status and only<br>
make copies as needed:<br>
most of the time, we won&#39;t make any:<br>
<br>
+static struct dhcp_optitem *concat_option(uint8_t *data, uint8_t len,<br>
uint8_t code)<br>
+{<br>
+           llist_t *item;<br>
+           struct dhcp_optitem *opt_item;<br>
+<br>
+           /* Check if an option with the code already exists.<br>
+            * A possible optimization is to create a bitmap of all<br>
existing options in the packet,<br>
+            * and iterate over the option list only if they exist.<br>
+            * This will result in bigger code, and because dhcp packets<br>
don&#39;t have too many options it<br>
+            * shouldn&#39;t have a big impact on performance.<br>
+            */<br>
+           for (item = client_data.envp; item != NULL; item = item-&gt;link) {<br>
+                       opt_item = (struct dhcp_optitem *)item-&gt;data;<br>
+                       if (opt_item-&gt;code == code) {<br>
+                                   /* This option was seen already, concatenate \
*/<br> +                                   uint8_t *new_data;<br>
+<br>
+                                   new_data = xmalloc(len + opt_item-&gt;len);<br>
+                                   memcpy(<br>
+                                               mempcpy(new_data, \
opt_item-&gt;data,<br> opt_item-&gt;len),<br>
+                                               data, len<br>
+                                   );<br>
+                                   opt_item-&gt;len += len;<br>
+                                   if (opt_item-&gt;malloced)<br>
+                                               free(opt_item-&gt;data);<br>
+                                   opt_item-&gt;malloced = 1;<br>
+                                   opt_item-&gt;data = new_data;<br>
+                                   return opt_item;<br>
+                       }<br>
+           }<br>
+<br>
+           /* This is a new option, add a new dhcp_optitem to the list */<br>
+           opt_item = xzalloc(sizeof(*opt_item));<br>
+           opt_item-&gt;code = code;<br>
+           /* opt_item-&gt;malloced = 0 */<br>
+           opt_item-&gt;data = data;<br>
+           opt_item-&gt;len = len;<br>
+           llist_add_to(&amp;client_data.envp, opt_item);<br>
+           return opt_item;<br>
  }<br>
<br>
Applying with this change. Please test current git.<br>
</blockquote></div>



_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic