qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH v2] slirp: Add support for stateless DHCPv6


From: Samuel Thibault
Subject: Re: [Qemu-devel] [PATCH v2] slirp: Add support for stateless DHCPv6
Date: Tue, 28 Jun 2016 23:56:33 +0200
User-agent: Mutt/1.5.21+34 (58baf7c9f32f) (2010-12-30)

Hello,

Thomas Huth, on Tue 28 Jun 2016 12:48:31 +0200, wrote:
> Provide basic support for stateless DHCPv6 (see RFC 3736) so
> that guests can also automatically boot via IPv6 with SLIRP
> (for IPv6 network booting, see RFC 5970 for details).
> 
> Tested with:
> 
>     qemu-system-ppc64 -nographic -vga none -boot n -net nic \
>         -net user,ipv6=yes,ipv4=no,tftp=/path/to/tftp,bootfile=ppc64.img
> 
> Signed-off-by: Thomas Huth <address@hidden>

Pushed to my tree, thanks!

Samuel
> ---
>  v2:
>  - Addressed review comments from Samuel for v1
>  - Moved the ALLDHCP_MULTICAST definition to dhcpv6.h instead of ip6.h
>  - Fixed a bug in the OPTION_ORO parsing (the index was not calculated
>    right)
> 
>  slirp/Makefile.objs |   2 +-
>  slirp/dhcpv6.c      | 209 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  slirp/dhcpv6.h      |  22 ++++++
>  slirp/udp6.c        |  13 +++-
>  4 files changed, 244 insertions(+), 2 deletions(-)
>  create mode 100644 slirp/dhcpv6.c
>  create mode 100644 slirp/dhcpv6.h
> 
> diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
> index 6748e4f..1baa1f1 100644
> --- a/slirp/Makefile.objs
> +++ b/slirp/Makefile.objs
> @@ -1,5 +1,5 @@
>  common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o \
> -               ip_input.o ip_output.o dnssearch.o
> +               ip_input.o ip_output.o dnssearch.o dhcpv6.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 udp6.o bootp.o tftp.o 
> arp_table.o \
>                  ndp_table.o
> diff --git a/slirp/dhcpv6.c b/slirp/dhcpv6.c
> new file mode 100644
> index 0000000..02c51c7
> --- /dev/null
> +++ b/slirp/dhcpv6.c
> @@ -0,0 +1,209 @@
> +/*
> + * SLIRP stateless DHCPv6
> + *
> + * We only support stateless DHCPv6, e.g. for network booting.
> + * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details.
> + *
> + * Copyright 2016 Thomas Huth, Red Hat Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License,
> + * or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "slirp.h"
> +#include "dhcpv6.h"
> +
> +/* DHCPv6 message types */
> +#define MSGTYPE_REPLY        7
> +#define MSGTYPE_INFO_REQUEST 11
> +
> +/* DHCPv6 option types */
> +#define OPTION_CLIENTID      1
> +#define OPTION_IAADDR        5
> +#define OPTION_ORO           6
> +#define OPTION_DNS_SERVERS   23
> +#define OPTION_BOOTFILE_URL  59
> +
> +struct requested_infos {
> +    uint8_t *client_id;
> +    int client_id_len;
> +    bool want_dns;
> +    bool want_boot_url;
> +};
> +
> +/**
> + * Analyze the info request message sent by the client to see what data it
> + * provided and what it wants to have. The information is gathered in the
> + * "requested_infos" struct. Note that client_id (if provided) points into
> + * the odata region, thus the caller must keep odata valid as long as it
> + * needs to access the requested_infos struct.
> + */
> +static int dhcpv6_parse_info_request(uint8_t *odata, int olen,
> +                                     struct requested_infos *ri)
> +{
> +    int i, req_opt;
> +
> +    while (olen > 4) {
> +        /* Parse one option */
> +        int option = odata[0] << 8 | odata[1];
> +        int len = odata[2] << 8 | odata[3];
> +
> +        if (len + 4 > olen) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "Guest sent bad DHCPv6 
> packet!\n");
> +            return -E2BIG;
> +        }
> +
> +        switch (option) {
> +        case OPTION_IAADDR:
> +            /* According to RFC3315, we must discard requests with IA option 
> */
> +            return -EINVAL;
> +        case OPTION_CLIENTID:
> +            if (len > 256) {
> +                /* Avoid very long IDs which could cause problems later */
> +                return -E2BIG;
> +            }
> +            ri->client_id = odata + 4;
> +            ri->client_id_len = len;
> +            break;
> +        case OPTION_ORO:        /* Option request option */
> +            if (len & 1) {
> +                return -EINVAL;
> +            }
> +            /* Check which options the client wants to have */
> +            for (i = 0; i < len; i += 2) {
> +                req_opt = odata[4 + i] << 8 | odata[4 + i + 1];
> +                switch (req_opt) {
> +                case OPTION_DNS_SERVERS:
> +                    ri->want_dns = true;
> +                    break;
> +                case OPTION_BOOTFILE_URL:
> +                    ri->want_boot_url = true;
> +                    break;
> +                default:
> +                    DEBUG_MISC((dfd, "dhcpv6: Unsupported option request 
> %d\n",
> +                                req_opt));
> +                }
> +            }
> +            break;
> +        default:
> +            DEBUG_MISC((dfd, "dhcpv6 info req: Unsupported option %d, 
> len=%d\n",
> +                        option, len));
> +        }
> +
> +        odata += len + 4;
> +        olen -= len + 4;
> +    }
> +
> +    return 0;
> +}
> +
> +
> +/**
> + * Handle information request messages
> + */
> +static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas,
> +                                uint32_t xid, uint8_t *odata, int olen)
> +{
> +    struct requested_infos ri = { NULL };
> +    struct sockaddr_in6 sa6, da6;
> +    struct mbuf *m;
> +    uint8_t *resp;
> +
> +    if (dhcpv6_parse_info_request(odata, olen, &ri) < 0) {
> +        return;
> +    }
> +
> +    m = m_get(slirp);
> +    if (!m) {
> +        return;
> +    }
> +    memset(m->m_data, 0, m->m_size);
> +    m->m_data += IF_MAXLINKHDR;
> +    resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr);
> +
> +    /* Fill in response */
> +    *resp++ = MSGTYPE_REPLY;
> +    *resp++ = (uint8_t)(xid >> 16);
> +    *resp++ = (uint8_t)(xid >> 8);
> +    *resp++ = (uint8_t)xid;
> +
> +    if (ri.client_id) {
> +        *resp++ = OPTION_CLIENTID >> 8;         /* option-code high byte */
> +        *resp++ = OPTION_CLIENTID;              /* option-code low byte */
> +        *resp++ = ri.client_id_len >> 8;        /* option-len high byte */
> +        *resp++ = ri.client_id_len;             /* option-len low byte */
> +        memcpy(resp, ri.client_id, ri.client_id_len);
> +        resp += ri.client_id_len;
> +    }
> +    if (ri.want_dns) {
> +        *resp++ = OPTION_DNS_SERVERS >> 8;      /* option-code high byte */
> +        *resp++ = OPTION_DNS_SERVERS;           /* option-code low byte */
> +        *resp++ = 0;                            /* option-len high byte */
> +        *resp++ = 16;                           /* option-len low byte */
> +        memcpy(resp, &slirp->vnameserver_addr6, 16);
> +        resp += 16;
> +    }
> +    if (ri.want_boot_url) {
> +        uint8_t *sa = slirp->vhost_addr6.s6_addr;
> +        int slen, smaxlen;
> +
> +        *resp++ = OPTION_BOOTFILE_URL >> 8;     /* option-code high byte */
> +        *resp++ = OPTION_BOOTFILE_URL;          /* option-code low byte */
> +        smaxlen = (uint8_t *)m->m_data + IF_MTU - (resp + 2);
> +        slen = snprintf((char *)resp + 2, smaxlen,
> +                        "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:";
> +                                "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s",
> +                        sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], 
> sa[7],
> +                        sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14],
> +                        sa[15], slirp->bootp_filename);
> +        slen = min(slen, smaxlen);
> +        *resp++ = slen >> 8;                    /* option-len high byte */
> +        *resp++ = slen;                         /* option-len low byte */
> +        resp += slen;
> +    }
> +
> +    sa6.sin6_addr = slirp->vhost_addr6;
> +    sa6.sin6_port = DHCPV6_SERVER_PORT;
> +    da6.sin6_addr = srcsas->sin6_addr;
> +    da6.sin6_port = srcsas->sin6_port;
> +    m->m_data += sizeof(struct ip6) + sizeof(struct udphdr);
> +    m->m_len = resp - (uint8_t *)m->m_data;
> +    udp6_output(NULL, m, &sa6, &da6);
> +}
> +
> +/**
> + * Handle DHCPv6 messages sent by the client
> + */
> +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m)
> +{
> +    uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr);
> +    int data_len = m->m_len - sizeof(struct udphdr);
> +    uint32_t xid;
> +
> +    if (data_len < 4) {
> +        return;
> +    }
> +
> +    xid = ntohl(*(uint32_t *)data) & 0xffffff;
> +
> +    switch (data[0]) {
> +    case MSGTYPE_INFO_REQUEST:
> +        dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4);
> +        break;
> +    default:
> +        DEBUG_MISC((dfd, "dhcpv6_input: Unsupported message type 0x%x\n",
> +                    data[0]));
> +    }
> +}
> diff --git a/slirp/dhcpv6.h b/slirp/dhcpv6.h
> new file mode 100644
> index 0000000..9189cd3
> --- /dev/null
> +++ b/slirp/dhcpv6.h
> @@ -0,0 +1,22 @@
> +/*
> + * Definitions and prototypes for SLIRP stateless DHCPv6
> + *
> + * Copyright 2016 Thomas Huth, Red Hat Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2
> + * or later. See the COPYING file in the top-level directory.
> + */
> +#ifndef SLIRP_DHCPV6_H
> +#define SLIRP_DHCPV6_H
> +
> +#define DHCPV6_SERVER_PORT 547
> +
> +#define ALLDHCP_MULTICAST { .s6_addr = \
> +                            { 0xff, 0x02, 0x00, 0x00,\
> +                            0x00, 0x00, 0x00, 0x00,\
> +                            0x00, 0x00, 0x00, 0x00,\
> +                            0x00, 0x01, 0x00, 0x02 } }
> +
> +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m);
> +
> +#endif
> diff --git a/slirp/udp6.c b/slirp/udp6.c
> index 94efb13..9fa314b 100644
> --- a/slirp/udp6.c
> +++ b/slirp/udp6.c
> @@ -7,6 +7,7 @@
>  #include "qemu-common.h"
>  #include "slirp.h"
>  #include "udp.h"
> +#include "dhcpv6.h"
>  
>  void udp6_input(struct mbuf *m)
>  {
> @@ -61,7 +62,17 @@ void udp6_input(struct mbuf *m)
>      lhost.sin6_addr = ip->ip_src;
>      lhost.sin6_port = uh->uh_sport;
>  
> -    /* TODO handle DHCP/BOOTP */
> +    /* handle DHCPv6 */
> +    if (ntohs(uh->uh_dport) == DHCPV6_SERVER_PORT &&
> +        (in6_equal(&ip->ip_dst, &slirp->vhost_addr6) ||
> +         in6_equal(&ip->ip_dst, &(struct in6_addr)ALLDHCP_MULTICAST))) {
> +        m->m_data += iphlen;
> +        m->m_len -= iphlen;
> +        dhcpv6_input(&lhost, m);
> +        m->m_data -= iphlen;
> +        m->m_len += iphlen;
> +        goto bad;
> +    }
>  
>      /* handle TFTP */
>      if (ntohs(uh->uh_dport) == TFTP_SERVER &&
> -- 
> 1.8.3.1
> 

-- 
Samuel
/*
 * [...] Note that 120 sec is defined in the protocol as the maximum
 * possible RTT.  I guess we'll have to use something other than TCP
 * to talk to the University of Mars.
 * PAWS allows us longer timeouts and large windows, so once implemented
 * ftp to mars will work nicely.
 */
(from /usr/src/linux/net/inet/tcp.c, concerning RTT [retransmission timeout])



reply via email to

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