150510ea2SDr. David Alan Gilbert /* 250510ea2SDr. David Alan Gilbert * Self-announce 350510ea2SDr. David Alan Gilbert * (c) 2017-2019 Red Hat, Inc. 450510ea2SDr. David Alan Gilbert * 550510ea2SDr. David Alan Gilbert * This work is licensed under the terms of the GNU GPL, version 2 or later. 650510ea2SDr. David Alan Gilbert * See the COPYING file in the top-level directory. 750510ea2SDr. David Alan Gilbert */ 850510ea2SDr. David Alan Gilbert 950510ea2SDr. David Alan Gilbert #include "qemu/osdep.h" 1050510ea2SDr. David Alan Gilbert #include "qemu-common.h" 1150510ea2SDr. David Alan Gilbert #include "net/announce.h" 127659505cSDr. David Alan Gilbert #include "net/net.h" 1350510ea2SDr. David Alan Gilbert #include "qapi/clone-visitor.h" 1450510ea2SDr. David Alan Gilbert #include "qapi/qapi-visit-net.h" 15a06cd488SDr. David Alan Gilbert #include "qapi/qapi-commands-net.h" 167659505cSDr. David Alan Gilbert #include "trace.h" 1750510ea2SDr. David Alan Gilbert 18944458b6SDr. David Alan Gilbert static GData *named_timers; 19944458b6SDr. David Alan Gilbert 2050510ea2SDr. David Alan Gilbert int64_t qemu_announce_timer_step(AnnounceTimer *timer) 2150510ea2SDr. David Alan Gilbert { 2250510ea2SDr. David Alan Gilbert int64_t step; 2350510ea2SDr. David Alan Gilbert 2450510ea2SDr. David Alan Gilbert step = timer->params.initial + 2550510ea2SDr. David Alan Gilbert (timer->params.rounds - timer->round - 1) * 2650510ea2SDr. David Alan Gilbert timer->params.step; 2750510ea2SDr. David Alan Gilbert 2850510ea2SDr. David Alan Gilbert if (step < 0 || step > timer->params.max) { 2950510ea2SDr. David Alan Gilbert step = timer->params.max; 3050510ea2SDr. David Alan Gilbert } 3150510ea2SDr. David Alan Gilbert timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step); 3250510ea2SDr. David Alan Gilbert 3350510ea2SDr. David Alan Gilbert return step; 3450510ea2SDr. David Alan Gilbert } 3550510ea2SDr. David Alan Gilbert 36944458b6SDr. David Alan Gilbert /* 37944458b6SDr. David Alan Gilbert * If 'free_named' is true, then remove the timer from the list 38944458b6SDr. David Alan Gilbert * and free the timer itself. 39944458b6SDr. David Alan Gilbert */ 40944458b6SDr. David Alan Gilbert void qemu_announce_timer_del(AnnounceTimer *timer, bool free_named) 4150510ea2SDr. David Alan Gilbert { 42944458b6SDr. David Alan Gilbert bool free_timer = false; 4350510ea2SDr. David Alan Gilbert if (timer->tm) { 4450510ea2SDr. David Alan Gilbert timer_free(timer->tm); 4550510ea2SDr. David Alan Gilbert timer->tm = NULL; 4650510ea2SDr. David Alan Gilbert } 47ef2fdbfbSDr. David Alan Gilbert qapi_free_strList(timer->params.interfaces); 48ef2fdbfbSDr. David Alan Gilbert timer->params.interfaces = NULL; 49944458b6SDr. David Alan Gilbert if (free_named && timer->params.has_id) { 50944458b6SDr. David Alan Gilbert AnnounceTimer *list_timer; 51944458b6SDr. David Alan Gilbert /* 52944458b6SDr. David Alan Gilbert * Sanity check: There should only be one timer on the list with 53944458b6SDr. David Alan Gilbert * the id. 54944458b6SDr. David Alan Gilbert */ 55944458b6SDr. David Alan Gilbert list_timer = g_datalist_get_data(&named_timers, timer->params.id); 56944458b6SDr. David Alan Gilbert assert(timer == list_timer); 57944458b6SDr. David Alan Gilbert free_timer = true; 58944458b6SDr. David Alan Gilbert g_datalist_remove_data(&named_timers, timer->params.id); 59944458b6SDr. David Alan Gilbert } 60944458b6SDr. David Alan Gilbert trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id); 61944458b6SDr. David Alan Gilbert g_free(timer->params.id); 62944458b6SDr. David Alan Gilbert timer->params.id = NULL; 63944458b6SDr. David Alan Gilbert 64944458b6SDr. David Alan Gilbert if (free_timer) { 65944458b6SDr. David Alan Gilbert g_free(timer); 66944458b6SDr. David Alan Gilbert } 6750510ea2SDr. David Alan Gilbert } 6850510ea2SDr. David Alan Gilbert 6950510ea2SDr. David Alan Gilbert /* 7050510ea2SDr. David Alan Gilbert * Under BQL/main thread 7150510ea2SDr. David Alan Gilbert * Reset the timer to the given parameters/type/notifier. 7250510ea2SDr. David Alan Gilbert */ 7350510ea2SDr. David Alan Gilbert void qemu_announce_timer_reset(AnnounceTimer *timer, 7450510ea2SDr. David Alan Gilbert AnnounceParameters *params, 7550510ea2SDr. David Alan Gilbert QEMUClockType type, 7650510ea2SDr. David Alan Gilbert QEMUTimerCB *cb, 7750510ea2SDr. David Alan Gilbert void *opaque) 7850510ea2SDr. David Alan Gilbert { 7950510ea2SDr. David Alan Gilbert /* 8050510ea2SDr. David Alan Gilbert * We're under the BQL, so the current timer can't 8150510ea2SDr. David Alan Gilbert * be firing, so we should be able to delete it. 8250510ea2SDr. David Alan Gilbert */ 83944458b6SDr. David Alan Gilbert qemu_announce_timer_del(timer, false); 8450510ea2SDr. David Alan Gilbert 8550510ea2SDr. David Alan Gilbert QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params); 8650510ea2SDr. David Alan Gilbert timer->round = params->rounds; 8750510ea2SDr. David Alan Gilbert timer->type = type; 8850510ea2SDr. David Alan Gilbert timer->tm = timer_new_ms(type, cb, opaque); 8950510ea2SDr. David Alan Gilbert } 907659505cSDr. David Alan Gilbert 917659505cSDr. David Alan Gilbert #ifndef ETH_P_RARP 927659505cSDr. David Alan Gilbert #define ETH_P_RARP 0x8035 937659505cSDr. David Alan Gilbert #endif 947659505cSDr. David Alan Gilbert #define ARP_HTYPE_ETH 0x0001 957659505cSDr. David Alan Gilbert #define ARP_PTYPE_IP 0x0800 967659505cSDr. David Alan Gilbert #define ARP_OP_REQUEST_REV 0x3 977659505cSDr. David Alan Gilbert 987659505cSDr. David Alan Gilbert static int announce_self_create(uint8_t *buf, 997659505cSDr. David Alan Gilbert uint8_t *mac_addr) 1007659505cSDr. David Alan Gilbert { 1017659505cSDr. David Alan Gilbert /* Ethernet header. */ 1027659505cSDr. David Alan Gilbert memset(buf, 0xff, 6); /* destination MAC addr */ 1037659505cSDr. David Alan Gilbert memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ 1047659505cSDr. David Alan Gilbert *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ 1057659505cSDr. David Alan Gilbert 1067659505cSDr. David Alan Gilbert /* RARP header. */ 1077659505cSDr. David Alan Gilbert *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ 1087659505cSDr. David Alan Gilbert *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ 1097659505cSDr. David Alan Gilbert *(buf + 18) = 6; /* hardware addr length (ethernet) */ 1107659505cSDr. David Alan Gilbert *(buf + 19) = 4; /* protocol addr length (IPv4) */ 1117659505cSDr. David Alan Gilbert *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ 1127659505cSDr. David Alan Gilbert memcpy(buf + 22, mac_addr, 6); /* source hw addr */ 1137659505cSDr. David Alan Gilbert memset(buf + 28, 0x00, 4); /* source protocol addr */ 1147659505cSDr. David Alan Gilbert memcpy(buf + 32, mac_addr, 6); /* target hw addr */ 1157659505cSDr. David Alan Gilbert memset(buf + 38, 0x00, 4); /* target protocol addr */ 1167659505cSDr. David Alan Gilbert 1177659505cSDr. David Alan Gilbert /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ 1187659505cSDr. David Alan Gilbert memset(buf + 42, 0x00, 18); 1197659505cSDr. David Alan Gilbert 1207659505cSDr. David Alan Gilbert return 60; /* len (FCS will be added by hardware) */ 1217659505cSDr. David Alan Gilbert } 1227659505cSDr. David Alan Gilbert 123*8a166615SMarc-André Lureau /* 124*8a166615SMarc-André Lureau * Helper to print ethernet mac address 125*8a166615SMarc-André Lureau */ 126*8a166615SMarc-André Lureau static const char *qemu_ether_ntoa(const MACAddr *mac) 127*8a166615SMarc-André Lureau { 128*8a166615SMarc-André Lureau static char ret[18]; 129*8a166615SMarc-André Lureau 130*8a166615SMarc-André Lureau snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x", 131*8a166615SMarc-André Lureau mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]); 132*8a166615SMarc-André Lureau 133*8a166615SMarc-André Lureau return ret; 134*8a166615SMarc-André Lureau } 135*8a166615SMarc-André Lureau 1367659505cSDr. David Alan Gilbert static void qemu_announce_self_iter(NICState *nic, void *opaque) 1377659505cSDr. David Alan Gilbert { 138ef2fdbfbSDr. David Alan Gilbert AnnounceTimer *timer = opaque; 1397659505cSDr. David Alan Gilbert uint8_t buf[60]; 1407659505cSDr. David Alan Gilbert int len; 141ef2fdbfbSDr. David Alan Gilbert bool skip; 1427659505cSDr. David Alan Gilbert 143ef2fdbfbSDr. David Alan Gilbert if (timer->params.has_interfaces) { 144ef2fdbfbSDr. David Alan Gilbert strList *entry = timer->params.interfaces; 145ef2fdbfbSDr. David Alan Gilbert /* Skip unless we find our name in the requested list */ 146ef2fdbfbSDr. David Alan Gilbert skip = true; 147ef2fdbfbSDr. David Alan Gilbert 148ef2fdbfbSDr. David Alan Gilbert while (entry) { 149ef2fdbfbSDr. David Alan Gilbert if (!strcmp(entry->value, nic->ncs->name)) { 150ef2fdbfbSDr. David Alan Gilbert /* Found us */ 151ef2fdbfbSDr. David Alan Gilbert skip = false; 152ef2fdbfbSDr. David Alan Gilbert break; 153ef2fdbfbSDr. David Alan Gilbert } 154ef2fdbfbSDr. David Alan Gilbert entry = entry->next; 155ef2fdbfbSDr. David Alan Gilbert } 156ef2fdbfbSDr. David Alan Gilbert } else { 157ef2fdbfbSDr. David Alan Gilbert skip = false; 158ef2fdbfbSDr. David Alan Gilbert } 159ef2fdbfbSDr. David Alan Gilbert 160944458b6SDr. David Alan Gilbert trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_", 161944458b6SDr. David Alan Gilbert nic->ncs->name, 162ef2fdbfbSDr. David Alan Gilbert qemu_ether_ntoa(&nic->conf->macaddr), skip); 163ef2fdbfbSDr. David Alan Gilbert 164ef2fdbfbSDr. David Alan Gilbert if (!skip) { 1657659505cSDr. David Alan Gilbert len = announce_self_create(buf, nic->conf->macaddr.a); 1667659505cSDr. David Alan Gilbert 1677659505cSDr. David Alan Gilbert qemu_send_packet_raw(qemu_get_queue(nic), buf, len); 16844b416adSDr. David Alan Gilbert 16944b416adSDr. David Alan Gilbert /* if the NIC provides it's own announcement support, use it as well */ 17044b416adSDr. David Alan Gilbert if (nic->ncs->info->announce) { 17144b416adSDr. David Alan Gilbert nic->ncs->info->announce(nic->ncs); 17244b416adSDr. David Alan Gilbert } 1737659505cSDr. David Alan Gilbert } 174ef2fdbfbSDr. David Alan Gilbert } 1757659505cSDr. David Alan Gilbert static void qemu_announce_self_once(void *opaque) 1767659505cSDr. David Alan Gilbert { 1777659505cSDr. David Alan Gilbert AnnounceTimer *timer = (AnnounceTimer *)opaque; 1787659505cSDr. David Alan Gilbert 179ef2fdbfbSDr. David Alan Gilbert qemu_foreach_nic(qemu_announce_self_iter, timer); 1807659505cSDr. David Alan Gilbert 1817659505cSDr. David Alan Gilbert if (--timer->round) { 1827659505cSDr. David Alan Gilbert qemu_announce_timer_step(timer); 1837659505cSDr. David Alan Gilbert } else { 184944458b6SDr. David Alan Gilbert qemu_announce_timer_del(timer, true); 1857659505cSDr. David Alan Gilbert } 1867659505cSDr. David Alan Gilbert } 1877659505cSDr. David Alan Gilbert 1887659505cSDr. David Alan Gilbert void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) 1897659505cSDr. David Alan Gilbert { 1907659505cSDr. David Alan Gilbert qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME, 1917659505cSDr. David Alan Gilbert qemu_announce_self_once, timer); 1927659505cSDr. David Alan Gilbert if (params->rounds) { 1937659505cSDr. David Alan Gilbert qemu_announce_self_once(timer); 1947659505cSDr. David Alan Gilbert } else { 195944458b6SDr. David Alan Gilbert qemu_announce_timer_del(timer, true); 1967659505cSDr. David Alan Gilbert } 1977659505cSDr. David Alan Gilbert } 198a06cd488SDr. David Alan Gilbert 199a06cd488SDr. David Alan Gilbert void qmp_announce_self(AnnounceParameters *params, Error **errp) 200a06cd488SDr. David Alan Gilbert { 201944458b6SDr. David Alan Gilbert AnnounceTimer *named_timer; 202944458b6SDr. David Alan Gilbert if (!params->has_id) { 203944458b6SDr. David Alan Gilbert params->id = g_strdup(""); 204944458b6SDr. David Alan Gilbert params->has_id = true; 205944458b6SDr. David Alan Gilbert } 206944458b6SDr. David Alan Gilbert 207944458b6SDr. David Alan Gilbert named_timer = g_datalist_get_data(&named_timers, params->id); 208944458b6SDr. David Alan Gilbert 209944458b6SDr. David Alan Gilbert if (!named_timer) { 210944458b6SDr. David Alan Gilbert named_timer = g_new0(AnnounceTimer, 1); 211944458b6SDr. David Alan Gilbert g_datalist_set_data(&named_timers, params->id, named_timer); 212944458b6SDr. David Alan Gilbert } 213944458b6SDr. David Alan Gilbert 214944458b6SDr. David Alan Gilbert qemu_announce_self(named_timer, params); 215a06cd488SDr. David Alan Gilbert } 216