xref: /openbmc/qemu/net/announce.c (revision 5cc8767d)
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_del(timer->tm);
45         timer_free(timer->tm);
46         timer->tm = NULL;
47     }
48     qapi_free_strList(timer->params.interfaces);
49     timer->params.interfaces = NULL;
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     }
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      */
84     qemu_announce_timer_del(timer, false);
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 }
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 {
126     AnnounceTimer *timer = opaque;
127     uint8_t buf[60];
128     int len;
129     bool skip;
130 
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;
135 
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     }
147 
148     trace_qemu_announce_self_iter(timer->params.has_id ? timer->params.id : "_",
149                                   nic->ncs->name,
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         }
161     }
162 }
163 static void qemu_announce_self_once(void *opaque)
164 {
165     AnnounceTimer *timer = (AnnounceTimer *)opaque;
166 
167     qemu_foreach_nic(qemu_announce_self_iter, timer);
168 
169     if (--timer->round) {
170         qemu_announce_timer_step(timer);
171     } else {
172         qemu_announce_timer_del(timer, true);
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 {
183         qemu_announce_timer_del(timer, true);
184     }
185 }
186 
187 void qmp_announce_self(AnnounceParameters *params, Error **errp)
188 {
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);
203 }
204