diff mbox

[RFC] slirp: Adding IPv6 NDP autoconfiguration

Message ID 20130311182617.GA20436@subiron.org
State New
Headers show

Commit Message

Guillaume Subiron March 11, 2013, 6:26 p.m. UTC
Hi, 

We are developing IPv6 in Qemu -net user case, starting with ICMPv6
NDP.  

With this patch, a Debian guest can perform autoconfiguration using
NDP. We have added a full IPv6->ICMPv6->NDP stack starting from
slirp_input and based on slirp IPv6 implementation. In the end, SLIRP
responds to Router Solicitations by sending the fc00:: prefix in a
Router Advertisement and the host computes a global IPv6. SLIRP also
responds to Neighbor Solicitations, of course. 
The next thing is to send Neighbor Solicitations when if_encap needs
an IPv6 that is not in ndp_table, similarly to ARP.  

This patch will be cut into 2 subpatchs to follow the submit rules of
Qemu (refactoring, then the NDP feature), but we'd like to get some
feedback on the general approach, to know if we are going in the good
direction.  

Our next goals are to develop ICMPv6 router ping (not external ping)
and to adapt UDP.

Comments

Guillaume Subiron March 11, 2013, 9:29 p.m. UTC | #1
I forgot to say, this patch is not rebased yet. Based on commit
7d2a929feba319c18603e324b1750830d6c8b7a1

Le Mon, Mar 11, 2013 at 07:26:17PM +0100, Guillaume Subiron claviotta :
> Hi, 
> 
> We are developing IPv6 in Qemu -net user case, starting with ICMPv6
> NDP.  
> 
> With this patch, a Debian guest can perform autoconfiguration using
> NDP. We have added a full IPv6->ICMPv6->NDP stack starting from
> slirp_input and based on slirp IPv6 implementation. In the end, SLIRP
> responds to Router Solicitations by sending the fc00:: prefix in a
> Router Advertisement and the host computes a global IPv6. SLIRP also
> responds to Neighbor Solicitations, of course. 
> The next thing is to send Neighbor Solicitations when if_encap needs
> an IPv6 that is not in ndp_table, similarly to ARP.  
> 
> This patch will be cut into 2 subpatchs to follow the submit rules of
> Qemu (refactoring, then the NDP feature), but we'd like to get some
> feedback on the general approach, to know if we are going in the good
> direction.  
> 
> Our next goals are to develop ICMPv6 router ping (not external ping)
> and to adapt UDP.
> 
> -- 
> Guillaume Subiron 
>   Mail - maethor@subiron.org
>    GPG - C7C4 455C
> Jabber - maethor@im.subiron.org
>    IRC - maethor@(freenode|geeknode)

