xref: /openbmc/qemu/net/announce.c (revision 750541c4)
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 }
42 
43 /*
44  * Under BQL/main thread
45  * Reset the timer to the given parameters/type/notifier.
46  */
47 void qemu_announce_timer_reset(AnnounceTimer *timer,
48                                AnnounceParameters *params,
49                                QEMUClockType type,
50                                QEMUTimerCB *cb,
51                                void *opaque)
52 {
53     /*
54      * We're under the BQL, so the current timer can't
55      * be firing, so we should be able to delete it.
56      */
57     qemu_announce_timer_del(timer);
58 
59     QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params);
60     timer->round = params->rounds;
61     timer->type = type;
62     timer->tm = timer_new_ms(type, cb, opaque);
63 }
64 
65 #ifndef ETH_P_RARP
66 #define ETH_P_RARP 0x8035
67 #endif
68 #define ARP_HTYPE_ETH 0x0001
69 #define ARP_PTYPE_IP 0x0800
70 #define ARP_OP_REQUEST_REV 0x3
71 
72 static int announce_self_create(uint8_t *buf,
73                                 uint8_t *mac_addr)
74 {
75     /* Ethernet header. */
76     memset(buf, 0xff, 6);         /* destination MAC addr */
77     memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
78     *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
79 
80     /* RARP header. */
81     *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
82     *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
83     *(buf + 18) = 6; /* hardware addr length (ethernet) */
84     *(buf + 19) = 4; /* protocol addr length (IPv4) */
85     *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
86     memcpy(buf + 22, mac_addr, 6); /* source hw addr */
87     memset(buf + 28, 0x00, 4);     /* source protocol addr */
88     memcpy(buf + 32, mac_addr, 6); /* target hw addr */
89     memset(buf + 38, 0x00, 4);     /* target protocol addr */
90 
91     /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
92     memset(buf + 42, 0x00, 18);
93 
94     return 60; /* len (FCS will be added by hardware) */
95 }
96 
97 static void qemu_announce_self_iter(NICState *nic, void *opaque)
98 {
99     uint8_t buf[60];
100     int len;
101 
102     trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr));
103     len = announce_self_create(buf, nic->conf->macaddr.a);
104 
105     qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
106 
107     /* if the NIC provides it's own announcement support, use it as well */
108     if (nic->ncs->info->announce) {
109         nic->ncs->info->announce(nic->ncs);
110     }
111 }
112 static void qemu_announce_self_once(void *opaque)
113 {
114     AnnounceTimer *timer = (AnnounceTimer *)opaque;
115 
116     qemu_foreach_nic(qemu_announce_self_iter, NULL);
117 
118     if (--timer->round) {
119         qemu_announce_timer_step(timer);
120     } else {
121         qemu_announce_timer_del(timer);
122     }
123 }
124 
125 void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params)
126 {
127     qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME,
128                               qemu_announce_self_once, timer);
129     if (params->rounds) {
130         qemu_announce_self_once(timer);
131     } else {
132         qemu_announce_timer_del(timer);
133     }
134 }
135 
136 void qmp_announce_self(AnnounceParameters *params, Error **errp)
137 {
138     static AnnounceTimer announce_timer;
139     qemu_announce_self(&announce_timer, params);
140 }
141