summaryrefslogtreecommitdiff
path: root/slirp
diff options
context:
space:
mode:
Diffstat (limited to 'slirp')
-rw-r--r--slirp/Makefile.objs6
-rw-r--r--slirp/cksum.c25
-rw-r--r--slirp/if.c2
-rw-r--r--slirp/ip6.h137
-rw-r--r--slirp/ip6_icmp.c350
-rw-r--r--slirp/ip6_icmp.h203
-rw-r--r--slirp/ip6_input.c67
-rw-r--r--slirp/ip6_output.c40
-rw-r--r--slirp/ndp_table.c90
-rw-r--r--slirp/slirp.c61
-rw-r--r--slirp/slirp.h37
-rw-r--r--slirp/socket.h7
12 files changed, 1019 insertions, 6 deletions
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
index 2daa9dc58d..4e3a289b08 100644
--- a/slirp/Makefile.objs
+++ b/slirp/Makefile.objs
@@ -1,3 +1,5 @@
-common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
+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
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
+common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o \
+ ndp_table.o
diff --git a/slirp/cksum.c b/slirp/cksum.c
index bc0d017d24..2ad0e6540d 100644
--- a/slirp/cksum.c
+++ b/slirp/cksum.c
@@ -138,3 +138,28 @@ cont:
REDUCE;
return (~sum & 0xffff);
}
+
+int ip6_cksum(struct mbuf *m)
+{
+ /* TODO: Optimize this by being able to pass the ip6_pseudohdr to cksum
+ * separately from the mbuf */
+ struct ip6 save_ip, *ip = mtod(m, struct ip6 *);
+ struct ip6_pseudohdr *ih = mtod(m, struct ip6_pseudohdr *);
+ int sum;
+
+ save_ip = *ip;
+
+ ih->ih_src = save_ip.ip_src;
+ ih->ih_dst = save_ip.ip_dst;
+ ih->ih_pl = htonl((uint32_t)ntohs(save_ip.ip_pl));
+ ih->ih_zero_hi = 0;
+ ih->ih_zero_lo = 0;
+ ih->ih_nh = save_ip.ip_nh;
+
+ sum = cksum(m, ((int)sizeof(struct ip6_pseudohdr))
+ + ntohl(ih->ih_pl));
+
+ *ip = save_ip;
+
+ return sum;
+}
diff --git a/slirp/if.c b/slirp/if.c
index 93d7cc0b43..2e21f438e8 100644
--- a/slirp/if.c
+++ b/slirp/if.c
@@ -194,7 +194,7 @@ void if_start(Slirp *slirp)
/* Try to send packet unless it already expired */
if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
- /* Packet is delayed due to pending ARP resolution */
+ /* Packet is delayed due to pending ARP or NDP resolution */
continue;
}
diff --git a/slirp/ip6.h b/slirp/ip6.h
new file mode 100644
index 0000000000..731ee72d77
--- /dev/null
+++ b/slirp/ip6.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#ifndef SLIRP_IP6_H_
+#define SLIRP_IP6_H_
+
+#include "net/eth.h"
+
+#define ALLNODES_MULTICAST { .s6_addr = \
+ { 0xff, 0x02, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x01 } }
+
+#define SOLICITED_NODE_PREFIX { .s6_addr = \
+ { 0xff, 0x02, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x01,\
+ 0xff, 0x00, 0x00, 0x00 } }
+
+#define LINKLOCAL_ADDR { .s6_addr = \
+ { 0xfe, 0x80, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x02 } }
+
+static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b)
+{
+ return memcmp(a, b, sizeof(*a)) == 0;
+}
+
+static inline bool in6_equal_net(const struct in6_addr *a,
+ const struct in6_addr *b,
+ int prefix_len)
+{
+ if (memcmp(a, b, prefix_len / 8) != 0) {
+ return 0;
+ }
+
+ if (prefix_len % 8 == 0) {
+ return 1;
+ }
+
+ return a->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8))
+ == b->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8));
+}
+
+static inline bool in6_equal_mach(const struct in6_addr *a,
+ const struct in6_addr *b,
+ int prefix_len)
+{
+ if (memcmp(&(a->s6_addr[(prefix_len + 7) / 8]),
+ &(b->s6_addr[(prefix_len + 7) / 8]),
+ 16 - (prefix_len + 7) / 8) != 0) {
+ return 0;
+ }
+
+ if (prefix_len % 8 == 0) {
+ return 1;
+ }
+
+ return (a->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1))
+ == (b->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1));
+}
+
+
+#define in6_equal_router(a)\
+ ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\
+ && in6_equal_mach(a, &slirp->vhost_addr6, slirp->vprefix_len))\
+ || (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\
+ && in6_equal_mach(a, &slirp->vhost_addr6, 64)))
+
+#define in6_equal_dns(a) 0
+
+#define in6_equal_host(a)\
+ (in6_equal_router(a) || in6_equal_dns(a))
+
+#define in6_solicitednode_multicast(a)\
+ (in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104))
+
+/* Compute emulated host MAC address from its ipv6 address */
+static inline void in6_compute_ethaddr(struct in6_addr ip,
+ uint8_t eth[ETH_ALEN])
+{
+ eth[0] = 0x52;
+ eth[1] = 0x56;
+ memcpy(&eth[2], &ip.s6_addr[16 - (ETH_ALEN - 2)], ETH_ALEN - 2);
+}
+
+/*
+ * Definitions for internet protocol version 6.
+ * Per RFC 2460, December 1998.
+ */
+#define IP6VERSION 6
+#define IP6_HOP_LIMIT 255
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip6 {
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t
+ ip_v:4, /* version */
+ ip_tc_hi:4, /* traffic class */
+ ip_tc_lo:4,
+ ip_fl_hi:4, /* flow label */
+ ip_fl_lo:16;
+#else
+ uint32_t
+ ip_tc_hi:4,
+ ip_v:4,
+ ip_fl_hi:4,
+ ip_tc_lo:4,
+ ip_fl_lo:16;
+#endif
+ uint16_t ip_pl; /* payload length */
+ uint8_t ip_nh; /* next header */
+ uint8_t ip_hl; /* hop limit */
+ struct in6_addr ip_src, ip_dst; /* source and dest address */
+} QEMU_PACKED;
+
+/*
+ * IPv6 pseudo-header used by upper-layer protocols
+ */
+struct ip6_pseudohdr {
+ struct in6_addr ih_src; /* source internet address */
+ struct in6_addr ih_dst; /* destination internet address */
+ uint32_t ih_pl; /* upper-layer packet length */
+ uint16_t ih_zero_hi; /* zero */
+ uint8_t ih_zero_lo; /* zero */
+ uint8_t ih_nh; /* next header */
+} QEMU_PACKED;
+
+
+#endif
diff --git a/slirp/ip6_icmp.c b/slirp/ip6_icmp.c
new file mode 100644
index 0000000000..9f3fd4a332
--- /dev/null
+++ b/slirp/ip6_icmp.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#include "qemu/osdep.h"
+#include "slirp.h"
+#include "ip6_icmp.h"
+#include "qemu/timer.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include <time.h>
+
+#define NDP_Interval g_rand_int_range(slirp->grand, \
+ NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
+
+static void ra_timer_handler(void *opaque)
+{
+ Slirp *slirp = opaque;
+ timer_mod(slirp->ra_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
+ ndp_send_ra(slirp);
+}
+
+void icmp6_init(Slirp *slirp)
+{
+ slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
+ timer_mod(slirp->ra_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
+}
+
+void icmp6_cleanup(Slirp *slirp)
+{
+ timer_del(slirp->ra_timer);
+ timer_free(slirp->ra_timer);
+}
+
+static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
+ struct icmp6 *icmp)
+{
+ struct mbuf *t = m_get(slirp);
+ t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
+ memcpy(t->m_data, m->m_data, t->m_len);
+
+ /* IPv6 Packet */
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_dst = ip->ip_src;
+ rip->ip_src = ip->ip_dst;
+
+ /* ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = ICMP6_ECHO_REPLY;
+ ricmp->icmp6_cksum = 0;
+
+ /* Checksum */
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 0);
+}
+
+/*
+ * Send NDP Router Advertisement
+ */
+void ndp_send_ra(Slirp *slirp)
+{
+ DEBUG_CALL("ndp_send_ra");
+
+ /* Build IPv6 packet */
+ struct mbuf *t = m_get(slirp);
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
+ rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
+ rip->ip_nh = IPPROTO_ICMPV6;
+ rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
+ + NDPOPT_LINKLAYER_LEN
+ + NDPOPT_PREFIXINFO_LEN);
+ t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
+
+ /* Build ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = ICMP6_NDP_RA;
+ ricmp->icmp6_code = 0;
+ ricmp->icmp6_cksum = 0;
+
+ /* NDP */
+ ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
+ ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
+ ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
+ ricmp->icmp6_nra.reserved = 0;
+ ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
+ ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
+ ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
+
+ /* 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);
+
+ /* 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;
+ opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
+ opt2->ndpopt_prefixinfo.L = 1;
+ opt2->ndpopt_prefixinfo.A = 1;
+ opt2->ndpopt_prefixinfo.reserved1 = 0;
+ opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
+ opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
+ opt2->ndpopt_prefixinfo.reserved2 = 0;
+ opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
+
+ /* ICMPv6 Checksum */
+ t->m_data -= NDPOPT_LINKLAYER_LEN;
+ t->m_data -= ICMP6_NDP_RA_MINLEN;
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 0);
+}
+
+/*
+ * Send NDP Neighbor Solitication
+ */
+void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
+{
+ DEBUG_CALL("ndp_send_ns");
+#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("target = %s", addrstr);
+#endif
+
+ /* Build IPv6 packet */
+ struct mbuf *t = m_get(slirp);
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_src = slirp->vhost_addr6;
+ rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
+ memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
+ rip->ip_nh = IPPROTO_ICMPV6;
+ rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
+ t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
+
+ /* Build ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = ICMP6_NDP_NS;
+ ricmp->icmp6_code = 0;
+ ricmp->icmp6_cksum = 0;
+
+ /* NDP */
+ ricmp->icmp6_nns.reserved = 0;
+ ricmp->icmp6_nns.target = addr;
+
+ /* Build NDP option */
+ t->m_data += ICMP6_NDP_NS_MINLEN;
+ struct ndpopt *opt = mtod(t, struct ndpopt *);
+ opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
+ opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
+ in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
+
+ /* ICMPv6 Checksum */
+ t->m_data -= ICMP6_NDP_NA_MINLEN;
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 1);
+}
+
+/*
+ * Send NDP Neighbor Advertisement
+ */
+static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
+{
+ /* Build IPv6 packet */
+ struct mbuf *t = m_get(slirp);
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_src = icmp->icmp6_nns.target;
+ if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
+ rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
+ } else {
+ rip->ip_dst = ip->ip_src;
+ }
+ rip->ip_nh = IPPROTO_ICMPV6;
+ rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
+ + NDPOPT_LINKLAYER_LEN);
+ t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
+
+ /* Build ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = ICMP6_NDP_NA;
+ ricmp->icmp6_code = 0;
+ ricmp->icmp6_cksum = 0;
+
+ /* NDP */
+ ricmp->icmp6_nna.R = NDP_IsRouter;
+ ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
+ ricmp->icmp6_nna.O = 1;
+ ricmp->icmp6_nna.reserved_hi = 0;
+ ricmp->icmp6_nna.reserved_lo = 0;
+ ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
+
+ /* Build NDP option */
+ t->m_data += ICMP6_NDP_NA_MINLEN;
+ struct ndpopt *opt = mtod(t, struct ndpopt *);
+ opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
+ opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
+ in6_compute_ethaddr(ricmp->icmp6_nna.target,
+ opt->ndpopt_linklayer);
+
+ /* ICMPv6 Checksum */
+ t->m_data -= ICMP6_NDP_NA_MINLEN;
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 0);
+}
+
+/*
+ * Process a NDP message
+ */
+static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
+ struct icmp6 *icmp)
+{
+ m->m_len += ETH_HLEN;
+ m->m_data -= ETH_HLEN;
+ struct ethhdr *eth = mtod(m, struct ethhdr *);
+ m->m_len -= ETH_HLEN;
+ m->m_data += ETH_HLEN;
+
+ switch (icmp->icmp6_type) {
+ case ICMP6_NDP_RS:
+ DEBUG_CALL(" type = Router Solicitation");
+ if (ip->ip_hl == 255
+ && icmp->icmp6_code == 0
+ && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
+ /* Gratuitous NDP */
+ ndp_table_add(slirp, ip->ip_src, eth->h_source);
+
+ ndp_send_ra(slirp);
+ }
+ break;
+
+ case ICMP6_NDP_RA:
+ DEBUG_CALL(" type = Router Advertisement");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Warning: guest sent NDP RA, but shouldn't");
+ break;
+
+ case ICMP6_NDP_NS:
+ DEBUG_CALL(" type = Neighbor Solicitation");
+ if (ip->ip_hl == 255
+ && icmp->icmp6_code == 0
+ && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
+ && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
+ && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
+ || in6_solicitednode_multicast(&ip->ip_dst))) {
+ if (in6_equal_host(&icmp->icmp6_nns.target)) {
+ /* Gratuitous NDP */
+ ndp_table_add(slirp, ip->ip_src, eth->h_source);
+ ndp_send_na(slirp, ip, icmp);
+ }
+ }
+ break;
+
+ case ICMP6_NDP_NA:
+ DEBUG_CALL(" type = Neighbor Advertisement");
+ if (ip->ip_hl == 255
+ && icmp->icmp6_code == 0
+ && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
+ && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
+ && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
+ || icmp->icmp6_nna.S == 0)) {
+ ndp_table_add(slirp, ip->ip_src, eth->h_source);
+ }
+ break;
+
+ case ICMP6_NDP_REDIRECT:
+ DEBUG_CALL(" type = Redirect");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Warning: guest sent NDP REDIRECT, but shouldn't");
+ break;
+ }
+}
+
+/*
+ * Process a received ICMPv6 message.
+ */
+void icmp6_input(struct mbuf *m)
+{
+ struct icmp6 *icmp;
+ struct ip6 *ip = mtod(m, struct ip6 *);
+ Slirp *slirp = m->slirp;
+ int hlen = sizeof(struct ip6);
+
+ DEBUG_CALL("icmp6_input");
+ DEBUG_ARG("m = %lx", (long) m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
+ goto end;
+ }
+
+ if (ip6_cksum(m)) {
+ goto end;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icmp = mtod(m, struct icmp6 *);
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
+ switch (icmp->icmp6_type) {
+ case ICMP6_ECHO_REQUEST:
+ if (in6_equal_host(&ip->ip_dst)) {
+ icmp6_send_echoreply(m, slirp, ip, icmp);
+ } else {
+ /* TODO */
+ error_report("external icmpv6 not supported yet");
+ }
+ break;
+
+ case ICMP6_NDP_RS:
+ case ICMP6_NDP_RA:
+ case ICMP6_NDP_NS:
+ case ICMP6_NDP_NA:
+ case ICMP6_NDP_REDIRECT:
+ ndp_input(m, slirp, ip, icmp);
+ break;
+
+ case ICMP6_UNREACH:
+ case ICMP6_TOOBIG:
+ case ICMP6_TIMXCEED:
+ case ICMP6_PARAMPROB:
+ /* XXX? report error? close socket? */
+ default:
+ break;
+ }
+
+end:
+ m_free(m);
+}
diff --git a/slirp/ip6_icmp.h b/slirp/ip6_icmp.h
new file mode 100644
index 0000000000..b2c40d6c96
--- /dev/null
+++ b/slirp/ip6_icmp.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#ifndef SLIRP_NETINET_ICMP6_H_
+#define SLIRP_NETINET_ICMP6_H_
+
+/*
+ * Interface Control Message Protocol version 6 Definitions.
+ * Per RFC 4443, March 2006.
+ *
+ * Network Discover Protocol Definitions.
+ * Per RFC 4861, September 2007.
+ */
+
+struct icmp6_echo { /* Echo Messages */
+ uint16_t id;
+ uint16_t seq_num;
+};
+
+/*
+ * NDP Messages
+ */
+struct ndp_rs { /* Router Solicitation Message */
+ uint32_t reserved;
+};
+
+struct ndp_ra { /* Router Advertisement Message */
+ uint8_t chl; /* Cur Hop Limit */
+#ifdef HOST_WORDS_BIGENDIAN
+ uint8_t
+ M:1,
+ O:1,
+ reserved:6;
+#else
+ uint8_t
+ reserved:6,
+ O:1,
+ M:1;
+#endif
+ uint16_t lifetime; /* Router Lifetime */
+ uint32_t reach_time; /* Reachable Time */
+ uint32_t retrans_time; /* Retrans Timer */
+} QEMU_PACKED;
+
+struct ndp_ns { /* Neighbor Solicitation Message */
+ uint32_t reserved;
+ struct in6_addr target; /* Target Address */
+} QEMU_PACKED;
+
+struct ndp_na { /* Neighbor Advertisement Message */
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t
+ R:1, /* Router Flag */
+ S:1, /* Solicited Flag */
+ O:1, /* Override Flag */
+ reserved_hi:5,
+ reserved_lo:24;
+#else
+ uint32_t
+ reserved_hi:5,
+ O:1,
+ S:1,
+ R:1,
+ reserved_lo:24;
+#endif
+ struct in6_addr target; /* Target Address */
+} QEMU_PACKED;
+
+struct ndp_redirect {
+ uint32_t reserved;
+ struct in6_addr target; /* Target Address */
+ struct in6_addr dest; /* Destination Address */
+} QEMU_PACKED;
+
+/*
+ * Structure of an icmpv6 header.
+ */
+struct icmp6 {
+ uint8_t icmp6_type; /* type of message, see below */
+ uint8_t icmp6_code; /* type sub code */
+ uint16_t icmp6_cksum; /* ones complement cksum of struct */
+ union {
+ struct icmp6_echo echo;
+ struct ndp_rs ndp_rs;
+ struct ndp_ra ndp_ra;
+ struct ndp_ns ndp_ns;
+ struct ndp_na ndp_na;
+ struct ndp_redirect ndp_redirect;
+ } icmp6_body;
+#define icmp6_echo icmp6_body.echo
+#define icmp6_nrs icmp6_body.ndp_rs
+#define icmp6_nra icmp6_body.ndp_ra
+#define icmp6_nns icmp6_body.ndp_ns
+#define icmp6_nna icmp6_body.ndp_na
+#define icmp6_redirect icmp6_body.ndp_redirect
+} QEMU_PACKED;
+
+#define ICMP6_MINLEN 4
+#define ICMP6_ECHO_MINLEN 8
+#define ICMP6_NDP_RS_MINLEN 8
+#define ICMP6_NDP_RA_MINLEN 16
+#define ICMP6_NDP_NS_MINLEN 24
+#define ICMP6_NDP_NA_MINLEN 24
+#define ICMP6_NDP_REDIRECT_MINLEN 40
+
+/*
+ * NDP Options
+ */
+struct ndpopt {
+ uint8_t ndpopt_type; /* Option type */
+ uint8_t ndpopt_len; /* /!\ In units of 8 octets */
+ union {
+ unsigned char linklayer_addr[6]; /* Source/Target Link-layer */
+ struct prefixinfo { /* Prefix Information */
+ uint8_t prefix_length;
+#ifdef HOST_WORDS_BIGENDIAN
+ uint8_t L:1, A:1, reserved1:6;
+#else
+ uint8_t reserved1:6, A:1, L:1;
+#endif
+ uint32_t valid_lt; /* Valid Lifetime */
+ uint32_t pref_lt; /* Preferred Lifetime */
+ 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
+} 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 */
+
+/* NDP options size, in octets. */
+#define NDPOPT_LINKLAYER_LEN 8
+#define NDPOPT_PREFIXINFO_LEN 32
+
+/*
+ * Definition of type and code field values.
+ * Per https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml
+ * Last Updated 2012-11-12
+ */
+
+/* Errors */
+#define ICMP6_UNREACH 1 /* Destination Unreachable */
+#define ICMP6_UNREACH_NO_ROUTE 0 /* no route to dest */
+#define ICMP6_UNREACH_DEST_PROHIB 1 /* com with dest prohibited */
+#define ICMP6_UNREACH_SCOPE 2 /* beyond scope of src addr */
+#define ICMP6_UNREACH_ADDRESS 3 /* address unreachable */
+#define ICMP6_UNREACH_PORT 4 /* port unreachable */
+#define ICMP6_UNREACH_SRC_FAIL 5 /* src addr failed */
+#define ICMP6_UNREACH_REJECT_ROUTE 6 /* reject route to dest */
+#define ICMP6_UNREACH_SRC_HDR_ERROR 7 /* error in src routing header */
+#define ICMP6_TOOBIG 2 /* Packet Too Big */
+#define ICMP6_TIMXCEED 3 /* Time Exceeded */
+#define ICMP6_TIMXCEED_INTRANS 0 /* hop limit exceeded in transit */
+#define ICMP6_TIMXCEED_REASS 1 /* ttl=0 in reass */
+#define ICMP6_PARAMPROB 4 /* Parameter Problem */
+#define ICMP6_PARAMPROB_HDR_FIELD 0 /* err header field */
+#define ICMP6_PARAMPROB_NXTHDR_TYPE 1 /* unrecognized Next Header type */
+#define ICMP6_PARAMPROB_IPV6_OPT 2 /* unrecognized IPv6 option */
+
+/* Informational Messages */
+#define ICMP6_ECHO_REQUEST 128 /* Echo Request */
+#define ICMP6_ECHO_REPLY 129 /* Echo Reply */
+#define ICMP6_NDP_RS 133 /* Router Solicitation (NDP) */
+#define ICMP6_NDP_RA 134 /* Router Advertisement (NDP) */
+#define ICMP6_NDP_NS 135 /* Neighbor Solicitation (NDP) */
+#define ICMP6_NDP_NA 136 /* Neighbor Advertisement (NDP) */
+#define ICMP6_NDP_REDIRECT 137 /* Redirect Message (NDP) */
+
+/*
+ * Router Configuration Variables (rfc4861#section-6)
+ */
+#define NDP_IsRouter 1
+#define NDP_AdvSendAdvertisements 1
+#define NDP_MaxRtrAdvInterval 600000
+#define NDP_MinRtrAdvInterval ((NDP_MaxRtrAdvInterval >= 9) ? \
+ NDP_MaxRtrAdvInterval / 3 : \
+ NDP_MaxRtrAdvInterval)
+#define NDP_AdvManagedFlag 0
+#define NDP_AdvOtherConfigFlag 0
+#define NDP_AdvLinkMTU 0
+#define NDP_AdvReachableTime 0
+#define NDP_AdvRetransTime 0
+#define NDP_AdvCurHopLimit 64
+#define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000)
+#define NDP_AdvValidLifetime 86400
+#define NDP_AdvOnLinkFlag 1
+#define NDP_AdvPrefLifetime 14400
+#define NDP_AdvAutonomousFlag 1
+
+void icmp6_init(Slirp *slirp);
+void icmp6_cleanup(Slirp *slirp);
+void icmp6_input(struct mbuf *);
+void ndp_send_ra(Slirp *slirp);
+void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
+
+#endif
diff --git a/slirp/ip6_input.c b/slirp/ip6_input.c
new file mode 100644
index 0000000000..add9e6a73a
--- /dev/null
+++ b/slirp/ip6_input.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#include "qemu/osdep.h"
+#include "slirp.h"
+#include "ip6_icmp.h"
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void ip6_init(Slirp *slirp)
+{
+ icmp6_init(slirp);
+}
+
+void ip6_cleanup(Slirp *slirp)
+{
+ icmp6_cleanup(slirp);
+}
+
+void ip6_input(struct mbuf *m)
+{
+ struct ip6 *ip6;
+
+ DEBUG_CALL("ip6_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ if (m->m_len < sizeof(struct ip6)) {
+ goto bad;
+ }
+
+ ip6 = mtod(m, struct ip6 *);
+
+ if (ip6->ip_v != IP6VERSION) {
+ goto bad;
+ }
+
+ /* check ip_ttl for a correct ICMP reply */
+ if (ip6->ip_hl == 0) {
+ /*icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");*/
+ goto bad;
+ }
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ switch (ip6->ip_nh) {
+ case IPPROTO_TCP:
+ /*tcp_input(m, hlen, (struct socket *)NULL);*/
+ break;
+ case IPPROTO_UDP:
+ /*udp_input(m, hlen);*/
+ break;
+ case IPPROTO_ICMPV6:
+ icmp6_input(m);
+ break;
+ default:
+ m_free(m);
+ }
+ return;
+bad:
+ m_free(m);
+}
diff --git a/slirp/ip6_output.c b/slirp/ip6_output.c
new file mode 100644
index 0000000000..762cbfe89c
--- /dev/null
+++ b/slirp/ip6_output.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "slirp.h"
+
+/* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+#define IF6_THRESH 10
+
+/*
+ * IPv6 output. The packet in mbuf chain m contains a IP header
+ */
+int ip6_output(struct socket *so, struct mbuf *m, int fast)
+{
+ struct ip6 *ip = mtod(m, struct ip6 *);
+
+ DEBUG_CALL("ip6_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /* Fill IPv6 header */
+ ip->ip_v = IP6VERSION;
+ ip->ip_hl = IP6_HOP_LIMIT;
+ ip->ip_tc_hi = 0;
+ ip->ip_tc_lo = 0;
+ ip->ip_fl_hi = 0;
+ ip->ip_fl_lo = 0;
+
+ if (fast) {
+ if_encap(m->slirp, m);
+ } else {
+ if_output(so, m);
+ }
+
+ return 0;
+}
diff --git a/slirp/ndp_table.c b/slirp/ndp_table.c
new file mode 100644
index 0000000000..9d4c39b45c
--- /dev/null
+++ b/slirp/ndp_table.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "slirp.h"
+
+void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t ethaddr[ETH_ALEN])
+{
+ NdpTable *ndp_table = &slirp->ndp_table;
+ int i;
+
+ DEBUG_CALL("ndp_table_add");
+#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("ip = %s", addrstr);
+#endif
+ DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ethaddr[0], ethaddr[1], ethaddr[2],
+ ethaddr[3], ethaddr[4], ethaddr[5]));
+
+ if (IN6_IS_ADDR_MULTICAST(&ip_addr) || IN6_IS_ADDR_UNSPECIFIED(&ip_addr)) {
+ /* Do not register multicast or unspecified addresses */
+ DEBUG_CALL(" abort: do not register multicast or unspecified address");
+ return;
+ }
+
+ /* Search for an entry */
+ for (i = 0; i < NDP_TABLE_SIZE; i++) {
+ if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) {
+ DEBUG_CALL(" already in table: update the entry");
+ /* Update the entry */
+ memcpy(ndp_table->table[i].eth_addr, ethaddr, ETH_ALEN);
+ return;
+ }
+ }
+
+ /* No entry found, create a new one */
+ DEBUG_CALL(" create new entry");
+ ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr;
+ memcpy(ndp_table->table[ndp_table->next_victim].eth_addr,
+ ethaddr, ETH_ALEN);
+ ndp_table->next_victim = (ndp_table->next_victim + 1) % NDP_TABLE_SIZE;
+}
+
+bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t out_ethaddr[ETH_ALEN])
+{
+ NdpTable *ndp_table = &slirp->ndp_table;
+ int i;
+
+ DEBUG_CALL("ndp_table_search");
+#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("ip = %s", addrstr);
+#endif
+
+ assert(!IN6_IS_ADDR_UNSPECIFIED(&ip_addr));
+
+ /* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */
+ if (IN6_IS_ADDR_MULTICAST(&ip_addr)) {
+ out_ethaddr[0] = 0x33; out_ethaddr[1] = 0x33;
+ out_ethaddr[2] = ip_addr.s6_addr[12];
+ out_ethaddr[3] = ip_addr.s6_addr[13];
+ out_ethaddr[4] = ip_addr.s6_addr[14];
+ out_ethaddr[5] = ip_addr.s6_addr[15];
+ DEBUG_ARGS((dfd, " multicast addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
+ out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
+ return 1;
+ }
+
+ for (i = 0; i < NDP_TABLE_SIZE; i++) {
+ if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) {
+ memcpy(out_ethaddr, ndp_table->table[i].eth_addr, ETH_ALEN);
+ DEBUG_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
+ out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
+ return 1;
+ }
+ }
+
+ DEBUG_CALL(" ip not found in table");
+ return 0;
+}
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 0466d330da..049c2cfb1e 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -210,10 +210,12 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp_init_once();
+ slirp->grand = g_rand_new();
slirp->restricted = restricted;
if_init(slirp);
ip_init(slirp);
+ ip6_init(slirp);
/* Initialise mbufs *after* setting the MTU */
m_init(slirp);
@@ -221,6 +223,19 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vnetwork_addr = vnetwork;
slirp->vnetwork_mask = vnetmask;
slirp->vhost_addr = vhost;
+#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
+ /* No inet_pton helper... */
+ memset(&slirp->vprefix_addr6, 0, sizeof(slirp->vprefix_addr6));
+ slirp->vprefix_addr6.s6_addr[0] = 0xfe;
+ slirp->vprefix_addr6.s6_addr[1] = 0xc0;
+ slirp->vprefix_len = 64;
+ slirp->vhost_addr6 = slirp->vprefix_addr6;
+ slirp->vhost_addr6.s6_addr[15] = 0x2;
+#else
+ inet_pton(AF_INET6, "fec0::0", &slirp->vprefix_addr6);
+ slirp->vprefix_len = 64;
+ inet_pton(AF_INET6, "fec0::2", &slirp->vhost_addr6);
+#endif
if (vhostname) {
pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
vhostname);
@@ -251,8 +266,11 @@ void slirp_cleanup(Slirp *slirp)
unregister_savevm(NULL, "slirp", slirp);
ip_cleanup(slirp);
+ ip6_cleanup(slirp);
m_cleanup(slirp);
+ g_rand_free(slirp->grand);
+
g_free(slirp->vdnssearch);
g_free(slirp->tftp_prefix);
g_free(slirp->bootp_filename);
@@ -744,6 +762,7 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
arp_input(slirp, pkt, pkt_len);
break;
case ETH_P_IP:
+ case ETH_P_IPV6:
m = m_get(slirp);
if (!m)
return;
@@ -757,8 +776,13 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
m->m_data += 2 + ETH_HLEN;
m->m_len -= 2 + ETH_HLEN;
- ip_input(m);
+ if (proto == ETH_P_IP) {
+ ip_input(m);
+ } else if (proto == ETH_P_IPV6) {
+ ip6_input(m);
+ }
break;
+
default:
break;
}
@@ -826,6 +850,31 @@ static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
}
}
+/* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no
+ * packet should be sent, 0 if the packet must be re-queued, 2 if the packet
+ * is ready to go.
+ */
+static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
+ uint8_t ethaddr[ETH_ALEN])
+{
+ const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);
+ if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) {
+ if (!ifm->resolution_requested) {
+ ndp_send_ns(slirp, ip6h->ip_dst);
+ ifm->resolution_requested = true;
+ ifm->expiration_date =
+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL;
+ }
+ return 0;
+ } else {
+ eh->h_proto = htons(ETH_P_IPV6);
+ in6_compute_ethaddr(ip6h->ip_src, eh->h_source);
+
+ /* Send this */
+ return 2;
+ }
+}
+
/* Output the IP packet to the ethernet device. Returns 0 if the packet must be
* re-queued.
*/
@@ -849,9 +898,15 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
}
break;
+ case IP6VERSION:
+ ret = if_encap6(slirp, ifm, eh, ethaddr);
+ if (ret < 2) {
+ return ret;
+ }
+ break;
+
default:
- /* Do not assert while we don't manage IP6VERSION */
- /* assert(0); */
+ g_assert_not_reached();
break;
}
diff --git a/slirp/slirp.h b/slirp/slirp.h
index a6741e77b1..393a9ca6ff 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -111,6 +111,8 @@ void free(void *ptr);
#include <sys/stropts.h>
#endif
+#include <glib.h>
+
#include "debug.h"
#include "qemu/queue.h"
@@ -119,12 +121,14 @@ void free(void *ptr);
#include "libslirp.h"
#include "ip.h"
+#include "ip6.h"
#include "tcp.h"
#include "tcp_timer.h"
#include "tcp_var.h"
#include "tcpip.h"
#include "udp.h"
#include "ip_icmp.h"
+#include "ip6_icmp.h"
#include "mbuf.h"
#include "sbuf.h"
#include "socket.h"
@@ -176,6 +180,23 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]);
bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
uint8_t out_ethaddr[ETH_ALEN]);
+struct ndpentry {
+ unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */
+ struct in6_addr ip_addr; /* sender IP address */
+} QEMU_PACKED;
+
+#define NDP_TABLE_SIZE 16
+
+typedef struct NdpTable {
+ struct ndpentry table[NDP_TABLE_SIZE];
+ int next_victim;
+} NdpTable;
+
+void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t ethaddr[ETH_ALEN]);
+bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t out_ethaddr[ETH_ALEN]);
+
struct Slirp {
QTAILQ_ENTRY(Slirp) entry;
u_int time_fasttimo;
@@ -186,6 +207,9 @@ struct Slirp {
struct in_addr vnetwork_addr;
struct in_addr vnetwork_mask;
struct in_addr vhost_addr;
+ struct in6_addr vprefix_addr6;
+ uint8_t vprefix_len;
+ struct in6_addr vhost_addr6;
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
@@ -234,6 +258,10 @@ struct Slirp {
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
ArpTable arp_table;
+ NdpTable ndp_table;
+
+ GRand *grand;
+ QEMUTimer *ra_timer;
void *opaque;
};
@@ -276,6 +304,7 @@ int translate_dnssearch(Slirp *s, const char ** names);
/* cksum.c */
int cksum(struct mbuf *m, int len);
+int ip6_cksum(struct mbuf *m);
/* if.c */
void if_init(Slirp *);
@@ -291,6 +320,14 @@ void ip_stripoptions(register struct mbuf *, struct mbuf *);
/* ip_output.c */
int ip_output(struct socket *, struct mbuf *);
+/* ip6_input.c */
+void ip6_init(Slirp *);
+void ip6_cleanup(Slirp *);
+void ip6_input(struct mbuf *);
+
+/* ip6_output */
+int ip6_output(struct socket *, struct mbuf *, int fast);
+
/* tcp_input.c */
void tcp_input(register struct mbuf *, int, struct socket *);
int tcp_mss(register struct tcpcb *, u_int);
diff --git a/slirp/socket.h b/slirp/socket.h
index c4afc9494f..bcebce110d 100644
--- a/slirp/socket.h
+++ b/slirp/socket.h
@@ -102,6 +102,13 @@ static inline int sockaddr_equal(struct sockaddr_storage *a,
return a4->sin_addr.s_addr == b4->sin_addr.s_addr
&& a4->sin_port == b4->sin_port;
}
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) a;
+ struct sockaddr_in6 *b6 = (struct sockaddr_in6 *) b;
+ return (in6_equal(&a6->sin6_addr, &b6->sin6_addr)
+ && a6->sin6_port == b6->sin6_port);
+ }
default:
g_assert_not_reached();
}