> diff --git a/roms/seabios b/roms/seabios
> --- a/roms/seabios
> +++ b/roms/seabios
> @@ -1 +1 @@
> -Subproject commit 4bd8aebf3534e10d9aa21e820903f2cf9120708c
> +Subproject commit 4bd8aebf3534e10d9aa21e820903f2cf9120708c-dirty
> diff --git a/roms/vgabios b/roms/vgabios
> --- a/roms/vgabios
> +++ b/roms/vgabios
> @@ -1 +1 @@
> -Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485
> +Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485-dirty
> diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
> index 2daa9dc..08ed5d8 100644
> --- a/slirp/Makefile.objs
> +++ b/slirp/Makefile.objs
> @@ -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 icmp6.o ip6_input.o ip6_output.o ip_input.o ip_output.o dnssearch.o
>  common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
> -common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
> +common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o ndp_table.o
> diff --git a/slirp/cksum.c b/slirp/cksum.c
> index 6328660..913dd7d 100644
> --- a/slirp/cksum.c
> +++ b/slirp/cksum.c
> @@ -137,3 +137,28 @@ 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;
> +}
> +
> +
> diff --git a/slirp/icmp6.c b/slirp/icmp6.c
> new file mode 100644
> index 0000000..64fb015
> --- /dev/null
> +++ b/slirp/icmp6.c
> @@ -0,0 +1,281 @@
> +#include "slirp.h"
> +#include "icmp6.h"
> +
> +/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
> +static const uint8_t special_ethaddr[ETH_ALEN] = {
> +    0x52, 0x55, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +void icmp6_init(Slirp *slirp)
> +{
> +    slirp->icmp6.so_next = slirp->icmp6.so_prev = &slirp->icmp6;
> +    slirp->icmp6_last_so = &slirp->icmp6;
> +}
> +
> +void icmp6_cleanup(Slirp *slirp)
> +{
> +    while (slirp->icmp6.so_next != &slirp->icmp6) {
> +        icmp6_detach(slirp->icmp6.so_next);
> +    }
> +}
> +
> +void icmp6_detach(struct socket *so)
> +{
> +    closesocket(so->s);
> +    sofree(so);
> +}
> +
> +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_ROUTER_SOL:
> +            DEBUG_CALL(" type = Routeur Solicitation");
> +            if (ip->ip_hl == 255
> +                    && icmp->icmp6_code == 0
> +                    && ip->ip_pl >= ICMP6_NDP_RS_MINLEN) {
> +                // :TODO:maethor:130308: 2 check missing
> +
> +                /* 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 = (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_LENGTH 
> +                                    + NDPOPT_PREFIXINFO_LENGTH);
> +                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_ROUTER_ADV;
> +                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_LENGTH / 8;
> +                memcpy(opt->ndpopt_linklayer, special_ethaddr, ETH_ALEN - 4);
> +                memcpy(&opt->ndpopt_linklayer[2], &slirp->vhost_addr, 4);
> +
> +                /* Prefix information (NDP option) */
> +                t->m_data += NDPOPT_LINKLAYER_LENGTH;
> +                struct ndpopt *opt2 = mtod(t, struct ndpopt *);
> +                opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
> +                opt2->ndpopt_len = NDPOPT_PREFIXINFO_LENGTH / 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_LENGTH;
> +                t->m_data -= ICMP6_NDP_RA_MINLEN;
> +                t->m_data -= sizeof(struct ip6);
> +                ricmp->icmp6_cksum = ip6_cksum(t);
> +
> +                ip6_output(NULL, t);
> +            }
> +            break;
> +
> +        case ICMP6_NDP_ROUTER_ADV:
> +            DEBUG_CALL(" type = Routeur Advertisement");
> +            fprintf(stderr, 
> +                    "Warning: guest sent NDP RA, but shouldn't\n");
> +            break;
> +
> +        case ICMP6_NDP_NEIGH_SOL:
> +            DEBUG_CALL(" type = Neighbor Solicitation");
> +            if (ip->ip_hl == 255
> +                    && icmp->icmp6_code == 0
> +                    && !in6_multicast(icmp->icmp6_nns.target)
> +                    && ip->ip_pl >= ICMP6_NDP_NS_MINLEN
> +                    && (!in6_unspecified(ip->ip_src) 
> +                        || in6_multicast(ip->ip_dst))) { 
> +                // :TODO:maethor:130308: 1 check missing
> +                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_LENGTH);
> +                    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_NEIGH_ADV;
> +                    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_LENGTH / 8;
> +                    memcpy(opt->ndpopt_linklayer, special_ethaddr, ETH_ALEN - 4);
> +                    memcpy(&opt->ndpopt_linklayer[2], &slirp->vhost_addr, 4);
> +                    
> +                    /* 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);
> +                }
> +            }
> +            break;
> +
> +        case ICMP6_NDP_NEIGH_ADV:
> +            DEBUG_CALL(" type = Neighbor Advertisement");
> +            if (ip->ip_hl == 255
> +                    && icmp->icmp6_code == 0
> +                    && 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;
> +}
> +
> +/*
> + * 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 (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_NDP_ROUTER_SOL:
> +        case ICMP6_NDP_ROUTER_ADV:
> +        case ICMP6_NDP_NEIGH_SOL:
> +        case ICMP6_NDP_NEIGH_ADV:
> +        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;
> +    } /* swith */
> +
> +end:
> +    m_free(m);
> +    return;
> +}
> +
> +void icmp6_receive(struct socket *so)
> +{
> +    struct mbuf *m = so->so_m;
> +    int hlen = sizeof(struct ip6);
> +    /*u_char error_code;*/
> +    struct icmp6 *icmp;
> +    int len;
> +
> +    m->m_data += hlen;
> +    m->m_len -= hlen;
> +    icmp = mtod(m, struct icmp6 *);
> +
> +    len = qemu_recv(so->s, icmp, m->m_len, 0);
> +
> +    m->m_data -= hlen;
> +    m->m_len += hlen;
> +
> +    if (len == -1 || len == 0) {
> +        /*
> +        if (errno == ENETUNREACH) {
> +            error_code = ICMP_UNREACH_NET;
> +        } else {
> +            error_code = ICMP_UNREACH_HOST;
> +        }
> +        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));
> +        */
> +    } else {
> +        /*icmp_reflect(so->so_m);*/
> +        so->so_m = NULL; /* Don't m_free() it again! */
> +    }
> +    icmp6_detach(so);
> +}
> diff --git a/slirp/icmp6.h b/slirp/icmp6.h
> new file mode 100644
> index 0000000..7c6f253
> --- /dev/null
> +++ b/slirp/icmp6.h
> @@ -0,0 +1,234 @@
> +#ifndef _NETINET_ICMP6_H_
> +#define _NETINET_ICMP6_H_
> +
> +/*
> + * Interface Control Message Protocol version 6 Definitions.
> + * Per RFC 4443, March 2006.
> + */
> +
> +typedef uint32_t n_time;
> +
> +/* 
> + * NDP Messages 
> + */
> +struct ndp_router_sol {
> +    uint32_t reserved;
> +};
> +
> +struct ndp_router_adv {
> +    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_neigh_sol {
> +    uint32_t reserved;
> +    struct in6_addr target; /* Target Address */
> +} QEMU_PACKED;
> +
> +struct ndp_neigh_adv {
> +#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 ndp_router_sol ndp_router_sol;
> +        struct ndp_router_adv ndp_router_adv;
> +        struct ndp_neigh_sol ndp_neigh_sol;
> +        struct ndp_neigh_adv ndp_neigh_adv;
> +        struct ndp_redirect ndp_redirect;
> +    } icmp6_body;
> +#define icmp6_nrs icmp6_body.ndp_router_sol
> +#define icmp6_nra icmp6_body.ndp_router_adv
> +#define icmp6_nns icmp6_body.ndp_neigh_sol
> +#define icmp6_nna icmp6_body.ndp_neigh_adv
> +#define icmp6_redirect icmp6_body.ndp_redirect
> +} QEMU_PACKED;
> +
> +/*
> + * icmp6 + ip6 pseudo-header, used to compute et verify checksum.
> + */
> +struct icmp6ip6 {
> +    struct 	ip6_pseudohdr   ip6;    /* overlaid ip structure */
> +    struct	icmp6   icmp6;          /* tcp header */
> +};
> +
> +
> +#define ICMP6_MINLEN    4   /* abs minimum: 32 bits */
> +#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
> +
> +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 MTU                         5   /* MTU */
> +
> +/* NDP options size, in octets. */
> +#define NDPOPT_LINKLAYER_LENGTH     8
> +#define NDPOPT_PREFIXINFO_LENGTH    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_ROUTER_SOL    133     /* Router Solicitation (NDP) */ 
> +#define ICMP6_NDP_ROUTER_ADV    134     /* Router Advertisement (NDP) */
> +#define ICMP6_NDP_NEIGH_SOL     135     /* Neighbor Solicitation (NDP) */
> +#define ICMP6_NDP_NEIGH_ADV     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_SOL   141     /* Inverse Neighbor Discovery Solicitation */
> +#define ICMP6_IND_ADV   142     /* Inverse Neighbor Discovery Advertisement */
> +#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_CP_SOL   148 /* Certification Path Solicitation (SEND) */
> +#define ICMP6_SEND_CP_ADV   149 /* Certification Path Advertisement (SEND) */
> +#define ICMP6_MRD_ADV   151     /* Multicast Router Advertisement (MRD) */
> +#define ICMP6_MRD_SOL   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:9)
> +#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 *);
> +void icmp6_error(struct mbuf *msrc, u_char type, u_char code, int minsize, 
> +                const char *message);
> +// :TODO:maethor:130307: 
> +//void icmp6_reflect(struct mbuf *);
> +void icmp6_receive(struct socket *so);
> +void icmp6_detach(struct socket *so);
> +
> +#endif
> diff --git a/slirp/if.c b/slirp/if.c
> index dcd5faf..a957ce2 100644
> --- a/slirp/if.c
> +++ b/slirp/if.c
> @@ -158,7 +158,7 @@ void if_start(Slirp *slirp)
>      bool from_batchq, next_from_batchq;
>      struct mbuf *ifm, *ifm_next, *ifqt;
>  
> -    DEBUG_CALL("if_start");
> +    /*DEBUG_CALL("if_start");*/
>  
>      if (slirp->if_start_busy) {
>          return;
> @@ -193,7 +193,7 @@ void if_start(Slirp *slirp)
>  
>          /* Try to send packet unless it already expired */
>          if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
> -            /* Packet is delayed due to pending ARP resolution */
> +            /* Packet is delayed due to pending ARP or NDP resolution */
>              continue;
>          }
>  
> diff --git a/slirp/ip6.h b/slirp/ip6.h
> new file mode 100644
> index 0000000..c853cd7
> --- /dev/null
> +++ b/slirp/ip6.h
> @@ -0,0 +1,98 @@
> +#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 LINKLOCAL_ADDR  { .s6_addr = \
> +                        { 0xfe, 0x80, 0x00, 0x00,\
> +                        0x00, 0x00, 0x00, 0x00,\
> +                        0x00, 0x00, 0x00, 0x00,\
> +                        0x00, 0x00, 0x00, 0x01 } }
> +
> +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 (prefix_len % 8) {
> +        assert(0); // ::TODO:maethor:130311: Manage this case
> +    } else {
> +        return (memcmp(&a, &b, prefix_len / 8) == 0);
> +    }
> +}
> +
> +static inline int in6_equal_mach(struct in6_addr a, struct in6_addr b, int prefix_len) {
> +    if (prefix_len % 8) {
> +        assert(0); // :TODO:maethor:130311: Manage this case
> +    } else {
> +        return (memcmp(&(a.s6_addr[prefix_len / 8]), 
> +                    &(b.s6_addr[prefix_len / 8]), 16 - prefix_len / 8) == 0);
> +    }
> +}
> +
> +#define in6_equal_router(a)\
> +    ((in6_equal_net(a, slirp->vprefix_addr6, slirp->vprefix_len)\
> +     || in6_equal_net(a, (struct in6_addr)LINKLOCAL_ADDR, 64))\
> +     && in6_equal_mach(a, slirp->vhost_addr6, slirp->vprefix_len))
> +
> +#define in6_equal_dns(a) 0
> +
> +#define in6_equal_host(a)\
> +    (in6_equal_router(a) || in6_equal_dns(a))
> +
> +typedef uint32_t n_long;                 /* long as received from the net */
> +
> +/*
> + * Definitions for internet protocol version 6.
> + * Per RFC 2460, December 1998.
> + */
> +#define	IP6VERSION	6
> +#define IP6_HOP_LIMIT 255
> +
> +/*
> + * Structure of an internet header, naked of options.
> + */
> +struct ip6 {
> +#ifdef HOST_WORDS_BIGENDIAN
> +    uint32_t 
> +        ip_v:4,			/* version */
> +        ip_tc_hi:4,		/* traffic class */
> +        ip_tc_lo:4,
> +        ip_fl_hi:4,     /* flow label */
> +        ip_fl_lo:16;
> +#else
> +    uint32_t
> +        ip_tc_hi:4,
> +        ip_v:4,
> +        ip_fl_hi:4,
> +        ip_tc_lo:4,
> +        ip_fl_lo:16;
> +#endif
> +    uint16_t    ip_pl;  /* payload length */
> +    uint8_t     ip_nh;  /* next header */
> +    uint8_t     ip_hl;  /* hop limit */
> +    struct in6_addr ip_src,ip_dst;  /* source and dest address */     
> +} QEMU_PACKED;
> +
> +/*
> + * IPv6 pseudo-header used by upper-layer protocols
> + */
> +struct ip6_pseudohdr {
> +    struct	in6_addr ih_src;    /* source internet address */
> +    struct	in6_addr ih_dst;    /* destination internet address */
> +    uint32_t    ih_pl;          /* upper-layer packet length */
> +    uint16_t    ih_zero_hi;     /* zero */
> +    uint8_t     ih_zero_lo;     /* zero */
> +    uint8_t     ih_nh;          /* next header */
> +} QEMU_PACKED;
> +
> +
> +#endif
> diff --git a/slirp/ip6_input.c b/slirp/ip6_input.c
> new file mode 100644
> index 0000000..32b4ea2
> --- /dev/null
> +++ b/slirp/ip6_input.c
> @@ -0,0 +1,68 @@
> +#include <slirp.h>
> +#include <qemu/osdep.h>
> +#include "icmp6.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)
> +{
> +    /*slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;*/
> +    /*udp_init(slirp);*/
> +    /*tcp_init(slirp);*/
> +    icmp6_init(slirp);
> +}
> +
> +void ip6_cleanup(Slirp *slirp)
> +{
> +    /*udp_cleanup(slirp);*/
> +    /*tcp_cleanup(slirp);*/
> +    icmp6_cleanup(slirp);
> +}
> +
> +void ip6_input(struct mbuf *m)
> +{
> +    register 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) {
> +        /*icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");*/ // :TODO:maethor:130307: 
> +        goto bad;
> +    }
> +
> +    /*
> +     * Switch out to protocol's input routine.
> +     */
> +    switch (ip6->ip_nh) {
> +        //case IPPROTO_TCP: :TODO:maethor:130307: 
> +        //    tcp_input(m, hlen, (struct socket *)NULL);
> +        //    break;
> +        //case IPPROTO_UDP:
> +        //    udp_input(m, hlen);
> +        //    break;
> +        case IPPROTO_ICMPV6:
> +            icmp6_input(m);
> +            break;
> +        default:
> +            m_free(m);
> +    }
> +    return;
> +bad:
> +    m_free(m);
> +}
> +
> diff --git a/slirp/ip6_output.c b/slirp/ip6_output.c
> new file mode 100644
> index 0000000..e38b421
> --- /dev/null
> +++ b/slirp/ip6_output.c
> @@ -0,0 +1,29 @@
> +#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)
> +{
> +    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_output(so, m);
> +   
> +    return 0;
> +}
> diff --git a/slirp/mbuf.c b/slirp/mbuf.c
> index 4fefb04..92c429e 100644
> --- a/slirp/mbuf.c
> +++ b/slirp/mbuf.c
> @@ -91,7 +91,7 @@ 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);
> diff --git a/slirp/mbuf.h b/slirp/mbuf.h
> index 3f3ab09..4110184 100644
> --- a/slirp/mbuf.h
> +++ b/slirp/mbuf.h
> @@ -82,7 +82,7 @@ struct m_hdr {
>  struct mbuf {
>  	struct	m_hdr m_hdr;
>  	Slirp *slirp;
> -	bool	arp_requested;
> +	bool	resolution_requested;
>  	uint64_t expiration_date;
>  	/* start of dynamic buffer area, must be last element */
>  	union M_dat {
> diff --git a/slirp/ndp_table.c b/slirp/ndp_table.c
> new file mode 100644
> index 0000000..b556d25
> --- /dev/null
> +++ b/slirp/ndp_table.c
> @@ -0,0 +1,79 @@
> +#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: fc00::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;
> +}
> diff --git a/slirp/slirp.c b/slirp/slirp.c
> index 0e6e232..e46212a 100644
> --- a/slirp/slirp.c
> +++ b/slirp/slirp.c
> @@ -135,8 +135,10 @@ int get_dns_addr(struct in_addr *pdns_addr)
>      }
>  
>      f = fopen("/etc/resolv.conf", "r");
> -    if (!f)
> +    if (!f) {
> +        fprintf(stderr, "Unable to open /etc/resolv.conf\n");
>          return -1;
> +    }
>  
>  #ifdef DEBUG
>      lprint("IP address of your DNS(s): ");
> @@ -168,8 +170,10 @@ int get_dns_addr(struct in_addr *pdns_addr)
>          }
>      }
>      fclose(f);
> -    if (!found)
> +    if (!found) {
> +        fprintf(stderr, "No IPv4 found in /etc/resolv.conf\n");
>          return -1;
> +    }
>      return 0;
>  }
>  
> @@ -214,6 +218,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 +226,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
>      slirp->vnetwork_addr = vnetwork;
>      slirp->vnetwork_mask = vnetmask;
>      slirp->vhost_addr = vhost;
> +    inet_pton(AF_INET6, "fc00::0", &slirp->vprefix_addr6);  // :TODO:maethor:130311: Utiliser un argument passé à la fonction
> +    slirp->vprefix_len = 64;                                // :TODO:maethor:130311: Utiliser un argument passé à la fonction
> +    inet_pton(AF_INET6, "fc00::1", &slirp->vhost_addr6);    // :TODO:maethor:130311: Utiliser un argument passé à la fonction
>      if (vhostname) {
>          pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
>                  vhostname);
> @@ -251,6 +259,7 @@ void slirp_cleanup(Slirp *slirp)
>      unregister_savevm(NULL, "slirp", slirp);
>  
>      ip_cleanup(slirp);
> +    ip6_cleanup(slirp);
>      m_cleanup(slirp);
>  
>      g_free(slirp->vdnssearch);
> @@ -413,6 +422,31 @@ void slirp_select_fill(int *pnfds,
>                          UPD_NFDS(so->s);
>                      }
>                  }
> +
> +                /*
> +                 * ICMPv6 sockets
> +                 */
> +                for (so = slirp->icmp6.so_next; so != &slirp->icmp6;
> +                     so = so_next) {
> +                    so_next = so->so_next;
> +
> +                    /*
> +                     * See if it's timed out
> +                     */
> +                    if (so->so_expire) {
> +                        if (so->so_expire <= curtime) {
> +                            icmp6_detach(so);
> +                            continue;
> +                        } else {
> +                            do_slowtimo = 1; /* Let socket expire */
> +                        }
> +                    }
> +
> +                    if (so->so_state & SS_ISFCONNECTED) {
> +                        FD_SET(so->s, readfds);
> +                        UPD_NFDS(so->s);
> +                    }
> +                }
>  	}
>  
>          *pnfds = nfds;
> @@ -594,6 +628,18 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
>                          icmp_receive(so);
>                      }
>                  }
> +
> +                /*
> +                 * Check incoming ICMPv6 relies.
> +                 */
> +                for (so = slirp->icmp6.so_next; so != &slirp->icmp6;
> +                     so = so_next) {
> +                     so_next = so->so_next;
> +
> +                    if (so->s != -1 && FD_ISSET(so->s, readfds)) {
> +                        icmp6_receive(so);
> +                    }
> +                }
>  	}
>  
>          if_start(slirp);
> @@ -682,6 +728,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;
> @@ -695,8 +742,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;
>  
> +            if (proto == ETH_P_IP) {
>                  ip_input(m);
> +            } else if (proto == ETH_P_IP6) {
> +                ip6_input(m);
> +            }
>              break;
> +
>          default:
>              break;
>      }
> @@ -707,21 +759,26 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
>   */
>  int if_encap(Slirp *slirp, struct mbuf *ifm)
>  {
> +    DEBUG_CALL("if_encap");
>      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;
> +    const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);
> +            char addrstr[INET6_ADDRSTRLEN];
>  
>      if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
>          return 1;
>      }
>  
> +    switch (iph->ip_v) {
> +        case IPVERSION:
>              if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) {
>                  uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
>                  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);
> @@ -747,23 +804,47 @@ 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_get_clock_ns(rt_clock) + 1000000000ULL;
>                  }
>                  return 0;
>              } else { 
> +                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)) {
> +                // :TODO:maethor:130311: NS
> +                return 0;
> +            } else {
> +                eh->h_proto = htons(ETH_P_IP6);
> +            }
> +            break;
> +
> +        default:
> +            assert(0);
> +            break;
> +    }
> +
>      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);
> +    DEBUG_ARGS((dfd, " SOURCE = %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, " DEST = %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);
> +    printf("TAILLE: =  %d\n", ifm->m_len);
>      slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
>      return 1;
>  }
> -}
>  
>  /* Drop host forwarding rule, return 0 if found. */
>  int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
> diff --git a/slirp/slirp.h b/slirp/slirp.h
> index fe0e65d..816a494 100644
> --- a/slirp/slirp.h
> +++ b/slirp/slirp.h
> @@ -138,12 +138,14 @@ void free(void *ptr);
>  
>  #include "libslirp.h"
>  #include "ip.h"
> +#include "ip6.h"
>  #include "tcp.h"
>  #include "tcp_timer.h"
>  #include "tcp_var.h"
>  #include "tcpip.h"
>  #include "udp.h"
>  #include "ip_icmp.h"
> +#include "icmp6.h"
>  #include "mbuf.h"
>  #include "sbuf.h"
>  #include "socket.h"
> @@ -163,6 +165,7 @@ 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               */
>  
>  #define ARPOP_REQUEST 1         /* ARP request */
>  #define ARPOP_REPLY   2         /* ARP reply   */
> @@ -201,6 +204,22 @@ 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;
>  
> @@ -208,6 +227,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;
>  
> @@ -250,12 +272,15 @@ struct Slirp {
>      /* icmp states */
>      struct socket icmp;
>      struct socket *icmp_last_so;
> +    struct socket icmp6;
> +    struct socket *icmp6_last_so;
>  
>      /* tftp states */
>      char *tftp_prefix;
>      struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
>  
>      ArpTable arp_table;
> +    NdpTable ndp_table;
>  
>      void *opaque;
>  };
> @@ -300,6 +325,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 *);
> @@ -315,6 +341,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 *);
> +
>  /* tcp_input.c */
>  void tcp_input(register struct mbuf *, int, struct socket *);
>  int tcp_mss(register struct tcpcb *, u_int);
diff mbox

