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