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_zero(&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    size_t pl_size = 0;
 147    struct in6_addr addr;
 148    uint32_t scope_id;
 149
 150    rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
 151    rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
 152    rip->ip_nh = IPPROTO_ICMPV6;
 153
 154    /* Build ICMPv6 packet */
 155    t->m_data += sizeof(struct ip6);
 156    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 157    ricmp->icmp6_type = ICMP6_NDP_RA;
 158    ricmp->icmp6_code = 0;
 159    ricmp->icmp6_cksum = 0;
 160
 161    /* NDP */
 162    ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit;
 163    ricmp->icmp6_nra.M = NDP_AdvManagedFlag;
 164    ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag;
 165    ricmp->icmp6_nra.reserved = 0;
 166    ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime);
 167    ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime);
 168    ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime);
 169    t->m_data += ICMP6_NDP_RA_MINLEN;
 170    pl_size += ICMP6_NDP_RA_MINLEN;
 171
 172    /* Source link-layer address (NDP option) */
 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    t->m_data += NDPOPT_LINKLAYER_LEN;
 178    pl_size += NDPOPT_LINKLAYER_LEN;
 179
 180    /* Prefix information (NDP option) */
 181    struct ndpopt *opt2 = mtod(t, struct ndpopt *);
 182    opt2->ndpopt_type = NDPOPT_PREFIX_INFO;
 183    opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8;
 184    opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len;
 185    opt2->ndpopt_prefixinfo.L = 1;
 186    opt2->ndpopt_prefixinfo.A = 1;
 187    opt2->ndpopt_prefixinfo.reserved1 = 0;
 188    opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime);
 189    opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime);
 190    opt2->ndpopt_prefixinfo.reserved2 = 0;
 191    opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6;
 192    t->m_data += NDPOPT_PREFIXINFO_LEN;
 193    pl_size += NDPOPT_PREFIXINFO_LEN;
 194
 195    /* Prefix information (NDP option) */
 196    if (get_dns6_addr(&addr, &scope_id) >= 0) {
 197        /* Host system does have an IPv6 DNS server, announce our proxy.  */
 198        struct ndpopt *opt3 = mtod(t, struct ndpopt *);
 199        opt3->ndpopt_type = NDPOPT_RDNSS;
 200        opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8;
 201        opt3->ndpopt_rdnss.reserved = 0;
 202        opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval);
 203        opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6;
 204        t->m_data += NDPOPT_RDNSS_LEN;
 205        pl_size += NDPOPT_RDNSS_LEN;
 206    }
 207
 208    rip->ip_pl = htons(pl_size);
 209    t->m_data -= sizeof(struct ip6) + pl_size;
 210    t->m_len = sizeof(struct ip6) + pl_size;
 211
 212    /* ICMPv6 Checksum */
 213    ricmp->icmp6_cksum = ip6_cksum(t);
 214
 215    ip6_output(NULL, t, 0);
 216}
 217
 218/*
 219 * Send NDP Neighbor Solitication
 220 */
 221void ndp_send_ns(Slirp *slirp, struct in6_addr addr)
 222{
 223    DEBUG_CALL("ndp_send_ns");
 224#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
 225    char addrstr[INET6_ADDRSTRLEN];
 226    inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN);
 227    DEBUG_ARG("target = %s", addrstr);
 228#endif
 229
 230    /* Build IPv6 packet */
 231    struct mbuf *t = m_get(slirp);
 232    struct ip6 *rip = mtod(t, struct ip6 *);
 233    rip->ip_src = slirp->vhost_addr6;
 234    rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX;
 235    memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3);
 236    rip->ip_nh = IPPROTO_ICMPV6;
 237    rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN);
 238    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 239
 240    /* Build ICMPv6 packet */
 241    t->m_data += sizeof(struct ip6);
 242    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 243    ricmp->icmp6_type = ICMP6_NDP_NS;
 244    ricmp->icmp6_code = 0;
 245    ricmp->icmp6_cksum = 0;
 246
 247    /* NDP */
 248    ricmp->icmp6_nns.reserved = 0;
 249    ricmp->icmp6_nns.target = addr;
 250
 251    /* Build NDP option */
 252    t->m_data += ICMP6_NDP_NS_MINLEN;
 253    struct ndpopt *opt = mtod(t, struct ndpopt *);
 254    opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE;
 255    opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
 256    in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer);
 257
 258    /* ICMPv6 Checksum */
 259    t->m_data -= ICMP6_NDP_NA_MINLEN;
 260    t->m_data -= sizeof(struct ip6);
 261    ricmp->icmp6_cksum = ip6_cksum(t);
 262
 263    ip6_output(NULL, t, 1);
 264}
 265
 266/*
 267 * Send NDP Neighbor Advertisement
 268 */
 269static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp)
 270{
 271    /* Build IPv6 packet */
 272    struct mbuf *t = m_get(slirp);
 273    struct ip6 *rip = mtod(t, struct ip6 *);
 274    rip->ip_src = icmp->icmp6_nns.target;
 275    if (in6_zero(&ip->ip_src)) {
 276        rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST;
 277    } else {
 278        rip->ip_dst = ip->ip_src;
 279    }
 280    rip->ip_nh = IPPROTO_ICMPV6;
 281    rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN
 282                        + NDPOPT_LINKLAYER_LEN);
 283    t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
 284
 285    /* Build ICMPv6 packet */
 286    t->m_data += sizeof(struct ip6);
 287    struct icmp6 *ricmp = mtod(t, struct icmp6 *);
 288    ricmp->icmp6_type = ICMP6_NDP_NA;
 289    ricmp->icmp6_code = 0;
 290    ricmp->icmp6_cksum = 0;
 291
 292    /* NDP */
 293    ricmp->icmp6_nna.R = NDP_IsRouter;
 294    ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst);
 295    ricmp->icmp6_nna.O = 1;
 296    ricmp->icmp6_nna.reserved_hi = 0;
 297    ricmp->icmp6_nna.reserved_lo = 0;
 298    ricmp->icmp6_nna.target = icmp->icmp6_nns.target;
 299
 300    /* Build NDP option */
 301    t->m_data += ICMP6_NDP_NA_MINLEN;
 302    struct ndpopt *opt = mtod(t, struct ndpopt *);
 303    opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET;
 304    opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8;
 305    in6_compute_ethaddr(ricmp->icmp6_nna.target,
 306                    opt->ndpopt_linklayer);
 307
 308    /* ICMPv6 Checksum */
 309    t->m_data -= ICMP6_NDP_NA_MINLEN;
 310    t->m_data -= sizeof(struct ip6);
 311    ricmp->icmp6_cksum = ip6_cksum(t);
 312
 313    ip6_output(NULL, t, 0);
 314}
 315
 316/*
 317 * Process a NDP message
 318 */
 319static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
 320        struct icmp6 *icmp)
 321{
 322    m->m_len += ETH_HLEN;
 323    m->m_data -= ETH_HLEN;
 324    struct ethhdr *eth = mtod(m, struct ethhdr *);
 325    m->m_len -= ETH_HLEN;
 326    m->m_data += ETH_HLEN;
 327
 328    switch (icmp->icmp6_type) {
 329    case ICMP6_NDP_RS:
 330        DEBUG_CALL(" type = Router Solicitation");
 331        if (ip->ip_hl == 255
 332                && icmp->icmp6_code == 0
 333                && ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) {
 334            /* Gratuitous NDP */
 335            ndp_table_add(slirp, ip->ip_src, eth->h_source);
 336
 337            ndp_send_ra(slirp);
 338        }
 339        break;
 340
 341    case ICMP6_NDP_RA:
 342        DEBUG_CALL(" type = Router Advertisement");
 343        qemu_log_mask(LOG_GUEST_ERROR,
 344                "Warning: guest sent NDP RA, but shouldn't");
 345        break;
 346
 347    case ICMP6_NDP_NS:
 348        DEBUG_CALL(" type = Neighbor Solicitation");
 349        if (ip->ip_hl == 255
 350                && icmp->icmp6_code == 0
 351                && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target)
 352                && ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN
 353                && (!in6_zero(&ip->ip_src)
 354                    || in6_solicitednode_multicast(&ip->ip_dst))) {
 355            if (in6_equal_host(&icmp->icmp6_nns.target)) {
 356                /* Gratuitous NDP */
 357                ndp_table_add(slirp, ip->ip_src, eth->h_source);
 358                ndp_send_na(slirp, ip, icmp);
 359            }
 360        }
 361        break;
 362
 363    case ICMP6_NDP_NA:
 364        DEBUG_CALL(" type = Neighbor Advertisement");
 365        if (ip->ip_hl == 255
 366                && icmp->icmp6_code == 0
 367                && ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN
 368                && !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target)
 369                && (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst)
 370                    || icmp->icmp6_nna.S == 0)) {
 371            ndp_table_add(slirp, ip->ip_src, eth->h_source);
 372        }
 373        break;
 374
 375    case ICMP6_NDP_REDIRECT:
 376        DEBUG_CALL(" type = Redirect");
 377        qemu_log_mask(LOG_GUEST_ERROR,
 378                "Warning: guest sent NDP REDIRECT, but shouldn't");
 379        break;
 380    }
 381}
 382
 383/*
 384 * Process a received ICMPv6 message.
 385 */
 386void icmp6_input(struct mbuf *m)
 387{
 388    struct icmp6 *icmp;
 389    struct ip6 *ip = mtod(m, struct ip6 *);
 390    Slirp *slirp = m->slirp;
 391    int hlen = sizeof(struct ip6);
 392
 393    DEBUG_CALL("icmp6_input");
 394    DEBUG_ARG("m = %lx", (long) m);
 395    DEBUG_ARG("m_len = %d", m->m_len);
 396
 397    if (ntohs(ip->ip_pl) < ICMP6_MINLEN) {
 398        goto end;
 399    }
 400
 401    if (ip6_cksum(m)) {
 402        goto end;
 403    }
 404
 405    m->m_len -= hlen;
 406    m->m_data += hlen;
 407    icmp = mtod(m, struct icmp6 *);
 408    m->m_len += hlen;
 409    m->m_data -= hlen;
 410
 411    DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type);
 412    switch (icmp->icmp6_type) {
 413    case ICMP6_ECHO_REQUEST:
 414        if (in6_equal_host(&ip->ip_dst)) {
 415            icmp6_send_echoreply(m, slirp, ip, icmp);
 416        } else {
 417            /* TODO */
 418            error_report("external icmpv6 not supported yet");
 419        }
 420        break;
 421
 422    case ICMP6_NDP_RS:
 423    case ICMP6_NDP_RA:
 424    case ICMP6_NDP_NS:
 425    case ICMP6_NDP_NA:
 426    case ICMP6_NDP_REDIRECT:
 427        ndp_input(m, slirp, ip, icmp);
 428        break;
 429
 430    case ICMP6_UNREACH:
 431    case ICMP6_TOOBIG:
 432    case ICMP6_TIMXCEED:
 433    case ICMP6_PARAMPROB:
 434        /* XXX? report error? close socket? */
 435    default:
 436        break;
 437    }
 438
 439end:
 440    m_free(m);
 441}
 442