Patch

diff --git a/roms/seabios b/roms/seabios
--- a/roms/seabios
+++ b/roms/seabios
@@ -1 +1 @@ 
-Subproject commit 4bd8aebf3534e10d9aa21e820903f2cf9120708c
+Subproject commit 4bd8aebf3534e10d9aa21e820903f2cf9120708c-dirty
diff --git a/roms/vgabios b/roms/vgabios
--- a/roms/vgabios
+++ b/roms/vgabios
@@ -1 +1 @@ 
-Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485
+Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485-dirty
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
index 2daa9dc..08ed5d8 100644
--- a/slirp/Makefile.objs
+++ b/slirp/Makefile.objs
@@ -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 icmp6.o ip6_input.o ip6_output.o ip_input.o ip_output.o dnssearch.o
 common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
-common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
+common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o ndp_table.o
diff --git a/slirp/cksum.c b/slirp/cksum.c
index 6328660..913dd7d 100644
--- a/slirp/cksum.c
+++ b/slirp/cksum.c
@@ -137,3 +137,28 @@  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;
+}
+
+
diff --git a/slirp/icmp6.c b/slirp/icmp6.c
new file mode 100644
index 0000000..64fb015
--- /dev/null
+++ b/slirp/icmp6.c
@@ -0,0 +1,281 @@ 
+#include "slirp.h"
+#include "icmp6.h"
+
+/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
+static const uint8_t special_ethaddr[ETH_ALEN] = {
+    0x52, 0x55, 0x00, 0x00, 0x00, 0x00
+};
+
+void icmp6_init(Slirp *slirp)
+{
+    slirp->icmp6.so_next = slirp->icmp6.so_prev = &slirp->icmp6;
+    slirp->icmp6_last_so = &slirp->icmp6;
+}
+
+void icmp6_cleanup(Slirp *slirp)
+{
+    while (slirp->icmp6.so_next != &slirp->icmp6) {
+        icmp6_detach(slirp->icmp6.so_next);
+    }
+}
+
+void icmp6_detach(struct socket *so)
+{
+    closesocket(so->s);
+    sofree(so);
+}
+
+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_ROUTER_SOL:
+            DEBUG_CALL(" type = Routeur Solicitation");
+            if (ip->ip_hl == 255
+                    && icmp->icmp6_code == 0
+                    && ip->ip_pl >= ICMP6_NDP_RS_MINLEN) {
+                // :TODO:maethor:130308: 2 check missing
+
+                /* 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 = (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_LENGTH 
+                                    + NDPOPT_PREFIXINFO_LENGTH);
+                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_ROUTER_ADV;
+                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_LENGTH / 8;
+                memcpy(opt->ndpopt_linklayer, special_ethaddr, ETH_ALEN - 4);
+                memcpy(&opt->ndpopt_linklayer[2], &slirp->vhost_addr, 4);
+
+                /* Prefix information (NDP option) */
+                t->m_data += NDPOPT_LINKLAYER_LENGTH;
+                struct ndpopt *opt2 = mtod(t, struct ndpopt *);
+                opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
+                opt2->ndpopt_len = NDPOPT_PREFIXINFO_LENGTH / 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_LENGTH;
+                t->m_data -= ICMP6_NDP_RA_MINLEN;
+                t->m_data -= sizeof(struct ip6);
+                ricmp->icmp6_cksum = ip6_cksum(t);
+
+                ip6_output(NULL, t);
+            }
+            break;
+
+        case ICMP6_NDP_ROUTER_ADV:
+            DEBUG_CALL(" type = Routeur Advertisement");
+            fprintf(stderr, 
+                    "Warning: guest sent NDP RA, but shouldn't\n");
+            break;
+
+        case ICMP6_NDP_NEIGH_SOL:
+            DEBUG_CALL(" type = Neighbor Solicitation");
+            if (ip->ip_hl == 255
+                    && icmp->icmp6_code == 0
+                    && !in6_multicast(icmp->icmp6_nns.target)
+                    && ip->ip_pl >= ICMP6_NDP_NS_MINLEN
+                    && (!in6_unspecified(ip->ip_src) 
+                        || in6_multicast(ip->ip_dst))) { 
+                // :TODO:maethor:130308: 1 check missing
+                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_LENGTH);
+                    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_NEIGH_ADV;
+                    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_LENGTH / 8;
+                    memcpy(opt->ndpopt_linklayer, special_ethaddr, ETH_ALEN - 4);
+                    memcpy(&opt->ndpopt_linklayer[2], &slirp->vhost_addr, 4);
+                    
+                    /* 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);
+                }
+            }
+            break;
+
+        case ICMP6_NDP_NEIGH_ADV:
+            DEBUG_CALL(" type = Neighbor Advertisement");
+            if (ip->ip_hl == 255
+                    && icmp->icmp6_code == 0
+                    && 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;
+}
+
+/*
+ * 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 (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_NDP_ROUTER_SOL:
+        case ICMP6_NDP_ROUTER_ADV:
+        case ICMP6_NDP_NEIGH_SOL:
+        case ICMP6_NDP_NEIGH_ADV:
+        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;
+    } /* swith */
+
+end:
+    m_free(m);
+    return;
+}
+
+void icmp6_receive(struct socket *so)
+{
+    struct mbuf *m = so->so_m;
+    int hlen = sizeof(struct ip6);
+    /*u_char error_code;*/
+    struct icmp6 *icmp;
+    int len;
+
+    m->m_data += hlen;
+    m->m_len -= hlen;
+    icmp = mtod(m, struct icmp6 *);
+
+    len = qemu_recv(so->s, icmp, m->m_len, 0);
+
+    m->m_data -= hlen;
+    m->m_len += hlen;
+
+    if (len == -1 || len == 0) {
+        /*
+        if (errno == ENETUNREACH) {
+            error_code = ICMP_UNREACH_NET;
+        } else {
+            error_code = ICMP_UNREACH_HOST;
+        }
+        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));
+        */
+    } else {
+        /*icmp_reflect(so->so_m);*/
+        so->so_m = NULL; /* Don't m_free() it again! */
+    }
+    icmp6_detach(so);
+}
diff --git a/slirp/icmp6.h b/slirp/icmp6.h
new file mode 100644
index 0000000..7c6f253
--- /dev/null
+++ b/slirp/icmp6.h
@@ -0,0 +1,234 @@ 
+#ifndef _NETINET_ICMP6_H_
+#define _NETINET_ICMP6_H_
+
+/*
+ * Interface Control Message Protocol version 6 Definitions.
+ * Per RFC 4443, March 2006.
+ */
+
+typedef uint32_t n_time;
+
+/* 
+ * NDP Messages 
+ */
+struct ndp_router_sol {
+    uint32_t reserved;
+};
+
+struct ndp_router_adv {
+    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_neigh_sol {
+    uint32_t reserved;
+    struct in6_addr target; /* Target Address */
+} QEMU_PACKED;
+
+struct ndp_neigh_adv {
+#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 ndp_router_sol ndp_router_sol;
+        struct ndp_router_adv ndp_router_adv;
+        struct ndp_neigh_sol ndp_neigh_sol;
+        struct ndp_neigh_adv ndp_neigh_adv;
+        struct ndp_redirect ndp_redirect;
+    } icmp6_body;
+#define icmp6_nrs icmp6_body.ndp_router_sol
+#define icmp6_nra icmp6_body.ndp_router_adv
+#define icmp6_nns icmp6_body.ndp_neigh_sol
+#define icmp6_nna icmp6_body.ndp_neigh_adv
+#define icmp6_redirect icmp6_body.ndp_redirect
+} QEMU_PACKED;
+
+/*
+ * icmp6 + ip6 pseudo-header, used to compute et verify checksum.
+ */
+struct icmp6ip6 {
+    struct 	ip6_pseudohdr   ip6;    /* overlaid ip structure */
+    struct	icmp6   icmp6;          /* tcp header */
+};
+
+
+#define ICMP6_MINLEN    4   /* abs minimum: 32 bits */
+#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
+
+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 MTU                         5   /* MTU */
+
+/* NDP options size, in octets. */
+#define NDPOPT_LINKLAYER_LENGTH     8
+#define NDPOPT_PREFIXINFO_LENGTH    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_ROUTER_SOL    133     /* Router Solicitation (NDP) */ 
+#define ICMP6_NDP_ROUTER_ADV    134     /* Router Advertisement (NDP) */
+#define ICMP6_NDP_NEIGH_SOL     135     /* Neighbor Solicitation (NDP) */
+#define ICMP6_NDP_NEIGH_ADV     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_SOL   141     /* Inverse Neighbor Discovery Solicitation */
+#define ICMP6_IND_ADV   142     /* Inverse Neighbor Discovery Advertisement */
+#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_CP_SOL   148 /* Certification Path Solicitation (SEND) */
+#define ICMP6_SEND_CP_ADV   149 /* Certification Path Advertisement (SEND) */
+#define ICMP6_MRD_ADV   151     /* Multicast Router Advertisement (MRD) */
+#define ICMP6_MRD_SOL   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:9)
+#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 *);
+void icmp6_error(struct mbuf *msrc, u_char type, u_char code, int minsize, 
+                const char *message);
+// :TODO:maethor:130307: 
+//void icmp6_reflect(struct mbuf *);
+void icmp6_receive(struct socket *so);
+void icmp6_detach(struct socket *so);
+
+#endif
diff --git a/slirp/if.c b/slirp/if.c
index dcd5faf..a957ce2 100644
--- a/slirp/if.c
+++ b/slirp/if.c
@@ -158,7 +158,7 @@  void if_start(Slirp *slirp)
     bool from_batchq, next_from_batchq;
     struct mbuf *ifm, *ifm_next, *ifqt;
 
