Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 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" |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 12 | #include "net/net.h" |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 13 | #include "qapi/clone-visitor.h" |
| 14 | #include "qapi/qapi-visit-net.h" |
Dr. David Alan Gilbert | a06cd48 | 2019-02-27 13:24:11 +0000 | [diff] [blame] | 15 | #include "qapi/qapi-commands-net.h" |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 16 | #include "trace.h" |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 17 | |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 18 | static GData *named_timers; |
| 19 | |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 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 | |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 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) |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 41 | { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 42 | bool free_timer = false; |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 43 | if (timer->tm) { |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 44 | timer_free(timer->tm); |
| 45 | timer->tm = NULL; |
| 46 | } |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 47 | qapi_free_strList(timer->params.interfaces); |
| 48 | timer->params.interfaces = NULL; |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 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 | } |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 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 | */ |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 83 | qemu_announce_timer_del(timer, false); |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 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 | } |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 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 | static void qemu_announce_self_iter(NICState *nic, void *opaque) |
| 124 | { |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 125 | AnnounceTimer *timer = opaque; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 126 | uint8_t buf[60]; |
| 127 | int len; |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 128 | bool skip; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 129 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 130 | if (timer->params.has_interfaces) { |
| 131 | strList *entry = timer->params.interfaces; |
| 132 | /* Skip unless we find our name in the requested list */ |
| 133 | skip = true; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 134 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 135 | while (entry) { |
| 136 | if (!strcmp(entry->value, nic->ncs->name)) { |
| 137 | /* Found us */ |
| 138 | skip = false; |
| 139 | break; |
| 140 | } |
| 141 | entry = entry->next; |
| 142 | } |
| 143 | } else { |
| 144 | skip = false; |
| 145 | } |
Dr. David Alan Gilbert | 44b416a | 2019-02-27 13:24:09 +0000 | [diff] [blame] | 146 | |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 147 | trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_", |
| 148 | nic->ncs->name, |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 149 | qemu_ether_ntoa(&nic->conf->macaddr), skip); |
| 150 | |
| 151 | if (!skip) { |
| 152 | len = announce_self_create(buf, nic->conf->macaddr.a); |
| 153 | |
| 154 | qemu_send_packet_raw(qemu_get_queue(nic), buf, len); |
| 155 | |
| 156 | /* if the NIC provides it's own announcement support, use it as well */ |
| 157 | if (nic->ncs->info->announce) { |
| 158 | nic->ncs->info->announce(nic->ncs); |
| 159 | } |
Dr. David Alan Gilbert | 44b416a | 2019-02-27 13:24:09 +0000 | [diff] [blame] | 160 | } |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 161 | } |
| 162 | static void qemu_announce_self_once(void *opaque) |
| 163 | { |
| 164 | AnnounceTimer *timer = (AnnounceTimer *)opaque; |
| 165 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 166 | qemu_foreach_nic(qemu_announce_self_iter, timer); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 167 | |
| 168 | if (--timer->round) { |
| 169 | qemu_announce_timer_step(timer); |
| 170 | } else { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 171 | qemu_announce_timer_del(timer, true); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 172 | } |
| 173 | } |
| 174 | |
| 175 | void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) |
| 176 | { |
| 177 | qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME, |
| 178 | qemu_announce_self_once, timer); |
| 179 | if (params->rounds) { |
| 180 | qemu_announce_self_once(timer); |
| 181 | } else { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 182 | qemu_announce_timer_del(timer, true); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 183 | } |
| 184 | } |
Dr. David Alan Gilbert | a06cd48 | 2019-02-27 13:24:11 +0000 | [diff] [blame] | 185 | |
| 186 | void qmp_announce_self(AnnounceParameters *params, Error **errp) |
| 187 | { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 188 | AnnounceTimer *named_timer; |
| 189 | if (!params->has_id) { |
| 190 | params->id = g_strdup(""); |
| 191 | params->has_id = true; |
| 192 | } |
| 193 | |
| 194 | named_timer = g_datalist_get_data(&named_timers, params->id); |
| 195 | |
| 196 | if (!named_timer) { |
| 197 | named_timer = g_new0(AnnounceTimer, 1); |
| 198 | g_datalist_set_data(&named_timers, params->id, named_timer); |
| 199 | } |
| 200 | |
| 201 | qemu_announce_self(named_timer, params); |
Dr. David Alan Gilbert | a06cd48 | 2019-02-27 13:24:11 +0000 | [diff] [blame] | 202 | } |