203 lines
5.8 KiB
C
203 lines
5.8 KiB
C
/*
|
|
* Self-announce
|
|
* (c) 2017-2019 Red Hat, Inc.
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu-common.h"
|
|
#include "net/announce.h"
|
|
#include "net/net.h"
|
|
#include "qapi/clone-visitor.h"
|
|
#include "qapi/qapi-visit-net.h"
|
|
#include "qapi/qapi-commands-net.h"
|
|
#include "trace.h"
|
|
|
|
static GData *named_timers;
|
|
|
|
int64_t qemu_announce_timer_step(AnnounceTimer *timer)
|
|
{
|
|
int64_t step;
|
|
|
|
step = timer->params.initial +
|
|
(timer->params.rounds - timer->round - 1) *
|
|
timer->params.step;
|
|
|
|
if (step < 0 || step > timer->params.max) {
|
|
step = timer->params.max;
|
|
}
|
|
timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step);
|
|
|
|
return step;
|
|
}
|
|
|
|
/*
|
|
* If 'free_named' is true, then remove the timer from the list
|
|
* and free the timer itself.
|
|
*/
|
|
void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named)
|
|
{
|
|
bool free_timer = false;
|
|
if (timer->tm) {
|
|
timer_del(timer->tm);
|
|
timer_free(timer->tm);
|
|
timer->tm = NULL;
|
|
}
|
|
qapi_free_strList(timer->params.interfaces);
|
|
timer->params.interfaces = NULL;
|
|
if (free_named && timer->params.has_id) {
|
|
AnnounceTimer *list_timer;
|
|
/*
|
|
* Sanity check: There should only be one timer on the list with
|
|
* the id.
|
|
*/
|
|
list_timer = g_datalist_get_data(&named_timers, timer->params.id);
|
|
assert(timer == list_timer);
|
|
free_timer = true;
|
|
g_datalist_remove_data(&named_timers, timer->params.id);
|
|
}
|
|
trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id);
|
|
g_free(timer->params.id);
|
|
timer->params.id = NULL;
|
|
|
|
if (free_timer) {
|
|
g_free(timer);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Under BQL/main thread
|
|
* Reset the timer to the given parameters/type/notifier.
|
|
*/
|
|
void qemu_announce_timer_reset(AnnounceTimer *timer,
|
|
AnnounceParameters *params,
|
|
QEMUClockType type,
|
|
QEMUTimerCB *cb,
|
|
void *opaque)
|
|
{
|
|
/*
|
|
* We're under the BQL, so the current timer can't
|
|
* be firing, so we should be able to delete it.
|
|
*/
|
|
qemu_announce_timer_del(timer, false);
|
|
|
|
QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
|
|
timer->round = params->rounds;
|
|
timer->type = type;
|
|
timer->tm = timer_new_ms(type, cb, opaque);
|
|
}
|
|
|
|
#ifndef ETH_P_RARP
|
|
#define ETH_P_RARP 0x8035
|
|
#endif
|
|
#define ARP_HTYPE_ETH 0x0001
|
|
#define ARP_PTYPE_IP 0x0800
|
|
#define ARP_OP_REQUEST_REV 0x3
|
|
|
|
static int announce_self_create(uint8_t *buf,
|
|
uint8_t *mac_addr)
|
|
{
|
|
/* Ethernet header. */
|
|
memset(buf, 0xff, 6); /* destination MAC addr */
|
|
memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
|
|
*(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
|
|
|
|
/* RARP header. */
|
|
*(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
|
|
*(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
|
|
*(buf + 18) = 6; /* hardware addr length (ethernet) */
|
|
*(buf + 19) = 4; /* protocol addr length (IPv4) */
|
|
*(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
|
|
memcpy(buf + 22, mac_addr, 6); /* source hw addr */
|
|
memset(buf + 28, 0x00, 4); /* source protocol addr */
|
|
memcpy(buf + 32, mac_addr, 6); /* target hw addr */
|
|
memset(buf + 38, 0x00, 4); /* target protocol addr */
|
|
|
|
/* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
|
|
memset(buf + 42, 0x00, 18);
|
|
|
|
return 60; /* len (FCS will be added by hardware) */
|
|
}
|
|
|
|
static void qemu_announce_self_iter(NICState *nic, void *opaque)
|
|
{
|
|
AnnounceTimer *timer = opaque;
|
|
uint8_t buf[60];
|
|
int len;
|
|
bool skip;
|
|
|
|
if (timer->params.has_interfaces) {
|
|
strList *entry = timer->params.interfaces;
|
|
/* Skip unless we find our name in the requested list */
|
|
skip = true;
|
|
|
|
while (entry) {
|
|
if (!strcmp(entry->value, nic->ncs->name)) {
|
|
/* Found us */
|
|
skip = false;
|
|
break;
|
|
}
|
|
entry = entry->next;
|
|
}
|
|
} else {
|
|
skip = false;
|
|
}
|
|
|
|
trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
|
|
nic->ncs->name,
|
|
qemu_ether_ntoa(&nic->conf->macaddr), skip);
|
|
|
|
if (!skip) {
|
|
len = announce_self_create(buf, nic->conf->macaddr.a);
|
|
|
|
qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
|
|
|
|
/* if the NIC provides it's own announcement support, use it as well */
|
|
if (nic->ncs->info->announce) {
|
|
nic->ncs->info->announce(nic->ncs);
|
|
}
|
|
}
|
|
}
|
|
static void qemu_announce_self_once(void *opaque)
|
|
{
|
|
AnnounceTimer *timer = (AnnounceTimer *)opaque;
|
|
|
|
qemu_foreach_nic(qemu_announce_self_iter, timer);
|
|
|
|
if (--timer->round) {
|
|
qemu_announce_timer_step(timer);
|
|
} else {
|
|
qemu_announce_timer_del(timer, true);
|
|
}
|
|
}
|
|
|
|
void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
|
|
{
|
|
qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
|
|
qemu_announce_self_once, timer);
|
|
if (params->rounds) {
|
|
qemu_announce_self_once(timer);
|
|
} else {
|
|
qemu_announce_timer_del(timer, true);
|
|
}
|
|
}
|
|
|
|
void qmp_announce_self(AnnounceParameters *params, Error **errp)
|
|
{
|
|
AnnounceTimer *named_timer;
|
|
if (!params->has_id) {
|
|
params->id = g_strdup("");
|
|
params->has_id = true;
|
|
}
|
|
|
|
named_timer = g_datalist_get_data(&named_timers, params->id);
|
|
|
|
if (!named_timer) {
|
|
named_timer = g_new0(AnnounceTimer, 1);
|
|
g_datalist_set_data(&named_timers, params->id, named_timer);
|
|
}
|
|
|
|
qemu_announce_self(named_timer, params);
|
|
}
|