On 08.02.2016 11:28, Samuel Thibault wrote: > From: Guillaume Subiron <maet...@subiron.org> > > This patch adds the functions needed to handle IPv6 packets. ICMPv6 and > NDP headers are implemented. > > Slirp is now able to send NDP Router or Neighbor Advertisement when it > receives Router or Neighbor Solicitation. Using a 64bit-sized IPv6 > prefix, the guest is now able to perform stateless autoconfiguration > (SLAAC) and to compute its IPv6 address. > > This patch adds an ndp_table, mainly inspired by arp_table, to keep an > NDP cache and manage network address resolution. > Slirp regularly sends NDP Neighbor Advertisement, as recommended by the > RFC, to make the guest refresh its route. > > This also adds ip6_cksum() to compute ICMPv6 checksums using IPv6 > pseudo-header. > > Some #define ETH_* are moved upper in slirp.h to make them accessible to > other slirp/*.h > > Signed-off-by: Guillaume Subiron <maet...@subiron.org> > Signed-off-by: Samuel Thibault <samuel.thiba...@ens-lyon.org> > --- > slirp/Makefile.objs | 4 +- > slirp/cksum.c | 23 ++++ > slirp/if.c | 2 +- > slirp/ip6.h | 139 +++++++++++++++++++++ > slirp/ip6_icmp.c | 350 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > slirp/ip6_icmp.h | 244 ++++++++++++++++++++++++++++++++++++ > slirp/ip6_input.c | 72 +++++++++++ > slirp/ip6_output.c | 41 ++++++ > slirp/ndp_table.c | 87 +++++++++++++ > slirp/slirp.c | 48 ++++++- > slirp/slirp.h | 34 +++++ > slirp/socket.h | 7 ++ > 12 files changed, 1045 insertions(+), 6 deletions(-) > create mode 100644 slirp/ip6.h > create mode 100644 slirp/ip6_icmp.c > create mode 100644 slirp/ip6_icmp.h > create mode 100644 slirp/ip6_input.c > create mode 100644 slirp/ip6_output.c > create mode 100644 slirp/ndp_table.c > > diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs > index 2daa9dc..2dfe8e0 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 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
The lines in this file are now longer than 80 columns ... could you please rearrange them so that they do not exceed this limit anymore? > diff --git a/slirp/cksum.c b/slirp/cksum.c > index bc0d017..d2b0a83 100644 > --- a/slirp/cksum.c > +++ b/slirp/cksum.c > @@ -138,3 +138,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; > +} Just a small remark here: The function looks ok to me, so it should be OK for now, but I think there's some possibility for optimization here: Either the fields in the pseudohdr could be aligned with the fields in the real ip6 header, so that you would not need to copy all fields over here, or the cksum() function could maybe be changed in a way so that it is possible to calculate the checksum of the pseudoheader and upper layer header separately (and then return the sum of both checksums), so that you don't have to replace the ip6 header in-place here. Anyway, no need to do that right now, IMHO, but we might keep this in mind as a future possibility for optimization. > diff --git a/slirp/if.c b/slirp/if.c > index 93d7cc0..2e21f43 100644 > --- a/slirp/if.c > +++ b/slirp/if.c > @@ -194,7 +194,7 @@ void if_start(Slirp *slirp) > > /* Try to send packet unless it already expired */ > if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) { > - /* Packet is delayed due to pending ARP resolution */ > + /* Packet is delayed due to pending ARP or NDP resolution */ > continue; > } > > diff --git a/slirp/ip6.h b/slirp/ip6.h > new file mode 100644 > index 0000000..9e65acd > --- /dev/null > +++ b/slirp/ip6.h > @@ -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_ IIRC, defines that start with an underscore are reserved by the C standard ... so maybe better use SLIRP_IP6_H or so instead of _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)) I think at least I personally would not introduce these additional three macros here, and use the original macros instead. But that's likely just my personal taste. BTW, at least in6_linklocal() seems to be unused, so you could remove that one. > +#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) What about using pointers to the structs as parameters? Passing whole structs here sounds cumbersome to me. (that also applies to the other inline functions below) > +{ > + 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))); checkpatch.pl complains here: ERROR: return is not a function, parentheses are not required There are also some other stylistic problems that checkpatch.pl reports in this file ... would be nice to fix them. > +} > + > +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); checkpatch.pl complains about the style here ... and I think you could also drop the parentheses around "ip.s6_addr[16-(ETH_ALEN-2)]". > +} > + > +/* > + * Definitions for internet protocol version 6. > + * Per RFC 2460, December 1998. > + */ > +#define IP6VERSION 6 > +#define IP6_HOP_LIMIT 255 > + > +/* > + * Structure of an internet header, naked of options. > + */ > +struct ip6 { > +#ifdef HOST_WORDS_BIGENDIAN > + uint32_t > + ip_v:4, /* version */ > + ip_tc_hi:4, /* traffic class */ > + ip_tc_lo:4, > + ip_fl_hi:4, /* flow label */ > + ip_fl_lo:16; > +#else > + uint32_t > + ip_tc_hi:4, > + ip_v:4, > + ip_fl_hi:4, > + ip_tc_lo:4, > + ip_fl_lo:16; > +#endif > + uint16_t ip_pl; /* payload length */ > + uint8_t ip_nh; /* next header */ > + uint8_t ip_hl; /* hop limit */ > + struct in6_addr ip_src, ip_dst; /* source and dest address */ > +} QEMU_PACKED; > + > +/* > + * IPv6 pseudo-header used by upper-layer protocols > + */ > +struct ip6_pseudohdr { > + struct in6_addr ih_src; /* source internet address */ > + struct in6_addr ih_dst; /* destination internet address */ > + uint32_t ih_pl; /* upper-layer packet length */ > + uint16_t ih_zero_hi; /* zero */ > + uint8_t ih_zero_lo; /* zero */ > + uint8_t ih_nh; /* next header */ > +} QEMU_PACKED; > + > + > +#endif > diff --git a/slirp/ip6_icmp.c b/slirp/ip6_icmp.c > new file mode 100644 > index 0000000..13f89af > --- /dev/null > +++ b/slirp/ip6_icmp.c > @@ -0,0 +1,350 @@ > +/* > + * 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 "qemu/error-report.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_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval); > + ndp_send_ra(slirp); > +} > + > +void icmp6_init(Slirp *slirp) > +{ > + srand(time(NULL)); That srand should maybe be done in main() instead? (Otherwise every subsystem might end up in repeating this all over the place) > + slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, > slirp); > + timer_mod(slirp->ra_timer, > + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval); > +} > + > +#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 *); Not sure how strictly this is handled in QEMU, but for proper portable C, variables should be declared at the beginning of a scope, as far as I know. > + 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 *); dito > + 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; Manipulating m_len is not really required here, is it? Also the variable declaration should be at the beginning of the scope again. > + switch (icmp->icmp6_type) { > + case ICMP6_NDP_RS: > + DEBUG_CALL(" type = Routeur Solicitation"); s/Routeur/Router/ > + 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"); s/Routeur/Router/ > + error_report("Warning: guest sent NDP RA, but shouldn't\n"); If you use error_report, then please do not use "\n" in the strings. OTOH, I wonder whether this should maybe rather be a qemu_log_mask(LOG_GUEST_ERROR, ...) instead? > + 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); Since this case statement is rather long and has a big indentation depth already, I'd like to suggest to move the above code into a separate function instead. > + } > + } > + 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"); > + error_report("Warning: guest sent NDP REDIRECT, but shouldn't\n"); Either remove the "\n" or use qemu_log_mask(LOG_GUEST_ERROR, ...) > + break; > + > + default: > + return; You can remove that default case. > + } > + return; You can remove that return statement. > +} > + > +/* > + * 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 *); Please move the variable declaration to the beginning of the scope. > + 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 *); dito > + 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 *); dito > + 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); I think you could shorten this to: t_m_data = rip; ? > + ricmp->icmp6_cksum = ip6_cksum(t); > + > + ip6_output(NULL, t, 0); > +} ... > +/* > + * Process a received ICMPv6 message. > + */ > +void icmp6_input(struct mbuf *m) > +{ > + struct icmp6 *icmp; > + struct ip6 *ip = mtod(m, struct ip6 *); > + Slirp *slirp = m->slirp; > + int hlen = sizeof(struct ip6); > + > + DEBUG_CALL("icmp6_input"); > + DEBUG_ARG("m = %lx", (long) m); > + DEBUG_ARG("m_len = %d", m->m_len); > + > + if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { > + goto end; > + } > + > + if (ip6_cksum(m)) { > + goto end; > + } > + > + m->m_len -= hlen; > + m->m_data += hlen; > + icmp = mtod(m, struct icmp6 *); > + m->m_len += hlen; > + m->m_data -= hlen; > + > + DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type); > + switch (icmp->icmp6_type) { > + case ICMP6_ECHO_REQUEST: > + if (in6_equal_host(ip->ip_dst)) { > + icmp6_send_echoreply(m, slirp, ip, icmp); > + } else { > + /* TODO */ > + error_report("external icmpv6 not supported yet\n"); Please remove the "\n" in the string. > + } > + 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; Superfluous return statement. > +} > diff --git a/slirp/ip6_icmp.h b/slirp/ip6_icmp.h > new file mode 100644 > index 0000000..1ae003b > --- /dev/null > +++ b/slirp/ip6_icmp.h > @@ -0,0 +1,244 @@ > +/* > + * 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_ Could you please use "SLIRP_IP6_ICMP_H" or so instead? At least please don't use underscore in the beginning. > + > +/* > + * 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) A lot of these defines seem to be define in <netinet/icmp6.h> already (as recommended by RFC 3542) ... would it be feasible to use that standard header, instead of redefining here everything? > +/* > + * Router Configuration Variables (rfc4861#section-6) > + */ > +#define NDP_IsRouter 1 > +#define NDP_AdvSendAdvertisements 1 > +#define NDP_MaxRtrAdvInterval 600000 > +#define NDP_MinRtrAdvInterval ((NDP_MaxRtrAdvInterval >= 9) ? \ > + 0.33*NDP_MaxRtrAdvInterval : \ Could you use NDP_MaxRtrAdvInterval/3 instead of 0.33*NDP_MaxRtrAdvInterval to avoid the usage of floats here? > + NDP_MaxRtrAdvInterval) > +#define NDP_AdvManagedFlag 0 > +#define NDP_AdvOtherConfigFlag 0 > +#define NDP_AdvLinkMTU 0 > +#define NDP_AdvReachableTime 0 > +#define NDP_AdvRetransTime 0 > +#define NDP_AdvCurHopLimit 64 > +#define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000) > +#define NDP_AdvValidLifetime 86400 > +#define NDP_AdvOnLinkFlag 1 > +#define NDP_AdvPrefLifetime 14400 > +#define NDP_AdvAutonomousFlag 1 > + > +void icmp6_init(Slirp *slirp); > +void icmp6_cleanup(Slirp *slirp); > +void icmp6_input(struct mbuf *); > +void ndp_send_ra(Slirp *slirp); > +void ndp_send_ns(Slirp *slirp, struct in6_addr addr); > + > +#endif ... Thomas