-    DEBUG_CALL("if_start");
+    /*DEBUG_CALL("if_start");*/
 
     if (slirp->if_start_busy) {
         return;
@@ -193,7 +193,7 @@  void if_start(Slirp *slirp)
 
         /* Try to send packet unless it already expired */
         if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
-            /* Packet is delayed due to pending ARP resolution */
+            /* Packet is delayed due to pending ARP or NDP resolution */
             continue;
         }
 
diff --git a/slirp/ip6.h b/slirp/ip6.h
new file mode 100644
index 0000000..c853cd7
--- /dev/null
+++ b/slirp/ip6.h
@@ -0,0 +1,98 @@ 
+#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 LINKLOCAL_ADDR  { .s6_addr = \
+                        { 0xfe, 0x80, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x00,\
+                        0x00, 0x00, 0x00, 0x01 } }
+
+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 (prefix_len % 8) {
+        assert(0); // ::TODO:maethor:130311: Manage this case
+    } else {
+        return (memcmp(&a, &b, prefix_len / 8) == 0);
+    }
+}
+
+static inline int in6_equal_mach(struct in6_addr a, struct in6_addr b, int prefix_len) {
+    if (prefix_len % 8) {
+        assert(0); // :TODO:maethor:130311: Manage this case
+    } else {
+        return (memcmp(&(a.s6_addr[prefix_len / 8]), 
+                    &(b.s6_addr[prefix_len / 8]), 16 - prefix_len / 8) == 0);
+    }
+}
+
+#define in6_equal_router(a)\
+    ((in6_equal_net(a, slirp->vprefix_addr6, slirp->vprefix_len)\
+     || in6_equal_net(a, (struct in6_addr)LINKLOCAL_ADDR, 64))\
+     && in6_equal_mach(a, slirp->vhost_addr6, slirp->vprefix_len))
+
+#define in6_equal_dns(a) 0
+
+#define in6_equal_host(a)\
+    (in6_equal_router(a) || in6_equal_dns(a))
+
+typedef uint32_t n_long;                 /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 6.
+ * Per RFC 2460, December 1998.
+ */
+#define	IP6VERSION	6
+#define IP6_HOP_LIMIT 255
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip6 {
+#ifdef HOST_WORDS_BIGENDIAN
+    uint32_t 
+        ip_v:4,			/* version */
+        ip_tc_hi:4,		/* traffic class */
+        ip_tc_lo:4,
+        ip_fl_hi:4,     /* flow label */
+        ip_fl_lo:16;
+#else
+    uint32_t
+        ip_tc_hi:4,
+        ip_v:4,
+        ip_fl_hi:4,
+        ip_tc_lo:4,
+        ip_fl_lo:16;
+#endif
+    uint16_t    ip_pl;  /* payload length */
+    uint8_t     ip_nh;  /* next header */
+    uint8_t     ip_hl;  /* hop limit */
+    struct in6_addr ip_src,ip_dst;  /* source and dest address */     
+} QEMU_PACKED;
+
+/*
+ * IPv6 pseudo-header used by upper-layer protocols
+ */
+struct ip6_pseudohdr {
+    struct	in6_addr ih_src;    /* source internet address */
+    struct	in6_addr ih_dst;    /* destination internet address */
+    uint32_t    ih_pl;          /* upper-layer packet length */
+    uint16_t    ih_zero_hi;     /* zero */
+    uint8_t     ih_zero_lo;     /* zero */
+    uint8_t     ih_nh;          /* next header */
+} QEMU_PACKED;
+
+
+#endif
diff --git a/slirp/ip6_input.c b/slirp/ip6_input.c
new file mode 100644
index 0000000..32b4ea2
--- /dev/null
+++ b/slirp/ip6_input.c
@@ -0,0 +1,68 @@ 
+#include <slirp.h>
+#include <qemu/osdep.h>
+#include "icmp6.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)
+{
+    /*slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;*/
+    /*udp_init(slirp);*/
+    /*tcp_init(slirp);*/
+    icmp6_init(slirp);
+}
+
+void ip6_cleanup(Slirp *slirp)
+{
+    /*udp_cleanup(slirp);*/
+    /*tcp_cleanup(slirp);*/
+    icmp6_cleanup(slirp);
+}
+
+void ip6_input(struct mbuf *m)
+{
+    register 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) {
+        /*icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");*/ // :TODO:maethor:130307: 
+        goto bad;
+    }
+
+    /*
+     * Switch out to protocol's input routine.
+     */
+    switch (ip6->ip_nh) {
+        //case IPPROTO_TCP: :TODO:maethor:130307: 
+        //    tcp_input(m, hlen, (struct socket *)NULL);
+        //    break;
+        //case IPPROTO_UDP:
+        //    udp_input(m, hlen);
+        //    break;
+        case IPPROTO_ICMPV6:
+            icmp6_input(m);
+            break;
+        default:
+            m_free(m);
+    }
+    return;
+bad:
+    m_free(m);
+}
+
diff --git a/slirp/ip6_output.c b/slirp/ip6_output.c
new file mode 100644
index 0000000..e38b421
--- /dev/null
+++ b/slirp/ip6_output.c
@@ -0,0 +1,29 @@ 
+#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)
+{
+    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_output(so, m);
+   
+    return 0;
+}
diff --git a/slirp/mbuf.c b/slirp/mbuf.c
index 4fefb04..92c429e 100644
--- a/slirp/mbuf.c
+++ b/slirp/mbuf.c
@@ -91,7 +91,7 @@  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);
diff --git a/slirp/mbuf.h b/slirp/mbuf.h
index 3f3ab09..4110184 100644
--- a/slirp/mbuf.h
+++ b/slirp/mbuf.h
@@ -82,7 +82,7 @@  struct m_hdr {
 struct mbuf {
 	struct	m_hdr m_hdr;
 	Slirp *slirp;
-	bool	arp_requested;
+	bool	resolution_requested;
 	uint64_t expiration_date;
 	/* start of dynamic buffer area, must be last element */
 	union M_dat {
diff --git a/slirp/ndp_table.c b/slirp/ndp_table.c
new file mode 100644
index 0000000..b556d25
--- /dev/null
+++ b/slirp/ndp_table.c
@@ -0,0 +1,79 @@ 
+#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: fc00::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;
+}
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 0e6e232..e46212a 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -135,8 +135,10 @@  int get_dns_addr(struct in_addr *pdns_addr)
     }
 
     f = fopen("/etc/resolv.conf", "r");
-    if (!f)
+    if (!f) {
+        fprintf(stderr, "Unable to open /etc/resolv.conf\n");
         return -1;
+    }
 
 #ifdef DEBUG
     lprint("IP address of your DNS(s): ");
@@ -168,8 +170,10 @@  int get_dns_addr(struct in_addr *pdns_addr)
         }
     }
     fclose(f);
