]>
Commit | Line | Data |
---|---|---|
50510ea2 DDAG |
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" | |
415b7327 | 10 | #include "qemu/cutils.h" |
50510ea2 | 11 | #include "net/announce.h" |
7659505c | 12 | #include "net/net.h" |
50510ea2 DDAG |
13 | #include "qapi/clone-visitor.h" |
14 | #include "qapi/qapi-visit-net.h" | |
a06cd488 | 15 | #include "qapi/qapi-commands-net.h" |
7659505c | 16 | #include "trace.h" |
50510ea2 | 17 | |
944458b6 DDAG |
18 | static GData *named_timers; |
19 | ||
50510ea2 DDAG |
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 | ||
944458b6 DDAG |
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) | |
50510ea2 | 41 | { |
944458b6 | 42 | bool free_timer = false; |
50510ea2 | 43 | if (timer->tm) { |
50510ea2 DDAG |
44 | timer_free(timer->tm); |
45 | timer->tm = NULL; | |
46 | } | |
ef2fdbfb DDAG |
47 | qapi_free_strList(timer->params.interfaces); |
48 | timer->params.interfaces = NULL; | |
7480874a | 49 | if (free_named && timer->params.id) { |
944458b6 DDAG |
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 | } | |
50510ea2 DDAG |
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 | */ | |
944458b6 | 83 | qemu_announce_timer_del(timer, false); |
50510ea2 DDAG |
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 | } | |
7659505c DDAG |
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 | ||
8a166615 MAL |
123 | /* |
124 | * Helper to print ethernet mac address | |
125 | */ | |
126 | static const char *qemu_ether_ntoa(const MACAddr *mac) | |
127 | { | |
128 | static char ret[18]; | |
129 | ||
130 | snprintf(ret, sizeof(ret), "%02x:%02x:%02x:%02x:%02x:%02x", | |
131 | mac->a[0], mac->a[1], mac->a[2], mac->a[3], mac->a[4], mac->a[5]); | |
132 | ||
133 | return ret; | |
134 | } | |
135 | ||
7659505c DDAG |
136 | static void qemu_announce_self_iter(NICState *nic, void *opaque) |
137 | { | |
ef2fdbfb | 138 | AnnounceTimer *timer = opaque; |
7659505c DDAG |
139 | uint8_t buf[60]; |
140 | int len; | |
ef2fdbfb DDAG |
141 | bool skip; |
142 | ||
143 | if (timer->params.has_interfaces) { | |
144 | strList *entry = timer->params.interfaces; | |
145 | /* Skip unless we find our name in the requested list */ | |
146 | skip = true; | |
147 | ||
148 | while (entry) { | |
149 | if (!strcmp(entry->value, nic->ncs->name)) { | |
150 | /* Found us */ | |
151 | skip = false; | |
152 | break; | |
153 | } | |
154 | entry = entry->next; | |
155 | } | |
156 | } else { | |
157 | skip = false; | |
158 | } | |
159 | ||
7480874a | 160 | trace_qemu_announce_self_iter(timer->params.id ?: "_", |
944458b6 | 161 | nic->ncs->name, |
ef2fdbfb | 162 | qemu_ether_ntoa(&nic->conf->macaddr), skip); |
7659505c | 163 | |
ef2fdbfb DDAG |
164 | if (!skip) { |
165 | len = announce_self_create(buf, nic->conf->macaddr.a); | |
7659505c | 166 | |
ef2fdbfb | 167 | qemu_send_packet_raw(qemu_get_queue(nic), buf, len); |
44b416ad | 168 | |
ef2fdbfb DDAG |
169 | /* if the NIC provides it's own announcement support, use it as well */ |
170 | if (nic->ncs->info->announce) { | |
171 | nic->ncs->info->announce(nic->ncs); | |
172 | } | |
44b416ad | 173 | } |
7659505c DDAG |
174 | } |
175 | static void qemu_announce_self_once(void *opaque) | |
176 | { | |
177 | AnnounceTimer *timer = (AnnounceTimer *)opaque; | |
178 | ||
ef2fdbfb | 179 | qemu_foreach_nic(qemu_announce_self_iter, timer); |
7659505c DDAG |
180 | |
181 | if (--timer->round) { | |
182 | qemu_announce_timer_step(timer); | |
183 | } else { | |
944458b6 | 184 | qemu_announce_timer_del(timer, true); |
7659505c DDAG |
185 | } |
186 | } | |
187 | ||
188 | void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) | |
189 | { | |
190 | qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME, | |
191 | qemu_announce_self_once, timer); | |
192 | if (params->rounds) { | |
193 | qemu_announce_self_once(timer); | |
194 | } else { | |
944458b6 | 195 | qemu_announce_timer_del(timer, true); |
7659505c DDAG |
196 | } |
197 | } | |
a06cd488 DDAG |
198 | |
199 | void qmp_announce_self(AnnounceParameters *params, Error **errp) | |
200 | { | |
944458b6 | 201 | AnnounceTimer *named_timer; |
7480874a MA |
202 | |
203 | if (!params->id) { | |
944458b6 | 204 | params->id = g_strdup(""); |
944458b6 DDAG |
205 | } |
206 | ||
207 | named_timer = g_datalist_get_data(&named_timers, params->id); | |
208 | ||
209 | if (!named_timer) { | |
210 | named_timer = g_new0(AnnounceTimer, 1); | |
211 | g_datalist_set_data(&named_timers, params->id, named_timer); | |
212 | } | |
213 | ||
214 | qemu_announce_self(named_timer, params); | |
a06cd488 | 215 | } |