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