-    if (!found)
+    if (!found) {
+        fprintf(stderr, "No IPv4 found in /etc/resolv.conf\n");
         return -1;
+    }
     return 0;
 }
 
@@ -214,6 +218,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 +226,9 @@  Slirp *slirp_init(int restricted, struct in_addr vnetwork,
     slirp->vnetwork_addr = vnetwork;
     slirp->vnetwork_mask = vnetmask;
     slirp->vhost_addr = vhost;
+    inet_pton(AF_INET6, "fc00::0", &slirp->vprefix_addr6);  // :TODO:maethor:130311: Utiliser un argument passé à la fonction
+    slirp->vprefix_len = 64;                                // :TODO:maethor:130311: Utiliser un argument passé à la fonction
+    inet_pton(AF_INET6, "fc00::1", &slirp->vhost_addr6);    // :TODO:maethor:130311: Utiliser un argument passé à la fonction
     if (vhostname) {
         pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
                 vhostname);
@@ -251,6 +259,7 @@  void slirp_cleanup(Slirp *slirp)
     unregister_savevm(NULL, "slirp", slirp);
 
     ip_cleanup(slirp);
+    ip6_cleanup(slirp);
     m_cleanup(slirp);
 
     g_free(slirp->vdnssearch);
@@ -413,6 +422,31 @@  void slirp_select_fill(int *pnfds,
                         UPD_NFDS(so->s);
                     }
                 }
