/* * 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_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); }