[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])