+
+                /*
+                 * ICMPv6 sockets
+                 */
+                for (so = slirp->icmp6.so_next; so != &slirp->icmp6;
+                     so = so_next) {
+                    so_next = so->so_next;
+
+                    /*
+                     * See if it's timed out
+                     */
+                    if (so->so_expire) {
+                        if (so->so_expire <= curtime) {
+                            icmp6_detach(so);
+                            continue;
+                        } else {
+                            do_slowtimo = 1; /* Let socket expire */
+                        }
+                    }
+
+                    if (so->so_state & SS_ISFCONNECTED) {
+                        FD_SET(so->s, readfds);
+                        UPD_NFDS(so->s);
+                    }
+                }
 	}
 
         *pnfds = nfds;
@@ -594,6 +628,18 @@  void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
                         icmp_receive(so);
                     }
                 }
+
+                /*
+                 * Check incoming ICMPv6 relies.
+                 */
+                for (so = slirp->icmp6.so_next; so != &slirp->icmp6;
+                     so = so_next) {
+                     so_next = so->so_next;
+
+                    if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+                        icmp6_receive(so);
+                    }
+                }
 	}
 
         if_start(slirp);
@@ -682,6 +728,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;
@@ -695,8 +742,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;
 
+            if (proto == ETH_P_IP) {
                 ip_input(m);
+            } else if (proto == ETH_P_IP6) {
+                ip6_input(m);
+            }
             break;
