qemu/slirp/ip6_icmp.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013
   3 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne.
   4 */
   5
   6#include "qemu/osdep.h"
   7#include "slirp.h"
   8#include "ip6_icmp.h"
   9#include "qemu/timer.h"
  10#include "qemu/error-report.h"
  11#include "qemu/log.h"
  12#include <time.h>
  13
  14#define NDP_Interval g_rand_int_range(slirp->grand, \
  15        NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval)
  16
  17static void ra_timer_handler(void *opaque)
  18{
  19    Slirp *slirp = opaque;
  20    timer_mod(slirp->ra_timer,
  21              qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
  22    ndp_send_ra(slirp);
  23}
  24
  25void icmp6_init(Slirp *slirp)
  26{
  27    if (!slirp->in6_enabled) {
  28        return;
  29    }
  30
  31    slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ra_timer_handler, slirp);
  32    timer_mod(slirp->ra_timer,
  33              qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
  34}
  35
  36void icmp6_cleanup(Slirp *slirp)
  37{
  38    if (!slirp->in6_enabled) {
  39        return;
  40    }
  41
  42    timer_del(slirp->ra_timer);
  43    timer_free(slirp->ra_timer);
  44}
  45
  46static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
  47        struct icmp6 *icmp)
  48{
  49    struct mbuf *t = m_get(slirp);
  50    t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl);
  51    memcpy(t->m_data, m->m_data, t->m_len);
  52
  53    /* IPv6 Packet */
  54    struct ip6 *rip = mtod(t, struct ip6 *);
  55    rip->ip_dst = ip->ip_src;
  56    rip->ip_src = ip->ip_dst;
  57
  58    /* ICMPv6 packet */
  59    t->m_data += sizeof(struct ip6);
  60    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
  61    ricmp->icmp6_type = ICMP6_ECHO_REPLY;
  62    ricmp->icmp6_cksum = 0;
  63
  64    /* Checksum */
  65    t->m_data -= sizeof(struct ip6);
  66    ricmp->icmp6_cksum = ip6_cksum(t);
  67
  68    ip6_output(NULL, t, 0);
  69}
  70
  71void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
  72{
  73    Slirp *slirp = m->slirp;
  74    struct mbuf *t;
  75    struct ip6 *ip = mtod(m, struct ip6 *);
  76
  77    DEBUG_CALL("icmp6_send_error");
  78    DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
  79
  80    if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
  81            IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
  82        /* TODO icmp error? */
  83        return;
  84    }
  85
  86    t = m_get(slirp);
  87
  88    /* IPv6 packet */
  89    struct ip6 *rip = mtod(t, struct ip6 *);
  90    rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
  91    rip->ip_dst = ip->ip_src;
  92#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
  93    char addrstr[INET6_ADDRSTRLEN];
  94    inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
  95    DEBUG_ARG("target = %s", addrstr);
  96#endif
  97
  98    rip->ip_nh = IPPROTO_ICMPV6;
  99    const int error_data_len = min(m->m_len,
 100            IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
 101    rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
 102    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 103
 104    /* ICMPv6 packet */
 105    t->m_data += sizeof(struct ip6);
 106    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 107    ricmp->icmp6_type = type;
 108    ricmp->icmp6_code = code;
 109    ricmp->icmp6_cksum = 0;
 110
 111    switch (type) {
 112    case ICMP6_UNREACH:
 113    case ICMP6_TIMXCEED:
 114        ricmp->icmp6_err.unused = 0;
 115        break;
 116    case ICMP6_TOOBIG:
 117        ricmp->icmp6_err.mtu = htonl(IF_MTU);
 118        break;
 119    case ICMP6_PARAMPROB:
 120        /* TODO: Handle this case */
 121        break;
 122    default:
 123        g_assert_not_reached();
 124        break;
 125    }
 126    t->m_data += ICMP6_ERROR_MINLEN;
 127    memcpy(t->m_data, m->m_data, error_data_len);
 128
 129    /* Checksum */
 130    t->m_data -= ICMP6_ERROR_MINLEN;
 131    t->m_data -= sizeof(struct ip6);
 132    ricmp->icmp6_cksum = ip6_cksum(t);
 133
 134    ip6_output(NULL, t, 0);
 135}
 136
 137/*
 138 * Send NDP Router Advertisement
 139 */
 140void ndp_send_ra(Slirp *slirp)
 141{
 142    DEBUG_CALL("ndp_send_ra");
 143
 144    /* Build IPv6 packet */
 145    struct mbuf *t = m_get(slirp);
 146    struct ip6 *rip = mtod(t, struct ip6 *);
 147    rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
 148    rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
 149    rip->ip_nh = IPPROTO_ICMPV6;
 150    rip->ip_pl = htons(ICMP6_NDP_RA_MINLEN
 151                        + NDPOPT_LINKLAYER_LEN
 152                        + NDPOPT_PREFIXINFO_LEN);
 153    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 154
 155    /* Build ICMPv6 packet */
 156    t->m_data += sizeof(struct ip6);
 157    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 158    ricmp->icmp6_type = ICMP6_NDP_RA;
 159    ricmp->icmp6_code = 0;
 160    ricmp->icmp6_cksum = 0;
 161
 162    /* NDP */
 163    ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
 164    ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
 165    ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
 166    ricmp->icmp6_nra.reserved = 0;
 167    ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
 168    ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
 169    ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
 170
 171    /* Source link-layer address (NDP option) */
 172    t->m_data += ICMP6_NDP_RA_MINLEN;
 173    struct ndpopt *opt = mtod(t, struct ndpopt *);
 174    opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
 175    opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
 176    in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer);
 177
 178    /* Prefix information (NDP option) */
 179    t->m_data += NDPOPT_LINKLAYER_LEN;
 180    struct ndpopt *opt2 = mtod(t, struct ndpopt *);
 181    opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
 182    opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
 183    opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
 184    opt2->ndpopt_prefixinfo.L = 1;
 185    opt2->ndpopt_prefixinfo.A = 1;
 186    opt2->ndpopt_prefixinfo.reserved1 = 0;
 187    opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
 188    opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
 189    opt2->ndpopt_prefixinfo.reserved2 = 0;
 190    opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
 191
 192    /* ICMPv6 Checksum */
 193    t->m_data -= NDPOPT_LINKLAYER_LEN;
 194    t->m_data -= ICMP6_NDP_RA_MINLEN;
 195    t->m_data -= sizeof(struct ip6);
 196    ricmp->icmp6_cksum = ip6_cksum(t);
 197
 198    ip6_output(NULL, t, 0);
 199}
 200
 201/*
 202 * Send NDP Neighbor Solitication
 203 */
 204void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
 205{
 206    DEBUG_CALL("ndp_send_ns");
 207#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
 208    char addrstr[INET6_ADDRSTRLEN];
 209    inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
 210    DEBUG_ARG("target = %s", addrstr);
 211#endif
 212
 213    /* Build IPv6 packet */
 214    struct mbuf *t = m_get(slirp);
 215    struct ip6 *rip = mtod(t, struct ip6 *);
 216    rip->ip_src = slirp->vhost_addr6;
 217    rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
 218    memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
 219    rip->ip_nh = IPPROTO_ICMPV6;
 220    rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
 221    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 222
 223    /* Build ICMPv6 packet */
 224    t->m_data += sizeof(struct ip6);
 225    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 226    ricmp->icmp6_type = ICMP6_NDP_NS;
 227    ricmp->icmp6_code = 0;
 228    ricmp->icmp6_cksum = 0;
 229
 230    /* NDP */
 231    ricmp->icmp6_nns.reserved = 0;
 232    ricmp->icmp6_nns.target = addr;
 233
 234    /* Build NDP option */
 235    t->m_data += ICMP6_NDP_NS_MINLEN;
 236    struct ndpopt *opt = mtod(t, struct ndpopt *);
 237    opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
 238    opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
 239    in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
 240
 241    /* ICMPv6 Checksum */
 242    t->m_data -= ICMP6_NDP_NA_MINLEN;
 243    t->m_data -= sizeof(struct ip6);
 244    ricmp->icmp6_cksum = ip6_cksum(t);
 245
 246    ip6_output(NULL, t, 1);
 247}
 248
 249/*
 250 * Send NDP Neighbor Advertisement
 251 */
 252static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
 253{
 254    /* Build IPv6 packet */
 255    struct mbuf *t = m_get(slirp);
 256    struct ip6 *rip = mtod(t, struct ip6 *);
 257    rip->ip_src = icmp->icmp6_nns.target;
 258    if (IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
 259        rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
 260    } else {
 261        rip->ip_dst = ip->ip_src;
 262    }
 263    rip->ip_nh = IPPROTO_ICMPV6;
 264    rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
 265                        + NDPOPT_LINKLAYER_LEN);
 266    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 267
 268    /* Build ICMPv6 packet */
 269    t->m_data += sizeof(struct ip6);
 270    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 271    ricmp->icmp6_type = ICMP6_NDP_NA;
 272    ricmp->icmp6_code = 0;
 273    ricmp->icmp6_cksum = 0;
 274
 275    /* NDP */
 276    ricmp->icmp6_nna.R = NDP_IsRouter;
 277    ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
 278    ricmp->icmp6_nna.O = 1;
 279    ricmp->icmp6_nna.reserved_hi = 0;
 280    ricmp->icmp6_nna.reserved_lo = 0;
 281    ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
 282
 283    /* Build NDP option */
 284    t->m_data += ICMP6_NDP_NA_MINLEN;
 285    struct ndpopt *opt = mtod(t, struct ndpopt *);
 286    opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
 287    opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
 288    in6_compute_ethaddr(ricmp->icmp6_nna.target,
 289                    opt->ndpopt_linklayer);
 290
 291    /* ICMPv6 Checksum */
 292    t->m_data -= ICMP6_NDP_NA_MINLEN;
 293    t->m_data -= sizeof(struct ip6);
 294    ricmp->icmp6_cksum = ip6_cksum(t);
 295
 296    ip6_output(NULL, t, 0);
 297}
 298
 299/*
 300 * Process a NDP message
 301 */
 302static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
 303        struct icmp6 *icmp)
 304{
 305    m->m_len += ETH_HLEN;
 306    m->m_data -= ETH_HLEN;
 307    struct ethhdr *eth = mtod(m, struct ethhdr *);
 308    m->m_len -= ETH_HLEN;
 309    m->m_data += ETH_HLEN;
 310
 311    switch (icmp->icmp6_type) {
 312    case ICMP6_NDP_RS:
 313        DEBUG_CALL(" type = Router Solicitation");
 314        if (ip->ip_hl == 255
 315                && icmp->icmp6_code == 0
 316                && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
 317            /* Gratuitous NDP */
 318            ndp_table_add(slirp, ip->ip_src, eth->h_source);
 319
 320            ndp_send_ra(slirp);
 321        }
 322        break;
 323
 324    case ICMP6_NDP_RA:
 325        DEBUG_CALL(" type = Router Advertisement");
 326        qemu_log_mask(LOG_GUEST_ERROR,
 327                "Warning: guest sent NDP RA, but shouldn't");
 328        break;
 329
 330    case ICMP6_NDP_NS:
 331        DEBUG_CALL(" type = Neighbor Solicitation");
 332        if (ip->ip_hl == 255
 333                && icmp->icmp6_code == 0
 334                && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
 335                && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
 336                && (!IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)
 337                    || in6_solicitednode_multicast(&ip->ip_dst))) {
 338            if (in6_equal_host(&icmp->icmp6_nns.target)) {
 339                /* Gratuitous NDP */
 340                ndp_table_add(slirp, ip->ip_src, eth->h_source);
 341                ndp_send_na(slirp, ip, icmp);
 342            }
 343        }
 344        break;
 345
 346    case ICMP6_NDP_NA:
 347        DEBUG_CALL(" type = Neighbor Advertisement");
 348        if (ip->ip_hl == 255
 349                && icmp->icmp6_code == 0
 350                && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
 351                && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
 352                && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
 353                    || icmp->icmp6_nna.S == 0)) {
 354            ndp_table_add(slirp, ip->ip_src, eth->h_source);
 355        }
 356        break;
 357
 358    case ICMP6_NDP_REDIRECT:
 359        DEBUG_CALL(" type = Redirect");
 360        qemu_log_mask(LOG_GUEST_ERROR,
 361                "Warning: guest sent NDP REDIRECT, but shouldn't");
 362        break;
 363    }
 364}
 365
 366/*
 367 * Process a received ICMPv6 message.
 368 */
 369void icmp6_input(struct mbuf *m)
 370{
 371    struct icmp6 *icmp;
 372    struct ip6 *ip = mtod(m, struct ip6 *);
 373    Slirp *slirp = m->slirp;
 374    int hlen = sizeof(struct ip6);
 375
 376    DEBUG_CALL("icmp6_input");
 377    DEBUG_ARG("m = %lx", (long) m);
 378    DEBUG_ARG("m_len = %d", m->m_len);
 379
 380    if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
 381        goto end;
 382    }
 383
 384    if (ip6_cksum(m)) {
 385        goto end;
 386    }
 387
 388    m->m_len -= hlen;
 389    m->m_data += hlen;
 390    icmp = mtod(m, struct icmp6 *);
 391    m->m_len += hlen;
 392    m->m_data -= hlen;
 393
 394    DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
 395    switch (icmp->icmp6_type) {
 396    case ICMP6_ECHO_REQUEST:
 397        if (in6_equal_host(&ip->ip_dst)) {
 398            icmp6_send_echoreply(m, slirp, ip, icmp);
 399        } else {
 400            /* TODO */
 401            error_report("external icmpv6 not supported yet");
 402        }
 403        break;
 404
 405    case ICMP6_NDP_RS:
 406    case ICMP6_NDP_RA:
 407    case ICMP6_NDP_NS:
 408    case ICMP6_NDP_NA:
 409    case ICMP6_NDP_REDIRECT:
 410        ndp_input(m, slirp, ip, icmp);
 411        break;
 412
 413    case ICMP6_UNREACH:
 414    case ICMP6_TOOBIG:
 415    case ICMP6_TIMXCEED:
 416    case ICMP6_PARAMPROB:
 417        /* XXX? report error? close socket? */
 418    default:
 419        break;
 420    }
 421
 422end:
 423    m_free(m);
 424}
 425