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) { |
| 44 | timer_del(timer->tm); |
| 45 | timer_free(timer->tm); |
| 46 | timer->tm = NULL; |
| 47 | } |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 48 | qapi_free_strList(timer->params.interfaces); |
| 49 | timer->params.interfaces = NULL; |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 50 | if (free_named && timer->params.has_id) { |
| 51 | AnnounceTimer *list_timer; |
| 52 | /* |
| 53 | * Sanity check: There should only be one timer on the list with |
| 54 | * the id. |
| 55 | */ |
| 56 | list_timer = g_datalist_get_data(&named_timers, timer->params.id); |
| 57 | assert(timer == list_timer); |
| 58 | free_timer = true; |
| 59 | g_datalist_remove_data(&named_timers, timer->params.id); |
| 60 | } |
| 61 | trace_qemu_announce_timer_del(free_named, free_timer, timer->params.id); |
| 62 | g_free(timer->params.id); |
| 63 | timer->params.id = NULL; |
| 64 | |
| 65 | if (free_timer) { |
| 66 | g_free(timer); |
| 67 | } |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | /* |
| 71 | * Under BQL/main thread |
| 72 | * Reset the timer to the given parameters/type/notifier. |
| 73 | */ |
| 74 | void qemu_announce_timer_reset(AnnounceTimer *timer, |
| 75 | AnnounceParameters *params, |
| 76 | QEMUClockType type, |
| 77 | QEMUTimerCB *cb, |
| 78 | void *opaque) |
| 79 | { |
| 80 | /* |
| 81 | * We're under the BQL, so the current timer can't |
| 82 | * be firing, so we should be able to delete it. |
| 83 | */ |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 84 | qemu_announce_timer_del(timer, false); |
Dr. David Alan Gilbert | 50510ea | 2019-02-27 13:24:05 +0000 | [diff] [blame] | 85 | |
| 86 | QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params); |
| 87 | timer->round = params->rounds; |
| 88 | timer->type = type; |
| 89 | timer->tm = timer_new_ms(type, cb, opaque); |
| 90 | } |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 91 | |
| 92 | #ifndef ETH_P_RARP |
| 93 | #define ETH_P_RARP 0x8035 |
| 94 | #endif |
| 95 | #define ARP_HTYPE_ETH 0x0001 |
| 96 | #define ARP_PTYPE_IP 0x0800 |
| 97 | #define ARP_OP_REQUEST_REV 0x3 |
| 98 | |
| 99 | static int announce_self_create(uint8_t *buf, |
| 100 | uint8_t *mac_addr) |
| 101 | { |
| 102 | /* Ethernet header. */ |
| 103 | memset(buf, 0xff, 6); /* destination MAC addr */ |
| 104 | memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ |
| 105 | *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ |
| 106 | |
| 107 | /* RARP header. */ |
| 108 | *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ |
| 109 | *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ |
| 110 | *(buf + 18) = 6; /* hardware addr length (ethernet) */ |
| 111 | *(buf + 19) = 4; /* protocol addr length (IPv4) */ |
| 112 | *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ |
| 113 | memcpy(buf + 22, mac_addr, 6); /* source hw addr */ |
| 114 | memset(buf + 28, 0x00, 4); /* source protocol addr */ |
| 115 | memcpy(buf + 32, mac_addr, 6); /* target hw addr */ |
| 116 | memset(buf + 38, 0x00, 4); /* target protocol addr */ |
| 117 | |
| 118 | /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ |
| 119 | memset(buf + 42, 0x00, 18); |
| 120 | |
| 121 | return 60; /* len (FCS will be added by hardware) */ |
| 122 | } |
| 123 | |
| 124 | static void qemu_announce_self_iter(NICState *nic, void *opaque) |
| 125 | { |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 126 | AnnounceTimer *timer = opaque; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 127 | uint8_t buf[60]; |
| 128 | int len; |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 129 | bool skip; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 130 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 131 | if (timer->params.has_interfaces) { |
| 132 | strList *entry = timer->params.interfaces; |
| 133 | /* Skip unless we find our name in the requested list */ |
| 134 | skip = true; |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 135 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 136 | while (entry) { |
| 137 | if (!strcmp(entry->value, nic->ncs->name)) { |
| 138 | /* Found us */ |
| 139 | skip = false; |
| 140 | break; |
| 141 | } |
| 142 | entry = entry->next; |
| 143 | } |
| 144 | } else { |
| 145 | skip = false; |
| 146 | } |
Dr. David Alan Gilbert | 44b416a | 2019-02-27 13:24:09 +0000 | [diff] [blame] | 147 | |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 148 | trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_", |
| 149 | nic->ncs->name, |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 150 | qemu_ether_ntoa(&nic->conf->macaddr), skip); |
| 151 | |
| 152 | if (!skip) { |
| 153 | len = announce_self_create(buf, nic->conf->macaddr.a); |
| 154 | |
| 155 | qemu_send_packet_raw(qemu_get_queue(nic), buf, len); |
| 156 | |
| 157 | /* if the NIC provides it's own announcement support, use it as well */ |
| 158 | if (nic->ncs->info->announce) { |
| 159 | nic->ncs->info->announce(nic->ncs); |
| 160 | } |
Dr. David Alan Gilbert | 44b416a | 2019-02-27 13:24:09 +0000 | [diff] [blame] | 161 | } |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 162 | } |
| 163 | static void qemu_announce_self_once(void *opaque) |
| 164 | { |
| 165 | AnnounceTimer *timer = (AnnounceTimer *)opaque; |
| 166 | |
Dr. David Alan Gilbert | ef2fdbf | 2019-06-20 19:47:02 +0100 | [diff] [blame] | 167 | qemu_foreach_nic(qemu_announce_self_iter, timer); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 168 | |
| 169 | if (--timer->round) { |
| 170 | qemu_announce_timer_step(timer); |
| 171 | } else { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 172 | qemu_announce_timer_del(timer, true); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 173 | } |
| 174 | } |
| 175 | |
| 176 | void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) |
| 177 | { |
| 178 | qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME, |
| 179 | qemu_announce_self_once, timer); |
| 180 | if (params->rounds) { |
| 181 | qemu_announce_self_once(timer); |
| 182 | } else { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 183 | qemu_announce_timer_del(timer, true); |
Dr. David Alan Gilbert | 7659505 | 2019-02-27 13:24:08 +0000 | [diff] [blame] | 184 | } |
| 185 | } |
Dr. David Alan Gilbert | a06cd48 | 2019-02-27 13:24:11 +0000 | [diff] [blame] | 186 | |
| 187 | void qmp_announce_self(AnnounceParameters *params, Error **errp) |
| 188 | { |
Dr. David Alan Gilbert | 944458b | 2019-06-20 19:47:04 +0100 | [diff] [blame] | 189 | AnnounceTimer *named_timer; |
| 190 | if (!params->has_id) { |
| 191 | params->id = g_strdup(""); |
| 192 | params->has_id = true; |
| 193 | } |
| 194 | |
| 195 | named_timer = g_datalist_get_data(&named_timers, params->id); |
| 196 | |
| 197 | if (!named_timer) { |
| 198 | named_timer = g_new0(AnnounceTimer, 1); |
| 199 | g_datalist_set_data(&named_timers, params->id, named_timer); |
| 200 | } |
| 201 | |
| 202 | qemu_announce_self(named_timer, params); |
Dr. David Alan Gilbert | a06cd48 | 2019-02-27 13:24:11 +0000 | [diff] [blame] | 203 | } |