+
         default:
             break;
     }
@@ -707,21 +759,26 @@  void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
  */
 int if_encap(Slirp *slirp, struct mbuf *ifm)
 {
+    DEBUG_CALL("if_encap");
     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;
+    const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);
+            char addrstr[INET6_ADDRSTRLEN];
 
     if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
         return 1;
     }
 
+    switch (iph->ip_v) {
+        case IPVERSION:
             if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) {
                 uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
                 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);
@@ -747,23 +804,47 @@  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_get_clock_ns(rt_clock) + 1000000000ULL;
                 }
                 return 0;
             } else { 
+                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)) {
+                // :TODO:maethor:130311: NS
+                return 0;
+            } else {
+                eh->h_proto = htons(ETH_P_IP6);
+            }
+            break;
+
+        default:
+            assert(0);
+            break;
+    }
+
     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);
+    DEBUG_ARGS((dfd, " SOURCE = %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, " DEST = %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);
+    printf("TAILLE: =  %d\n", ifm->m_len);
     slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
     return 1;
 }
-}
 
 /* Drop host forwarding rule, return 0 if found. */
 int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
diff --git a/slirp/slirp.h b/slirp/slirp.h
index fe0e65d..816a494 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -138,12 +138,14 @@  void free(void *ptr);
 
 #include "libslirp.h"
 #include "ip.h"
+#include "ip6.h"
 #include "tcp.h"
 #include "tcp_timer.h"
 #include "tcp_var.h"
 #include "tcpip.h"
 #include "udp.h"
 #include "ip_icmp.h"
+#include "icmp6.h"
 #include "mbuf.h"
 #include "sbuf.h"
 #include "socket.h"
@@ -163,6 +165,7 @@  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               */
 
 #define ARPOP_REQUEST 1         /* ARP request */
 #define ARPOP_REPLY   2         /* ARP reply   */
@@ -201,6 +204,22 @@  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;
 
@@ -208,6 +227,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;
 
@@ -250,12 +272,15 @@  struct Slirp {
     /* icmp states */
     struct socket icmp;
     struct socket *icmp_last_so;
+    struct socket icmp6;
+    struct socket *icmp6_last_so;
 
     /* tftp states */
     char *tftp_prefix;
     struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
 
     ArpTable arp_table;
+    NdpTable ndp_table;
 
     void *opaque;
 };
@@ -300,6 +325,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 *);
@@ -315,6 +341,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 *);
+
 /* tcp_input.c */
 void tcp_input(register struct mbuf *, int, struct socket *);
 int tcp_mss(register struct tcpcb *, u_int);