xref: /qemu/net/announce.c (revision 7480874a)
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"
10415b7327SMarc-André Lureau #include "qemu/cutils.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 
qemu_announce_timer_step(AnnounceTimer * timer)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  */
qemu_announce_timer_del(AnnounceTimer * timer,bool free_named)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;
49*7480874aSMarkus Armbruster     if (free_named && timer->params.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  */
qemu_announce_timer_reset(AnnounceTimer * timer,AnnounceParameters * params,QEMUClockType type,QEMUTimerCB * cb,void * opaque)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 
announce_self_create(uint8_t * buf,uint8_t * mac_addr)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 
1238a166615SMarc-André Lureau /*
1248a166615SMarc-André Lureau  * Helper to print ethernet mac address
1258a166615SMarc-André Lureau  */
qemu_ether_ntoa(const MACAddr * mac)1268a166615SMarc-André Lureau static const char *qemu_ether_ntoa(const MACAddr *mac)
1278a166615SMarc-André Lureau {
1288a166615SMarc-André Lureau     static char ret[18];
1298a166615SMarc-André Lureau 
1308a166615SMarc-André Lureau     snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x",
1318a166615SMarc-André Lureau              mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]);
1328a166615SMarc-André Lureau 
1338a166615SMarc-André Lureau     return ret;
1348a166615SMarc-André Lureau }
1358a166615SMarc-André Lureau 
qemu_announce_self_iter(NICState * nic,void * opaque)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 
160*7480874aSMarkus Armbruster     trace_qemu_announce_self_iter(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 }
qemu_announce_self_once(void * opaque)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 
qemu_announce_self(AnnounceTimer * timer,AnnounceParameters * params)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 
qmp_announce_self(AnnounceParameters * params,Error ** errp)199a06cd488SDr. David Alan Gilbert void qmp_announce_self(AnnounceParameters *params, Error **errp)
200a06cd488SDr. David Alan Gilbert {
201944458b6SDr. David Alan Gilbert     AnnounceTimer *named_timer;
202*7480874aSMarkus Armbruster 
203*7480874aSMarkus Armbruster     if (!params->id) {
204944458b6SDr. David Alan Gilbert         params->id = g_strdup("");
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