summaryrefslogtreecommitdiffstats
path: root/qemu/slirp
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/slirp')
-rw-r--r--qemu/slirp/Makefile.objs6
-rw-r--r--qemu/slirp/arp_table.c1
-rw-r--r--qemu/slirp/bootp.c15
-rw-r--r--qemu/slirp/cksum.c26
-rw-r--r--qemu/slirp/dnssearch.c4
-rw-r--r--qemu/slirp/if.c34
-rw-r--r--qemu/slirp/if.h4
-rw-r--r--qemu/slirp/ip6.h141
-rw-r--r--qemu/slirp/ip6_icmp.c424
-rw-r--r--qemu/slirp/ip6_icmp.h213
-rw-r--r--qemu/slirp/ip6_input.c78
-rw-r--r--qemu/slirp/ip6_output.c40
-rw-r--r--qemu/slirp/ip_icmp.c42
-rw-r--r--qemu/slirp/ip_icmp.h4
-rw-r--r--qemu/slirp/ip_input.c27
-rw-r--r--qemu/slirp/ip_output.c5
-rw-r--r--qemu/slirp/libslirp.h11
-rw-r--r--qemu/slirp/mbuf.c32
-rw-r--r--qemu/slirp/mbuf.h8
-rw-r--r--qemu/slirp/misc.c12
-rw-r--r--qemu/slirp/misc.h5
-rw-r--r--qemu/slirp/ndp_table.c90
-rw-r--r--qemu/slirp/sbuf.c5
-rw-r--r--qemu/slirp/slirp.c229
-rw-r--r--qemu/slirp/slirp.h111
-rw-r--r--qemu/slirp/socket.c301
-rw-r--r--qemu/slirp/socket.h78
-rw-r--r--qemu/slirp/tcp.h2
-rw-r--r--qemu/slirp/tcp_input.c260
-rw-r--r--qemu/slirp/tcp_output.c54
-rw-r--r--qemu/slirp/tcp_subr.c176
-rw-r--r--qemu/slirp/tcp_timer.c4
-rw-r--r--qemu/slirp/tcpip.h40
-rw-r--r--qemu/slirp/tftp.c135
-rw-r--r--qemu/slirp/tftp.h7
-rw-r--r--qemu/slirp/udp.c90
-rw-r--r--qemu/slirp/udp.h10
-rw-r--r--qemu/slirp/udp6.c168
38 files changed, 2343 insertions, 549 deletions
diff --git a/qemu/slirp/Makefile.objs b/qemu/slirp/Makefile.objs
index 2daa9dc58..6748e4f60 100644
--- a/qemu/slirp/Makefile.objs
+++ b/qemu/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 udp6.o bootp.o tftp.o arp_table.o \
+ ndp_table.o
diff --git a/qemu/slirp/arp_table.c b/qemu/slirp/arp_table.c
index bcaeb4486..354704355 100644
--- a/qemu/slirp/arp_table.c
+++ b/qemu/slirp/arp_table.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "slirp.h"
void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN])
diff --git a/qemu/slirp/bootp.c b/qemu/slirp/bootp.c
index b7db9fa33..7b3232bdc 100644
--- a/qemu/slirp/bootp.c
+++ b/qemu/slirp/bootp.c
@@ -21,8 +21,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
+#if defined(_WIN32)
+/* Windows ntohl() returns an u_long value.
+ * Add a type cast to match the format strings. */
+# define ntohl(n) ((uint32_t)ntohl(n))
+#endif
+
/* XXX: only DHCP is supported */
#define LEASE_TIME (24 * 3600)
@@ -155,7 +162,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
if (preq_addr.s_addr != htonl(0L))
- DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
+ DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr));
else
DPRINTF("\n");
@@ -234,7 +241,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
q += 4;
if (bc) {
- DPRINTF("%s addr=%08x\n",
+ DPRINTF("%s addr=%08" PRIx32 "\n",
(dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
ntohl(daddr.sin_addr.s_addr));
@@ -302,7 +309,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
} else {
static const char nak_msg[] = "requested address not available";
- DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr.s_addr));
+ DPRINTF("nak'ed addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr));
*q++ = RFC2132_MSG_TYPE;
*q++ = 1;
@@ -319,7 +326,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
m->m_len = sizeof(struct bootp_t) -
sizeof(struct ip) - sizeof(struct udphdr);
- udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+ udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
}
void bootp_input(struct mbuf *m)
diff --git a/qemu/slirp/cksum.c b/qemu/slirp/cksum.c
index 63286600e..2ad0e6540 100644
--- a/qemu/slirp/cksum.c
+++ b/qemu/slirp/cksum.c
@@ -30,6 +30,7 @@
* in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
*/
+#include "qemu/osdep.h"
#include <slirp.h>
/*
@@ -137,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/qemu/slirp/dnssearch.c b/qemu/slirp/dnssearch.c
index 4c9064ecb..aed2f13af 100644
--- a/qemu/slirp/dnssearch.c
+++ b/qemu/slirp/dnssearch.c
@@ -22,9 +22,7 @@
* THE SOFTWARE.
*/
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#include "qemu/osdep.h"
#include <glib.h>
#include "slirp.h"
diff --git a/qemu/slirp/if.c b/qemu/slirp/if.c
index fb7acf87d..9b02180db 100644
--- a/qemu/slirp/if.c
+++ b/qemu/slirp/if.c
@@ -5,6 +5,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include "qemu/timer.h"
@@ -27,9 +28,9 @@ ifs_remque(struct mbuf *ifm)
void
if_init(Slirp *slirp)
{
- slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
- slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
- slirp->next_m = &slirp->if_batchq;
+ slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq;
+ slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq;
+ slirp->next_m = (struct mbuf *) &slirp->if_batchq;
}
/*
@@ -53,8 +54,8 @@ if_output(struct socket *so, struct mbuf *ifm)
int on_fastq = 1;
DEBUG_CALL("if_output");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("ifm = %lx", (long)ifm);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("ifm = %p", ifm);
/*
* First remove the mbuf from m_usedlist,
@@ -73,7 +74,8 @@ if_output(struct socket *so, struct mbuf *ifm)
* We mustn't put this packet back on the fastq (or we'll send it out of order)
* XXX add cache here?
*/
- for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
+ for (ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
+ (struct quehead *) ifq != &slirp->if_batchq;
ifq = ifq->ifq_prev) {
if (so == ifq->ifq_so) {
/* A match! */
@@ -85,7 +87,7 @@ if_output(struct socket *so, struct mbuf *ifm)
/* No match, check which queue to put it on */
if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
- ifq = slirp->if_fastq.ifq_prev;
+ ifq = (struct mbuf *) slirp->if_fastq.qh_rlink;
on_fastq = 1;
/*
* Check if this packet is a part of the last
@@ -97,9 +99,9 @@ if_output(struct socket *so, struct mbuf *ifm)
goto diddit;
}
} else {
- ifq = slirp->if_batchq.ifq_prev;
+ ifq = (struct mbuf *) slirp->if_batchq.qh_rlink;
/* Set next_m if the queue was empty so far */
- if (slirp->next_m == &slirp->if_batchq) {
+ if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
slirp->next_m = ifm;
}
}
@@ -165,10 +167,10 @@ void if_start(Slirp *slirp)
}
slirp->if_start_busy = true;
- if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
- ifm_next = slirp->if_fastq.ifq_next;
+ if (slirp->if_fastq.qh_link != &slirp->if_fastq) {
+ ifm_next = (struct mbuf *) slirp->if_fastq.qh_link;
next_from_batchq = false;
- } else if (slirp->next_m != &slirp->if_batchq) {
+ } else if ((struct quehead *) slirp->next_m != &slirp->if_batchq) {
/* Nothing on fastq, pick up from batchq via next_m */
ifm_next = slirp->next_m;
next_from_batchq = true;
@@ -181,19 +183,19 @@ void if_start(Slirp *slirp)
from_batchq = next_from_batchq;
ifm_next = ifm->ifq_next;
- if (ifm_next == &slirp->if_fastq) {
+ if ((struct quehead *) ifm_next == &slirp->if_fastq) {
/* No more packets in fastq, switch to batchq */
ifm_next = slirp->next_m;
next_from_batchq = true;
}
- if (ifm_next == &slirp->if_batchq) {
+ if ((struct quehead *) ifm_next == &slirp->if_batchq) {
/* end of batchq */
ifm_next = NULL;
}
/* 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;
}
@@ -217,7 +219,7 @@ void if_start(Slirp *slirp)
/* Next packet in fastq is from the same session */
ifm_next = next;
next_from_batchq = false;
- } else if (slirp->next_m == &slirp->if_batchq) {
+ } else if ((struct quehead *) slirp->next_m == &slirp->if_batchq) {
/* Set next_m and ifm_next if the session packet is now the
* only one on batchq */
slirp->next_m = ifm_next = next;
diff --git a/qemu/slirp/if.h b/qemu/slirp/if.h
index 33270239f..c7a5c5724 100644
--- a/qemu/slirp/if.h
+++ b/qemu/slirp/if.h
@@ -17,7 +17,7 @@
#define IF_MRU 1500
#define IF_COMP IF_AUTOCOMP /* Flags for compression */
-/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
-#define IF_MAXLINKHDR (2 + 14 + 40)
+/* 2 for alignment, 14 for ethernet */
+#define IF_MAXLINKHDR (2 + ETH_HLEN)
#endif
diff --git a/qemu/slirp/ip6.h b/qemu/slirp/ip6.h
new file mode 100644
index 000000000..8ddfa242c
--- /dev/null
+++ b/qemu/slirp/ip6.h
@@ -0,0 +1,141 @@
+/*
+ * 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)\
+ ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len)\
+ && in6_equal_mach(a, &slirp->vnameserver_addr6, slirp->vprefix_len))\
+ || (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64)\
+ && in6_equal_mach(a, &slirp->vnameserver_addr6, 64)))
+
+#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/qemu/slirp/ip6_icmp.c b/qemu/slirp/ip6_icmp.c
new file mode 100644
index 000000000..09571bcd6
--- /dev/null
+++ b/qemu/slirp/ip6_icmp.c
@@ -0,0 +1,424 @@
+/*
+ * 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)
+{
+ if (!slirp->in6_enabled) {
+ return;
+ }
+
+ 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)
+{
+ if (!slirp->in6_enabled) {
+ return;
+ }
+
+ 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);
+}
+
+void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
+{
+ Slirp *slirp = m->slirp;
+ struct mbuf *t;
+ struct ip6 *ip = mtod(m, struct ip6 *);
+
+ DEBUG_CALL("icmp6_send_error");
+ DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
+
+ if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
+ IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
+ /* TODO icmp error? */
+ return;
+ }
+
+ t = m_get(slirp);
+
+ /* IPv6 packet */
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
+ rip->ip_dst = ip->ip_src;
+#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("target = %s", addrstr);
+#endif
+
+ rip->ip_nh = IPPROTO_ICMPV6;
+ const int error_data_len = min(m->m_len,
+ IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
+ rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
+ t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
+
+ /* ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = type;
+ ricmp->icmp6_code = code;
+ ricmp->icmp6_cksum = 0;
+
+ switch (type) {
+ case ICMP6_UNREACH:
+ case ICMP6_TIMXCEED:
+ ricmp->icmp6_err.unused = 0;
+ break;
+ case ICMP6_TOOBIG:
+ ricmp->icmp6_err.mtu = htonl(IF_MTU);
+ break;
+ case ICMP6_PARAMPROB:
+ /* TODO: Handle this case */
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ t->m_data += ICMP6_ERROR_MINLEN;
+ memcpy(t->m_data, m->m_data, error_data_len);
+
+ /* Checksum */
+ t->m_data -= ICMP6_ERROR_MINLEN;
+ 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/qemu/slirp/ip6_icmp.h b/qemu/slirp/ip6_icmp.h
new file mode 100644
index 000000000..9460bf837
--- /dev/null
+++ b/qemu/slirp/ip6_icmp.h
@@ -0,0 +1,213 @@
+/*
+ * 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;
+};
+
+union icmp6_error_body {
+ uint32_t unused;
+ uint32_t pointer;
+ uint32_t mtu;
+};
+
+/*
+ * 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 {
+ union icmp6_error_body error_body;
+ 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_err icmp6_body.error_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_ERROR_MINLEN 8
+#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 icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code);
+void ndp_send_ra(Slirp *slirp);
+void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
+
+#endif
diff --git a/qemu/slirp/ip6_input.c b/qemu/slirp/ip6_input.c
new file mode 100644
index 000000000..ac2e3ea88
--- /dev/null
+++ b/qemu/slirp/ip6_input.c
@@ -0,0 +1,78 @@
+/*
+ * 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;
+ Slirp *slirp = m->slirp;
+
+ if (!slirp->in6_enabled) {
+ goto bad;
+ }
+
+ 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;
+ }
+
+ if (ntohs(ip6->ip_pl) > IF_MTU) {
+ icmp6_send_error(m, ICMP6_TOOBIG, 0);
+ goto bad;
+ }
+
+ /* check ip_ttl for a correct ICMP reply */
+ if (ip6->ip_hl == 0) {
+ icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS);
+ goto bad;
+ }
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ switch (ip6->ip_nh) {
+ case IPPROTO_TCP:
+ NTOHS(ip6->ip_pl);
+ tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6);
+ break;
+ case IPPROTO_UDP:
+ udp6_input(m);
+ break;
+ case IPPROTO_ICMPV6:
+ icmp6_input(m);
+ break;
+ default:
+ m_free(m);
+ }
+ return;
+bad:
+ m_free(m);
+}
diff --git a/qemu/slirp/ip6_output.c b/qemu/slirp/ip6_output.c
new file mode 100644
index 000000000..762cbfe89
--- /dev/null
+++ b/qemu/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/qemu/slirp/ip_icmp.c b/qemu/slirp/ip_icmp.c
index 9f1cb08a1..5ffc7a683 100644
--- a/qemu/slirp/ip_icmp.c
+++ b/qemu/slirp/ip_icmp.c
@@ -30,6 +30,7 @@
* ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
*/
+#include "qemu/osdep.h"
#include "slirp.h"
#include "ip_icmp.h"
@@ -37,7 +38,7 @@
/* Be nice and tell them it's just a pseudo-ping packet */
static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
-/* list of actions for icmp_error() on RX of an icmp message */
+/* list of actions for icmp_send_error() on RX of an icmp message */
static const int icmp_flush[19] = {
/* ECHO REPLY (0) */ 0,
1,
@@ -100,7 +101,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n",
errno, strerror(errno)));
- icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
+ icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
icmp_detach(so);
}
@@ -125,7 +126,7 @@ icmp_input(struct mbuf *m, int hlen)
Slirp *slirp = m->slirp;
DEBUG_CALL("icmp_input");
- DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("m_len = %d", m->m_len);
/*
@@ -157,12 +158,12 @@ icmp_input(struct mbuf *m, int hlen)
goto freeit;
} else {
struct socket *so;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
if ((so = socreate(slirp)) == NULL) goto freeit;
if (icmp_send(so, m, hlen) == 0) {
return;
}
- if(udp_attach(so) == -1) {
+ if (udp_attach(so, AF_INET) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno)));
sofree(so);
@@ -170,8 +171,10 @@ icmp_input(struct mbuf *m, int hlen)
goto end_error;
}
so->so_m = m;
+ so->so_ffamily = AF_INET;
so->so_faddr = ip->ip_dst;
so->so_fport = htons(7);
+ so->so_lfamily = AF_INET;
so->so_laddr = ip->ip_src;
so->so_lport = htons(9);
so->so_iptos = ip->ip_tos;
@@ -179,25 +182,14 @@ icmp_input(struct mbuf *m, int hlen)
so->so_state = SS_ISFCONNECTED;
/* Send the packet */
- addr.sin_family = AF_INET;
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- /* It's an alias */
- if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
- if (get_dns_addr(&addr.sin_addr) < 0)
- addr.sin_addr = loopback_addr;
- } else {
- addr.sin_addr = loopback_addr;
- }
- } else {
- addr.sin_addr = so->so_faddr;
- }
- addr.sin_port = so->so_fport;
+ addr = so->fhost.ss;
+ sotranslate_out(so, &addr);
+
if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
- (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) {
DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
errno,strerror(errno)));
- icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
udp_detach(so);
}
} /* if ip->ip_dst.s_addr == alias_addr.s_addr */
@@ -243,7 +235,7 @@ end_error:
#define ICMP_MAXDATALEN (IP_MSS-28)
void
-icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message)
{
unsigned hlen, shlen, s_ip_len;
@@ -251,8 +243,8 @@ icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
register struct icmp *icp;
register struct mbuf *m;
- DEBUG_CALL("icmp_error");
- DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_CALL("icmp_send_error");
+ DEBUG_ARG("msrc = %p", msrc);
DEBUG_ARG("msrc_len = %d", msrc->m_len);
if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
@@ -441,7 +433,7 @@ void icmp_receive(struct socket *so)
}
DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno,
strerror(errno)));
- icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
+ icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
} else {
icmp_reflect(so->so_m);
so->so_m = NULL; /* Don't m_free() it again! */
diff --git a/qemu/slirp/ip_icmp.h b/qemu/slirp/ip_icmp.h
index be4426b8e..846761d08 100644
--- a/qemu/slirp/ip_icmp.h
+++ b/qemu/slirp/ip_icmp.h
@@ -156,8 +156,8 @@ struct icmp {
void icmp_init(Slirp *slirp);
void icmp_cleanup(Slirp *slirp);
void icmp_input(struct mbuf *, int);
-void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
- const char *message);
+void icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message);
void icmp_reflect(struct mbuf *);
void icmp_receive(struct socket *so);
void icmp_detach(struct socket *so);
diff --git a/qemu/slirp/ip_input.c b/qemu/slirp/ip_input.c
index 880bdfd3c..cdd54833a 100644
--- a/qemu/slirp/ip_input.c
+++ b/qemu/slirp/ip_input.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include <qemu/osdep.h>
#include "ip_icmp.h"
@@ -79,12 +80,16 @@ ip_input(struct mbuf *m)
register struct ip *ip;
int hlen;
+ if (!slirp->in_enabled) {
+ goto bad;
+ }
+
DEBUG_CALL("ip_input");
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("m_len = %d", m->m_len);
if (m->m_len < sizeof (struct ip)) {
- return;
+ goto bad;
}
ip = mtod(m, struct ip *);
@@ -131,9 +136,9 @@ ip_input(struct mbuf *m)
m_adj(m, ip->ip_len - m->m_len);
/* check ip_ttl for a correct ICMP reply */
- if(ip->ip_ttl==0) {
- icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
- goto bad;
+ if (ip->ip_ttl == 0) {
+ icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl");
+ goto bad;
}
/*
@@ -199,7 +204,7 @@ ip_input(struct mbuf *m)
*/
switch (ip->ip_p) {
case IPPROTO_TCP:
- tcp_input(m, hlen, (struct socket *)NULL);
+ tcp_input(m, hlen, (struct socket *)NULL, AF_INET);
break;
case IPPROTO_UDP:
udp_input(m, hlen);
@@ -232,9 +237,9 @@ ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
int i, next;
DEBUG_CALL("ip_reass");
- DEBUG_ARG("ip = %lx", (long)ip);
- DEBUG_ARG("fp = %lx", (long)fp);
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("ip = %p", ip);
+ DEBUG_ARG("fp = %p", fp);
+ DEBUG_ARG("m = %p", m);
/*
* Presence of header sizes in mbufs
@@ -400,7 +405,7 @@ static void
ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
{
DEBUG_CALL("ip_enq");
- DEBUG_ARG("prev = %lx", (long)prev);
+ DEBUG_ARG("prev = %p", prev);
p->ipf_prev = prev;
p->ipf_next = prev->ipf_next;
((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
@@ -636,7 +641,7 @@ typedef uint32_t n_time;
}
return (0);
bad:
- icmp_error(m, type, code, 0, 0);
+ icmp_send_error(m, type, code, 0, 0);
return (1);
}
diff --git a/qemu/slirp/ip_output.c b/qemu/slirp/ip_output.c
index c82830fe7..0d6b3b831 100644
--- a/qemu/slirp/ip_output.c
+++ b/qemu/slirp/ip_output.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
/* Number of packets queued before we start sending
@@ -60,8 +61,8 @@ ip_output(struct socket *so, struct mbuf *m0)
int len, off, error = 0;
DEBUG_CALL("ip_output");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("m0 = %lx", (long)m0);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("m0 = %p", m0);
ip = mtod(m, struct ip *);
/*
diff --git a/qemu/slirp/libslirp.h b/qemu/slirp/libslirp.h
index 5bdcbd50f..127aa41d4 100644
--- a/qemu/slirp/libslirp.h
+++ b/qemu/slirp/libslirp.h
@@ -8,11 +8,14 @@ typedef struct Slirp Slirp;
int get_dns_addr(struct in_addr *pdns_addr);
-Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+Slirp *slirp_init(int restricted, bool in_enabled, 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, const char **vdnssearch,
+ bool in6_enabled,
+ struct in6_addr vprefix_addr6, uint8_t vprefix_len,
+ struct in6_addr vhost6, const char *vhostname,
+ const char *tftp_path, const char *bootfile,
+ struct in_addr vdhcp_start, struct in_addr vnameserver,
+ struct in6_addr vnameserver6, const char **vdnssearch,
void *opaque);
void slirp_cleanup(Slirp *slirp);
diff --git a/qemu/slirp/mbuf.c b/qemu/slirp/mbuf.c
index 4fefb043b..d13698839 100644
--- a/qemu/slirp/mbuf.c
+++ b/qemu/slirp/mbuf.c
@@ -15,29 +15,30 @@
* the flags
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#define MBUF_THRESH 30
/*
* Find a nice value for msize
- * XXX if_maxlinkhdr already in mtu
*/
-#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
+#define SLIRP_MSIZE\
+ (offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + IF_MTU)
void
m_init(Slirp *slirp)
{
- slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist;
- slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist;
+ slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist;
+ slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist;
}
void m_cleanup(Slirp *slirp)
{
struct mbuf *m, *next;
- m = slirp->m_usedlist.m_next;
- while (m != &slirp->m_usedlist) {
+ m = (struct mbuf *) slirp->m_usedlist.qh_link;
+ while ((struct quehead *) m != &slirp->m_usedlist) {
next = m->m_next;
if (m->m_flags & M_EXT) {
free(m->m_ext);
@@ -45,8 +46,8 @@ void m_cleanup(Slirp *slirp)
free(m);
m = next;
}
- m = slirp->m_freelist.m_next;
- while (m != &slirp->m_freelist) {
+ m = (struct mbuf *) slirp->m_freelist.qh_link;
+ while ((struct quehead *) m != &slirp->m_freelist) {
next = m->m_next;
free(m);
m = next;
@@ -69,7 +70,7 @@ m_get(Slirp *slirp)
DEBUG_CALL("m_get");
- if (slirp->m_freelist.m_next == &slirp->m_freelist) {
+ if (slirp->m_freelist.qh_link == &slirp->m_freelist) {
m = (struct mbuf *)malloc(SLIRP_MSIZE);
if (m == NULL) goto end_error;
slirp->mbuf_alloced++;
@@ -77,7 +78,7 @@ m_get(Slirp *slirp)
flags = M_DOFREE;
m->slirp = slirp;
} else {
- m = slirp->m_freelist.m_next;
+ m = (struct mbuf *) slirp->m_freelist.qh_link;
remque(m);
}
@@ -91,10 +92,10 @@ m_get(Slirp *slirp)
m->m_len = 0;
m->m_nextpkt = NULL;
m->m_prevpkt = NULL;
- m->arp_requested = false;
+ m->resolution_requested = false;
m->expiration_date = (uint64_t)-1;
end_error:
- DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m = %p", m);
return m;
}
@@ -103,7 +104,7 @@ m_free(struct mbuf *m)
{
DEBUG_CALL("m_free");
- DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m = %p", m);
if(m) {
/* Remove from m_usedlist */
@@ -221,10 +222,11 @@ dtom(Slirp *slirp, void *dat)
struct mbuf *m;
DEBUG_CALL("dtom");
- DEBUG_ARG("dat = %lx", (long )dat);
+ DEBUG_ARG("dat = %p", dat);
/* bug corrected for M_EXT buffers */
- for (m = slirp->m_usedlist.m_next; m != &slirp->m_usedlist;
+ for (m = (struct mbuf *) slirp->m_usedlist.qh_link;
+ (struct quehead *) m != &slirp->m_usedlist;
m = m->m_next) {
if (m->m_flags & M_EXT) {
if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
diff --git a/qemu/slirp/mbuf.h b/qemu/slirp/mbuf.h
index b144f1ce3..36fb81409 100644
--- a/qemu/slirp/mbuf.h
+++ b/qemu/slirp/mbuf.h
@@ -79,13 +79,11 @@ struct mbuf {
int m_len; /* Amount of data in this mbuf */
Slirp *slirp;
- bool arp_requested;
+ bool resolution_requested;
uint64_t expiration_date;
+ char *m_ext;
/* start of dynamic buffer area, must be last element */
- union {
- char m_dat[1]; /* ANSI don't like 0 sized arrays */
- char *m_ext;
- };
+ char m_dat[];
};
#define ifq_prev m_prev
diff --git a/qemu/slirp/misc.c b/qemu/slirp/misc.c
index 578e8b2c1..2fbd04856 100644
--- a/qemu/slirp/misc.c
+++ b/qemu/slirp/misc.c
@@ -5,6 +5,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include <libslirp.h>
@@ -16,11 +17,6 @@
int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
#endif
-struct quehead {
- struct quehead *qh_link;
- struct quehead *qh_rlink;
-};
-
inline void
insque(void *a, void *b)
{
@@ -123,9 +119,9 @@ fork_exec(struct socket *so, const char *ex, int do_pty)
pid_t pid;
DEBUG_CALL("fork_exec");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("ex = %lx", (long)ex);
- DEBUG_ARG("do_pty = %lx", (long)do_pty);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("ex = %p", ex);
+ DEBUG_ARG("do_pty = %x", do_pty);
if (do_pty == 2) {
return 0;
diff --git a/qemu/slirp/misc.h b/qemu/slirp/misc.h
index 41a32583d..0d0c059e6 100644
--- a/qemu/slirp/misc.h
+++ b/qemu/slirp/misc.h
@@ -45,6 +45,11 @@ struct emu_t {
struct emu_t *next;
};
+struct slirp_quehead {
+ struct slirp_quehead *qh_link;
+ struct slirp_quehead *qh_rlink;
+};
+
void slirp_insque(void *, void *);
void slirp_remque(void *);
int add_exec(struct ex_list **, int, char *, struct in_addr, int);
diff --git a/qemu/slirp/ndp_table.c b/qemu/slirp/ndp_table.c
new file mode 100644
index 000000000..9d4c39b45
--- /dev/null
+++ b/qemu/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/qemu/slirp/sbuf.c b/qemu/slirp/sbuf.c
index 08ec2b4f4..dd4cb8c13 100644
--- a/qemu/slirp/sbuf.c
+++ b/qemu/slirp/sbuf.c
@@ -5,6 +5,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include <qemu/main-loop.h>
@@ -72,8 +73,8 @@ sbappend(struct socket *so, struct mbuf *m)
int ret = 0;
DEBUG_CALL("sbappend");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("m->m_len = %d", m->m_len);
/* Shouldn't happen, but... e.g. foreign host closes connection */
diff --git a/qemu/slirp/slirp.c b/qemu/slirp/slirp.c
index 35f819afb..9f4bea3d3 100644
--- a/qemu/slirp/slirp.c
+++ b/qemu/slirp/slirp.c
@@ -21,11 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/timer.h"
+#include "qemu/error-report.h"
#include "sysemu/char.h"
#include "slirp.h"
#include "hw/hw.h"
+#include "qemu/cutils.h"
/* host loopback address */
struct in_addr loopback_addr;
@@ -197,21 +200,29 @@ static void slirp_init_once(void)
static void slirp_state_save(QEMUFile *f, void *opaque);
static int slirp_state_load(QEMUFile *f, void *opaque, int version_id);
-Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+Slirp *slirp_init(int restricted, bool in_enabled, 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, const char **vdnssearch,
+ bool in6_enabled,
+ struct in6_addr vprefix_addr6, uint8_t vprefix_len,
+ struct in6_addr vhost6, const char *vhostname,
+ const char *tftp_path, const char *bootfile,
+ struct in_addr vdhcp_start, struct in_addr vnameserver,
+ struct in6_addr vnameserver6, const char **vdnssearch,
void *opaque)
{
Slirp *slirp = g_malloc0(sizeof(Slirp));
slirp_init_once();
+ slirp->grand = g_rand_new();
slirp->restricted = restricted;
+ slirp->in_enabled = in_enabled;
+ slirp->in6_enabled = in6_enabled;
+
if_init(slirp);
ip_init(slirp);
+ ip6_init(slirp);
/* Initialise mbufs *after* setting the MTU */
m_init(slirp);
@@ -219,6 +230,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vnetwork_addr = vnetwork;
slirp->vnetwork_mask = vnetmask;
slirp->vhost_addr = vhost;
+ slirp->vprefix_addr6 = vprefix_addr6;
+ slirp->vprefix_len = vprefix_len;
+ slirp->vhost_addr6 = vhost6;
if (vhostname) {
pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
vhostname);
@@ -227,6 +241,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->bootp_filename = g_strdup(bootfile);
slirp->vdhcp_startaddr = vdhcp_start;
slirp->vnameserver_addr = vnameserver;
+ slirp->vnameserver_addr6 = vnameserver6;
if (vdnssearch) {
translate_dnssearch(slirp, vdnssearch);
@@ -234,7 +249,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->opaque = opaque;
- register_savevm(NULL, "slirp", 0, 3,
+ register_savevm(NULL, "slirp", 0, 4,
slirp_state_save, slirp_state_load, slirp);
QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry);
@@ -249,8 +264,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);
@@ -516,7 +534,12 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
* test for G_IO_IN below if this succeeds
*/
if (revents & G_IO_PRI) {
- sorecvoob(so);
+ ret = sorecvoob(so);
+ if (ret < 0) {
+ /* Socket error might have resulted in the socket being
+ * removed, do not try to do anything more with it. */
+ continue;
+ }
}
/*
* Check sockets for reading
@@ -535,6 +558,11 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
if (ret > 0) {
tcp_output(sototcpcb(so));
}
+ if (ret < 0) {
+ /* Socket error might have resulted in the socket being
+ * removed, do not try to do anything more with it. */
+ continue;
+ }
}
/*
@@ -566,7 +594,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
/*
* Continue tcp_input
*/
- tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so,
+ so->so_ffamily);
/* continue; */
} else {
ret = sowrite(so);
@@ -615,7 +644,8 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
}
}
- tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so,
+ so->so_ffamily);
} /* SS_ISFCONNECTING */
#endif
}
@@ -678,6 +708,10 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
int ar_op;
struct ex_list *ex_ptr;
+ if (!slirp->in_enabled) {
+ return;
+ }
+
ar_op = ntohs(ah->ar_op);
switch(ar_op) {
case ARPOP_REQUEST:
@@ -742,40 +776,42 @@ 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;
- /* Note: we add to align the IP header */
- if (M_FREEROOM(m) < pkt_len + 2) {
- m_inc(m, pkt_len + 2);
+ /* Note: we add 2 to align the IP header on 4 bytes,
+ * and add the margin for the tcpiphdr overhead */
+ if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) {
+ m_inc(m, pkt_len + TCPIPHDR_DELTA + 2);
}
- m->m_len = pkt_len + 2;
- memcpy(m->m_data + 2, pkt, pkt_len);
+ m->m_len = pkt_len + TCPIPHDR_DELTA + 2;
+ memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len);
- m->m_data += 2 + ETH_HLEN;
- m->m_len -= 2 + ETH_HLEN;
+ m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN;
+ m->m_len -= TCPIPHDR_DELTA + 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;
}
}
-/* Output the IP packet to the ethernet device. Returns 0 if the packet must be
- * re-queued.
+/* Prepare the IPv4 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.
*/
-int if_encap(Slirp *slirp, struct mbuf *ifm)
+static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
+ uint8_t ethaddr[ETH_ALEN])
{
- uint8_t buf[1600];
- struct ethhdr *eh = (struct ethhdr *)buf;
- uint8_t ethaddr[ETH_ALEN];
const struct ip *iph = (const struct ip *)ifm->m_data;
- if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
- return 1;
- }
-
if (iph->ip_dst.s_addr == 0) {
/* 0.0.0.0 can not be a destination address, something went wrong,
* avoid making it worse */
@@ -786,7 +822,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
struct ethhdr *reh = (struct ethhdr *)arp_req;
struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
- if (!ifm->arp_requested) {
+ if (!ifm->resolution_requested) {
/* If the client addr is not known, send an ARP request */
memset(reh->h_dest, 0xff, ETH_ALEN);
memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
@@ -812,22 +848,93 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
rah->ar_tip = iph->ip_dst.s_addr;
slirp->client_ipaddr = iph->ip_dst;
slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
- ifm->arp_requested = true;
+ ifm->resolution_requested = true;
/* Expire request and drop outgoing packet after 1 second */
ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL;
}
return 0;
} else {
- memcpy(eh->h_dest, ethaddr, ETH_ALEN);
memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
/* XXX: not correct */
memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
eh->h_proto = htons(ETH_P_IP);
- memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
- slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
+
+ /* Send this */
+ return 2;
+ }
+}
+
+/* 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.
+ */
+int if_encap(Slirp *slirp, struct mbuf *ifm)
+{
+ uint8_t buf[1600];
+ struct ethhdr *eh = (struct ethhdr *)buf;
+ uint8_t ethaddr[ETH_ALEN];
+ const struct ip *iph = (const struct ip *)ifm->m_data;
+ int ret;
+
+ if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
return 1;
}
+
+ switch (iph->ip_v) {
+ case IPVERSION:
+ ret = if_encap4(slirp, ifm, eh, ethaddr);
+ if (ret < 2) {
+ return ret;
+ }
+ break;
+
+ case IP6VERSION:
+ ret = if_encap6(slirp, ifm, eh, ethaddr);
+ if (ret < 2) {
+ return ret;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ memcpy(eh->h_dest, ethaddr, ETH_ALEN);
+ DEBUG_ARGS((dfd, " src = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ eh->h_source[0], eh->h_source[1], eh->h_source[2],
+ eh->h_source[3], eh->h_source[4], eh->h_source[5]));
+ DEBUG_ARGS((dfd, " dst = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ eh->h_dest[0], eh->h_dest[1], eh->h_dest[2],
+ eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]));
+ memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
+ slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
+ return 1;
}
/* Drop host forwarding rule, return 0 if found. */
@@ -1011,10 +1118,26 @@ static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
static void slirp_socket_save(QEMUFile *f, struct socket *so)
{
qemu_put_be32(f, so->so_urgc);
- qemu_put_be32(f, so->so_faddr.s_addr);
- qemu_put_be32(f, so->so_laddr.s_addr);
- qemu_put_be16(f, so->so_fport);
- qemu_put_be16(f, so->so_lport);
+ qemu_put_be16(f, so->so_ffamily);
+ switch (so->so_ffamily) {
+ case AF_INET:
+ qemu_put_be32(f, so->so_faddr.s_addr);
+ qemu_put_be16(f, so->so_fport);
+ break;
+ default:
+ error_report(
+ "so_ffamily unknown, unable to save so_faddr and so_fport\n");
+ }
+ qemu_put_be16(f, so->so_lfamily);
+ switch (so->so_lfamily) {
+ case AF_INET:
+ qemu_put_be32(f, so->so_laddr.s_addr);
+ qemu_put_be16(f, so->so_lport);
+ break;
+ default:
+ error_report(
+ "so_ffamily unknown, unable to save so_laddr and so_lport\n");
+ }
qemu_put_byte(f, so->so_iptos);
qemu_put_byte(f, so->so_emu);
qemu_put_byte(f, so->so_type);
@@ -1128,16 +1251,40 @@ static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
return 0;
}
-static int slirp_socket_load(QEMUFile *f, struct socket *so)
+static int slirp_socket_load(QEMUFile *f, struct socket *so, int version_id)
{
if (tcp_attach(so) < 0)
return -ENOMEM;
so->so_urgc = qemu_get_be32(f);
- so->so_faddr.s_addr = qemu_get_be32(f);
- so->so_laddr.s_addr = qemu_get_be32(f);
- so->so_fport = qemu_get_be16(f);
- so->so_lport = qemu_get_be16(f);
+ if (version_id <= 3) {
+ so->so_ffamily = AF_INET;
+ so->so_faddr.s_addr = qemu_get_be32(f);
+ so->so_laddr.s_addr = qemu_get_be32(f);
+ so->so_fport = qemu_get_be16(f);
+ so->so_lport = qemu_get_be16(f);
+ } else {
+ so->so_ffamily = qemu_get_be16(f);
+ switch (so->so_ffamily) {
+ case AF_INET:
+ so->so_faddr.s_addr = qemu_get_be32(f);
+ so->so_fport = qemu_get_be16(f);
+ break;
+ default:
+ error_report(
+ "so_ffamily unknown, unable to restore so_faddr and so_lport");
+ }
+ so->so_lfamily = qemu_get_be16(f);
+ switch (so->so_lfamily) {
+ case AF_INET:
+ so->so_laddr.s_addr = qemu_get_be32(f);
+ so->so_lport = qemu_get_be16(f);
+ break;
+ default:
+ error_report(
+ "so_ffamily unknown, unable to restore so_laddr and so_lport");
+ }
+ }
so->so_iptos = qemu_get_byte(f);
so->so_emu = qemu_get_byte(f);
so->so_type = qemu_get_byte(f);
@@ -1173,7 +1320,7 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
if (!so)
return -ENOMEM;
- ret = slirp_socket_load(f, so);
+ ret = slirp_socket_load(f, so, version_id);
if (ret < 0)
return ret;
diff --git a/qemu/slirp/slirp.h b/qemu/slirp/slirp.h
index 6589d7eef..203deec48 100644
--- a/qemu/slirp/slirp.h
+++ b/qemu/slirp/slirp.h
@@ -1,11 +1,9 @@
#ifndef __COMMON_H__
#define __COMMON_H__
-#include "config-host.h"
#include "slirp_config.h"
#ifdef _WIN32
-# include <inttypes.h>
typedef char *caddr_t;
@@ -16,52 +14,20 @@ typedef char *caddr_t;
# include <iphlpapi.h>
#else
-# define ioctlsocket ioctl
-# define closesocket(s) close(s)
# if !defined(__HAIKU__)
# define O_BINARY 0
# endif
#endif
-#include <sys/types.h>
#ifdef HAVE_SYS_BITYPES_H
# include <sys/bitypes.h>
#endif
-#include <sys/time.h>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
#ifndef HAVE_MEMMOVE
#define memmove(x, y, z) bcopy(y, x, z)
#endif
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
-
#ifndef _WIN32
#include <sys/uio.h>
#endif
@@ -82,11 +48,9 @@ void *malloc(size_t arg);
void free(void *ptr);
#endif
-#include <fcntl.h>
#ifndef NO_UNIX_SOCKETS
#include <sys/un.h>
#endif
-#include <signal.h>
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
@@ -114,36 +78,34 @@ void free(void *ptr);
#include <ppp/slirppp.h>
#endif
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-#include <sys/stat.h>
-
/* Avoid conflicting with the libc insque() and remque(), which
have different prototypes. */
#define insque slirp_insque
#define remque slirp_remque
+#define quehead slirp_quehead
#ifdef HAVE_SYS_STROPTS_H
#include <sys/stropts.h>
#endif
+#include <glib.h>
+
#include "debug.h"
#include "qemu/queue.h"
#include "qemu/sockets.h"
+#include "net/eth.h"
#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"
@@ -158,12 +120,6 @@ void free(void *ptr);
#include "bootp.h"
#include "tftp.h"
-#define ETH_ALEN 6
-#define ETH_HLEN 14
-
-#define ETH_P_IP 0x0800 /* Internet Protocol packet */
-#define ETH_P_ARP 0x0806 /* Address Resolution packet */
-
#define ARPOP_REQUEST 1 /* ARP request */
#define ARPOP_REPLY 2 /* ARP reply */
@@ -201,18 +157,41 @@ 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;
u_int last_slowtimo;
bool do_slowtimo;
+ bool in_enabled, in6_enabled;
+
/* virtual network configuration */
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;
+ struct in6_addr vnameserver_addr6;
struct in_addr client_ipaddr;
char client_hostname[33];
@@ -221,12 +200,13 @@ struct Slirp {
struct ex_list *exec_list;
/* mbuf states */
- struct mbuf m_freelist, m_usedlist;
+ struct quehead m_freelist;
+ struct quehead m_usedlist;
int mbuf_alloced;
/* if states */
- struct mbuf if_fastq; /* fast queue (for interactive data) */
- struct mbuf if_batchq; /* queue for non-interactive data */
+ struct quehead if_fastq; /* fast queue (for interactive data) */
+ struct quehead if_batchq; /* queue for non-interactive data */
struct mbuf *next_m; /* pointer to next mbuf to output */
bool if_start_busy; /* avoid if_start recursion */
@@ -259,6 +239,10 @@ struct Slirp {
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
ArpTable arp_table;
+ NdpTable ndp_table;
+
+ GRand *grand;
+ QEMUTimer *ra_timer;
void *opaque;
};
@@ -301,6 +285,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 *);
@@ -316,8 +301,16 @@ 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 *);
+void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af);
int tcp_mss(register struct tcpcb *, u_int);
/* tcp_output.c */
@@ -328,11 +321,12 @@ void tcp_setpersist(register struct tcpcb *);
void tcp_init(Slirp *);
void tcp_cleanup(Slirp *);
void tcp_template(struct tcpcb *);
-void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
+void tcp_respond(struct tcpcb *, register struct tcpiphdr *,
+ register struct mbuf *, tcp_seq, tcp_seq, int, unsigned short);
struct tcpcb * tcp_newtcpcb(struct socket *);
struct tcpcb * tcp_close(register struct tcpcb *);
void tcp_sockclosed(struct tcpcb *);
-int tcp_fconnect(struct socket *);
+int tcp_fconnect(struct socket *, unsigned short af);
void tcp_connect(struct socket *);
int tcp_attach(struct socket *);
uint8_t tcp_tos(struct socket *);
@@ -353,9 +347,4 @@ struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
#define max(x,y) ((x) > (y) ? (x) : (y))
#endif
-#ifdef _WIN32
-#undef errno
-#define errno (WSAGetLastError())
-#endif
-
#endif
diff --git a/qemu/slirp/socket.c b/qemu/slirp/socket.c
index 37ac5cf2f..a10eff18c 100644
--- a/qemu/slirp/socket.c
+++ b/qemu/slirp/socket.c
@@ -5,6 +5,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include <slirp.h>
#include "ip_icmp.h"
@@ -15,24 +16,26 @@
static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so);
-struct socket *
-solookup(struct socket *head, struct in_addr laddr, u_int lport,
- struct in_addr faddr, u_int fport)
+struct socket *solookup(struct socket **last, struct socket *head,
+ struct sockaddr_storage *lhost, struct sockaddr_storage *fhost)
{
- struct socket *so;
-
- for (so = head->so_next; so != head; so = so->so_next) {
- if (so->so_lport == lport &&
- so->so_laddr.s_addr == laddr.s_addr &&
- so->so_faddr.s_addr == faddr.s_addr &&
- so->so_fport == fport)
- break;
- }
-
- if (so == head)
- return (struct socket *)NULL;
- return so;
+ struct socket *so = *last;
+
+ /* Optimisation */
+ if (so != head && sockaddr_equal(&(so->lhost.ss), lhost)
+ && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
+ return so;
+ }
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (sockaddr_equal(&(so->lhost.ss), lhost)
+ && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
+ *last = so;
+ return so;
+ }
+ }
+ return (struct socket *)NULL;
}
/*
@@ -91,7 +94,7 @@ size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
int mss = so->so_tcpcb->t_maxseg;
DEBUG_CALL("sopreprbuf");
- DEBUG_ARG("so = %lx", (long )so);
+ DEBUG_ARG("so = %p", so);
if (len <= 0)
return 0;
@@ -155,7 +158,7 @@ soread(struct socket *so)
struct iovec iov[2];
DEBUG_CALL("soread");
- DEBUG_ARG("so = %lx", (long )so);
+ DEBUG_ARG("so = %p", so);
/*
* No need to check if there's enough room to read.
@@ -173,9 +176,24 @@ soread(struct socket *so)
if (nn < 0 && (errno == EINTR || errno == EAGAIN))
return 0;
else {
+ int err;
+ socklen_t slen = sizeof err;
+
+ err = errno;
+ if (nn == 0) {
+ getsockopt(so->s, SOL_SOCKET, SO_ERROR,
+ &err, &slen);
+ }
+
DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
sofcantrcvmore(so);
- tcp_sockclosed(sototcpcb(so));
+
+ if (err == ECONNRESET || err == ECONNREFUSED
+ || err == ENOTCONN || err == EPIPE) {
+ tcp_drop(sototcpcb(so), err);
+ } else {
+ tcp_sockclosed(sototcpcb(so));
+ }
return -1;
}
}
@@ -215,7 +233,7 @@ int soreadbuf(struct socket *so, const char *buf, int size)
struct iovec iov[2];
DEBUG_CALL("soreadbuf");
- DEBUG_ARG("so = %lx", (long )so);
+ DEBUG_ARG("so = %p", so);
/*
* No need to check if there's enough room to read.
@@ -257,13 +275,14 @@ err:
* so when OOB data arrives, we soread() it and everything
* in the send buffer is sent as urgent data
*/
-void
+int
sorecvoob(struct socket *so)
{
struct tcpcb *tp = sototcpcb(so);
+ int ret;
DEBUG_CALL("sorecvoob");
- DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("so = %p", so);
/*
* We take a guess at how much urgent data has arrived.
@@ -273,11 +292,15 @@ sorecvoob(struct socket *so)
* urgent data, or the read() doesn't return all the
* urgent data.
*/
- soread(so);
- tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
- tp->t_force = 1;
- tcp_output(tp);
- tp->t_force = 0;
+ ret = soread(so);
+ if (ret > 0) {
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ tcp_output(tp);
+ tp->t_force = 0;
+ }
+
+ return ret;
}
/*
@@ -293,7 +316,7 @@ sosendoob(struct socket *so)
int n, len;
DEBUG_CALL("sosendoob");
- DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("so = %p", so);
DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
if (so->so_urgc > 2048)
@@ -351,7 +374,7 @@ sowrite(struct socket *so)
struct iovec iov[2];
DEBUG_CALL("sowrite");
- DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("so = %p", so);
if (so->so_urgc) {
sosendoob(so);
@@ -437,11 +460,12 @@ sowrite(struct socket *so)
void
sorecvfrom(struct socket *so)
{
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct sockaddr_storage addr;
+ struct sockaddr_storage saddr, daddr;
+ socklen_t addrlen = sizeof(struct sockaddr_storage);
DEBUG_CALL("sorecvfrom");
- DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("so = %p", so);
if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
char buff[256];
@@ -459,7 +483,7 @@ sorecvfrom(struct socket *so)
DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
errno,strerror(errno)));
- icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
} else {
icmp_reflect(so->so_m);
so->so_m = NULL; /* Don't m_free() it again! */
@@ -479,7 +503,18 @@ sorecvfrom(struct socket *so)
if (!m) {
return;
}
- m->m_data += IF_MAXLINKHDR;
+ switch (so->so_ffamily) {
+ case AF_INET:
+ m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr);
+ break;
+ case AF_INET6:
+ m->m_data += IF_MAXLINKHDR + sizeof(struct ip6)
+ + sizeof(struct udphdr);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
/*
* XXX Shouldn't FIONREAD packets destined for port 53,
@@ -501,13 +536,37 @@ sorecvfrom(struct socket *so)
DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
m->m_len, errno,strerror(errno)));
if(m->m_len<0) {
- u_char code=ICMP_UNREACH_PORT;
-
- if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
- else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
-
- DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
- icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ /* Report error as ICMP */
+ switch (so->so_lfamily) {
+ uint8_t code;
+ case AF_INET:
+ code = ICMP_UNREACH_PORT;
+
+ if (errno == EHOSTUNREACH) {
+ code = ICMP_UNREACH_HOST;
+ } else if (errno == ENETUNREACH) {
+ code = ICMP_UNREACH_NET;
+ }
+
+ DEBUG_MISC((dfd, " rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
+ break;
+ case AF_INET6:
+ code = ICMP6_UNREACH_PORT;
+
+ if (errno == EHOSTUNREACH) {
+ code = ICMP6_UNREACH_ADDRESS;
+ } else if (errno == ENETUNREACH) {
+ code = ICMP6_UNREACH_NO_ROUTE;
+ }
+
+ DEBUG_MISC((dfd, " rx error, tx icmp6 ICMP_UNREACH:%i\n", code));
+ icmp6_send_error(so->so_m, ICMP6_UNREACH, code);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
m_free(m);
} else {
/*
@@ -525,9 +584,26 @@ sorecvfrom(struct socket *so)
/*
* If this packet was destined for CTL_ADDR,
- * make it look like that's where it came from, done by udp_output
+ * make it look like that's where it came from
*/
- udp_output(so, m, &addr);
+ saddr = addr;
+ sotranslate_in(so, &saddr);
+ daddr = so->lhost.ss;
+
+ switch (so->so_ffamily) {
+ case AF_INET:
+ udp_output(so, m, (struct sockaddr_in *) &saddr,
+ (struct sockaddr_in *) &daddr,
+ so->so_iptos);
+ break;
+ case AF_INET6:
+ udp6_output(so, m, (struct sockaddr_in6 *) &saddr,
+ (struct sockaddr_in6 *) &daddr);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
} /* rx error */
} /* if ping packet */
}
@@ -538,33 +614,20 @@ sorecvfrom(struct socket *so)
int
sosendto(struct socket *so, struct mbuf *m)
{
- Slirp *slirp = so->slirp;
int ret;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
DEBUG_CALL("sosendto");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("m = %lx", (long)m);
-
- addr.sin_family = AF_INET;
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- /* It's an alias */
- if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
- if (get_dns_addr(&addr.sin_addr) < 0)
- addr.sin_addr = loopback_addr;
- } else {
- addr.sin_addr = loopback_addr;
- }
- } else
- addr.sin_addr = so->so_faddr;
- addr.sin_port = so->so_fport;
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("m = %p", m);
- DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ addr = so->fhost.ss;
+ DEBUG_CALL(" sendto()ing)");
+ sotranslate_out(so, &addr);
/* Don't care what port we get */
ret = sendto(so->s, m->m_data, m->m_len, 0,
- (struct sockaddr *)&addr, sizeof (struct sockaddr));
+ (struct sockaddr *)&addr, sockaddr_size(&addr));
if (ret < 0)
return -1;
@@ -619,6 +682,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= (SS_FACCEPTCONN | flags);
+ so->so_lfamily = AF_INET;
so->so_lport = lport; /* Kept in network format */
so->so_laddr.s_addr = laddr; /* Ditto */
@@ -645,6 +709,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_ffamily = AF_INET;
so->so_fport = addr.sin_port;
if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
so->so_faddr = slirp->vhost_addr;
@@ -718,3 +783,113 @@ sofwdrain(struct socket *so)
else
sofcantsendmore(so);
}
+
+/*
+ * Translate addr in host addr when it is a virtual address
+ */
+void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
+{
+ Slirp *slirp = so->slirp;
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+ switch (addr->ss_family) {
+ case AF_INET:
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&sin->sin_addr) < 0) {
+ sin->sin_addr = loopback_addr;
+ }
+ } else {
+ sin->sin_addr = loopback_addr;
+ }
+ }
+
+ DEBUG_MISC((dfd, " addr.sin_port=%d, "
+ "addr.sin_addr.s_addr=%.16s\n",
+ ntohs(sin->sin_port), inet_ntoa(sin->sin_addr)));
+ break;
+
+ case AF_INET6:
+ 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 */
+ sin6->sin6_addr = in6addr_loopback;
+ /*}*/
+ } else {
+ sin6->sin6_addr = in6addr_loopback;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void sotranslate_in(struct socket *so, struct sockaddr_storage *addr)
+{
+ Slirp *slirp = so->slirp;
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+ switch (addr->ss_family) {
+ case AF_INET:
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+
+ if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
+ sin->sin_addr = slirp->vhost_addr;
+ } else if (sin->sin_addr.s_addr == loopback_addr.s_addr ||
+ so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+ sin->sin_addr = so->so_faddr;
+ }
+ }
+ break;
+
+ case AF_INET6:
+ if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6,
+ slirp->vprefix_len)) {
+ if (in6_equal(&sin6->sin6_addr, &in6addr_loopback)
+ || !in6_equal(&so->so_faddr6, &slirp->vhost_addr6)) {
+ sin6->sin6_addr = so->so_faddr6;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Translate connections from localhost to the real hostname
+ */
+void sotranslate_accept(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+
+ switch (so->so_ffamily) {
+ case AF_INET:
+ if (so->so_faddr.s_addr == INADDR_ANY ||
+ (so->so_faddr.s_addr & loopback_mask) ==
+ (loopback_addr.s_addr & loopback_mask)) {
+ so->so_faddr = slirp->vhost_addr;
+ }
+ break;
+
+ case AF_INET6:
+ if (in6_equal(&so->so_faddr6, &in6addr_any) ||
+ in6_equal(&so->so_faddr6, &in6addr_loopback)) {
+ so->so_faddr6 = slirp->vhost_addr6;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/qemu/slirp/socket.h b/qemu/slirp/socket.h
index 57e0407eb..b602e69b9 100644
--- a/qemu/slirp/socket.h
+++ b/qemu/slirp/socket.h
@@ -31,10 +31,27 @@ struct socket {
struct tcpiphdr *so_ti; /* Pointer to the original ti within
* so_mconn, for non-blocking connections */
int so_urgc;
- struct in_addr so_faddr; /* foreign host table entry */
- struct in_addr so_laddr; /* local host table entry */
- uint16_t so_fport; /* foreign port */
- uint16_t so_lport; /* local port */
+ union { /* foreign host */
+ struct sockaddr_storage ss;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } fhost;
+#define so_faddr fhost.sin.sin_addr
+#define so_fport fhost.sin.sin_port
+#define so_faddr6 fhost.sin6.sin6_addr
+#define so_fport6 fhost.sin6.sin6_port
+#define so_ffamily fhost.ss.ss_family
+
+ union { /* local host */
+ struct sockaddr_storage ss;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } lhost;
+#define so_laddr lhost.sin.sin_addr
+#define so_lport lhost.sin.sin_port
+#define so_laddr6 lhost.sin6.sin6_addr
+#define so_lport6 lhost.sin6.sin6_port
+#define so_lfamily lhost.ss.ss_family
uint8_t so_iptos; /* Type of service */
uint8_t so_emu; /* Is the socket emulated? */
@@ -76,11 +93,53 @@ struct socket {
#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
-struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int);
-struct socket * socreate(Slirp *);
+static inline int sockaddr_equal(struct sockaddr_storage *a,
+ struct sockaddr_storage *b)
+{
+ if (a->ss_family != b->ss_family) {
+ return 0;
+ }
+
+ switch (a->ss_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *a4 = (struct sockaddr_in *) a;
+ struct sockaddr_in *b4 = (struct sockaddr_in *) b;
+ 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();
+ }
+
+ return 0;
+}
+
+static inline socklen_t sockaddr_size(struct sockaddr_storage *a)
+{
+ switch (a->ss_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+struct socket *solookup(struct socket **, struct socket *,
+ struct sockaddr_storage *, struct sockaddr_storage *);
+struct socket *socreate(Slirp *);
void sofree(struct socket *);
int soread(struct socket *);
-void sorecvoob(struct socket *);
+int sorecvoob(struct socket *);
int sosendoob(struct socket *);
int sowrite(struct socket *);
void sorecvfrom(struct socket *);
@@ -94,4 +153,9 @@ struct iovec; /* For win32 */
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
int soreadbuf(struct socket *so, const char *buf, int size);
+void sotranslate_out(struct socket *, struct sockaddr_storage *);
+void sotranslate_in(struct socket *, struct sockaddr_storage *);
+void sotranslate_accept(struct socket *);
+
+
#endif /* _SOCKET_H_ */
diff --git a/qemu/slirp/tcp.h b/qemu/slirp/tcp.h
index 2e2b4033a..61befcde5 100644
--- a/qemu/slirp/tcp.h
+++ b/qemu/slirp/tcp.h
@@ -106,6 +106,8 @@ struct tcphdr {
*/
#undef TCP_MSS
#define TCP_MSS 1460
+#undef TCP6_MSS
+#define TCP6_MSS 1440
#undef TCP_MAXWIN
#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
diff --git a/qemu/slirp/tcp_input.c b/qemu/slirp/tcp_input.c
index f946db8dc..e2b5d4ebb 100644
--- a/qemu/slirp/tcp_input.c
+++ b/qemu/slirp/tcp_input.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include "ip_icmp.h"
@@ -213,9 +214,10 @@ present:
* protocol specification dated September, 1981 very closely.
*/
void
-tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
+tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
{
- struct ip save_ip, *ip;
+ struct ip save_ip, *ip;
+ struct ip6 save_ip6, *ip6;
register struct tcpiphdr *ti;
caddr_t optp = NULL;
int optlen = 0;
@@ -227,12 +229,15 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
int iss = 0;
u_long tiwin;
int ret;
+ struct sockaddr_storage lhost, fhost;
+ struct sockaddr_in *lhost4, *fhost4;
+ struct sockaddr_in6 *lhost6, *fhost6;
struct ex_list *ex_ptr;
Slirp *slirp;
DEBUG_CALL("tcp_input");
- DEBUG_ARGS((dfd, " m = %8lx iphlen = %2d inso = %lx\n",
- (long )m, iphlen, (long )inso ));
+ DEBUG_ARGS((dfd, " m = %p iphlen = %2d inso = %p\n",
+ m, iphlen, inso));
/*
* If called with m == 0, then we're continuing the connect
@@ -253,37 +258,83 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
}
slirp = m->slirp;
- /*
- * Get IP and TCP header together in first mbuf.
- * Note: IP leaves IP header in first mbuf.
- */
- ti = mtod(m, struct tcpiphdr *);
- if (iphlen > sizeof(struct ip )) {
- ip_stripoptions(m, (struct mbuf *)0);
- iphlen=sizeof(struct ip );
- }
- /* XXX Check if too short */
+ ip = mtod(m, struct ip *);
+ ip6 = mtod(m, struct ip6 *);
+ switch (af) {
+ case AF_INET:
+ if (iphlen > sizeof(struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+ /* XXX Check if too short */
- /*
- * Save a copy of the IP header in case we want restore it
- * for sending an ICMP error message in response.
- */
- ip=mtod(m, struct ip *);
- save_ip = *ip;
- save_ip.ip_len+= iphlen;
- /*
- * Checksum extended TCP header and data.
- */
- tlen = ((struct ip *)ti)->ip_len;
- tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
- memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
- ti->ti_x1 = 0;
- ti->ti_len = htons((uint16_t)tlen);
- len = sizeof(struct ip ) + tlen;
- if(cksum(m, len)) {
- goto drop;
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+ save_ip.ip_len += iphlen;
+
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ m->m_data -= sizeof(struct tcpiphdr) - sizeof(struct ip)
+ - sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr) - sizeof(struct ip)
+ - sizeof(struct tcphdr);
+ ti = mtod(m, struct tcpiphdr *);
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ip->ip_len;
+ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+ memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ memset(&ti->ti, 0, sizeof(ti->ti));
+ ti->ti_x0 = 0;
+ ti->ti_src = save_ip.ip_src;
+ ti->ti_dst = save_ip.ip_dst;
+ ti->ti_pr = save_ip.ip_p;
+ ti->ti_len = htons((uint16_t)tlen);
+ break;
+
+ case AF_INET6:
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip6 = *ip6;
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ m->m_data -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ + sizeof(struct tcphdr));
+ m->m_len += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ + sizeof(struct tcphdr));
+ ti = mtod(m, struct tcpiphdr *);
+
+ tlen = ip6->ip_pl;
+ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+ memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ memset(&ti->ti, 0, sizeof(ti->ti));
+ ti->ti_x0 = 0;
+ ti->ti_src6 = save_ip6.ip_src;
+ ti->ti_dst6 = save_ip6.ip_dst;
+ ti->ti_nh6 = save_ip6.ip_nh;
+ ti->ti_len = htons((uint16_t)tlen);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
+ if (cksum(m, len)) {
+ goto drop;
}
/*
@@ -320,17 +371,31 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
* Locate pcb for segment.
*/
findso:
- so = slirp->tcp_last_so;
- if (so->so_fport != ti->ti_dport ||
- so->so_lport != ti->ti_sport ||
- so->so_laddr.s_addr != ti->ti_src.s_addr ||
- so->so_faddr.s_addr != ti->ti_dst.s_addr) {
- so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport,
- ti->ti_dst, ti->ti_dport);
- if (so)
- slirp->tcp_last_so = so;
+ lhost.ss_family = af;
+ fhost.ss_family = af;
+ switch (af) {
+ case AF_INET:
+ lhost4 = (struct sockaddr_in *) &lhost;
+ lhost4->sin_addr = ti->ti_src;
+ lhost4->sin_port = ti->ti_sport;
+ fhost4 = (struct sockaddr_in *) &fhost;
+ fhost4->sin_addr = ti->ti_dst;
+ fhost4->sin_port = ti->ti_dport;
+ break;
+ case AF_INET6:
+ lhost6 = (struct sockaddr_in6 *) &lhost;
+ lhost6->sin6_addr = ti->ti_src6;
+ lhost6->sin6_port = ti->ti_sport;
+ fhost6 = (struct sockaddr_in6 *) &fhost;
+ fhost6->sin6_addr = ti->ti_dst6;
+ fhost6->sin6_port = ti->ti_dport;
+ break;
+ default:
+ g_assert_not_reached();
}
+ so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost);
+
/*
* If the state is CLOSED (i.e., TCB does not exist) then
* all data in the incoming segment is discarded.
@@ -374,13 +439,21 @@ findso:
sbreserve(&so->so_snd, TCP_SNDSPACE);
sbreserve(&so->so_rcv, TCP_RCVSPACE);
- so->so_laddr = ti->ti_src;
- so->so_lport = ti->ti_sport;
- so->so_faddr = ti->ti_dst;
- so->so_fport = ti->ti_dport;
-
- if ((so->so_iptos = tcp_tos(so)) == 0)
- so->so_iptos = ((struct ip *)ti)->ip_tos;
+ so->lhost.ss = lhost;
+ so->fhost.ss = fhost;
+
+ so->so_iptos = tcp_tos(so);
+ if (so->so_iptos == 0) {
+ switch (af) {
+ case AF_INET:
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+ break;
+ case AF_INET6:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
tp = sototcpcb(so);
tp->t_state = TCPS_LISTEN;
@@ -559,8 +632,9 @@ findso:
* If this is destined for the control address, then flag to
* tcp_ctl once connected, otherwise connect
*/
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
+ if (af == AF_INET &&
+ (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
/* May be an add exec */
@@ -584,24 +658,60 @@ findso:
goto cont_input;
}
- if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
- u_char code=ICMP_UNREACH_NET;
+ if ((tcp_fconnect(so, so->so_ffamily) == -1) &&
+ (errno != EAGAIN) &&
+ (errno != EINPROGRESS) && (errno != EWOULDBLOCK)
+ ) {
+ uint8_t code;
DEBUG_MISC((dfd, " tcp fconnect errno = %d-%s\n",
errno,strerror(errno)));
if(errno == ECONNREFUSED) {
/* ACK the SYN, send RST to refuse the connection */
- tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
- TH_RST|TH_ACK);
+ tcp_respond(tp, ti, m, ti->ti_seq + 1, (tcp_seq) 0,
+ TH_RST | TH_ACK, af);
} else {
- if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ switch (af) {
+ case AF_INET:
+ code = ICMP_UNREACH_NET;
+ if (errno == EHOSTUNREACH) {
+ code = ICMP_UNREACH_HOST;
+ }
+ break;
+ case AF_INET6:
+ code = ICMP6_UNREACH_NO_ROUTE;
+ if (errno == EHOSTUNREACH) {
+ code = ICMP6_UNREACH_ADDRESS;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
HTONL(ti->ti_seq); /* restore tcp header */
HTONL(ti->ti_ack);
HTONS(ti->ti_win);
HTONS(ti->ti_urp);
m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
- *ip=save_ip;
- icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+ switch (af) {
+ case AF_INET:
+ m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip)
+ - sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct ip)
+ - sizeof(struct tcphdr);
+ *ip = save_ip;
+ icmp_send_error(m, ICMP_UNREACH, code, 0, strerror(errno));
+ break;
+ case AF_INET6:
+ m->m_data += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ + sizeof(struct tcphdr));
+ m->m_len -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+ + sizeof(struct tcphdr));
+ *ip6 = save_ip6;
+ icmp6_send_error(m, ICMP6_UNREACH, code);
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
tcp_close(tp);
m_free(m);
@@ -616,6 +726,12 @@ findso:
so->so_ti = ti;
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
tp->t_state = TCPS_SYN_RECEIVED;
+ /*
+ * Initialize receive sequence numbers now so that we can send a
+ * valid RST if the remote end rejects our connection.
+ */
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
tcp_template(tp);
}
return;
@@ -917,8 +1033,8 @@ trimthenstep6:
if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
- DEBUG_MISC((dfd, " dup ack m = %lx so = %lx\n",
- (long )m, (long )so));
+ DEBUG_MISC((dfd, " dup ack m = %p so = %p\n",
+ m, so));
/*
* If we have outstanding data (other than
* a window probe), this is a completely
@@ -1273,11 +1389,11 @@ dropafterack:
dropwithreset:
/* reuses m if m!=NULL, m_free() unnecessary */
if (tiflags & TH_ACK)
- tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST, af);
else {
if (tiflags & TH_SYN) ti->ti_len++;
- tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
- TH_RST|TH_ACK);
+ tcp_respond(tp, ti, m, ti->ti_seq + ti->ti_len, (tcp_seq) 0,
+ TH_RST | TH_ACK, af);
}
return;
@@ -1296,7 +1412,7 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
int opt, optlen;
DEBUG_CALL("tcp_dooptions");
- DEBUG_ARGS((dfd, " tp = %lx cnt=%i\n", (long)tp, cnt));
+ DEBUG_ARGS((dfd, " tp = %p cnt=%i\n", tp, cnt));
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
@@ -1377,7 +1493,7 @@ tcp_xmit_timer(register struct tcpcb *tp, int rtt)
register short delta;
DEBUG_CALL("tcp_xmit_timer");
- DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("tp = %p", tp);
DEBUG_ARG("rtt = %d", rtt);
if (tp->t_srtt != 0) {
@@ -1465,10 +1581,22 @@ tcp_mss(struct tcpcb *tp, u_int offer)
int mss;
DEBUG_CALL("tcp_mss");
- DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("tp = %p", tp);
DEBUG_ARG("offer = %d", offer);
- mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
+ switch (so->so_ffamily) {
+ case AF_INET:
+ mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+ + sizeof(struct ip);
+ break;
+ case AF_INET6:
+ mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+ + sizeof(struct ip6);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
if (offer)
mss = min(mss, offer);
mss = max(mss, 32);
diff --git a/qemu/slirp/tcp_output.c b/qemu/slirp/tcp_output.c
index 8aa3d9047..99b0a9b1c 100644
--- a/qemu/slirp/tcp_output.c
+++ b/qemu/slirp/tcp_output.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
static const u_char tcp_outflags[TCP_NSTATES] = {
@@ -60,13 +61,15 @@ tcp_output(struct tcpcb *tp)
register long len, win;
int off, flags, error;
register struct mbuf *m;
- register struct tcpiphdr *ti;
+ register struct tcpiphdr *ti, tcpiph_save;
+ struct ip *ip;
+ struct ip6 *ip6;
u_char opt[MAX_TCPOPTLEN];
unsigned optlen, hdrlen;
int idle, sendalot;
DEBUG_CALL("tcp_output");
- DEBUG_ARG("tp = %lx", (long )tp);
+ DEBUG_ARG("tp = %p", tp);
/*
* Determine length of data that should be transmitted,
@@ -446,16 +449,45 @@ send:
* the template, but need a way to checksum without them.
*/
m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+ tcpiph_save = *mtod(m, struct tcpiphdr *);
+
+ switch (so->so_ffamily) {
+ case AF_INET:
+ m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip);
+ m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip);
+ ip = mtod(m, struct ip *);
+
+ ip->ip_len = m->m_len;
+ ip->ip_dst = tcpiph_save.ti_dst;
+ ip->ip_src = tcpiph_save.ti_src;
+ ip->ip_p = tcpiph_save.ti_pr;
+
+ ip->ip_ttl = IPDEFTTL;
+ ip->ip_tos = so->so_iptos;
+ error = ip_output(so, m);
+ break;
+
+ case AF_INET6:
+ m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip6);
+ m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip6);
+ ip6 = mtod(m, struct ip6 *);
+
+ ip6->ip_pl = tcpiph_save.ti_len;
+ ip6->ip_dst = tcpiph_save.ti_dst6;
+ ip6->ip_src = tcpiph_save.ti_src6;
+ ip6->ip_nh = tcpiph_save.ti_nh6;
+
+ error = ip6_output(so, m, 0);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
- {
-
- ((struct ip *)ti)->ip_len = m->m_len;
-
- ((struct ip *)ti)->ip_ttl = IPDEFTTL;
- ((struct ip *)ti)->ip_tos = so->so_iptos;
-
- error = ip_output(so, m);
- }
if (error) {
out:
return (error);
diff --git a/qemu/slirp/tcp_subr.c b/qemu/slirp/tcp_subr.c
index 7571c5a28..6b9fef200 100644
--- a/qemu/slirp/tcp_subr.c
+++ b/qemu/slirp/tcp_subr.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
/* patchable/settable parameters for tcp */
@@ -75,13 +76,30 @@ tcp_template(struct tcpcb *tp)
register struct tcpiphdr *n = &tp->t_template;
n->ti_mbuf = NULL;
- n->ti_x1 = 0;
- n->ti_pr = IPPROTO_TCP;
- n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
- n->ti_src = so->so_faddr;
- n->ti_dst = so->so_laddr;
- n->ti_sport = so->so_fport;
- n->ti_dport = so->so_lport;
+ memset(&n->ti, 0, sizeof(n->ti));
+ n->ti_x0 = 0;
+ switch (so->so_ffamily) {
+ case AF_INET:
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof(struct tcphdr));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+ break;
+
+ case AF_INET6:
+ n->ti_nh6 = IPPROTO_TCP;
+ n->ti_len = htons(sizeof(struct tcphdr));
+ n->ti_src6 = so->so_faddr6;
+ n->ti_dst6 = so->so_laddr6;
+ n->ti_sport = so->so_fport6;
+ n->ti_dport = so->so_lport6;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
n->ti_seq = 0;
n->ti_ack = 0;
@@ -108,7 +126,7 @@ tcp_template(struct tcpcb *tp)
*/
void
tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
- tcp_seq ack, tcp_seq seq, int flags)
+ tcp_seq ack, tcp_seq seq, int flags, unsigned short af)
{
register int tlen;
int win = 0;
@@ -130,6 +148,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_data += IF_MAXLINKHDR;
*mtod(m, struct tcpiphdr *) = *ti;
ti = mtod(m, struct tcpiphdr *);
+ memset(&ti->ti, 0, sizeof(ti->ti));
flags = TH_ACK;
} else {
/*
@@ -141,16 +160,26 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_len = sizeof (struct tcpiphdr);
tlen = 0;
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
- xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
- xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+ switch (af) {
+ case AF_INET:
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+ break;
+ case AF_INET6:
+ xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+ break;
+ default:
+ g_assert_not_reached();
+ }
#undef xchg
}
ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
tlen += sizeof (struct tcpiphdr);
m->m_len = tlen;
- ti->ti_mbuf = NULL;
- ti->ti_x1 = 0;
+ ti->ti_mbuf = NULL;
+ ti->ti_x0 = 0;
ti->ti_seq = htonl(seq);
ti->ti_ack = htonl(ack);
ti->ti_x2 = 0;
@@ -163,14 +192,49 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = cksum(m, tlen);
- ((struct ip *)ti)->ip_len = tlen;
-
- if(flags & TH_RST)
- ((struct ip *)ti)->ip_ttl = MAXTTL;
- else
- ((struct ip *)ti)->ip_ttl = IPDEFTTL;
- (void) ip_output((struct socket *)0, m);
+ struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *));
+ struct ip *ip;
+ struct ip6 *ip6;
+
+ switch (af) {
+ case AF_INET:
+ m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip);
+ m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip);
+ ip = mtod(m, struct ip *);
+ ip->ip_len = tlen;
+ ip->ip_dst = tcpiph_save.ti_dst;
+ ip->ip_src = tcpiph_save.ti_src;
+ ip->ip_p = tcpiph_save.ti_pr;
+
+ if (flags & TH_RST) {
+ ip->ip_ttl = MAXTTL;
+ } else {
+ ip->ip_ttl = IPDEFTTL;
+ }
+
+ ip_output(NULL, m);
+ break;
+
+ case AF_INET6:
+ m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip6);
+ m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+ - sizeof(struct ip6);
+ ip6 = mtod(m, struct ip6 *);
+ ip6->ip_pl = tlen;
+ ip6->ip_dst = tcpiph_save.ti_dst6;
+ ip6->ip_src = tcpiph_save.ti_src6;
+ ip6->ip_nh = tcpiph_save.ti_nh6;
+
+ ip6_output(NULL, m, 0);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
}
/*
@@ -189,7 +253,7 @@ tcp_newtcpcb(struct socket *so)
memset((char *) tp, 0, sizeof(struct tcpcb));
tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
- tp->t_maxseg = TCP_MSS;
+ tp->t_maxseg = (so->so_ffamily == AF_INET) ? TCP_MSS : TCP6_MSS;
tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
tp->t_socket = so;
@@ -224,7 +288,7 @@ tcp_newtcpcb(struct socket *so)
struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
{
DEBUG_CALL("tcp_drop");
- DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("tp = %p", tp);
DEBUG_ARG("errno = %d", errno);
if (TCPS_HAVERCVDSYN(tp->t_state)) {
@@ -249,7 +313,7 @@ tcp_close(struct tcpcb *tp)
register struct mbuf *m;
DEBUG_CALL("tcp_close");
- DEBUG_ARG("tp = %lx", (long )tp);
+ DEBUG_ARG("tp = %p", tp);
/* free the reassembly queue, if any */
t = tcpfrag_list_first(tp);
@@ -290,7 +354,11 @@ tcp_sockclosed(struct tcpcb *tp)
{
DEBUG_CALL("tcp_sockclosed");
- DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("tp = %p", tp);
+
+ if (!tp) {
+ return;
+ }
switch (tp->t_state) {
@@ -310,8 +378,7 @@ tcp_sockclosed(struct tcpcb *tp)
tp->t_state = TCPS_LAST_ACK;
break;
}
- if (tp)
- tcp_output(tp);
+ tcp_output(tp);
}
/*
@@ -324,42 +391,29 @@ tcp_sockclosed(struct tcpcb *tp)
* nonblocking. Connect returns after the SYN is sent, and does
* not wait for ACK+SYN.
*/
-int tcp_fconnect(struct socket *so)
+int tcp_fconnect(struct socket *so, unsigned short af)
{
- Slirp *slirp = so->slirp;
int ret=0;
DEBUG_CALL("tcp_fconnect");
- DEBUG_ARG("so = %lx", (long )so);
+ DEBUG_ARG("so = %p", so);
- if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) {
+ ret = so->s = qemu_socket(af, SOCK_STREAM, 0);
+ if (ret >= 0) {
int opt, s=so->s;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
qemu_set_nonblock(s);
socket_set_fast_reuse(s);
opt = 1;
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
- addr.sin_family = AF_INET;
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- /* It's an alias */
- if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
- if (get_dns_addr(&addr.sin_addr) < 0)
- addr.sin_addr = loopback_addr;
- } else {
- addr.sin_addr = loopback_addr;
- }
- } else
- addr.sin_addr = so->so_faddr;
- addr.sin_port = so->so_fport;
-
- DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
- "addr.sin_addr.s_addr=%.16s\n",
- ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ addr = so->fhost.ss;
+ DEBUG_CALL(" connect()ing")
+ sotranslate_out(so, &addr);
+
/* We don't care what port we get */
- ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+ ret = connect(s, (struct sockaddr *)&addr, sockaddr_size(&addr));
/*
* If it's not in progress, it failed, so we just return 0,
@@ -387,13 +441,13 @@ void tcp_connect(struct socket *inso)
{
Slirp *slirp = inso->slirp;
struct socket *so;
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(struct sockaddr_storage);
struct tcpcb *tp;
int s, opt;
DEBUG_CALL("tcp_connect");
- DEBUG_ARG("inso = %lx", (long)inso);
+ DEBUG_ARG("inso = %p", inso);
/*
* If it's an SS_ACCEPTONCE socket, no need to socreate()
@@ -413,8 +467,8 @@ void tcp_connect(struct socket *inso)
free(so); /* NOT sofree */
return;
}
- so->so_laddr = inso->so_laddr;
- so->so_lport = inso->so_lport;
+ so->lhost = inso->lhost;
+ so->so_ffamily = inso->so_ffamily;
}
tcp_mss(sototcpcb(so), 0);
@@ -430,14 +484,8 @@ void tcp_connect(struct socket *inso)
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
socket_set_nodelay(s);
- so->so_fport = addr.sin_port;
- so->so_faddr = addr.sin_addr;
- /* Translate connections from localhost to the real hostname */
- if (so->so_faddr.s_addr == 0 ||
- (so->so_faddr.s_addr & loopback_mask) ==
- (loopback_addr.s_addr & loopback_mask)) {
- so->so_faddr = slirp->vhost_addr;
- }
+ so->fhost.ss = addr;
+ sotranslate_accept(so);
/* Close the accept() socket, set right state */
if (inso->so_state & SS_FACCEPTONCE) {
@@ -564,8 +612,8 @@ tcp_emu(struct socket *so, struct mbuf *m)
char *bptr;
DEBUG_CALL("tcp_emu");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("m = %p", m);
switch(so->so_emu) {
int x, i;
@@ -900,7 +948,7 @@ int tcp_ctl(struct socket *so)
int do_pty;
DEBUG_CALL("tcp_ctl");
- DEBUG_ARG("so = %lx", (long )so);
+ DEBUG_ARG("so = %p", so);
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
/* Check if it's pty_exec */
diff --git a/qemu/slirp/tcp_timer.c b/qemu/slirp/tcp_timer.c
index 6c5bb11cc..8f5dd772a 100644
--- a/qemu/slirp/tcp_timer.c
+++ b/qemu/slirp/tcp_timer.c
@@ -30,6 +30,7 @@
* tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
*/
+#include "qemu/osdep.h"
#include <slirp.h>
static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
@@ -277,7 +278,8 @@ tcp_timers(register struct tcpcb *tp, int timer)
* correspondent TCP to respond.
*/
tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
- tp->rcv_nxt, tp->snd_una - 1, 0);
+ tp->rcv_nxt, tp->snd_una - 1, 0,
+ tp->t_socket->so_ffamily);
tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
} else
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
diff --git a/qemu/slirp/tcpip.h b/qemu/slirp/tcpip.h
index 7974ce3d5..124b4a9f6 100644
--- a/qemu/slirp/tcpip.h
+++ b/qemu/slirp/tcpip.h
@@ -37,15 +37,32 @@
* Tcp+ip header, after ip options removed.
*/
struct tcpiphdr {
- struct ipovly ti_i; /* overlaid ip structure */
- struct tcphdr ti_t; /* tcp header */
+ struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
+ union {
+ struct {
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+ uint8_t ih_x1; /* (unused) */
+ uint8_t ih_pr; /* protocol */
+ } ti_i4;
+ struct {
+ struct in6_addr ih_src;
+ struct in6_addr ih_dst;
+ uint8_t ih_x1;
+ uint8_t ih_nh;
+ } ti_i6;
+ } ti;
+ uint16_t ti_x0;
+ uint16_t ti_len; /* protocol length */
+ struct tcphdr ti_t; /* tcp header */
};
-#define ti_mbuf ti_i.ih_mbuf.mptr
-#define ti_x1 ti_i.ih_x1
-#define ti_pr ti_i.ih_pr
-#define ti_len ti_i.ih_len
-#define ti_src ti_i.ih_src
-#define ti_dst ti_i.ih_dst
+#define ti_mbuf ih_mbuf.mptr
+#define ti_pr ti.ti_i4.ih_pr
+#define ti_src ti.ti_i4.ih_src
+#define ti_dst ti.ti_i4.ih_dst
+#define ti_src6 ti.ti_i6.ih_src
+#define ti_dst6 ti.ti_i6.ih_dst
+#define ti_nh6 ti.ti_i6.ih_nh
#define ti_sport ti_t.th_sport
#define ti_dport ti_t.th_dport
#define ti_seq ti_t.th_seq
@@ -65,6 +82,13 @@ struct tcpiphdr {
#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
+/* This is the difference between the size of a tcpiphdr structure, and the
+ * size of actual ip+tcp headers, rounded up since we need to align data. */
+#define TCPIPHDR_DELTA\
+ (max(0,\
+ (sizeof(struct tcpiphdr)\
+ - sizeof(struct ip) - sizeof(struct tcphdr) + 3) & ~3))
+
/*
* Just a clean way to get to the first byte
* of the packet
diff --git a/qemu/slirp/tftp.c b/qemu/slirp/tftp.c
index a329fb281..12b5ff6e2 100644
--- a/qemu/slirp/tftp.c
+++ b/qemu/slirp/tftp.c
@@ -22,8 +22,10 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include "qemu-common.h"
+#include "qemu/cutils.h"
static inline int tftp_session_in_use(struct tftp_session *spt)
{
@@ -45,7 +47,8 @@ static void tftp_session_terminate(struct tftp_session *spt)
spt->slirp = NULL;
}
-static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
+static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
+ struct tftp_t *tp)
{
struct tftp_session *spt;
int k;
@@ -67,7 +70,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
found:
memset(spt, 0, sizeof(*spt));
- memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
+ spt->client_addr = *srcsas;
spt->fd = -1;
spt->client_port = tp->udp.uh_sport;
spt->slirp = slirp;
@@ -77,7 +80,8 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
return k;
}
-static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
+static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
+ struct tftp_t *tp)
{
struct tftp_session *spt;
int k;
@@ -86,7 +90,7 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
spt = &slirp->tftp_sessions[k];
if (tftp_session_in_use(spt)) {
- if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
+ if (sockaddr_equal(&spt->client_addr, srcsas)) {
if (spt->client_port == tp->udp.uh_sport) {
return k;
}
@@ -119,11 +123,53 @@ static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
return bytes_read;
}
+static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
+ struct mbuf *m)
+{
+ struct tftp_t *tp;
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ if (spt->client_addr.ss_family == AF_INET6) {
+ m->m_data += sizeof(struct ip6);
+ } else {
+ m->m_data += sizeof(struct ip);
+ }
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udphdr);
+
+ return tp;
+}
+
+static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
+ struct tftp_t *recv_tp)
+{
+ if (spt->client_addr.ss_family == AF_INET6) {
+ struct sockaddr_in6 sa6, da6;
+
+ sa6.sin6_addr = spt->slirp->vhost_addr6;
+ sa6.sin6_port = recv_tp->udp.uh_dport;
+ da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
+ da6.sin6_port = spt->client_port;
+
+ udp6_output(NULL, m, &sa6, &da6);
+ } else {
+ struct sockaddr_in sa4, da4;
+
+ sa4.sin_addr = spt->slirp->vhost_addr;
+ sa4.sin_port = recv_tp->udp.uh_dport;
+ da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
+ da4.sin_port = spt->client_port;
+
+ udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY);
+ }
+}
+
static int tftp_send_oack(struct tftp_session *spt,
const char *keys[], uint32_t values[], int nb,
struct tftp_t *recv_tp)
{
- struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int i, n = 0;
@@ -131,13 +177,9 @@ static int tftp_send_oack(struct tftp_session *spt,
m = m_get(spt->slirp);
if (!m)
- return -1;
-
- memset(m->m_data, 0, m->m_size);
+ return -1;
- m->m_data += IF_MAXLINKHDR;
- tp = (void *)m->m_data;
- m->m_data += sizeof(struct udpiphdr);
+ tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_OACK);
for (i = 0; i < nb; i++) {
@@ -147,15 +189,8 @@ static int tftp_send_oack(struct tftp_session *spt,
values[i]) + 1;
}
- saddr.sin_addr = recv_tp->ip.ip_dst;
- saddr.sin_port = recv_tp->udp.uh_dport;
-
- daddr.sin_addr = spt->client_ip;
- daddr.sin_port = spt->client_port;
-
- m->m_len = sizeof(struct tftp_t) - 514 + n -
- sizeof(struct ip) - sizeof(struct udphdr);
- udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+ m->m_len = sizeof(struct tftp_t) - 514 + n - sizeof(struct udphdr);
+ tftp_udp_output(spt, m, recv_tp);
return 0;
}
@@ -164,7 +199,6 @@ static void tftp_send_error(struct tftp_session *spt,
uint16_t errorcode, const char *msg,
struct tftp_t *recv_tp)
{
- struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
@@ -176,24 +210,15 @@ static void tftp_send_error(struct tftp_session *spt,
memset(m->m_data, 0, m->m_size);
- m->m_data += IF_MAXLINKHDR;
- tp = (void *)m->m_data;
- m->m_data += sizeof(struct udpiphdr);
+ tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_ERROR);
tp->x.tp_error.tp_error_code = htons(errorcode);
pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
- saddr.sin_addr = recv_tp->ip.ip_dst;
- saddr.sin_port = recv_tp->udp.uh_dport;
-
- daddr.sin_addr = spt->client_ip;
- daddr.sin_port = spt->client_port;
-
- m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
- sizeof(struct ip) - sizeof(struct udphdr);
-
- udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+ m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg)
+ - sizeof(struct udphdr);
+ tftp_udp_output(spt, m, recv_tp);
out:
tftp_session_terminate(spt);
@@ -202,7 +227,6 @@ out:
static void tftp_send_next_block(struct tftp_session *spt,
struct tftp_t *recv_tp)
{
- struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int nobytes;
@@ -215,19 +239,11 @@ static void tftp_send_next_block(struct tftp_session *spt,
memset(m->m_data, 0, m->m_size);
- m->m_data += IF_MAXLINKHDR;
- tp = (void *)m->m_data;
- m->m_data += sizeof(struct udpiphdr);
+ tp = tftp_prep_mbuf_data(spt, m);
tp->tp_op = htons(TFTP_DATA);
tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
- saddr.sin_addr = recv_tp->ip.ip_dst;
- saddr.sin_port = recv_tp->udp.uh_dport;
-
- daddr.sin_addr = spt->client_ip;
- daddr.sin_port = spt->client_port;
-
nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
if (nobytes < 0) {
@@ -240,10 +256,8 @@ static void tftp_send_next_block(struct tftp_session *spt,
return;
}
- m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
- sizeof(struct ip) - sizeof(struct udphdr);
-
- udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+ m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - sizeof(struct udphdr);
+ tftp_udp_output(spt, m, recv_tp);
if (nobytes == 512) {
tftp_session_update(spt);
@@ -255,7 +269,8 @@ static void tftp_send_next_block(struct tftp_session *spt,
spt->block_nr++;
}
-static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
+static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
+ struct tftp_t *tp, int pktlen)
{
struct tftp_session *spt;
int s, k;
@@ -266,12 +281,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
int nb_options = 0;
/* check if a session already exists and if so terminate it */
- s = tftp_session_find(slirp, tp);
+ s = tftp_session_find(slirp, srcsas, tp);
if (s >= 0) {
tftp_session_terminate(&slirp->tftp_sessions[s]);
}
- s = tftp_session_allocate(slirp, tp);
+ s = tftp_session_allocate(slirp, srcsas, tp);
if (s < 0) {
return;
@@ -396,11 +411,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_send_next_block(spt, tp);
}
-static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
+static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
+ struct tftp_t *tp, int pktlen)
{
int s;
- s = tftp_session_find(slirp, tp);
+ s = tftp_session_find(slirp, srcsas, tp);
if (s < 0) {
return;
@@ -409,11 +425,12 @@ static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_send_next_block(&slirp->tftp_sessions[s], tp);
}
-static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
+static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
+ struct tftp_t *tp, int pktlen)
{
int s;
- s = tftp_session_find(slirp, tp);
+ s = tftp_session_find(slirp, srcsas, tp);
if (s < 0) {
return;
@@ -422,21 +439,21 @@ static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
tftp_session_terminate(&slirp->tftp_sessions[s]);
}
-void tftp_input(struct mbuf *m)
+void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
{
struct tftp_t *tp = (struct tftp_t *)m->m_data;
switch(ntohs(tp->tp_op)) {
case TFTP_RRQ:
- tftp_handle_rrq(m->slirp, tp, m->m_len);
+ tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ACK:
- tftp_handle_ack(m->slirp, tp, m->m_len);
+ tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
break;
case TFTP_ERROR:
- tftp_handle_error(m->slirp, tp, m->m_len);
+ tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
break;
}
}
diff --git a/qemu/slirp/tftp.h b/qemu/slirp/tftp.h
index e1cc24b9b..1cb1adf59 100644
--- a/qemu/slirp/tftp.h
+++ b/qemu/slirp/tftp.h
@@ -16,7 +16,6 @@
#define TFTP_FILENAME_MAX 512
struct tftp_t {
- struct ip ip;
struct udphdr udp;
uint16_t tp_op;
union {
@@ -30,20 +29,20 @@ struct tftp_t {
} tp_error;
char tp_buf[512 + 2];
} x;
-};
+} __attribute__((packed));
struct tftp_session {
Slirp *slirp;
char *filename;
int fd;
- struct in_addr client_ip;
+ struct sockaddr_storage client_addr;
uint16_t client_port;
uint32_t block_nr;
int timestamp;
};
-void tftp_input(struct mbuf *m);
+void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m);
#endif
diff --git a/qemu/slirp/udp.c b/qemu/slirp/udp.c
index f77e00f5a..247024fd8 100644
--- a/qemu/slirp/udp.c
+++ b/qemu/slirp/udp.c
@@ -38,6 +38,7 @@
* terms and conditions of the copyright.
*/
+#include "qemu/osdep.h"
#include <slirp.h>
#include "ip_icmp.h"
@@ -70,9 +71,11 @@ udp_input(register struct mbuf *m, int iphlen)
int len;
struct ip save_ip;
struct socket *so;
+ struct sockaddr_storage lhost;
+ struct sockaddr_in *lhost4;
DEBUG_CALL("udp_input");
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("iphlen = %d", iphlen);
/*
@@ -125,6 +128,11 @@ udp_input(register struct mbuf *m, int iphlen)
}
}
+ lhost.ss_family = AF_INET;
+ lhost4 = (struct sockaddr_in *) &lhost;
+ lhost4->sin_addr = ip->ip_src;
+ lhost4->sin_port = uh->uh_sport;
+
/*
* handle DHCP/BOOTP
*/
@@ -140,7 +148,11 @@ udp_input(register struct mbuf *m, int iphlen)
*/
if (ntohs(uh->uh_dport) == TFTP_SERVER &&
ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
- tftp_input(m);
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ tftp_input(&lhost, m);
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
goto bad;
}
@@ -151,25 +163,7 @@ udp_input(register struct mbuf *m, int iphlen)
/*
* Locate pcb for datagram.
*/
- so = slirp->udp_last_so;
- if (so == &slirp->udb || so->so_lport != uh->uh_sport ||
- so->so_laddr.s_addr != ip->ip_src.s_addr) {
- struct socket *tmp;
-
- for (tmp = slirp->udb.so_next; tmp != &slirp->udb;
- tmp = tmp->so_next) {
- if (tmp->so_lport == uh->uh_sport &&
- tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
- so = tmp;
- break;
- }
- }
- if (tmp == &slirp->udb) {
- so = NULL;
- } else {
- slirp->udp_last_so = so;
- }
- }
+ so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL);
if (so == NULL) {
/*
@@ -180,7 +174,7 @@ udp_input(register struct mbuf *m, int iphlen)
if (!so) {
goto bad;
}
- if(udp_attach(so) == -1) {
+ if (udp_attach(so, AF_INET) == -1) {
DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
errno,strerror(errno)));
sofree(so);
@@ -190,6 +184,7 @@ udp_input(register struct mbuf *m, int iphlen)
/*
* Setup fields
*/
+ so->so_lfamily = AF_INET;
so->so_laddr = ip->ip_src;
so->so_lport = uh->uh_sport;
@@ -202,6 +197,7 @@ udp_input(register struct mbuf *m, int iphlen)
*/
}
+ so->so_ffamily = AF_INET;
so->so_faddr = ip->ip_dst; /* XXX */
so->so_fport = uh->uh_dport; /* XXX */
@@ -217,7 +213,9 @@ udp_input(register struct mbuf *m, int iphlen)
m->m_data -= iphlen;
*ip=save_ip;
DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
- icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0,
+ strerror(errno));
+ goto bad;
}
m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
@@ -233,7 +231,7 @@ bad:
m_free(m);
}
-int udp_output2(struct socket *so, struct mbuf *m,
+int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos)
{
@@ -241,8 +239,8 @@ int udp_output2(struct socket *so, struct mbuf *m,
int error = 0;
DEBUG_CALL("udp_output");
- DEBUG_ARG("so = %lx", (long)so);
- DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("so = %p", so);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
@@ -284,35 +282,11 @@ int udp_output2(struct socket *so, struct mbuf *m,
return (error);
}
-int udp_output(struct socket *so, struct mbuf *m,
- struct sockaddr_in *addr)
-
-{
- Slirp *slirp = so->slirp;
- struct sockaddr_in saddr, daddr;
-
- saddr = *addr;
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
-
- if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
- saddr.sin_addr = slirp->vhost_addr;
- } else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
- so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
- saddr.sin_addr = so->so_faddr;
- }
- }
- daddr.sin_addr = so->so_laddr;
- daddr.sin_port = so->so_lport;
-
- return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
-}
-
int
-udp_attach(struct socket *so)
+udp_attach(struct socket *so, unsigned short af)
{
- if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) {
+ so->s = qemu_socket(af, SOCK_DGRAM, 0);
+ if (so->s != -1) {
so->so_expire = curtime + SO_EXPIRE;
insque(so, &so->slirp->udb);
}
@@ -375,13 +349,9 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
socket_set_fast_reuse(so->s);
getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
- so->so_fport = addr.sin_port;
- if (addr.sin_addr.s_addr == 0 ||
- addr.sin_addr.s_addr == loopback_addr.s_addr) {
- so->so_faddr = slirp->vhost_addr;
- } else {
- so->so_faddr = addr.sin_addr;
- }
+ so->fhost.sin = addr;
+ sotranslate_accept(so);
+ so->so_lfamily = AF_INET;
so->so_lport = lport;
so->so_laddr.s_addr = laddr;
if (flags != SS_FACCEPTONCE)
diff --git a/qemu/slirp/udp.h b/qemu/slirp/udp.h
index 9bf31fe7b..10cc7809b 100644
--- a/qemu/slirp/udp.h
+++ b/qemu/slirp/udp.h
@@ -76,12 +76,16 @@ struct mbuf;
void udp_init(Slirp *);
void udp_cleanup(Slirp *);
void udp_input(register struct mbuf *, int);
-int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
-int udp_attach(struct socket *);
+int udp_attach(struct socket *, unsigned short af);
void udp_detach(struct socket *);
struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
-int udp_output2(struct socket *so, struct mbuf *m,
+int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos);
+
+void udp6_input(register struct mbuf *);
+int udp6_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr);
+
#endif
diff --git a/qemu/slirp/udp6.c b/qemu/slirp/udp6.c
new file mode 100644
index 000000000..a23026f2e
--- /dev/null
+++ b/qemu/slirp/udp6.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "slirp.h"
+#include "qemu/osdep.h"
+#include "udp.h"
+
+void udp6_input(struct mbuf *m)
+{
+ Slirp *slirp = m->slirp;
+ struct ip6 *ip, save_ip;
+ struct udphdr *uh;
+ int iphlen = sizeof(struct ip6);
+ int len;
+ struct socket *so;
+ struct sockaddr_in6 lhost;
+
+ DEBUG_CALL("udp6_input");
+ DEBUG_ARG("m = %lx", (long)m);
+
+ if (slirp->restricted) {
+ goto bad;
+ }
+
+ ip = mtod(m, struct ip6 *);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+ uh = mtod(m, struct udphdr *);
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+
+ if (ip6_cksum(m)) {
+ goto bad;
+ }
+
+ len = ntohs((uint16_t)uh->uh_ulen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ if (ntohs(ip->ip_pl) != len) {
+ if (len > ntohs(ip->ip_pl)) {
+ goto bad;
+ }
+ m_adj(m, len - ntohs(ip->ip_pl));
+ ip->ip_pl = htons(len);
+ }
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+
+ /* Locate pcb for datagram. */
+ lhost.sin6_family = AF_INET6;
+ lhost.sin6_addr = ip->ip_src;
+ lhost.sin6_port = uh->uh_sport;
+
+ /* TODO handle DHCP/BOOTP */
+
+ /* handle TFTP */
+ if (ntohs(uh->uh_dport) == TFTP_SERVER &&
+ !memcmp(ip->ip_dst.s6_addr, slirp->vhost_addr6.s6_addr, 16)) {
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ tftp_input((struct sockaddr_storage *)&lhost, m);
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
+ goto bad;
+ }
+
+ so = solookup(&slirp->udp_last_so, &slirp->udb,
+ (struct sockaddr_storage *) &lhost, NULL);
+
+ if (so == NULL) {
+ /* If there's no socket for this packet, create one. */
+ so = socreate(slirp);
+ if (!so) {
+ goto bad;
+ }
+ if (udp_attach(so, AF_INET6) == -1) {
+ DEBUG_MISC((dfd, " udp6_attach errno = %d-%s\n",
+ errno, strerror(errno)));
+ sofree(so);
+ goto bad;
+ }
+
+ /* Setup fields */
+ so->so_lfamily = AF_INET6;
+ so->so_laddr6 = ip->ip_src;
+ so->so_lport6 = uh->uh_sport;
+ }
+
+ so->so_ffamily = AF_INET6;
+ so->so_faddr6 = ip->ip_dst; /* XXX */
+ so->so_fport6 = uh->uh_dport; /* XXX */
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ /*
+ * Now we sendto() the packet.
+ */
+ if (sosendto(so, m) == -1) {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip = save_ip;
+ DEBUG_MISC((dfd, "udp tx errno = %d-%s\n", errno, strerror(errno)));
+ icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE);
+ goto bad;
+ }
+
+ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip = save_ip;
+ so->so_m = m;
+
+ return;
+bad:
+ m_free(m);
+}
+
+int udp6_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in6 *saddr, struct sockaddr_in6 *daddr)
+{
+ struct ip6 *ip;
+ struct udphdr *uh;
+
+ DEBUG_CALL("udp6_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /* adjust for header */
+ m->m_data -= sizeof(struct udphdr);
+ m->m_len += sizeof(struct udphdr);
+ uh = mtod(m, struct udphdr *);
+ m->m_data -= sizeof(struct ip6);
+ m->m_len += sizeof(struct ip6);
+ ip = mtod(m, struct ip6 *);
+
+ /* Build IP header */
+ ip->ip_pl = htons(m->m_len - sizeof(struct ip6));
+ ip->ip_nh = IPPROTO_UDP;
+ ip->ip_src = saddr->sin6_addr;
+ ip->ip_dst = daddr->sin6_addr;
+
+ /* Build UDP header */
+ uh->uh_sport = saddr->sin6_port;
+ uh->uh_dport = daddr->sin6_port;
+ uh->uh_ulen = ip->ip_pl;
+ uh->uh_sum = 0;
+ uh->uh_sum = ip6_cksum(m);
+ if (uh->uh_sum == 0) {
+ uh->uh_sum = 0xffff;
+ }
+
+ return ip6_output(so, m, 0);
+}