xref: /openbmc/qemu/net/announce.c (revision 08528271)
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 int64_t qemu_announce_timer_step(AnnounceTimer *timer)
19 {
20     int64_t step;
21 
22     step =  timer->params.initial +
23             (timer->params.rounds - timer->round - 1) *
24             timer->params.step;
25 
26     if (step < 0 || step > timer->params.max) {
27         step = timer->params.max;
28     }
29     timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step);
30 
31     return step;
32 }
33 
34 void qemu_announce_timer_del(AnnounceTimer *timer)
35 {
36     if (timer->tm) {
37         timer_del(timer->tm);
38         timer_free(timer->tm);
39         timer->tm = NULL;
40     }
41     qapi_free_strList(timer->params.interfaces);
42     timer->params.interfaces = NULL;
43 }
44 
45 /*
46  * Under BQL/main thread
47  * Reset the timer to the given parameters/type/notifier.
48  */
49 void qemu_announce_timer_reset(AnnounceTimer *timer,
50                                AnnounceParameters *params,
51                                QEMUClockType type,
52                                QEMUTimerCB *cb,
53                                void *opaque)
54 {
55     /*
56      * We're under the BQL, so the current timer can't
57      * be firing, so we should be able to delete it.
58      */
59     qemu_announce_timer_del(timer);
60 
61     QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
62     timer->round = params->rounds;
63     timer->type = type;
64     timer->tm = timer_new_ms(type, cb, opaque);
65 }
66 
67 #ifndef ETH_P_RARP
68 #define ETH_P_RARP 0x8035
69 #endif
70 #define ARP_HTYPE_ETH 0x0001
71 #define ARP_PTYPE_IP 0x0800
72 #define ARP_OP_REQUEST_REV 0x3
73 
74 static int announce_self_create(uint8_t *buf,
75                                 uint8_t *mac_addr)
76 {
77     /* Ethernet header. */
78     memset(buf, 0xff, 6);         /* destination MAC addr */
79     memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
80     *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
81 
82     /* RARP header. */
83     *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
84     *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
85     *(buf + 18) = 6; /* hardware addr length (ethernet) */
86     *(buf + 19) = 4; /* protocol addr length (IPv4) */
87     *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
88     memcpy(buf + 22, mac_addr, 6); /* source hw addr */
89     memset(buf + 28, 0x00, 4);     /* source protocol addr */
90     memcpy(buf + 32, mac_addr, 6); /* target hw addr */
91     memset(buf + 38, 0x00, 4);     /* target protocol addr */
92 
93     /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
94     memset(buf + 42, 0x00, 18);
95 
96     return 60; /* len (FCS will be added by hardware) */
97 }
98 
99 static void qemu_announce_self_iter(NICState *nic, void *opaque)
100 {
101     AnnounceTimer *timer = opaque;
102     uint8_t buf[60];
103     int len;
104     bool skip;
105 
106     if (timer->params.has_interfaces) {
107         strList *entry = timer->params.interfaces;
108         /* Skip unless we find our name in the requested list */
109         skip = true;
110 
111         while (entry) {
112             if (!strcmp(entry->value, nic->ncs->name)) {
113                 /* Found us */
114                 skip = false;
115                 break;
116             }
117             entry = entry->next;
118         }
119     } else {
120         skip = false;
121     }
122 
123     trace_qemu_announce_self_iter(nic->ncs->name,
124                                   qemu_ether_ntoa(&nic->conf->macaddr), skip);
125 
126     if (!skip) {
127         len = announce_self_create(buf, nic->conf->macaddr.a);
128 
129         qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
130 
131         /* if the NIC provides it's own announcement support, use it as well */
132         if (nic->ncs->info->announce) {
133             nic->ncs->info->announce(nic->ncs);
134         }
135     }
136 }
137 static void qemu_announce_self_once(void *opaque)
138 {
139     AnnounceTimer *timer = (AnnounceTimer *)opaque;
140 
141     qemu_foreach_nic(qemu_announce_self_iter, timer);
142 
143     if (--timer->round) {
144         qemu_announce_timer_step(timer);
145     } else {
146         qemu_announce_timer_del(timer);
147     }
148 }
149 
150 void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
151 {
152     qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
153                               qemu_announce_self_once, timer);
154     if (params->rounds) {
155         qemu_announce_self_once(timer);
156     } else {
157         qemu_announce_timer_del(timer);
158     }
159 }
160 
161 void qmp_announce_self(AnnounceParameters *params, Error **errp)
162 {
163     static AnnounceTimer announce_timer;
164     qemu_announce_self(&announce_timer, params);
165 }
166