qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH] Add domain-search option to slirp's DHCP server


From: Jan Kiszka
Subject: Re: [Qemu-devel] [PATCH] Add domain-search option to slirp's DHCP server
Date: Thu, 15 Nov 2012 10:33:54 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.1.12) Gecko/20080226 SUSE/2.0.0.12-1.1 Thunderbird/2.0.0.12 Mnenhy/0.7.5.666

On 2012-10-27 19:53, Klaus Stengel wrote:
> This patch will allow the user to include the domain-search option in
> replies from the built-in DHCP server. The domain suffixes can be
> specified by adding dnssearch= entries to the "-net user" parameter.
> 
> Signed-off-by: Klaus Stengel <address@hidden>
> ---
>  net/slirp.c         |   35 +++++-
>  qapi-schema.json    |    4 +
>  qemu-options.hx     |   18 +++-
>  slirp/Makefile.objs |    2 +-
>  slirp/bootp.c       |   12 ++
>  slirp/dnssearch.c   |  320 
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  slirp/libslirp.h    |    3 +-
>  slirp/slirp.c       |    8 +-
>  slirp/slirp.h       |    5 +
>  9 files changed, 398 insertions(+), 9 deletions(-)
>  create mode 100644 slirp/dnssearch.c
> 
> diff --git a/net/slirp.c b/net/slirp.c
> index bf86a44..72ecc5e 100644
> --- a/net/slirp.c
> +++ b/net/slirp.c
> @@ -136,7 +136,7 @@ static int net_slirp_init(NetClientState *peer, const 
> char *model,
>                            const char *vhostname, const char *tftp_export,
>                            const char *bootfile, const char *vdhcp_start,
>                            const char *vnameserver, const char *smb_export,
> -                          const char *vsmbserver)
> +                          const char *vsmbserver, const char **dnssearch)
>  {
>      /* default settings according to historic slirp */
>      struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
> @@ -242,7 +242,7 @@ static int net_slirp_init(NetClientState *peer, const 
> char *model,
>      s = DO_UPCAST(SlirpState, nc, nc);
> 
>      s->slirp = slirp_init(restricted, net, mask, host, vhostname,
> -                          tftp_export, bootfile, dhcp, dns, s);
> +                          tftp_export, bootfile, dhcp, dns, dnssearch, s);
>      QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
> 
>      for (config = slirp_configs; config; config = config->next) {
> @@ -699,6 +699,31 @@ net_init_slirp_configs(const StringList *fwd, int flags)
>      }
>  }
> 
> +static const char**
> +slirp_dnssearch(const StringList *dnsname) {
> +    const StringList *c = dnsname;
> +    size_t i = 0, num_opts = 0;
> +    const char **ret;
> +
> +    while (c) {
> +        num_opts++;
> +        c = c->next;
> +    }
> +
> +    if (num_opts == 0) {
> +        return NULL;
> +    }
> +
> +    ret = g_malloc((num_opts + 1) * sizeof(*ret));
> +    c = dnsname;
> +    while (c) {
> +        ret[i++] = c->value->str;
> +        c = c->next;
> +    }
> +    ret[i] = NULL;
> +    return ret;
> +}
> +
>  int net_init_slirp(const NetClientOptions *opts, const char *name,
>                     NetClientState *peer)
>  {
> @@ -706,6 +731,7 @@ int net_init_slirp(const NetClientOptions *opts, const 
> char *name,
>      char *vnet;
>      int ret;
>      const NetdevUserOptions *user;
> +    const char **dnssearch;
> 
>      assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
>      user = opts->user;
> @@ -714,6 +740,8 @@ int net_init_slirp(const NetClientOptions *opts, const 
> char *name,
>             user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
>             NULL;
> 
> +    dnssearch = slirp_dnssearch(user->dnssearch);
> +
>      /* all optional fields are initialized to "all bits zero" */
> 
>      net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
> @@ -722,7 +750,7 @@ int net_init_slirp(const NetClientOptions *opts, const 
> char *name,
>      ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
>                           user->host, user->hostname, user->tftp,
>                           user->bootfile, user->dhcpstart, user->dns, 
> user->smb,
> -                         user->smbserver);
> +                         user->smbserver, dnssearch);
> 
>      while (slirp_configs) {
>          config = slirp_configs;
> @@ -731,6 +759,7 @@ int net_init_slirp(const NetClientOptions *opts, const 
> char *name,
>      }
> 
>      g_free(vnet);
> +    g_free(dnssearch);
> 
>      return ret;
>  }
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 6fd263e..b24ce95 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2297,6 +2297,9 @@
>  #
>  # @dns: #optional guest-visible address of the virtual nameserver
>  #
> +# @dnssearch: #optional list of DNS suffixes to search, passed as DHCP option
> +#             to the guest
> +#
>  # @smb: #optional root directory of the built-in SMB server
>  #
>  # @smbserver: #optional IP address of the built-in SMB server
> @@ -2319,6 +2322,7 @@
>      '*bootfile':  'str',
>      '*dhcpstart': 'str',
>      '*dns':       'str',
> +    '*dnssearch': ['String'],
>      '*smb':       'str',
>      '*smbserver': 'str',
>      '*hostfwd':   ['String'],
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 46f0539..a6efc56 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1282,8 +1282,8 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
>      "                create a new Network Interface Card and connect it to 
> VLAN 'n'\n"
>  #ifdef CONFIG_SLIRP
>      "-net 
> user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=on|off]\n"
> -    "         
> [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n"
> -    "         [,hostfwd=rule][,guestfwd=rule]"
> +    "         
> [,hostname=host][,dhcpstart=addr][,dns=addr][,dnssearch=domain][,tftp=dir]\n"
> +    "         [,bootfile=f][,hostfwd=rule][,guestfwd=rule]"
>  #ifndef _WIN32
>                                               "[,smb=dir[,smbserver=addr]]\n"
>  #endif
> @@ -1392,7 +1392,7 @@ able to contact the host and no guest IP packets will 
> be routed over the host
>  to the outside. This option does not affect any explicitly set forwarding 
> rules.
> 
>  @item address@hidden
> -Specifies the client hostname reported by the builtin DHCP server.
> +Specifies the client hostname reported by the built-in DHCP server.
> 
>  @item address@hidden
>  Specify the first of the 16 IPs the built-in DHCP server can assign. Default
> @@ -1403,6 +1403,18 @@ Specify the guest-visible address of the virtual 
> nameserver. The address must
>  be different from the host address. Default is the 3rd IP in the guest 
> network,
>  i.e. x.x.x.3.
> 
> address@hidden address@hidden
> +Provides an entry for the domain-search list sent by the built-in
> +DHCP server. More than one domain suffix can be transmitted by specifying
> +this option multiple times. If supported, this will cause the guest to
> +automatically try to append the given domain suffix(es) in case a domain name
> +can not be resolved.
> +
> +Example:
> address@hidden
> +qemu -net user,dnssearch=mgmt.example.org,dnssearch=example.org [...]
> address@hidden example
> +
>  @item address@hidden
>  When using the user mode network stack, activate a built-in TFTP
>  server. The files in @var{dir} will be exposed as the root of a TFTP server.
> diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
> index bb43d3c..2daa9dc 100644
> --- a/slirp/Makefile.objs
> +++ b/slirp/Makefile.objs
> @@ -1,3 +1,3 @@
> -common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
> +common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
>  common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o 
> tcp_output.o
>  common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
> diff --git a/slirp/bootp.c b/slirp/bootp.c
> index 64eac7d..b7db9fa 100644
> --- a/slirp/bootp.c
> +++ b/slirp/bootp.c
> @@ -287,6 +287,18 @@ static void bootp_reply(Slirp *slirp, const struct 
> bootp_t *bp)
>              memcpy(q, slirp->client_hostname, val);
>              q += val;
>          }
> +
> +        if (slirp->vdnssearch) {
> +            size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend);
> +            val = slirp->vdnssearch_len;
> +            if (val + 1 > spaceleft) {
> +                g_warning("DHCP packet size exceeded, "
> +                    "omitting domain-search option.");
> +            } else {
> +                memcpy(q, slirp->vdnssearch, val);
> +                q += val;
> +            }
> +        }
>      } else {
>          static const char nak_msg[] = "requested address not available";
> 
> diff --git a/slirp/dnssearch.c b/slirp/dnssearch.c
> new file mode 100644
> index 0000000..898f7d8
> --- /dev/null
> +++ b/slirp/dnssearch.c
> @@ -0,0 +1,320 @@
> +/*
> + * Domain search option for DHCP (RFC 3397)
> + *
> + * Copyright (c) 2012 Klaus Stengel
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a 
> copy
> + * of this software and associated documentation files (the "Software"), to 
> deal
> + * in the Software without restriction, including without limitation the 
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <glib.h>
> +#include "slirp.h"
> +
> +static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119;
> +static const uint8_t MAX_OPT_LEN = 255;
> +static const uint8_t OPT_HEADER_LEN = 2;
> +static const uint8_t REFERENCE_LEN = 2;
> +
> +struct compact_domain;
> +
> +typedef struct compact_domain {
> +    struct compact_domain *self;
> +    struct compact_domain *refdom;
> +    uint8_t *labels;
> +    size_t len;
> +    size_t common_octets;
> +} CompactDomain;
> +
> +static size_t
> +domain_suffix_diffoff(const CompactDomain *a, const CompactDomain *b)
> +{
> +    size_t la = a->len, lb = b->len;
> +    uint8_t *da = a->labels + la, *db = b->labels + lb;
> +    size_t i, lm = (la < lb) ? la : lb;
> +
> +    for (i = 0; i < lm; i++) {
> +        da--; db--;
> +        if (*da != *db) {
> +            break;
> +        }
> +    }
> +    return i;
> +}
> +
> +static int
> +domain_suffix_ord(const void *cva, const void *cvb)
> +{
> +    const CompactDomain *a = cva, *b = cvb;
> +    size_t la = a->len, lb = b->len;
> +    size_t doff = domain_suffix_diffoff(a, b);
> +    uint8_t ca = a->labels[la - doff];
> +    uint8_t cb = b->labels[lb - doff];
> +
> +    if (ca < cb) {
> +        return -1;
> +    }
> +    if (ca > cb) {
> +        return 1;
> +    }
> +    if (la < lb) {
> +        return -1;
> +    }
> +    if (la > lb) {
> +        return 1;
> +    }
> +    return 0;
> +}
> +
> +static size_t
> +domain_common_label(CompactDomain *a, CompactDomain *b)
> +{
> +    size_t res, doff = domain_suffix_diffoff(a, b);
> +    uint8_t *first_eq_pos = a->labels + (a->len - doff);
> +    uint8_t *label = a->labels;
> +
> +    while (*label && label < first_eq_pos) {
> +        label += *label + 1;
> +    }
> +    res = a->len - (label - a->labels);
> +    /* only report if it can help to reduce the packet size */
> +    return (res > REFERENCE_LEN) ? res : 0;
> +}
> +
> +static void
> +domain_fixup_order(CompactDomain *cd, size_t n)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < n; i++) {
> +        CompactDomain *cur = cd + i, *next = cd[i].self;
> +
> +        while (!cur->common_octets) {
> +            CompactDomain *tmp = next->self; /* backup target value */
> +
> +            next->self = cur;
> +            cur->common_octets++;
> +
> +            cur = next;
> +            next = tmp;
> +        }
> +    }
> +}
> +
> +static void
> +domain_mklabels(CompactDomain *cd, const char *input)
> +{
> +    uint8_t *len_marker = cd->labels;
> +    uint8_t *output = len_marker; /* pre-incremented */
> +    const char *in = input;
> +    char cur_chr;
> +    size_t len = 0;
> +
> +    if (cd->len == 0) {
> +        goto fail;
> +    }
> +    cd->len++;
> +
> +    do {
> +        cur_chr = *in++;
> +        if (cur_chr == '.' || cur_chr == '\0') {
> +            len = output - len_marker;
> +            if ((len == 0 && cur_chr == '.') || len >= 64) {
> +                goto fail;
> +            }
> +            *len_marker = len;
> +
> +            output++;
> +            len_marker = output;
> +        } else {
> +            output++;
> +            *output = cur_chr;
> +        }
> +    } while (cur_chr != '\0');
> +
> +    /* ensure proper zero-termination */
> +    if (len != 0) {
> +        *len_marker = 0;
> +        cd->len++;
> +    }
> +    return;
> +
> +fail:
> +    g_warning("failed to parse domain name '%s'\n", input);
> +    cd->len = 0;
> +}
> +
> +static void
> +domain_mkxrefs(CompactDomain *doms, CompactDomain *last, size_t depth)
> +{
> +    CompactDomain *i = doms, *target = doms;
> +
> +    do {
> +        if (i->labels < target->labels) {
> +            target = i;
> +        }
> +    } while (i++ != last);
> +
> +    for (i = doms; i != last; i++) {
> +        CompactDomain *group_last;
> +        size_t next_depth;
> +
> +        if (i->common_octets == depth) {
> +            continue;
> +        }
> +
> +        next_depth = -1;
> +        for (group_last = i; group_last != last; group_last++) {
> +            size_t co = group_last->common_octets;
> +            if (co <= depth) {
> +                break;
> +            }
> +            if (co < next_depth) {
> +                next_depth = co;
> +            }
> +        }
> +        domain_mkxrefs(i, group_last, next_depth);
> +
> +        i = group_last;
> +        if (i == last) {
> +            break;
> +        }
> +    }
> +
> +    if (depth == 0) {
> +        return;
> +    }
> +
> +    i = doms;
> +    do {
> +        if (i != target && i->refdom == NULL) {
> +            i->refdom = target;
> +            i->common_octets = depth;
> +        }
> +    } while (i++ != last);
> +}
> +
> +static size_t
> +domain_compactify(CompactDomain *domains, size_t n)
> +{
> +    uint8_t *start = domains->self->labels, *outptr = start;
> +    size_t i;
> +
> +    for (i = 0; i < n; i++) {
> +        CompactDomain *cd = domains[i].self;
> +        CompactDomain *rd = cd->refdom;
> +
> +        if (rd != NULL) {
> +            size_t moff = (rd->labels - start)
> +                    + (rd->len - cd->common_octets);
> +            if (moff < 0x3FFFu) {
> +                cd->len -= cd->common_octets - 2;
> +                cd->labels[cd->len - 1] = moff & 0xFFu;
> +                cd->labels[cd->len - 2] = 0xC0u | (moff >> 8);
> +            }
> +        }
> +
> +        if (cd->labels != outptr) {
> +            memmove(outptr, cd->labels, cd->len);
> +            cd->labels = outptr;
> +        }
> +        outptr += cd->len;
> +    }
> +    return outptr - start;
> +}
> +
> +int
> +translate_dnssearch(Slirp *s, const char **names)
> +{
> +    size_t blocks, bsrc_start, bsrc_end, bdst_start;
> +    size_t i, num_domains, memreq = 0;
> +    uint8_t *result = NULL, *outptr;
> +    CompactDomain *domains = NULL;
> +    const char **nameptr = names;
> +
> +    while (*nameptr != NULL) {
> +        nameptr++;
> +    }
> +
> +    num_domains = nameptr - names;
> +    if (num_domains == 0) {
> +        return -2;
> +    }
> +
> +    domains = g_malloc(num_domains * sizeof(*domains));
> +
> +    for (i = 0; i < num_domains; i++) {
> +        size_t nlen = strlen(names[i]);
> +        memreq += nlen + 2; /* 1 zero octet + 1 label length octet */
> +        domains[i].self = domains + i;
> +        domains[i].len = nlen;
> +        domains[i].common_octets = 0;
> +        domains[i].refdom = NULL;
> +    }
> +
> +    /* reserve extra 2 header bytes for each 255 bytes of output */
> +    memreq += ((memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN) * OPT_HEADER_LEN;
> +    result = g_malloc(memreq * sizeof(*result));
> +
> +    outptr = result;
> +    for (i = 0; i < num_domains; i++) {
> +        domains[i].labels = outptr;
> +        domain_mklabels(domains + i, names[i]);
> +        outptr += domains[i].len;
> +    }
> +
> +    if (outptr == result) {
> +        g_free(domains);
> +        g_free(result);
> +        return -1;
> +    }
> +
> +    qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord);
> +    domain_fixup_order(domains, num_domains);
> +
> +    for (i = 1; i < num_domains; i++) {
> +        size_t cl = domain_common_label(domains + i - 1, domains + i);
> +        domains[i - 1].common_octets = cl;
> +    }
> +
> +    domain_mkxrefs(domains, domains + num_domains - 1, 0);
> +    memreq = domain_compactify(domains, num_domains);
> +
> +    blocks = (memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN;
> +    bsrc_end = memreq;
> +    bsrc_start = (blocks - 1) * MAX_OPT_LEN;
> +    bdst_start = bsrc_start + blocks * OPT_HEADER_LEN;
> +    memreq += blocks * OPT_HEADER_LEN;
> +
> +    while (blocks--) {
> +        size_t len = bsrc_end - bsrc_start;
> +        memmove(result + bdst_start, result + bsrc_start, len);
> +        result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH;
> +        result[bdst_start - 1] = len;
> +        bsrc_end = bsrc_start;
> +        bsrc_start -= MAX_OPT_LEN;
> +        bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN;
> +    }
> +
> +    g_free(domains);
> +    s->vdnssearch = result;
> +    s->vdnssearch_len = memreq;
> +    return 0;
> +}
> diff --git a/slirp/libslirp.h b/slirp/libslirp.h
> index 9b471b5..64878c4 100644
> --- a/slirp/libslirp.h
> +++ b/slirp/libslirp.h
> @@ -12,7 +12,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
>                    struct in_addr vnetmask, struct in_addr vhost,
>                    const char *vhostname, const char *tftp_path,
>                    const char *bootfile, struct in_addr vdhcp_start,
> -                  struct in_addr vnameserver, void *opaque);
> +                  struct in_addr vnameserver, const char **vdnssearch,
> +                   void *opaque);
>  void slirp_cleanup(Slirp *slirp);
> 
>  void slirp_update_timeout(uint32_t *timeout);
> diff --git a/slirp/slirp.c b/slirp/slirp.c
> index 38e0a21..3395d50 100644
> --- a/slirp/slirp.c
> +++ b/slirp/slirp.c
> @@ -203,7 +203,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
>                    struct in_addr vnetmask, struct in_addr vhost,
>                    const char *vhostname, const char *tftp_path,
>                    const char *bootfile, struct in_addr vdhcp_start,
> -                  struct in_addr vnameserver, void *opaque)
> +                  struct in_addr vnameserver, const char **vdnssearch,
> +                  void *opaque)
>  {
>      Slirp *slirp = g_malloc0(sizeof(Slirp));
> 
> @@ -233,6 +234,10 @@ Slirp *slirp_init(int restricted, struct in_addr 
> vnetwork,
>      slirp->vdhcp_startaddr = vdhcp_start;
>      slirp->vnameserver_addr = vnameserver;
> 
> +    if (vdnssearch) {
> +        translate_dnssearch(slirp, vdnssearch);
> +    }
> +
>      slirp->opaque = opaque;
> 
>      register_savevm(NULL, "slirp", 0, 3,
> @@ -252,6 +257,7 @@ void slirp_cleanup(Slirp *slirp)
>      ip_cleanup(slirp);
>      m_cleanup(slirp);
> 
> +    g_free(slirp->vdnssearch);
>      g_free(slirp->tftp_prefix);
>      g_free(slirp->bootp_filename);
>      g_free(slirp);
> diff --git a/slirp/slirp.h b/slirp/slirp.h
> index f2c5eca..f169b97 100644
> --- a/slirp/slirp.h
> +++ b/slirp/slirp.h
> @@ -235,6 +235,8 @@ struct Slirp {
>      /* bootp/dhcp states */
>      BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
>      char *bootp_filename;
> +    size_t vdnssearch_len;
> +    uint8_t *vdnssearch;
> 
>      /* tcp states */
>      struct socket tcb;
> @@ -294,6 +296,9 @@ void lprint(const char *, ...) GCC_FMT_ATTR(1, 2);
>  #define SO_OPTIONS DO_KEEPALIVE
>  #define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
> 
> +/* dnssearch.c */
> +int translate_dnssearch(Slirp *, const char **);
> +
>  /* cksum.c */
>  int cksum(struct mbuf *m, int len);
> 
> --
> 1.7.2.5
> 

Thanks for this nice feature. Applied to slirp queue with just a few
style adjustments.

Jan

-- 
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux



reply via email to

[Prev in Thread] Current Thread [Next in Thread]