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