aboutsummaryrefslogtreecommitdiff
path: root/slirp
diff options
context:
space:
mode:
Diffstat (limited to 'slirp')
-rw-r--r--slirp/Makefile.objs2
-rw-r--r--slirp/dhcpv6.c209
-rw-r--r--slirp/dhcpv6.h22
-rw-r--r--slirp/ip6.h9
-rw-r--r--slirp/ip6_icmp.c27
-rw-r--r--slirp/ip6_icmp.h12
-rw-r--r--slirp/libslirp.h1
-rw-r--r--slirp/slirp.c126
-rw-r--r--slirp/socket.c7
-rw-r--r--slirp/tftp.c4
-rw-r--r--slirp/udp6.c13
11 files changed, 392 insertions, 40 deletions
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
index 6748e4f60a..1baa1f1c7c 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 0000000000..02c51c7756
--- /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 0000000000..9189cd3f2d
--- /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/ip6.h b/slirp/ip6.h
index 8ddfa242c4..da23de66f1 100644
--- a/slirp/ip6.h
+++ b/slirp/ip6.h
@@ -26,6 +26,12 @@
0x00, 0x00, 0x00, 0x00,\
0x00, 0x00, 0x00, 0x02 } }
+#define ZERO_ADDR { .s6_addr = \
+ { 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00 } }
+
static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b)
{
return memcmp(a, b, sizeof(*a)) == 0;
@@ -84,6 +90,9 @@ static inline bool in6_equal_mach(const struct in6_addr *a,
#define in6_solicitednode_multicast(a)\
(in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104))
+#define in6_zero(a)\
+ (in6_equal(a, &(struct in6_addr)ZERO_ADDR))
+
/* Compute emulated host MAC address from its ipv6 address */
static inline void in6_compute_ethaddr(struct in6_addr ip,
uint8_t eth[ETH_ALEN])
diff --git a/slirp/ip6_icmp.c b/slirp/ip6_icmp.c
index 48016a9f85..6d18e28985 100644
--- a/slirp/ip6_icmp.c
+++ b/slirp/ip6_icmp.c
@@ -148,7 +148,11 @@ void ndp_send_ra(Slirp *slirp)
rip->ip_nh = IPPROTO_ICMPV6;
rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
+ NDPOPT_LINKLAYER_LEN
- + NDPOPT_PREFIXINFO_LEN);
+ + NDPOPT_PREFIXINFO_LEN
+#ifndef _WIN32
+ + NDPOPT_RDNSS_LEN
+#endif
+ );
t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
/* Build ICMPv6 packet */
@@ -166,16 +170,16 @@ void ndp_send_ra(Slirp *slirp)
ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
+ t->m_data += ICMP6_NDP_RA_MINLEN;
/* Source link-layer address (NDP option) */
- t->m_data += ICMP6_NDP_RA_MINLEN;
struct ndpopt *opt = mtod(t, struct ndpopt *);
opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
+ t->m_data += NDPOPT_LINKLAYER_LEN;
/* Prefix information (NDP option) */
- t->m_data += NDPOPT_LINKLAYER_LEN;
struct ndpopt *opt2 = mtod(t, struct ndpopt *);
opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
@@ -187,8 +191,25 @@ void ndp_send_ra(Slirp *slirp)
opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
opt2->ndpopt_prefixinfo.reserved2 = 0;
opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
+ t->m_data += NDPOPT_PREFIXINFO_LEN;
+
+#ifndef _WIN32
+ /* Prefix information (NDP option) */
+ /* disabled for windows for now, until get_dns6_addr is implemented */
+ struct ndpopt *opt3 = mtod(t, struct ndpopt *);
+ opt3->ndpopt_type = NDPOPT_RDNSS;
+ opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
+ opt3->ndpopt_rdnss.reserved = 0;
+ opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
+ opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
+ t->m_data += NDPOPT_RDNSS_LEN;
+#endif
/* ICMPv6 Checksum */
+#ifndef _WIN32
+ t->m_data -= NDPOPT_RDNSS_LEN;
+#endif
+ t->m_data -= NDPOPT_PREFIXINFO_LEN;
t->m_data -= NDPOPT_LINKLAYER_LEN;
t->m_data -= ICMP6_NDP_RA_MINLEN;
t->m_data -= sizeof(struct ip6);
diff --git a/slirp/ip6_icmp.h b/slirp/ip6_icmp.h
index 9460bf837a..2282d29076 100644
--- a/slirp/ip6_icmp.h
+++ b/slirp/ip6_icmp.h
@@ -122,6 +122,7 @@ struct ndpopt {
uint8_t ndpopt_len; /* /!\ In units of 8 octets */
union {
unsigned char linklayer_addr[6]; /* Source/Target Link-layer */
+#define ndpopt_linklayer ndpopt_body.linklayer_addr
struct prefixinfo { /* Prefix Information */
uint8_t prefix_length;
#ifdef HOST_WORDS_BIGENDIAN
@@ -134,19 +135,26 @@ struct ndpopt {
uint32_t reserved2;
struct in6_addr prefix;
} QEMU_PACKED prefixinfo;
- } ndpopt_body;
-#define ndpopt_linklayer ndpopt_body.linklayer_addr
#define ndpopt_prefixinfo ndpopt_body.prefixinfo
+ struct rdnss {
+ uint16_t reserved;
+ uint32_t lifetime;
+ struct in6_addr addr;
+ } QEMU_PACKED rdnss;
+#define ndpopt_rdnss ndpopt_body.rdnss
+ } ndpopt_body;
} QEMU_PACKED;
/* NDP options type */
#define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */
#define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */
#define NDPOPT_PREFIX_INFO 3 /* Prefix Information */
+#define NDPOPT_RDNSS 25 /* Recursive DNS Server Address */
/* NDP options size, in octets. */
#define NDPOPT_LINKLAYER_LEN 8
#define NDPOPT_PREFIXINFO_LEN 32
+#define NDPOPT_RDNSS_LEN 24
/*
* Definition of type and code field values.
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 127aa41d40..b6fc584219 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -7,6 +7,7 @@ struct Slirp;
typedef struct Slirp Slirp;
int get_dns_addr(struct in_addr *pdns_addr);
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id);
Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 9f4bea3d3b..7eb183d0e9 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -30,6 +30,10 @@
#include "hw/hw.h"
#include "qemu/cutils.h"
+#ifndef _WIN32
+#include <net/if.h>
+#endif
+
/* host loopback address */
struct in_addr loopback_addr;
/* host loopback network mask */
@@ -46,7 +50,13 @@ static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
QTAILQ_HEAD_INITIALIZER(slirp_instances);
static struct in_addr dns_addr;
+#ifndef _WIN32
+static struct in6_addr dns6_addr;
+#endif
static u_int dns_addr_time;
+#ifndef _WIN32
+static u_int dns6_addr_time;
+#endif
#define TIMEOUT_FAST 2 /* milliseconds */
#define TIMEOUT_SLOW 499 /* milliseconds */
@@ -100,6 +110,11 @@ int get_dns_addr(struct in_addr *pdns_addr)
return 0;
}
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
+{
+ return -1;
+}
+
static void winsock_cleanup(void)
{
WSACleanup();
@@ -107,33 +122,39 @@ static void winsock_cleanup(void)
#else
-static struct stat dns_addr_stat;
+static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
+ socklen_t addrlen,
+ struct stat *cached_stat, u_int *cached_time)
+{
+ struct stat old_stat;
+ if (curtime - *cached_time < TIMEOUT_DEFAULT) {
+ memcpy(pdns_addr, cached_addr, addrlen);
+ return 0;
+ }
+ old_stat = *cached_stat;
+ if (stat("/etc/resolv.conf", cached_stat) != 0) {
+ return -1;
+ }
+ if (cached_stat->st_dev == old_stat.st_dev
+ && cached_stat->st_ino == old_stat.st_ino
+ && cached_stat->st_size == old_stat.st_size
+ && cached_stat->st_mtime == old_stat.st_mtime) {
+ memcpy(pdns_addr, cached_addr, addrlen);
+ return 0;
+ }
+ return 1;
+}
-int get_dns_addr(struct in_addr *pdns_addr)
+static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
+ socklen_t addrlen, uint32_t *scope_id,
+ u_int *cached_time)
{
char buff[512];
char buff2[257];
FILE *f;
int found = 0;
- struct in_addr tmp_addr;
-
- if (dns_addr.s_addr != 0) {
- struct stat old_stat;
- if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
- *pdns_addr = dns_addr;
- return 0;
- }
- old_stat = dns_addr_stat;
- if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
- return -1;
- if ((dns_addr_stat.st_dev == old_stat.st_dev)
- && (dns_addr_stat.st_ino == old_stat.st_ino)
- && (dns_addr_stat.st_size == old_stat.st_size)
- && (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
- *pdns_addr = dns_addr;
- return 0;
- }
- }
+ void *tmp_addr = alloca(addrlen);
+ unsigned if_index;
f = fopen("/etc/resolv.conf", "r");
if (!f)
@@ -144,13 +165,25 @@ int get_dns_addr(struct in_addr *pdns_addr)
#endif
while (fgets(buff, 512, f) != NULL) {
if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
- if (!inet_aton(buff2, &tmp_addr))
+ char *c = strchr(buff2, '%');
+ if (c) {
+ if_index = if_nametoindex(c + 1);
+ *c = '\0';
+ } else {
+ if_index = 0;
+ }
+
+ if (!inet_pton(af, buff2, tmp_addr)) {
continue;
+ }
/* If it's the first one, set it to dns_addr */
if (!found) {
- *pdns_addr = tmp_addr;
- dns_addr = tmp_addr;
- dns_addr_time = curtime;
+ memcpy(pdns_addr, tmp_addr, addrlen);
+ memcpy(cached_addr, tmp_addr, addrlen);
+ if (scope_id) {
+ *scope_id = if_index;
+ }
+ *cached_time = curtime;
}
#ifdef DEBUG
else
@@ -163,8 +196,14 @@ int get_dns_addr(struct in_addr *pdns_addr)
break;
}
#ifdef DEBUG
- else
- fprintf(stderr, "%s", inet_ntoa(tmp_addr));
+ else {
+ char s[INET6_ADDRSTRLEN];
+ char *res = inet_ntop(af, tmp_addr, s, sizeof(s));
+ if (!res) {
+ res = "(string conversion error)";
+ }
+ fprintf(stderr, "%s", res);
+ }
#endif
}
}
@@ -174,6 +213,39 @@ int get_dns_addr(struct in_addr *pdns_addr)
return 0;
}
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+ static struct stat dns_addr_stat;
+
+ if (dns_addr.s_addr != 0) {
+ int ret;
+ ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr),
+ &dns_addr_stat, &dns_addr_time);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+ return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr,
+ sizeof(dns_addr), NULL, &dns_addr_time);
+}
+
+int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
+{
+ static struct stat dns6_addr_stat;
+
+ if (!in6_zero(&dns6_addr)) {
+ int ret;
+ ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),
+ &dns6_addr_stat, &dns6_addr_time);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+ return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr,
+ sizeof(dns6_addr),
+ scope_id, &dns6_addr_time);
+}
+
#endif
static void slirp_init_once(void)
diff --git a/slirp/socket.c b/slirp/socket.c
index b336586c7b..02e89ce1f2 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -816,9 +816,12 @@ void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6,
slirp->vprefix_len)) {
if (in6_equal(&so->so_faddr6, &slirp->vnameserver_addr6)) {
- /*if (get_dns_addr(&addr) < 0) {*/ /* TODO */
+ uint32_t scope_id;
+ if (get_dns6_addr(&sin6->sin6_addr, &scope_id) >= 0) {
+ sin6->sin6_scope_id = scope_id;
+ } else {
sin6->sin6_addr = in6addr_loopback;
- /*}*/
+ }
} else {
sin6->sin6_addr = in6addr_loopback;
}
diff --git a/slirp/tftp.c b/slirp/tftp.c
index 12b5ff6e25..367340222d 100644
--- a/slirp/tftp.c
+++ b/slirp/tftp.c
@@ -208,8 +208,6 @@ static void tftp_send_error(struct tftp_session *spt,
goto out;
}
- memset(m->m_data, 0, m->m_size);
-
tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_ERROR);
@@ -237,8 +235,6 @@ static void tftp_send_next_block(struct tftp_session *spt,
return;
}
- memset(m->m_data, 0, m->m_size);
-
tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_DATA);
diff --git a/slirp/udp6.c b/slirp/udp6.c
index 94efb13240..9fa314bc2d 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 &&