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