@@ -1,3 +1,3 @@
-common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
+common-obj-y = cksum.o if.o ip_icmp.o ip6_icmp.o ip6_input.o ip6_output.o ip_input.o ip_output.o dnssearch.o
common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
-common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
+common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o ndp_table.o
@@ -137,3 +137,26 @@ cont:
REDUCE;
return (~sum & 0xffff);
}
+
+int ip6_cksum(struct mbuf *m)
+{
+ 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;
+}
new file mode 100644
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IP6_H_
+#define _IP6_H_
+
+#define in6_multicast(a) IN6_IS_ADDR_MULTICAST(&(a))
+#define in6_linklocal(a) IN6_IS_ADDR_LINKLOCAL(&(a))
+#define in6_unspecified(a) IN6_IS_ADDR_UNSPECIFIED(&(a))
+
+#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 int in6_equal(struct in6_addr a, struct in6_addr b)
+{
+ return memcmp(&a, &b, sizeof(a)) == 0;
+}
+
+static inline int in6_equal_net(struct in6_addr a, 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 int in6_equal_mach(struct in6_addr a, struct in6_addr b,
+ int prefix_len)
+{
+ if (memcmp(&(a.s6_addr[(prefix_len + 7) / 8]),
+ &(b.s6_addr[(prefix_len + 7) / 8]),
+ 16 - (prefix_len + 7) / 8) != 0) {
+ return 0;
+ }
+
+ if (prefix_len % 8 == 0) {
+ return 1;
+ }
+
+ return (a.s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1))
+ == (b.s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1));
+}
+
+#define in6_equal_router(a)\
+ ((in6_equal_net(a, slirp->vprefix_addr6, slirp->vprefix_len)\
+ && in6_equal_mach(a, slirp->vhost_addr6, slirp->vprefix_len))\
+ || (in6_equal_net(a, (struct in6_addr)LINKLOCAL_ADDR, 64)\
+ && in6_equal_mach(a, slirp->vhost_addr6, 64)))
+
+#define in6_equal_dns(a) 0
+
+#define in6_equal_host(a)\
+ (in6_equal_router(a) || in6_equal_dns(a))
+
+#define in6_solicitednode_multicast(a)\
+ (in6_equal_net(a, (struct in6_addr)SOLICITED_NODE_PREFIX, 104))
+
+/* Compute emulated host MAC address from its ipv6 address */
+static inline void in6_compute_ethaddr(struct in6_addr ip,
+ uint8_t eth[ETH_ALEN])
+{
+ eth[0] = 0x52;
+ eth[1] = 0x56;
+ memcpy(ð[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
new file mode 100644
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "slirp.h"
+#include "ip6_icmp.h"
+#include "qemu/timer.h"
+#include <stdlib.h>
+#include <time.h>
+
+#define rand_a_b(a, b)\
+ (rand()%(int)(b-a)+a)
+#define NDP_Interval rand_a_b(NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
+
+static void ra_timer_handler(void *opaque)
+{
+ Slirp *slirp = opaque;
+ timer_mod(slirp->ra_timer, qemu_clock_get_s(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
+ ndp_send_ra(slirp);
+}
+
+void icmp6_init(Slirp *slirp)
+{
+ srand(time(NULL));
+ slirp->ra_timer = timer_new_s(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
+ timer_mod(slirp->ra_timer, qemu_clock_get_s(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
+}
+
+#undef NDP_Interval
+#undef rand_a_b
+
+void icmp6_cleanup(Slirp *slirp)
+{
+ timer_del(slirp->ra_timer);
+ timer_free(slirp->ra_timer);
+}
+
+static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
+ struct icmp6 *icmp)
+{
+ struct mbuf *t = m_get(slirp);
+ t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
+ memcpy(t->m_data, m->m_data, t->m_len);
+
+ /* IPv6 Packet */
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_dst = ip->ip_src;
+ rip->ip_src = ip->ip_dst;
+
+ /* ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = ICMP6_ECHO_REPLY;
+ ricmp->icmp6_cksum = 0;
+
+ /* Checksum */
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 0);
+}
+
+/*
+ * 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 = Routeur 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 = Routeur Advertisement");
+ fprintf(stderr,
+ "Warning: guest sent NDP RA, but shouldn't\n");
+ break;
+
+ case ICMP6_NDP_NS:
+ DEBUG_CALL(" type = Neighbor Solicitation");
+ if (ip->ip_hl == 255
+ && icmp->icmp6_code == 0
+ && !in6_multicast(icmp->icmp6_nns.target)
+ && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
+ && (!in6_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);
+
+ /* 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_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_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);
+ }
+ }
+ 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_multicast(icmp->icmp6_nna.target)
+ && (!in6_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");
+ fprintf(stderr,
+ "Warning: guest sent NDP REDIRECT, but shouldn't\n");
+ break;
+
+ default:
+ return;
+ }
+ return;
+}
+
+/*
+ * 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)
+{
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
+ DEBUG_CALL("ndp_send_ns");
+ DEBUG_ARG("target = %s", addrstr);
+
+ /* 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);
+}
+
+/*
+ * 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 */
+ lprint("external icmpv6 not supported yet\n");
+ }
+ 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);
+ return;
+}
new file mode 100644
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _NETINET_ICMP6_H_
+#define _NETINET_ICMP6_H_
+
+/*
+ * Interface Control Message Protocol version 6 Definitions.
+ * Per RFC 4443, March 2006.
+ *
+ * Network Discover Protocol Definitions.
+ * Per RFC 4861, September 2007.
+ */
+
+struct icmp6_echo { /* Echo Messages */
+ uint16_t id;
+ uint16_t seq_num;
+};
+
+/*
+ * NDP Messages
+ */
+struct ndp_rs { /* Router Solicitation Message */
+ uint32_t reserved;
+};
+
+struct ndp_ra { /* Router Advertisement Message */
+ uint8_t chl; /* Cur Hop Limit */
+#ifdef HOST_WORDS_BIGENDIAN
+ uint8_t
+ M:1,
+ O:1,
+ reserved:6;
+#else
+ uint8_t
+ reserved:6,
+ O:1,
+ M:1;
+#endif
+ uint16_t lifetime; /* Router Lifetime */
+ uint32_t reach_time; /* Reachable Time */
+ uint32_t retrans_time; /* Retrans Timer */
+} QEMU_PACKED;
+
+struct ndp_ns { /* Neighbor Solicitation Message */
+ uint32_t reserved;
+ struct in6_addr target; /* Target Address */
+} QEMU_PACKED;
+
+struct ndp_na { /* Neighbor Advertisement Message */
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t
+ R:1, /* Router Flag */
+ S:1, /* Solicited Flag */
+ O:1, /* Override Flag */
+ reserved_hi:5,
+ reserved_lo:24;
+#else
+ uint32_t
+ reserved_hi:5,
+ O:1,
+ S:1,
+ R:1,
+ reserved_lo:24;
+#endif
+ struct in6_addr target; /* Target Address */
+} QEMU_PACKED;
+
+struct ndp_redirect {
+ uint32_t reserved;
+ struct in6_addr target; /* Target Address */
+ struct in6_addr dest; /* Destination Address */
+} QEMU_PACKED;
+
+/*
+ * Structure of an icmpv6 header.
+ */
+struct icmp6 {
+ uint8_t icmp6_type; /* type of message, see below */
+ uint8_t icmp6_code; /* type sub code */
+ uint16_t icmp6_cksum; /* ones complement cksum of struct */
+ union {
+ struct icmp6_echo echo;
+ struct ndp_rs ndp_rs;
+ struct ndp_ra ndp_ra;
+ struct ndp_ns ndp_ns;
+ struct ndp_na ndp_na;
+ struct ndp_redirect ndp_redirect;
+ } icmp6_body;
+#define icmp6_echo icmp6_body.echo
+#define icmp6_nrs icmp6_body.ndp_rs
+#define icmp6_nra icmp6_body.ndp_ra
+#define icmp6_nns icmp6_body.ndp_ns
+#define icmp6_nna icmp6_body.ndp_na
+#define icmp6_redirect icmp6_body.ndp_redirect
+} QEMU_PACKED;
+
+#define ICMP6_MINLEN 4
+#define ICMP6_ECHO_MINLEN 8
+#define ICMP6_NDP_RS_MINLEN 8
+#define ICMP6_NDP_RA_MINLEN 16
+#define ICMP6_NDP_NS_MINLEN 24
+#define ICMP6_NDP_NA_MINLEN 24
+#define ICMP6_NDP_REDIRECT_MINLEN 40
+
+/*
+ * NDP Options
+ */
+struct ndpopt {
+ uint8_t ndpopt_type; /* Option type */
+ uint8_t ndpopt_len; /* /!\ In units of 8 octets */
+ union {
+ unsigned char linklayer_addr[6]; /* Source/Target Link-layer */
+ struct prefixinfo { /* Prefix Information */
+ uint8_t prefix_length;
+#ifdef HOST_WORDS_BIGENDIAN
+ uint8_t L:1, A:1, reserved1:6;
+#else
+ uint8_t reserved1:6, A:1, L:1;
+#endif
+ uint32_t valid_lt; /* Valid Lifetime */
+ uint32_t pref_lt; /* Preferred Lifetime */
+ uint32_t reserved2;
+ struct in6_addr prefix;
+ } QEMU_PACKED prefixinfo;
+ } ndpopt_body;
+#define ndpopt_linklayer ndpopt_body.linklayer_addr
+#define ndpopt_prefixinfo ndpopt_body.prefixinfo
+} QEMU_PACKED;
+
+/* NDP options type */
+#define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */
+#define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */
+#define NDPOPT_PREFIX_INFO 3 /* Prefix Information */
+#define NDPOPT_REDIRECTED_HDR 4 /* Redirected Header */
+#define NDPOPT_MTU 5 /* MTU */
+
+/* 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_MCASTLST_QUERY 130 /* Multicast Listener Query */
+#define ICMP6_MCASTLST_REPORT 131 /* Multicast Listener Report */
+#define ICMP6_MCASTLST_DONE 132 /* Multicast Listener Done */
+#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) */
+#define ICMP6_ROUTERRENUM 138 /* Router Renumbering */
+#define ICMP6_ROUTERRENUM_COMMAND 0 /* router renum command */
+#define ICMP6_ROUTERRENUM_RESULT 1 /* router renum result */
+#define ICMP6_ROUTERRENUM_RESET 255 /* sequence number reset */
+#define ICMP6_NODEINFO_QUERY 139 /* ICMP Node Information Query */
+#define ICMP6_NODEINFO_QUERY_IPV6 0 /* subject is an IPv6 */
+#define ICMP6_NODEINFO_QUERY_NAME 1 /* subj is a name (or empty) */
+#define ICMP6_NODEINFO_QUERY_IPV4 2 /* subject is an IPv4 */
+#define ICMP6_NODEINFO_RESP 140 /* ICMP Node Information Response */
+#define ICMP6_NODEINFO_RESP_SUCCESS 0 /* successful reply */
+#define ICMP6_NODEINFO_RESP_REFUSAL 1 /* refuses to supply answer */
+#define ICMP6_NODEINFO_RESP_UNKNOWN 2 /* Qtype unknown to the resp */
+#define ICMP6_IND_S 141 /* Inverse Neighbor Discovery Sol. */
+#define ICMP6_IND_A 142 /* Inverse Neighbor Discovery Adv. */
+#define ICMP6_MLD 143 /* Multicast Listener Discovery reports */
+#define ICMP6_HAAD_REQUEST 144 /* Home Agent Address Discovery Request */
+#define ICMP6_HAAD_REPLY 145 /* Home Agent Address Discovery Reply */
+#define ICMP6_MP_SOL 146 /* Mobile Prefix Solicitation */
+#define ICMP6_MP_ADV 147 /* Mobile Prefix Advertisement */
+#define ICMP6_SEND_CPS 148 /* Certification Path Solicitation (SEND) */
+#define ICMP6_SEND_CPA 149 /* Certification Path Adv. (SEND) */
+#define ICMP6_MRD_RA 151 /* Multicast Router Advertisement (MRD) */
+#define ICMP6_MRD_RS 152 /* Multicast Router Solicitation (MRD) */
+#define ICMP6_MRD_TERM 153 /* Multicast Router Termination (MRD) */
+#define ICMP6_FMIP6 154 /* FMIPv6 Messages */
+#define ICMP6_FMIP6_RTSOLPR 2 /* RtSolPr */
+#define ICMP6_FMIP6_PRRTADV 3 /* PrRtAdv */
+#define ICMP6_RPL_CONTROL 155 /* RPL Control Message */
+#define ICMP6_ILNP6_LU 156 /* ILNPv6 Locator Update Message */
+#define ICMP6_DUPADDR_REQUEST 157 /* Duplicate Address Request */
+#define ICMP6_DUPADDR_CONFIRM 158 /* Duplicate Address Confirmation */
+
+#define ICMP6_INFOTYPE(type) ((type) >= 128)
+
+/*
+ * Router Configuration Variables (rfc4861#section-6)
+ */
+#define NDP_IsRouter 1
+#define NDP_AdvSendAdvertisements 1
+#define NDP_MaxRtrAdvInterval 600
+#define NDP_MinRtrAdvInterval ((NDP_MaxRtrAdvInterval >= 9) ? \
+ 0.33*NDP_MaxRtrAdvInterval : \
+ 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)
+#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 *);
+/* :TODO:maethor:130312: icmp6_error
+void icmp6_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message);
+*/
+void ndp_send_ra(Slirp *slirp);
+void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
+
+#endif
new file mode 100644
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <qemu/osdep.h>
+#include "ip6_icmp.h"
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void ip6_init(Slirp *slirp)
+{
+ icmp6_init(slirp);
+}
+
+void ip6_cleanup(Slirp *slirp)
+{
+ icmp6_cleanup(slirp);
+}
+
+void ip6_input(struct mbuf *m)
+{
+ struct ip6 *ip6;
+
+ DEBUG_CALL("ip6_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ if (m->m_len < sizeof(struct ip6)) {
+ return;
+ }
+
+ ip6 = mtod(m, struct ip6 *);
+
+ if (ip6->ip_v != IP6VERSION) {
+ goto bad;
+ }
+
+ /* check ip_ttl for a correct ICMP reply */
+ if (ip6->ip_hl == 0) {
+ /* :TODO:maethor:130307: icmp6_error */
+ /*icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");*/
+ goto bad;
+ }
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ switch (ip6->ip_nh) {
+#if 0
+ case IPPROTO_TCP:
+ /* :TODO:maethor:130307: TCP */
+ tcp_input(m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ /* :TODO:maethor:130312: UDP */
+ udp_input(m, hlen);
+ break;
+#endif
+ case IPPROTO_ICMPV6:
+ icmp6_input(m);
+ break;
+ default:
+ m_free(m);
+ }
+ return;
+bad:
+ m_free(m);
+}
new file mode 100644
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#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;
+}
new file mode 100644
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013
+ * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#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;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ DEBUG_CALL("ndp_table_add");
+ inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("ip = %s", addrstr);
+ 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_multicast(ip_addr) || in6_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;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ DEBUG_CALL("ndp_table_search");
+ inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("ip = %s", addrstr);
+
+ assert(!in6_unspecified(ip_addr));
+
+ /* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */
+ if (in6_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;
+}
@@ -214,6 +214,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
if_init(slirp);
ip_init(slirp);
+ ip6_init(slirp);
/* Initialise mbufs *after* setting the MTU */
m_init(slirp);
@@ -221,6 +222,12 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vnetwork_addr = vnetwork;
slirp->vnetwork_mask = vnetmask;
slirp->vhost_addr = vhost;
+ /* :TODO:maethor:130311: Use a parameter passed to the function */
+ inet_pton(AF_INET6, "fec0::0", &slirp->vprefix_addr6);
+ /* :TODO:maethor:130311: Use a parameter passed to the function */
+ slirp->vprefix_len = 64;
+ /* :TODO:maethor:130311: Use a parameter passed to the function */
+ inet_pton(AF_INET6, "fec0::2", &slirp->vhost_addr6);
if (vhostname) {
pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
vhostname);
@@ -251,6 +258,7 @@ void slirp_cleanup(Slirp *slirp)
unregister_savevm(NULL, "slirp", slirp);
ip_cleanup(slirp);
+ ip6_cleanup(slirp);
m_cleanup(slirp);
g_free(slirp->vdnssearch);
@@ -744,6 +752,7 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
arp_input(slirp, pkt, pkt_len);
break;
case ETH_P_IP:
+ case ETH_P_IP6:
m = m_get(slirp);
if (!m)
return;
@@ -757,8 +766,13 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
m->m_data += 2 + ETH_HLEN;
m->m_len -= 2 + ETH_HLEN;
- ip_input(m);
+ if (proto == ETH_P_IP) {
+ ip_input(m);
+ } else if (proto == ETH_P_IP6) {
+ ip6_input(m);
+ }
break;
+
default:
break;
}
@@ -773,6 +787,8 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
struct ethhdr *eh = (struct ethhdr *)buf;
uint8_t ethaddr[ETH_ALEN];
const struct ip *iph = (const struct ip *)ifm->m_data;
+ const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);
+ char addrstr[INET6_ADDRSTRLEN];
if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
return 1;
@@ -817,18 +833,33 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
ifm->expiration_date =
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL;
}
+ return 0;
+ } else {
+ 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);
+ break;
+ }
+
+ case IP6VERSION:
+ inet_ntop(AF_INET6, &(ip6h->ip_dst), addrstr, INET6_ADDRSTRLEN);
+ 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 {
- 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);
- break;
+ eh->h_proto = htons(ETH_P_IP6);
+ in6_compute_ethaddr(ip6h->ip_src, eh->h_source);
}
+ break;
default:
- /* Do not assert while we don't manage IP6VERSION */
- /* assert(0); */
+ assert(0);
break;
}
@@ -141,15 +141,18 @@ void free(void *ptr);
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_IP6 0x86DD /* IPv6 packet */
#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"
@@ -201,6 +204,23 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]);
bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
uint8_t out_ethaddr[ETH_ALEN]);
+struct ndpentry {
+ unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */
+ struct in6_addr ip_addr; /* sender IP address */
+} QEMU_PACKED;
+
+#define NDP_TABLE_SIZE 16
+
+typedef struct NdpTable {
+ struct ndpentry table[NDP_TABLE_SIZE];
+ int next_victim;
+} NdpTable;
+
+void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t ethaddr[ETH_ALEN]);
+bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr,
+ uint8_t out_ethaddr[ETH_ALEN]);
+
struct Slirp {
QTAILQ_ENTRY(Slirp) entry;
u_int time_fasttimo;
@@ -211,6 +231,9 @@ struct Slirp {
struct in_addr vnetwork_addr;
struct in_addr vnetwork_mask;
struct in_addr vhost_addr;
+ struct in6_addr vprefix_addr6;
+ uint8_t vprefix_len;
+ struct in6_addr vhost_addr6;
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
@@ -259,6 +282,9 @@ struct Slirp {
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
ArpTable arp_table;
+ NdpTable ndp_table;
+
+ QEMUTimer *ra_timer;
void *opaque;
};
@@ -303,6 +329,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 *);
@@ -318,6 +345,14 @@ void ip_stripoptions(register struct mbuf *, struct mbuf *);
/* ip_output.c */
int ip_output(struct socket *, struct mbuf *);
+/* ip6_input.c */
+void ip6_init(Slirp *);
+void ip6_cleanup(Slirp *);
+void ip6_input(struct mbuf *);
+
+/* ip6_output */
+int ip6_output(struct socket *, struct mbuf *, int fast);
+
/* tcp_input.c */
void tcp_input(register struct mbuf *, int, struct socket *);
int tcp_mss(register struct tcpcb *, u_int);