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 +#include +#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 + +/* 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);