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_del(timer->tm);
  45        timer_free(timer->tm);
  46        timer->tm = NULL;
  47    }
  48    qapi_free_strList(timer->params.interfaces);
  49    timer->params.interfaces = NULL;
  50    if (free_named && timer->params.has_id) {
  51        AnnounceTimer *list_timer;
  52        /*
  53         * Sanity check: There should only be one timer on the list with
  54         * the id.
  55         */
  56        list_timer = g_datalist_get_data(&named_timers, timer->params.id);
  57        assert(timer == list_timer);
  58        free_timer = true;
  59        g_datalist_remove_data(&named_timers, timer->params.id);
  60    }
  61    trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
  62    g_free(timer->params.id);
  63    timer->params.id = NULL;
  64
  65    if (free_timer) {
  66        g_free(timer);
  67    }
  68}
  69
  70/*
  71 * Under BQL/main thread
  72 * Reset the timer to the given parameters/type/notifier.
  73 */
  74void qemu_announce_timer_reset(AnnounceTimer *timer,
  75                               AnnounceParameters *params,
  76                               QEMUClockType type,
  77                               QEMUTimerCB *cb,
  78                               void *opaque)
  79{
  80    /*
  81     * We're under the BQL, so the current timer can't
  82     * be firing, so we should be able to delete it.
  83     */
  84    qemu_announce_timer_del(timer, false);
  85
  86    QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
  87    timer->round = params->rounds;
  88    timer->type = type;
  89    timer->tm = timer_new_ms(type, cb, opaque);
  90}
  91
  92#ifndef ETH_P_RARP
  93#define ETH_P_RARP 0x8035
  94#endif
  95#define ARP_HTYPE_ETH 0x0001
  96#define ARP_PTYPE_IP 0x0800
  97#define ARP_OP_REQUEST_REV 0x3
  98
  99static int announce_self_create(uint8_t *buf,
 100                                uint8_t *mac_addr)
 101{
 102    /* Ethernet header. */
 103    memset(buf, 0xff, 6);         /* destination MAC addr */
 104    memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
 105    *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
 106
 107    /* RARP header. */
 108    *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
 109    *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
 110    *(buf + 18) = 6; /* hardware addr length (ethernet) */
 111    *(buf + 19) = 4; /* protocol addr length (IPv4) */
 112    *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
 113    memcpy(buf + 22, mac_addr, 6); /* source hw addr */
 114    memset(buf + 28, 0x00, 4);     /* source protocol addr */
 115    memcpy(buf + 32, mac_addr, 6); /* target hw addr */
 116    memset(buf + 38, 0x00, 4);     /* target protocol addr */
 117
 118    /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
 119    memset(buf + 42, 0x00, 18);
 120
 121    return 60; /* len (FCS will be added by hardware) */
 122}
 123
 124static void qemu_announce_self_iter(NICState *nic, void *opaque)
 125{
 126    AnnounceTimer *timer = opaque;
 127    uint8_t buf[60];
 128    int len;
 129    bool skip;
 130
 131    if (timer->params.has_interfaces) {
 132        strList *entry = timer->params.interfaces;
 133        /* Skip unless we find our name in the requested list */
 134        skip = true;
 135
 136        while (entry) {
 137            if (!strcmp(entry->value, nic->ncs->name)) {
 138                /* Found us */
 139                skip = false;
 140                break;
 141            }
 142            entry = entry->next;
 143        }
 144    } else {
 145        skip = false;
 146    }
 147
 148    trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
 149                                  nic->ncs->name,
 150                                  qemu_ether_ntoa(&nic->conf->macaddr), skip);
 151
 152    if (!skip) {
 153        len = announce_self_create(buf, nic->conf->macaddr.a);
 154
 155        qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
 156
 157        /* if the NIC provides it's own announcement support, use it as well */
 158        if (nic->ncs->info->announce) {
 159            nic->ncs->info->announce(nic->ncs);
 160        }
 161    }
 162}
 163static void qemu_announce_self_once(void *opaque)
 164{
 165    AnnounceTimer *timer = (AnnounceTimer *)opaque;
 166
 167    qemu_foreach_nic(qemu_announce_self_iter, timer);
 168
 169    if (--timer->round) {
 170        qemu_announce_timer_step(timer);
 171    } else {
 172        qemu_announce_timer_del(timer, true);
 173    }
 174}
 175
 176void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
 177{
 178    qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
 179                              qemu_announce_self_once, timer);
 180    if (params->rounds) {
 181        qemu_announce_self_once(timer);
 182    } else {
 183        qemu_announce_timer_del(timer, true);
 184    }
 185}
 186
 187void qmp_announce_self(AnnounceParameters *params, Error **errp)
 188{
 189    AnnounceTimer *named_timer;
 190    if (!params->has_id) {
 191        params->id = g_strdup("");
 192        params->has_id = true;
 193    }
 194
 195    named_timer = g_datalist_get_data(&named_timers, params->id);
 196
 197    if (!named_timer) {
 198        named_timer = g_new0(AnnounceTimer, 1);
 199        g_datalist_set_data(&named_timers, params->id, named_timer);
 200    }
 201
 202    qemu_announce_self(named_timer, params);
 203}
 204