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 | |