qemu/net/announce.c
<<
>>
Prefs
   1/*
   2 *  Self-announce
   3 *  (c) 2017-2019 Red Hat, Inc.
   4 *
   5 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   6 * See the COPYING file in the top-level directory.
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qemu-common.h"
  11#include "net/announce.h"
  12#include "net/net.h"
  13#include "qapi/clone-visitor.h"
  14#include "qapi/qapi-visit-net.h"
  15#include "qapi/qapi-commands-net.h"
  16#include "trace.h"
  17
  18static GData *named_timers;
  19
  20int64_t qemu_announce_timer_step(AnnounceTimer *timer)
  21{
  22    int64_t step;
  23
  24    step =  timer->params.initial +
  25            (timer->params.rounds - timer->round - 1) *
  26            timer->params.step;
  27
  28    if (step < 0 || step > timer->params.max) {
  29        step = timer->params.max;
  30    }
  31    timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step);
  32
  33    return step;
  34}
  35
  36/*
  37 * If 'free_named' is true, then remove the timer from the list
  38 * and free the timer itself.
  39 */
  40void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named)
  41{
  42    bool free_timer = false;
  43    if (timer->tm) {
  44        timer_free(timer->tm);
  45        timer->tm = NULL;
  46    }
  47    qapi_free_strList(timer->params.interfaces);
  48    timer->params.interfaces = NULL;
  49    if (free_named && timer->params.has_id) {
  50        AnnounceTimer *list_timer;
  51        /*
  52         * Sanity check: There should only be one timer on the list with
  53         * the id.
  54         */
  55        list_timer = g_datalist_get_data(&named_timers, timer->params.id);
  56        assert(timer == list_timer);
  57        free_timer = true;
  58        g_datalist_remove_data(&named_timers, timer->params.id);
  59    }
  60    trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
  61    g_free(timer->params.id);
  62    timer->params.id = NULL;
  63
  64    if (free_timer) {
  65        g_free(timer);
  66    }
  67}
  68
  69/*
  70 * Under BQL/main thread
  71 * Reset the timer to the given parameters/type/notifier.
  72 */
  73void qemu_announce_timer_reset(AnnounceTimer *timer,
  74                               AnnounceParameters *params,
  75                               QEMUClockType type,
  76                               QEMUTimerCB *cb,
  77                               void *opaque)
  78{
  79    /*
  80     * We're under the BQL, so the current timer can't
  81     * be firing, so we should be able to delete it.
  82     */
  83    qemu_announce_timer_del(timer, false);
  84
  85    QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
  86    timer->round = params->rounds;
  87    timer->type = type;
  88    timer->tm = timer_new_ms(type, cb, opaque);
  89}
  90
  91#ifndef ETH_P_RARP
  92#define ETH_P_RARP 0x8035
  93#endif
  94#define ARP_HTYPE_ETH 0x0001
  95#define ARP_PTYPE_IP 0x0800
  96#define ARP_OP_REQUEST_REV 0x3
  97
  98static int announce_self_create(uint8_t *buf,
  99                                uint8_t *mac_addr)
 100{
 101    /* Ethernet header. */
 102    memset(buf, 0xff, 6);         /* destination MAC addr */
 103    memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
 104    *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
 105
 106    /* RARP header. */
 107    *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
 108    *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
 109    *(buf + 18) = 6; /* hardware addr length (ethernet) */
 110    *(buf + 19) = 4; /* protocol addr length (IPv4) */
 111    *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
 112    memcpy(buf + 22, mac_addr, 6); /* source hw addr */
 113    memset(buf + 28, 0x00, 4);     /* source protocol addr */
 114    memcpy(buf + 32, mac_addr, 6); /* target hw addr */
 115    memset(buf + 38, 0x00, 4);     /* target protocol addr */
 116
 117    /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
 118    memset(buf + 42, 0x00, 18);
 119
 120    return 60; /* len (FCS will be added by hardware) */
 121}
 122
 123/*
 124 * Helper to print ethernet mac address
 125 */
 126static const char *qemu_ether_ntoa(const MACAddr *mac)
 127{
 128    static char ret[18];
 129
 130    snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x",
 131             mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]);
 132
 133    return ret;
 134}
 135
 136static void qemu_announce_self_iter(NICState *nic, void *opaque)
 137{
 138    AnnounceTimer *timer = opaque;
 139    uint8_t buf[60];
 140    int len;
 141    bool skip;
 142
 143    if (timer->params.has_interfaces) {
 144        strList *entry = timer->params.interfaces;
 145        /* Skip unless we find our name in the requested list */
 146        skip = true;
 147
 148        while (entry) {
 149            if (!strcmp(entry->value, nic->ncs->name)) {
 150                /* Found us */
 151                skip = false;
 152                break;
 153            }
 154            entry = entry->next;
 155        }
 156    } else {
 157        skip = false;
 158    }
 159
 160    trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
 161                                  nic->ncs->name,
 162                                  qemu_ether_ntoa(&nic->conf->macaddr), skip);
 163
 164    if (!skip) {
 165        len = announce_self_create(buf, nic->conf->macaddr.a);
 166
 167        qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
 168
 169        /* if the NIC provides it's own announcement support, use it as well */
 170        if (nic->ncs->info->announce) {
 171            nic->ncs->info->announce(nic->ncs);
 172        }
 173    }
 174}
 175static void qemu_announce_self_once(void *opaque)
 176{
 177    AnnounceTimer *timer = (AnnounceTimer *)opaque;
 178
 179    qemu_foreach_nic(qemu_announce_self_iter, timer);
 180
 181    if (--timer->round) {
 182        qemu_announce_timer_step(timer);
 183    } else {
 184        qemu_announce_timer_del(timer, true);
 185    }
 186}
 187
 188void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
 189{
 190    qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
 191                              qemu_announce_self_once, timer);
 192    if (params->rounds) {
 193        qemu_announce_self_once(timer);
 194    } else {
 195        qemu_announce_timer_del(timer, true);
 196    }
 197}
 198
 199void qmp_announce_self(AnnounceParameters *params, Error **errp)
 200{
 201    AnnounceTimer *named_timer;
 202    if (!params->has_id) {
 203        params->id = g_strdup("");
 204        params->has_id = true;
 205    }
 206
 207    named_timer = g_datalist_get_data(&named_timers, params->id);
 208
 209    if (!named_timer) {
 210        named_timer = g_new0(AnnounceTimer, 1);
 211        g_datalist_set_data(&named_timers, params->id, named_timer);
 212    }
 213
 214    qemu_announce_self(named_timer, params);
 215}
 216