[libvirt] [PATCH RESEND] Add support for <option> tag in network config

Eric Blake eblake at redhat.com
Tue Feb 26 18:21:57 UTC 2013


[adding Pieter, in case he didn't see the reply.]

On 02/26/2013 11:05 AM, Laine Stump wrote:
> My original response to the patch and to Michal's review is at the end
> of this message for historical purposes. I had written it up, made some
> small changes to the patch to address problems I found, and pushed it,
> but somehow managed to never hit "Send" on the email :-(
> 
> In the meantime, I read Eric's review of my followup patch that added
> support for a "force" attribute:
> 
>    https://www.redhat.com/archives/libvir-list/2013-February/msg01413.html
> 
> Eric's self-debate here got me thinking more seriously about the future
> implications of this original "numeric-only" <option> patch, and of
> something that has been becoming more apparent to me as I've been taking
> a whack at implementing support for named options on top of it.
> 
> In particular, the "value" of a dhcp option can be one of several types,
> and in some cases it can be a repeated list (especially of IP addresses,
> but there are a few that are lists of numbers). As it stands, we treat
> "value" as an opaque string, and just pass it through to dnsmasq as-is
> (except for checking for embedded spaces and \). For example, this:
> 
>     <option number='6' value='1.2.3.4,5.6.7.8'/>
> 
> results in the following line in dnsmasq.conf:
> 
>     dhcp-option=6,1.2.3.4,5.6.7.8
> 
> That works, but if we ever decide we want to do a better job of
> validating the value (rather than just relying on dnsmasq), then we
> arrive at a situation where the data that has been parsed out of the XML
> must be further subdivided and parsed to get at the individual values in
> the list. I can recall at least twice in recent memory that I dinged a
> patch during review for exactly this reason.
> 
> On the other hand, I don't want to over-engineer this problem so much
> that we never push *anything*.
> 
> Without even arriving at a decision about this, I'm now thinking that
> maybe we should revert the earlier <option> patch until after the
> release, and re-push it after the named-option support is done
> (potentially with some changes).

I also like the idea of reverting this change so that 1.0.3 doesn't
leave us stuck with a half-baked design that we will later regret
because we have to do back-compat support alongside a more friendly
design with option names.  Having a bit more time on our side to get the
interface right in 1.0.4 feels like it will be beneficial in the long
run.  Pieter, can you chime in within the next 24 hours?  If not, I'm
okay if Laine does the revert before DV cuts rc2.

> 
> Any other opinions?
> 
> 
> 
> ===========================================================================
> On 02/22/2013 04:37 AM, Michal Privoznik wrote:
>> On 21.02.2013 23:40, Pieter Hollants wrote:
>>> This patch adds support for a new <option>-Tag in the <dhcp> block of network configs,
>>> based on a subset of the fifth proposal by Laine Stump in the mailing list discussion at
>>> https://www.redhat.com/archives/libvir-list/2012-November/msg01054.html. Any such defined
>>> option will result in a dhcp-option=<number>,"<value>" statement in the generated dnsmasq
>>> configuration file.
>>>
>>> Currently, DHCP options can be specified by number only and there is no whitelisting or
>>> blacklisting of option numbers, which should probably be added.
>>>
>>> Signed-off-by: Pieter Hollants <pieter at hollants.com>
>>> ---
>>>  AUTHORS.in                                        |    1 +
>>>  docs/schemas/network.rng                          |    6 +++
>>>  docs/schemas/networkcommon.rng                    |    6 +++
>>>  src/conf/network_conf.c                           |   54 +++++++++++++++++++++
>>>  src/conf/network_conf.h                           |   10 ++++
>>>  src/network/bridge_driver.c                       |   10 ++++
>>>  tests/networkxml2xmlin/netboot-network.xml        |    1 +
>>>  tests/networkxml2xmlin/netboot-proxy-network.xml  |    1 +
>>>  tests/networkxml2xmlout/netboot-network.xml       |    1 +
>>>  tests/networkxml2xmlout/netboot-proxy-network.xml |    1 +
>>>  10 Dateien geändert, 91 Zeilen hinzugefügt(+)
>>>
>>> diff --git a/AUTHORS.in b/AUTHORS.in
>>> index 39fe68d..074185e 100644
>>> --- a/AUTHORS.in
>>> +++ b/AUTHORS.in
>>> @@ -74,6 +74,7 @@ Michel Ponceau <michel.ponceau at bull.net>
>>>  Nobuhiro Itou <fj0873gn at aa.jp.fujitsu.com>
>>>  Pete Vetere <pvetere at redhat.com>
>>>  Philippe Berthault <philippe.berthault at Bull.net>
>>> +Pieter Hollants <pieter at hollants.com>
>>>  Saori Fukuta <fukuta.saori at jp.fujitsu.com>
>>>  Shigeki Sakamoto <fj0588di at aa.jp.fujitsu.com>
>>>  Shuveb Hussain <shuveb at binarykarma.com>
>> This is not necessary. AUTHORS file is now completely generated since v0.10.2-207-g7b21981.
>>
>>> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
>>> index 09d7c73..a2ce1c9 100644
>>> --- a/docs/schemas/network.rng
>>> +++ b/docs/schemas/network.rng
>>> @@ -293,6 +293,12 @@
>>>                      </optional>
>>>                    </element>
>>>                  </optional>
>>> +                <zeroOrMore>
>>> +                  <element name="option">
>>> +                    <attribute name="number"><ref name="unsignedByte"/></attribute>
>>> +                    <attribute name="value"><text/></attribute>
> 
> Actually it appears that it's legal to send some options with no value
> at all; this is even mentioned in dnsmasq documentation. So value needs
> to be optional in the RNG, and in the parsing/formatting.
> 
>>> +                  </element>
>>> +                </zeroOrMore>
>>>                </element>
>>>              </optional>
>>>            </element>
>> Maybe it's my lack of experience, but there's no mapping from numbers defined in RFC2132 to human readable strings and vice versa in my brain. I think, <option name='router' value='192.168.0.1'/> or <option name='ntp-server' value='1.2.3.4'/> is more readable than their numbered alternatives <option number='3' .../> and <option number='42' .../>.
> 
> I think we can all agree on that. However, there is no official standard
> alpha ID name for the options (although they're named in the rfc, it's
> with a multi-word section title, not a single identifier", and dnsmasq
> (at least) doesn't support names for all options. So we need to support
> specifying numeric options anyway, and that's a good start on full support.
> 
> As a followup to this patch, hopefully one of us will have the
> ambition+time to make a patch that adds a "name" attribute to <option,
> with an enum listing all the named options supported by dnsmasq, and
> special validation of the contents of value for those we know more
> about. When a name is given, it would be checked against the list of
> known names, then transferred directly into the dnsmasq commandline if
> it was known (after doing the extra validation of the value)
> 
> There is one other thing I think we need to add right away though - a
> "force" attribute - if set to "yes', the dhcp server would be told to
> always send that option, even if it wasn't requested (the default is to
> only send if requested);  the way to do this with dnsmasq is to use
> "dhcp-option-force=..." instead of "dhcp-option=...". (That can also be
> a separate patch submitted after this one).
> 
> 
>>
>>> diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng
>>> index 51ff759..3510928 100644
>>> --- a/docs/schemas/networkcommon.rng
>>> +++ b/docs/schemas/networkcommon.rng
>>> @@ -173,6 +173,12 @@
>>>      </data>
>>>    </define>
>>>  
>>> +  <define name='unsignedByte'>
>>> +    <data type='integer'>
>>> +      <param name="minInclusive">0</param>
>>> +      <param name="maxInclusive">255</param>
>>> +    </data>
>>> +  </define>
>> No need to invent new type; uint8range from basictypes.rng is just fine.
>>
>>>    <define name='unsignedShort'>
>>>      <data type='integer'>
>>>        <param name="minInclusive">0</param>
>>> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
>>> index 3604ff7..9d84c7e 100644
>>> --- a/src/conf/network_conf.c
>>> +++ b/src/conf/network_conf.c
>>> @@ -777,6 +777,45 @@ cleanup:
>>>  }
>>>  
>>>  static int
>>> +virNetworkDHCPOptionDefParseXML(const char *networkName,
>>> +                                xmlNodePtr node,
>>> +                                virNetworkDHCPOptionDefPtr option)
>>> +{
>>> +    char *number = NULL;
>>> +    int ret = -1;
>>> +
>>> +    if (!(number = virXMLPropString(node, "number"))) {
>>> +        virReportError(VIR_ERR_XML_ERROR,
>>> +                       _("Option definition in IPv4 network '%s' "
>>> +                         "must have number attribute"),
>>> +                       networkName);
>>> +        goto cleanup;
>>> +    }
>>> +    if (number &&
>>> +        virStrToLong_ui(number, NULL, 10, &option->number) < 0) {
>>> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>>> +                       _("Cannot parse <option> 'number' attribute"));
>>> +        goto cleanup;
>>> +    }
>> After previous 'if' @number is either NULL in which case you goto cleanup; or is not NULL in which case the first part of condition is always true and thus can be dropped. Moreover, I think the error message can be tuned to give even more hint about its origin. Something like: ("Malformed <option> number attribute: %s", number); But that's not a show stopper. However, the error is not VIR_ERR_INTERNAL_ERROR, but VIR_ERR_XML_ERROR or VIR_ERR_XML_DETAIL.
>>
>>> +    /* TODO: either whitelist numbers or blacklist numbers already occupied
>>> +     *       by other XML statements (eg. submask) */
> 
> I don't think that's very useful, since that list could (and hopefully
> will) change over time, and new libvirt must always accept any XML that
> was allowed by an older version of libvirt. I'm always in favor of
> having a single way to specify any given configuration, but in this case
> I don't really think we can.
> 
>>> +    if (!(option->value = virXMLPropString(node, "value"))) {
>>> +        virReportError(VIR_ERR_XML_ERROR,
>>> +                       _("Option definition in IPv4 network '%s' "
>>> +                         "must have value attribute"),
>>> +                       networkName);
> 
> See my comment above. Not finding a value string isn't an error, because
> it's legal (and in some cases useful) to specify an option with no
> associated value. (Unfortunately there is no way to differentiate
> between "value not found" and "out of memory while trying to strdup the
> value we found", so we have to simply eliminate error checking here.
> 
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    ret = 0;
>>> +
>>> +cleanup:
>>> +    VIR_FREE(number);
>>> +    return ret;
>>> +}
>>> +
>>> +
>>> +static int
>>>  virNetworkDHCPDefParseXML(const char *networkName,
>>>                            xmlNodePtr node,
>>>                            virNetworkIpDefPtr def)
>>> @@ -837,6 +876,17 @@ virNetworkDHCPDefParseXML(const char *networkName,
>>>              def->bootfile = file;
>>>              def->bootserver = inaddr;
>>>              VIR_FREE(server);
>>> +        } else if (cur->type == XML_ELEMENT_NODE &&
>>> +            xmlStrEqual(cur->name, BAD_CAST "option")) {
>>> +            if (VIR_REALLOC_N(def->options, def->noptions + 1) < 0) {
>>> +                virReportOOMError();
>>> +                return -1;
>>> +            }
>>> +            if (virNetworkDHCPOptionDefParseXML(networkName, cur,
>>> +                                                &def->options[def->noptions])) {
>>> +                return -1;
>>> +            }
>>> +            def->noptions++;
>>>          }
>>>  
>>>          cur = cur->next;
>>> @@ -2045,6 +2095,10 @@ virNetworkIpDefFormat(virBufferPtr buf,
>>>              virBufferAddLit(buf, "/>\n");
>>>  
>>>          }
>>> +        for (ii = 0 ; ii < def->noptions ; ii++) {
>>> +            virBufferAsprintf(buf, "<option number='%u' value='%s' />\n",
> 
> I actually don't like having the extra space before the /> here. We're
> inconsistent about that in our code, but I *think* we tended more toward
> eliminating that space in the recent past.
> 
> In addition to that, since the value could contain *anything*, we need
> to format it into xml with virBufferEscapeString().
> 
>>> +                              def->options[ii].number, def->options[ii].value);
>>> +        }
>>>  
>>>          virBufferAdjustIndent(buf, -2);
>>>          virBufferAddLit(buf, "</dhcp>\n");
>>> diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
>>> index 4c634ed..14f852a 100644
>>> --- a/src/conf/network_conf.h
>>> +++ b/src/conf/network_conf.h
>>> @@ -77,6 +77,13 @@ struct _virNetworkDHCPHostDef {
>>>      virSocketAddr ip;
>>>  };
>>>  
>>> +typedef struct _virNetworkDHCPOptionDef virNetworkDHCPOptionDef;
>>> +typedef virNetworkDHCPOptionDef *virNetworkDHCPOptionDefPtr;
>>> +struct _virNetworkDHCPOptionDef {
>>> +    unsigned int number;
>>> +    char *value;
>>> +};
>>> +
>>>  typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef;
>>>  typedef virNetworkDNSTxtDef *virNetworkDNSTxtDefPtr;
>>>  struct _virNetworkDNSTxtDef {
>>> @@ -139,6 +146,9 @@ struct _virNetworkIpDef {
>>>      char *tftproot;
>>>      char *bootfile;
>>>      virSocketAddr bootserver;
>>> +
>>> +    size_t noptions;            /* Zero or more additional dhcp options */
>>> +    virNetworkDHCPOptionDefPtr options;
>>>     };
>>>  
>>>  typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
>>> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
>>> index c834f83..c7c0a9e 100644
>>> --- a/src/network/bridge_driver.c
>>> +++ b/src/network/bridge_driver.c
>>> @@ -926,6 +926,15 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
>>>                      virBufferAsprintf(&configbuf, "dhcp-boot=%s\n", ipdef->bootfile);
>>>                  }
>>>              }
>>> +
>>> +            /* Any additional DHCP options? */
>>> +            if (ipdef->noptions > 0) {
> 
> No need for the "if (ipdef->noptions > 0) - that's taken care of by the
> for loop already.
> 
> 
>>> +                for (r = 0 ; r < ipdef->noptions ; r++) {
>>> +                    virBufferAsprintf(&configbuf, "dhcp-option=%u,\"%s\"\n",
> 
> All of the dnsmasq.conf examples I've seen (with the one exception of
> the 'option 252="\n"' that you used in the test data) do *not* quote the
> value unless it contains an embedded space (or an escaped character, as
> in the case of "\n"). To make the generated conf file as close to the
> examples as possible, I'm changing this to *not* include quotes unless
> there is an embedded space or "\".
> 
> (Through experimentation I found that \n with no quotes yielded the two
> character string 0x5c 0x6E, while "\n" quoted resulted in sending a
> single newline character)
> 
> I just sent mail to the dnsmasq list asking if the quotes are necessary
> in any other case, and will submit a relevant patch if so.
> 
>>> +                                      ipdef->options[r].number,
>>> +                                      ipdef->options[r].value);
> 
> We've done no sanity checking on the contents of value. Is there any
> danger of a rogue metacharacter in dnsmasq.conf causing problems? I seem
> to recall asking that question before and being told "no", and I'm going
> to continue assuming that for now, but I still think it would be good
> practice to do some basic validation of the value if we can determine an
> all-encompassing valid character set that is something smaller than
> "everything" :-)
> 
>>> +                }
>>> +            }
>>>          }
>>>          ipdef = (ipdef == ipv6def) ? NULL : ipv6def;
>>>      }
>>> @@ -959,6 +968,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
>>>      virBufferAsprintf(&configbuf, "addn-hosts=%s\n",
>>>                        dctx->addnhostsfile->path);
>>>  
>>> +
>> This looks odd.
>>
>>>      /* Are we doing RA instead of radvd? */
>>>      if (DNSMASQ_RA_SUPPORT(caps)) {
>>>          if (ipv6def)
>>> diff --git a/tests/networkxml2xmlin/netboot-network.xml b/tests/networkxml2xmlin/netboot-network.xml
>>> index ed75663..4de8976 100644
>>> --- a/tests/networkxml2xmlin/netboot-network.xml
>>> +++ b/tests/networkxml2xmlin/netboot-network.xml
>>> @@ -9,6 +9,7 @@
>>>      <dhcp>
>>>        <range start="192.168.122.2" end="192.168.122.254" />
>>>        <bootp file="pxeboot.img" />
>>> +      <option number="252" value="\n" />
>>>      </dhcp>
>>>    </ip>
>>>  </network>
>>> diff --git a/tests/networkxml2xmlin/netboot-proxy-network.xml b/tests/networkxml2xmlin/netboot-proxy-network.xml
>>> index ecb6738..4c5c480 100644
>>> --- a/tests/networkxml2xmlin/netboot-proxy-network.xml
>>> +++ b/tests/networkxml2xmlin/netboot-proxy-network.xml
>>> @@ -8,6 +8,7 @@
>>>      <dhcp>
>>>        <range start="192.168.122.2" end="192.168.122.254" />
>>>        <bootp file="pxeboot.img" server="10.20.30.40" />
>>> +      <option number="252" value="\n" />
>>>      </dhcp>
>>>    </ip>
>>>  </network>
>>> diff --git a/tests/networkxml2xmlout/netboot-network.xml b/tests/networkxml2xmlout/netboot-network.xml
>>> index b8a4d99..c3ea95a 100644
>>> --- a/tests/networkxml2xmlout/netboot-network.xml
>>> +++ b/tests/networkxml2xmlout/netboot-network.xml
>>> @@ -9,6 +9,7 @@
>>>      <dhcp>
>>>        <range start='192.168.122.2' end='192.168.122.254' />
>>>        <bootp file='pxeboot.img' />
>>> +      <option number='252' value='\n' />
>>>      </dhcp>
>>>    </ip>
>>>  </network>
>>> diff --git a/tests/networkxml2xmlout/netboot-proxy-network.xml b/tests/networkxml2xmlout/netboot-proxy-network.xml
>>> index e11c50b..f5f2c0d 100644
>>> --- a/tests/networkxml2xmlout/netboot-proxy-network.xml
>>> +++ b/tests/networkxml2xmlout/netboot-proxy-network.xml
>>> @@ -8,6 +8,7 @@
>>>      <dhcp>
>>>        <range start='192.168.122.2' end='192.168.122.254' />
>>>        <bootp file='pxeboot.img' server='10.20.30.40' />
>>> +      <option number='252' value='\n' />
>>>      </dhcp>
>>>    </ip>
>>>  </network>
>>>
>> Maybe we should add test to networkxml2conf test as well, because the tests you are introducing make sure we don't break XML. However, we need to make sure we produce the right config file for dnsmasq. Something like:
> 
> I Agree.
> 
>>
>> diff --git a/tests/networkxml2confdata/netboot-network.conf b/tests/networkxml2confdata/netboot-network.conf
>> index b6f3c23..62fab51 100644
>> --- a/tests/networkxml2confdata/netboot-network.conf
>> +++ b/tests/networkxml2confdata/netboot-network.conf
>> @@ -17,6 +17,7 @@ dhcp-no-override
>>  enable-tftp
>>  tftp-root=/var/lib/tftproot
>>  dhcp-boot=pxeboot.img
>> +dhcp-option=252,"blah"
>>  dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases
>>  dhcp-lease-max=253
>>  dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile
>> diff --git a/tests/networkxml2confdata/netboot-network.xml b/tests/networkxml2confdata/netboot-network.xml
>> index b8a4d99..24ede16 100644
>> --- a/tests/networkxml2confdata/netboot-network.xml
>> +++ b/tests/networkxml2confdata/netboot-network.xml
>> @@ -9,6 +9,7 @@
>>      <dhcp>
>>        <range start='192.168.122.2' end='192.168.122.254' />
>>        <bootp file='pxeboot.img' />
>> +      <option number='252' value='blah'/>
>>      </dhcp>
>>    </ip>
>>  </network>
>>
>>
>> Overall status, I'm tentative to give ACK. However, I'd like to hear one more opinion on the correctness of XML schema. Elder libvirt developers remember times when XML schema change needed ACK from at least one of Daniels :)
> 
> Well, I'm usually hesitant about any xml addition for fear of getting it
> wrong, but since it was me who originally suggested this schema, and
> there was no negative comment about it on the list at the time, I'm
> giving my ACK (even though I'm not lucky enough to be named Daniel :-)).
> 
> There is still work to be done:
> 
> 1) recognize the "force" attribute.
> 
> 1) recognize the "name" attribute, and validate value accordingly
> 
> 2) deal with ipv6 options (this currently only works for ipv4)
> 
> 3) properly deal with vendor-specific options
> 
> 4) We may need some tweaking wrt quote marks around the values.
> 
> but I think those can be done as followups.
> 
> I've made the few changes you and I pointed out (including the addition
> of options to a couple of networkxml2conf test cases) and pushed the result.
> 
> 
> --
> libvir-list mailing list
> libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list
> 

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 621 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20130226/d194af47/attachment-0001.sig>


More information about the libvir-list mailing list