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
 123static void qemu_announce_self_iter(NICState *nic, void *opaque)
 124{
 125    AnnounceTimer *timer = opaque;
 126    uint8_t buf[60];
 127    int len;
 128    bool skip;
 129
 130    if (timer->params.has_interfaces) {
 131        strList *entry = timer->params.interfaces;
 132        /* Skip unless we find our name in the requested list */
 133        skip = true;
 134
 135        while (entry) {
 136            if (!strcmp(entry->value, nic->ncs->name)) {
 137                /* Found us */
 138                skip = false;
 139                break;
 140            }
 141            entry = entry->next;
 142        }
 143    } else {
 144        skip = false;
 145    }
 146
 147    trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
 148                                  nic->ncs->name,
 149                                  qemu_ether_ntoa(&nic->conf->macaddr), skip);
 150
 151    if (!skip) {
 152        len = announce_self_create(buf, nic->conf->macaddr.a);
 153
 154        qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
 155
 156        /* if the NIC provides it's own announcement support, use it as well */
 157        if (nic->ncs->info->announce) {
 158            nic->ncs->info->announce(nic->ncs);
 159        }
 160    }
 161}
 162static void qemu_announce_self_once(void *opaque)
 163{
 164    AnnounceTimer *timer = (AnnounceTimer *)opaque;
 165
 166    qemu_foreach_nic(qemu_announce_self_iter, timer);
 167
 168    if (--timer->round) {
 169        qemu_announce_timer_step(timer);
 170    } else {
 171        qemu_announce_timer_del(timer, true);
 172    }
 173}
 174
 175void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
 176{
 177    qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
 178                              qemu_announce_self_once, timer);
 179    if (params->rounds) {
 180        qemu_announce_self_once(timer);
 181    } else {
 182        qemu_announce_timer_del(timer, true);
 183    }
 184}
 185
 186void qmp_announce_self(AnnounceParameters *params, Error **errp)
 187{
 188    AnnounceTimer *named_timer;
 189    if (!params->has_id) {
 190        params->id = g_strdup("");
 191        params->has_id = true;
 192    }
 193
 194    named_timer = g_datalist_get_data(&named_timers, params->id);
 195
 196    if (!named_timer) {
 197        named_timer = g_new0(AnnounceTimer, 1);
 198        g_datalist_set_data(&named_timers, params->id, named_timer);
 199    }
 200
 201    qemu_announce_self(named_timer, params);
 202}
 203