xref: /openbmc/qemu/net/net.c (revision fa62cb98)
1fd9400b3SPaolo Bonzini /*
2fd9400b3SPaolo Bonzini  * QEMU System Emulator
3fd9400b3SPaolo Bonzini  *
4fd9400b3SPaolo Bonzini  * Copyright (c) 2003-2008 Fabrice Bellard
5fd9400b3SPaolo Bonzini  *
6fd9400b3SPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
7fd9400b3SPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
8fd9400b3SPaolo Bonzini  * in the Software without restriction, including without limitation the rights
9fd9400b3SPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10fd9400b3SPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
11fd9400b3SPaolo Bonzini  * furnished to do so, subject to the following conditions:
12fd9400b3SPaolo Bonzini  *
13fd9400b3SPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
14fd9400b3SPaolo Bonzini  * all copies or substantial portions of the Software.
15fd9400b3SPaolo Bonzini  *
16fd9400b3SPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17fd9400b3SPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18fd9400b3SPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19fd9400b3SPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20fd9400b3SPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21fd9400b3SPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22fd9400b3SPaolo Bonzini  * THE SOFTWARE.
23fd9400b3SPaolo Bonzini  */
24e688df6bSMarkus Armbruster 
252744d920SPeter Maydell #include "qemu/osdep.h"
26fd9400b3SPaolo Bonzini 
271422e32dSPaolo Bonzini #include "net/net.h"
28fd9400b3SPaolo Bonzini #include "clients.h"
29fd9400b3SPaolo Bonzini #include "hub.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
311422e32dSPaolo Bonzini #include "net/slirp.h"
32d60b20cfSDmitry Krivenok #include "net/eth.h"
33fd9400b3SPaolo Bonzini #include "util.h"
34fd9400b3SPaolo Bonzini 
3583c9089eSPaolo Bonzini #include "monitor/monitor.h"
36f348b6d1SVeronia Bahaa #include "qemu/help_option.h"
379af23989SMarkus Armbruster #include "qapi/qapi-commands-net.h"
38f9bb0c1fSJason Wang #include "qapi/qapi-visit-net.h"
39452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
40cc7a8ea7SMarkus Armbruster #include "qapi/qmp/qerror.h"
41d49b6836SMarkus Armbruster #include "qemu/error-report.h"
421de7afc9SPaolo Bonzini #include "qemu/sockets.h"
43f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
441de7afc9SPaolo Bonzini #include "qemu/config-file.h"
45856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
4627eb3722SThomas Huth #include "qemu/id.h"
471de7afc9SPaolo Bonzini #include "qemu/iov.h"
48ad6f932fSPaolo Bonzini #include "qemu/qemu-print.h"
496a1751b7SAlex Bligh #include "qemu/main-loop.h"
50922a01a0SMarkus Armbruster #include "qemu/option.h"
515166fe0aSLaurent Vivier #include "qemu/keyval.h"
52e688df6bSMarkus Armbruster #include "qapi/error.h"
53fd9400b3SPaolo Bonzini #include "qapi/opts-visitor.h"
5454d31236SMarkus Armbruster #include "sysemu/runstate.h"
550c7af1a7SRao, Lei #include "net/colo-compare.h"
56fdccce45SYang Hongyang #include "net/filter.h"
57aa9156f4Szhanghailiang #include "qapi/string-output-visitor.h"
58f3eedcddSLaurent Vivier #include "qapi/qobject-input-visitor.h"
59a6775371SAkihiko Odaki #include "standard-headers/linux/virtio_net.h"
60fd9400b3SPaolo Bonzini 
61fd9400b3SPaolo Bonzini /* Net bridge is currently not supported for W32. */
62fd9400b3SPaolo Bonzini #if !defined(_WIN32)
63fd9400b3SPaolo Bonzini # define CONFIG_NET_BRIDGE
64fd9400b3SPaolo Bonzini #endif
65fd9400b3SPaolo Bonzini 
66ca77d85eSMichael S. Tsirkin static VMChangeStateEntry *net_change_state_entry;
67ae71d13dSMarkus Armbruster NetClientStateList net_clients;
68fd9400b3SPaolo Bonzini 
69f3eedcddSLaurent Vivier typedef struct NetdevQueueEntry {
70f3eedcddSLaurent Vivier     Netdev *nd;
71f3eedcddSLaurent Vivier     Location loc;
72f3eedcddSLaurent Vivier     QSIMPLEQ_ENTRY(NetdevQueueEntry) entry;
73f3eedcddSLaurent Vivier } NetdevQueueEntry;
74f3eedcddSLaurent Vivier 
75f3eedcddSLaurent Vivier typedef QSIMPLEQ_HEAD(, NetdevQueueEntry) NetdevQueue;
76f3eedcddSLaurent Vivier 
77f3eedcddSLaurent Vivier static NetdevQueue nd_queue = QSIMPLEQ_HEAD_INITIALIZER(nd_queue);
78f3eedcddSLaurent Vivier 
792cdeca04SDavid Woodhouse static GHashTable *nic_model_help;
802cdeca04SDavid Woodhouse 
81e8c5c452SDavid Woodhouse static int nb_nics;
82e8c5c452SDavid Woodhouse static NICInfo nd_table[MAX_NICS];
83e8c5c452SDavid Woodhouse 
84fd9400b3SPaolo Bonzini /***********************************************************/
85fd9400b3SPaolo Bonzini /* network device redirectors */
86fd9400b3SPaolo Bonzini 
convert_host_port(struct sockaddr_in * saddr,const char * host,const char * port,Error ** errp)8730e4226bSLaurent Vivier int convert_host_port(struct sockaddr_in *saddr, const char *host,
8830e4226bSLaurent Vivier                       const char *port, Error **errp)
8930e4226bSLaurent Vivier {
9030e4226bSLaurent Vivier     struct hostent *he;
9130e4226bSLaurent Vivier     const char *r;
9230e4226bSLaurent Vivier     long p;
9330e4226bSLaurent Vivier 
9430e4226bSLaurent Vivier     memset(saddr, 0, sizeof(*saddr));
9530e4226bSLaurent Vivier 
9630e4226bSLaurent Vivier     saddr->sin_family = AF_INET;
9730e4226bSLaurent Vivier     if (host[0] == '\0') {
9830e4226bSLaurent Vivier         saddr->sin_addr.s_addr = 0;
9930e4226bSLaurent Vivier     } else {
10030e4226bSLaurent Vivier         if (qemu_isdigit(host[0])) {
10130e4226bSLaurent Vivier             if (!inet_aton(host, &saddr->sin_addr)) {
10230e4226bSLaurent Vivier                 error_setg(errp, "host address '%s' is not a valid "
10330e4226bSLaurent Vivier                            "IPv4 address", host);
10430e4226bSLaurent Vivier                 return -1;
10530e4226bSLaurent Vivier             }
10630e4226bSLaurent Vivier         } else {
10730e4226bSLaurent Vivier             he = gethostbyname(host);
10830e4226bSLaurent Vivier             if (he == NULL) {
10930e4226bSLaurent Vivier                 error_setg(errp, "can't resolve host address '%s'", host);
11030e4226bSLaurent Vivier                 return -1;
11130e4226bSLaurent Vivier             }
11230e4226bSLaurent Vivier             saddr->sin_addr = *(struct in_addr *)he->h_addr;
11330e4226bSLaurent Vivier         }
11430e4226bSLaurent Vivier     }
11530e4226bSLaurent Vivier     if (qemu_strtol(port, &r, 0, &p) != 0) {
11630e4226bSLaurent Vivier         error_setg(errp, "port number '%s' is invalid", port);
11730e4226bSLaurent Vivier         return -1;
11830e4226bSLaurent Vivier     }
11930e4226bSLaurent Vivier     saddr->sin_port = htons(p);
12030e4226bSLaurent Vivier     return 0;
12130e4226bSLaurent Vivier }
12230e4226bSLaurent Vivier 
parse_host_port(struct sockaddr_in * saddr,const char * str,Error ** errp)123bcd4dfd6SMao Zhongyi int parse_host_port(struct sockaddr_in *saddr, const char *str,
124bcd4dfd6SMao Zhongyi                     Error **errp)
125fd9400b3SPaolo Bonzini {
126add99347SStefano Garzarella     gchar **substrings;
12730e4226bSLaurent Vivier     int ret;
12859292384SPeter Maydell 
129add99347SStefano Garzarella     substrings = g_strsplit(str, ":", 2);
130add99347SStefano Garzarella     if (!substrings || !substrings[0] || !substrings[1]) {
131bcd4dfd6SMao Zhongyi         error_setg(errp, "host address '%s' doesn't contain ':' "
132bcd4dfd6SMao Zhongyi                    "separating host from port", str);
133add99347SStefano Garzarella         ret = -1;
134add99347SStefano Garzarella         goto out;
135bcd4dfd6SMao Zhongyi     }
136add99347SStefano Garzarella 
13730e4226bSLaurent Vivier     ret = convert_host_port(saddr, substrings[0], substrings[1], errp);
138add99347SStefano Garzarella 
139add99347SStefano Garzarella out:
140add99347SStefano Garzarella     g_strfreev(substrings);
141add99347SStefano Garzarella     return ret;
142fd9400b3SPaolo Bonzini }
143fd9400b3SPaolo Bonzini 
qemu_mac_strdup_printf(const uint8_t * macaddr)144890ee6abSScott Feldman char *qemu_mac_strdup_printf(const uint8_t *macaddr)
145890ee6abSScott Feldman {
146890ee6abSScott Feldman     return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
147890ee6abSScott Feldman                            macaddr[0], macaddr[1], macaddr[2],
148890ee6abSScott Feldman                            macaddr[3], macaddr[4], macaddr[5]);
149890ee6abSScott Feldman }
150890ee6abSScott Feldman 
qemu_set_info_str(NetClientState * nc,const char * fmt,...)15153b85d95SLaurent Vivier void qemu_set_info_str(NetClientState *nc, const char *fmt, ...)
15253b85d95SLaurent Vivier {
15353b85d95SLaurent Vivier     va_list ap;
15453b85d95SLaurent Vivier 
15553b85d95SLaurent Vivier     va_start(ap, fmt);
15653b85d95SLaurent Vivier     vsnprintf(nc->info_str, sizeof(nc->info_str), fmt, ap);
15753b85d95SLaurent Vivier     va_end(ap);
15853b85d95SLaurent Vivier }
15953b85d95SLaurent Vivier 
qemu_format_nic_info_str(NetClientState * nc,uint8_t macaddr[6])160fd9400b3SPaolo Bonzini void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6])
161fd9400b3SPaolo Bonzini {
16253b85d95SLaurent Vivier     qemu_set_info_str(nc, "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
16353b85d95SLaurent Vivier                       nc->model, macaddr[0], macaddr[1], macaddr[2],
164fd9400b3SPaolo Bonzini                       macaddr[3], macaddr[4], macaddr[5]);
165fd9400b3SPaolo Bonzini }
166fd9400b3SPaolo Bonzini 
1672bc22a58SShannon Zhao static int mac_table[256] = {0};
1682bc22a58SShannon Zhao 
qemu_macaddr_set_used(MACAddr * macaddr)1692bc22a58SShannon Zhao static void qemu_macaddr_set_used(MACAddr *macaddr)
1702bc22a58SShannon Zhao {
1712bc22a58SShannon Zhao     int index;
1722bc22a58SShannon Zhao 
1732bc22a58SShannon Zhao     for (index = 0x56; index < 0xFF; index++) {
1742bc22a58SShannon Zhao         if (macaddr->a[5] == index) {
1752bc22a58SShannon Zhao             mac_table[index]++;
1762bc22a58SShannon Zhao         }
1772bc22a58SShannon Zhao     }
1782bc22a58SShannon Zhao }
1792bc22a58SShannon Zhao 
qemu_macaddr_set_free(MACAddr * macaddr)1802bc22a58SShannon Zhao static void qemu_macaddr_set_free(MACAddr *macaddr)
1812bc22a58SShannon Zhao {
1822bc22a58SShannon Zhao     int index;
1832bc22a58SShannon Zhao     static const MACAddr base = { .a = { 0x52, 0x54, 0x00, 0x12, 0x34, 0 } };
1842bc22a58SShannon Zhao 
1852bc22a58SShannon Zhao     if (memcmp(macaddr->a, &base.a, (sizeof(base.a) - 1)) != 0) {
1862bc22a58SShannon Zhao         return;
1872bc22a58SShannon Zhao     }
1882bc22a58SShannon Zhao     for (index = 0x56; index < 0xFF; index++) {
1892bc22a58SShannon Zhao         if (macaddr->a[5] == index) {
1902bc22a58SShannon Zhao             mac_table[index]--;
1912bc22a58SShannon Zhao         }
1922bc22a58SShannon Zhao     }
1932bc22a58SShannon Zhao }
1942bc22a58SShannon Zhao 
qemu_macaddr_get_free(void)1952bc22a58SShannon Zhao static int qemu_macaddr_get_free(void)
1962bc22a58SShannon Zhao {
1972bc22a58SShannon Zhao     int index;
1982bc22a58SShannon Zhao 
1992bc22a58SShannon Zhao     for (index = 0x56; index < 0xFF; index++) {
2002bc22a58SShannon Zhao         if (mac_table[index] == 0) {
2012bc22a58SShannon Zhao             return index;
2022bc22a58SShannon Zhao         }
2032bc22a58SShannon Zhao     }
2042bc22a58SShannon Zhao 
2052bc22a58SShannon Zhao     return -1;
2062bc22a58SShannon Zhao }
2072bc22a58SShannon Zhao 
qemu_macaddr_default_if_unset(MACAddr * macaddr)208fd9400b3SPaolo Bonzini void qemu_macaddr_default_if_unset(MACAddr *macaddr)
209fd9400b3SPaolo Bonzini {
210fd9400b3SPaolo Bonzini     static const MACAddr zero = { .a = { 0,0,0,0,0,0 } };
2112bc22a58SShannon Zhao     static const MACAddr base = { .a = { 0x52, 0x54, 0x00, 0x12, 0x34, 0 } };
212fd9400b3SPaolo Bonzini 
2132bc22a58SShannon Zhao     if (memcmp(macaddr, &zero, sizeof(zero)) != 0) {
2142bc22a58SShannon Zhao         if (memcmp(macaddr->a, &base.a, (sizeof(base.a) - 1)) != 0) {
215fd9400b3SPaolo Bonzini             return;
2162bc22a58SShannon Zhao         } else {
2172bc22a58SShannon Zhao             qemu_macaddr_set_used(macaddr);
2182bc22a58SShannon Zhao             return;
2192bc22a58SShannon Zhao         }
2202bc22a58SShannon Zhao     }
2212bc22a58SShannon Zhao 
222fd9400b3SPaolo Bonzini     macaddr->a[0] = 0x52;
223fd9400b3SPaolo Bonzini     macaddr->a[1] = 0x54;
224fd9400b3SPaolo Bonzini     macaddr->a[2] = 0x00;
225fd9400b3SPaolo Bonzini     macaddr->a[3] = 0x12;
226fd9400b3SPaolo Bonzini     macaddr->a[4] = 0x34;
2272bc22a58SShannon Zhao     macaddr->a[5] = qemu_macaddr_get_free();
2282bc22a58SShannon Zhao     qemu_macaddr_set_used(macaddr);
229fd9400b3SPaolo Bonzini }
230fd9400b3SPaolo Bonzini 
231fd9400b3SPaolo Bonzini /**
232fd9400b3SPaolo Bonzini  * Generate a name for net client
233fd9400b3SPaolo Bonzini  *
234c963530aSAmos Kong  * Only net clients created with the legacy -net option and NICs need this.
235fd9400b3SPaolo Bonzini  */
assign_name(NetClientState * nc1,const char * model)236fd9400b3SPaolo Bonzini static char *assign_name(NetClientState *nc1, const char *model)
237fd9400b3SPaolo Bonzini {
238fd9400b3SPaolo Bonzini     NetClientState *nc;
239fd9400b3SPaolo Bonzini     int id = 0;
240fd9400b3SPaolo Bonzini 
241fd9400b3SPaolo Bonzini     QTAILQ_FOREACH(nc, &net_clients, next) {
242fd9400b3SPaolo Bonzini         if (nc == nc1) {
243fd9400b3SPaolo Bonzini             continue;
244fd9400b3SPaolo Bonzini         }
245c963530aSAmos Kong         if (strcmp(nc->model, model) == 0) {
246fd9400b3SPaolo Bonzini             id++;
247fd9400b3SPaolo Bonzini         }
248fd9400b3SPaolo Bonzini     }
249fd9400b3SPaolo Bonzini 
2504bf2c138SHani Benhabiles     return g_strdup_printf("%s.%d", model, id);
251fd9400b3SPaolo Bonzini }
252fd9400b3SPaolo Bonzini 
qemu_net_client_destructor(NetClientState * nc)253f7860455SJason Wang static void qemu_net_client_destructor(NetClientState *nc)
254f7860455SJason Wang {
255f7860455SJason Wang     g_free(nc);
256f7860455SJason Wang }
25725c01bd1SJason Wang static ssize_t qemu_deliver_packet_iov(NetClientState *sender,
25825c01bd1SJason Wang                                        unsigned flags,
25925c01bd1SJason Wang                                        const struct iovec *iov,
26025c01bd1SJason Wang                                        int iovcnt,
26125c01bd1SJason Wang                                        void *opaque);
262f7860455SJason Wang 
qemu_net_client_setup(NetClientState * nc,NetClientInfo * info,NetClientState * peer,const char * model,const char * name,NetClientDestructor * destructor,bool is_datapath)26318a1541aSJason Wang static void qemu_net_client_setup(NetClientState *nc,
26418a1541aSJason Wang                                   NetClientInfo *info,
265fd9400b3SPaolo Bonzini                                   NetClientState *peer,
266fd9400b3SPaolo Bonzini                                   const char *model,
267f7860455SJason Wang                                   const char *name,
2682f849dbdSJason Wang                                   NetClientDestructor *destructor,
2692f849dbdSJason Wang                                   bool is_datapath)
270fd9400b3SPaolo Bonzini {
271fd9400b3SPaolo Bonzini     nc->info = info;
272fd9400b3SPaolo Bonzini     nc->model = g_strdup(model);
273fd9400b3SPaolo Bonzini     if (name) {
274fd9400b3SPaolo Bonzini         nc->name = g_strdup(name);
275fd9400b3SPaolo Bonzini     } else {
276fd9400b3SPaolo Bonzini         nc->name = assign_name(nc, model);
277fd9400b3SPaolo Bonzini     }
278fd9400b3SPaolo Bonzini 
279fd9400b3SPaolo Bonzini     if (peer) {
280fd9400b3SPaolo Bonzini         assert(!peer->peer);
281fd9400b3SPaolo Bonzini         nc->peer = peer;
282fd9400b3SPaolo Bonzini         peer->peer = nc;
283fd9400b3SPaolo Bonzini     }
284fd9400b3SPaolo Bonzini     QTAILQ_INSERT_TAIL(&net_clients, nc, next);
285fd9400b3SPaolo Bonzini 
2863e033a46SYang Hongyang     nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc);
287f7860455SJason Wang     nc->destructor = destructor;
2882f849dbdSJason Wang     nc->is_datapath = is_datapath;
289fdccce45SYang Hongyang     QTAILQ_INIT(&nc->filters);
29018a1541aSJason Wang }
29118a1541aSJason Wang 
qemu_new_net_client(NetClientInfo * info,NetClientState * peer,const char * model,const char * name)29218a1541aSJason Wang NetClientState *qemu_new_net_client(NetClientInfo *info,
29318a1541aSJason Wang                                     NetClientState *peer,
29418a1541aSJason Wang                                     const char *model,
29518a1541aSJason Wang                                     const char *name)
29618a1541aSJason Wang {
29718a1541aSJason Wang     NetClientState *nc;
29818a1541aSJason Wang 
29918a1541aSJason Wang     assert(info->size >= sizeof(NetClientState));
30018a1541aSJason Wang 
30118a1541aSJason Wang     nc = g_malloc0(info->size);
302f7860455SJason Wang     qemu_net_client_setup(nc, info, peer, model, name,
3032f849dbdSJason Wang                           qemu_net_client_destructor, true);
3042f849dbdSJason Wang 
3052f849dbdSJason Wang     return nc;
3062f849dbdSJason Wang }
3072f849dbdSJason Wang 
qemu_new_net_control_client(NetClientInfo * info,NetClientState * peer,const char * model,const char * name)3082f849dbdSJason Wang NetClientState *qemu_new_net_control_client(NetClientInfo *info,
3092f849dbdSJason Wang                                             NetClientState *peer,
3102f849dbdSJason Wang                                             const char *model,
3112f849dbdSJason Wang                                             const char *name)
3122f849dbdSJason Wang {
3132f849dbdSJason Wang     NetClientState *nc;
3142f849dbdSJason Wang 
3152f849dbdSJason Wang     assert(info->size >= sizeof(NetClientState));
3162f849dbdSJason Wang 
3172f849dbdSJason Wang     nc = g_malloc0(info->size);
3182f849dbdSJason Wang     qemu_net_client_setup(nc, info, peer, model, name,
3192f849dbdSJason Wang                           qemu_net_client_destructor, false);
32018a1541aSJason Wang 
321fd9400b3SPaolo Bonzini     return nc;
322fd9400b3SPaolo Bonzini }
323fd9400b3SPaolo Bonzini 
qemu_new_nic(NetClientInfo * info,NICConf * conf,const char * model,const char * name,MemReentrancyGuard * reentrancy_guard,void * opaque)324fd9400b3SPaolo Bonzini NICState *qemu_new_nic(NetClientInfo *info,
325fd9400b3SPaolo Bonzini                        NICConf *conf,
326fd9400b3SPaolo Bonzini                        const char *model,
327fd9400b3SPaolo Bonzini                        const char *name,
3287d0fefdfSAkihiko Odaki                        MemReentrancyGuard *reentrancy_guard,
329fd9400b3SPaolo Bonzini                        void *opaque)
330fd9400b3SPaolo Bonzini {
3311ceef9f2SJason Wang     NetClientState **peers = conf->peers.ncs;
332fd9400b3SPaolo Bonzini     NICState *nic;
333575a1c0eSJiri Pirko     int i, queues = MAX(1, conf->peers.queues);
334fd9400b3SPaolo Bonzini 
335f394b2e2SEric Blake     assert(info->type == NET_CLIENT_DRIVER_NIC);
336fd9400b3SPaolo Bonzini     assert(info->size >= sizeof(NICState));
337fd9400b3SPaolo Bonzini 
338f6b26cf2SJason Wang     nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
339f6b26cf2SJason Wang     nic->ncs = (void *)nic + info->size;
340fd9400b3SPaolo Bonzini     nic->conf = conf;
3419050f976SAkihiko Odaki     nic->reentrancy_guard = reentrancy_guard,
342fd9400b3SPaolo Bonzini     nic->opaque = opaque;
343fd9400b3SPaolo Bonzini 
344f6b26cf2SJason Wang     for (i = 0; i < queues; i++) {
345f6b26cf2SJason Wang         qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
3462f849dbdSJason Wang                               NULL, true);
3471ceef9f2SJason Wang         nic->ncs[i].queue_index = i;
3481ceef9f2SJason Wang     }
3491ceef9f2SJason Wang 
350fd9400b3SPaolo Bonzini     return nic;
351fd9400b3SPaolo Bonzini }
352fd9400b3SPaolo Bonzini 
qemu_get_subqueue(NICState * nic,int queue_index)3531ceef9f2SJason Wang NetClientState *qemu_get_subqueue(NICState *nic, int queue_index)
3541ceef9f2SJason Wang {
355f6b26cf2SJason Wang     return nic->ncs + queue_index;
3561ceef9f2SJason Wang }
3571ceef9f2SJason Wang 
qemu_get_queue(NICState * nic)358b356f76dSJason Wang NetClientState *qemu_get_queue(NICState *nic)
359b356f76dSJason Wang {
3601ceef9f2SJason Wang     return qemu_get_subqueue(nic, 0);
361b356f76dSJason Wang }
362b356f76dSJason Wang 
qemu_get_nic(NetClientState * nc)363cc1f0f45SJason Wang NICState *qemu_get_nic(NetClientState *nc)
364cc1f0f45SJason Wang {
3651ceef9f2SJason Wang     NetClientState *nc0 = nc - nc->queue_index;
3661ceef9f2SJason Wang 
367f6b26cf2SJason Wang     return (NICState *)((void *)nc0 - nc->info->size);
368cc1f0f45SJason Wang }
369cc1f0f45SJason Wang 
qemu_get_nic_opaque(NetClientState * nc)370cc1f0f45SJason Wang void *qemu_get_nic_opaque(NetClientState *nc)
371cc1f0f45SJason Wang {
372cc1f0f45SJason Wang     NICState *nic = qemu_get_nic(nc);
373cc1f0f45SJason Wang 
374cc1f0f45SJason Wang     return nic->opaque;
375cc1f0f45SJason Wang }
376cc1f0f45SJason Wang 
qemu_get_peer(NetClientState * nc,int queue_index)3770165daaeSCindy Lu NetClientState *qemu_get_peer(NetClientState *nc, int queue_index)
3780165daaeSCindy Lu {
3790165daaeSCindy Lu     assert(nc != NULL);
3800165daaeSCindy Lu     NetClientState *ncs = nc + queue_index;
3810165daaeSCindy Lu     return ncs->peer;
3820165daaeSCindy Lu }
3830165daaeSCindy Lu 
qemu_cleanup_net_client(NetClientState * nc)384fd9400b3SPaolo Bonzini static void qemu_cleanup_net_client(NetClientState *nc)
385fd9400b3SPaolo Bonzini {
386fd9400b3SPaolo Bonzini     QTAILQ_REMOVE(&net_clients, nc, next);
387fd9400b3SPaolo Bonzini 
388cc2a9043SAndreas Färber     if (nc->info->cleanup) {
389fd9400b3SPaolo Bonzini         nc->info->cleanup(nc);
390fd9400b3SPaolo Bonzini     }
391cc2a9043SAndreas Färber }
392fd9400b3SPaolo Bonzini 
qemu_free_net_client(NetClientState * nc)393fd9400b3SPaolo Bonzini static void qemu_free_net_client(NetClientState *nc)
394fd9400b3SPaolo Bonzini {
395067404beSJan Kiszka     if (nc->incoming_queue) {
396067404beSJan Kiszka         qemu_del_net_queue(nc->incoming_queue);
397fd9400b3SPaolo Bonzini     }
398fd9400b3SPaolo Bonzini     if (nc->peer) {
399fd9400b3SPaolo Bonzini         nc->peer->peer = NULL;
400fd9400b3SPaolo Bonzini     }
401fd9400b3SPaolo Bonzini     g_free(nc->name);
402fd9400b3SPaolo Bonzini     g_free(nc->model);
403f7860455SJason Wang     if (nc->destructor) {
404f7860455SJason Wang         nc->destructor(nc);
405f7860455SJason Wang     }
406fd9400b3SPaolo Bonzini }
407fd9400b3SPaolo Bonzini 
qemu_del_net_client(NetClientState * nc)408fd9400b3SPaolo Bonzini void qemu_del_net_client(NetClientState *nc)
409fd9400b3SPaolo Bonzini {
4101ceef9f2SJason Wang     NetClientState *ncs[MAX_QUEUE_NUM];
4111ceef9f2SJason Wang     int queues, i;
412fdccce45SYang Hongyang     NetFilterState *nf, *next;
4131ceef9f2SJason Wang 
414f394b2e2SEric Blake     assert(nc->info->type != NET_CLIENT_DRIVER_NIC);
4157fb43911SPaolo Bonzini 
4161ceef9f2SJason Wang     /* If the NetClientState belongs to a multiqueue backend, we will change all
4171ceef9f2SJason Wang      * other NetClientStates also.
4181ceef9f2SJason Wang      */
4191ceef9f2SJason Wang     queues = qemu_find_net_clients_except(nc->name, ncs,
420f394b2e2SEric Blake                                           NET_CLIENT_DRIVER_NIC,
4211ceef9f2SJason Wang                                           MAX_QUEUE_NUM);
4221ceef9f2SJason Wang     assert(queues != 0);
4231ceef9f2SJason Wang 
424fdccce45SYang Hongyang     QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) {
425fdccce45SYang Hongyang         object_unparent(OBJECT(nf));
426fdccce45SYang Hongyang     }
427fdccce45SYang Hongyang 
428fd9400b3SPaolo Bonzini     /* If there is a peer NIC, delete and cleanup client, but do not free. */
429f394b2e2SEric Blake     if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
430cc1f0f45SJason Wang         NICState *nic = qemu_get_nic(nc->peer);
431fd9400b3SPaolo Bonzini         if (nic->peer_deleted) {
432fd9400b3SPaolo Bonzini             return;
433fd9400b3SPaolo Bonzini         }
434fd9400b3SPaolo Bonzini         nic->peer_deleted = true;
4351ceef9f2SJason Wang 
4361ceef9f2SJason Wang         for (i = 0; i < queues; i++) {
4371ceef9f2SJason Wang             ncs[i]->peer->link_down = true;
4381ceef9f2SJason Wang         }
4391ceef9f2SJason Wang 
440fd9400b3SPaolo Bonzini         if (nc->peer->info->link_status_changed) {
441fd9400b3SPaolo Bonzini             nc->peer->info->link_status_changed(nc->peer);
442fd9400b3SPaolo Bonzini         }
4431ceef9f2SJason Wang 
4441ceef9f2SJason Wang         for (i = 0; i < queues; i++) {
4451ceef9f2SJason Wang             qemu_cleanup_net_client(ncs[i]);
4461ceef9f2SJason Wang         }
4471ceef9f2SJason Wang 
448fd9400b3SPaolo Bonzini         return;
449fd9400b3SPaolo Bonzini     }
450fd9400b3SPaolo Bonzini 
4511ceef9f2SJason Wang     for (i = 0; i < queues; i++) {
4521ceef9f2SJason Wang         qemu_cleanup_net_client(ncs[i]);
4531ceef9f2SJason Wang         qemu_free_net_client(ncs[i]);
4541ceef9f2SJason Wang     }
455948ecf21SJason Wang }
456948ecf21SJason Wang 
qemu_del_nic(NICState * nic)457948ecf21SJason Wang void qemu_del_nic(NICState *nic)
458948ecf21SJason Wang {
459575a1c0eSJiri Pirko     int i, queues = MAX(nic->conf->peers.queues, 1);
4601ceef9f2SJason Wang 
4612bc22a58SShannon Zhao     qemu_macaddr_set_free(&nic->conf->macaddr);
4622bc22a58SShannon Zhao 
463d2abc563SYuri Benditovich     for (i = 0; i < queues; i++) {
464d2abc563SYuri Benditovich         NetClientState *nc = qemu_get_subqueue(nic, i);
465fd9400b3SPaolo Bonzini         /* If this is a peer NIC and peer has already been deleted, free it now. */
466fd9400b3SPaolo Bonzini         if (nic->peer_deleted) {
467d2abc563SYuri Benditovich             qemu_free_net_client(nc->peer);
468d2abc563SYuri Benditovich         } else if (nc->peer) {
469d2abc563SYuri Benditovich             /* if there are RX packets pending, complete them */
470d2abc563SYuri Benditovich             qemu_purge_queued_packets(nc->peer);
471fd9400b3SPaolo Bonzini         }
472fd9400b3SPaolo Bonzini     }
473fd9400b3SPaolo Bonzini 
4741ceef9f2SJason Wang     for (i = queues - 1; i >= 0; i--) {
4751ceef9f2SJason Wang         NetClientState *nc = qemu_get_subqueue(nic, i);
4761ceef9f2SJason Wang 
477fd9400b3SPaolo Bonzini         qemu_cleanup_net_client(nc);
478fd9400b3SPaolo Bonzini         qemu_free_net_client(nc);
479fd9400b3SPaolo Bonzini     }
480f6b26cf2SJason Wang 
481f6b26cf2SJason Wang     g_free(nic);
4821ceef9f2SJason Wang }
483fd9400b3SPaolo Bonzini 
qemu_foreach_nic(qemu_nic_foreach func,void * opaque)484fd9400b3SPaolo Bonzini void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
485fd9400b3SPaolo Bonzini {
486fd9400b3SPaolo Bonzini     NetClientState *nc;
487fd9400b3SPaolo Bonzini 
488fd9400b3SPaolo Bonzini     QTAILQ_FOREACH(nc, &net_clients, next) {
489f394b2e2SEric Blake         if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
4901ceef9f2SJason Wang             if (nc->queue_index == 0) {
491cc1f0f45SJason Wang                 func(qemu_get_nic(nc), opaque);
492fd9400b3SPaolo Bonzini             }
493fd9400b3SPaolo Bonzini         }
494fd9400b3SPaolo Bonzini     }
4951ceef9f2SJason Wang }
496fd9400b3SPaolo Bonzini 
qemu_has_ufo(NetClientState * nc)497d6085e3aSStefan Hajnoczi bool qemu_has_ufo(NetClientState *nc)
4981f55ac45SVincenzo Maffione {
499d6085e3aSStefan Hajnoczi     if (!nc || !nc->info->has_ufo) {
5001f55ac45SVincenzo Maffione         return false;
5011f55ac45SVincenzo Maffione     }
5021f55ac45SVincenzo Maffione 
503d6085e3aSStefan Hajnoczi     return nc->info->has_ufo(nc);
5041f55ac45SVincenzo Maffione }
5051f55ac45SVincenzo Maffione 
qemu_has_uso(NetClientState * nc)506f03e0cf6SYuri Benditovich bool qemu_has_uso(NetClientState *nc)
507f03e0cf6SYuri Benditovich {
508f03e0cf6SYuri Benditovich     if (!nc || !nc->info->has_uso) {
509f03e0cf6SYuri Benditovich         return false;
510f03e0cf6SYuri Benditovich     }
511f03e0cf6SYuri Benditovich 
512f03e0cf6SYuri Benditovich     return nc->info->has_uso(nc);
513f03e0cf6SYuri Benditovich }
514f03e0cf6SYuri Benditovich 
qemu_has_vnet_hdr(NetClientState * nc)515d6085e3aSStefan Hajnoczi bool qemu_has_vnet_hdr(NetClientState *nc)
5161f55ac45SVincenzo Maffione {
517d6085e3aSStefan Hajnoczi     if (!nc || !nc->info->has_vnet_hdr) {
5181f55ac45SVincenzo Maffione         return false;
5191f55ac45SVincenzo Maffione     }
5201f55ac45SVincenzo Maffione 
521d6085e3aSStefan Hajnoczi     return nc->info->has_vnet_hdr(nc);
5221f55ac45SVincenzo Maffione }
5231f55ac45SVincenzo Maffione 
qemu_has_vnet_hdr_len(NetClientState * nc,int len)524d6085e3aSStefan Hajnoczi bool qemu_has_vnet_hdr_len(NetClientState *nc, int len)
5251f55ac45SVincenzo Maffione {
526d6085e3aSStefan Hajnoczi     if (!nc || !nc->info->has_vnet_hdr_len) {
5271f55ac45SVincenzo Maffione         return false;
5281f55ac45SVincenzo Maffione     }
5291f55ac45SVincenzo Maffione 
530d6085e3aSStefan Hajnoczi     return nc->info->has_vnet_hdr_len(nc, len);
5311f55ac45SVincenzo Maffione }
5321f55ac45SVincenzo Maffione 
qemu_set_offload(NetClientState * nc,int csum,int tso4,int tso6,int ecn,int ufo,int uso4,int uso6)533d6085e3aSStefan Hajnoczi void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
5342ab0ec31SAndrew Melnychenko                           int ecn, int ufo, int uso4, int uso6)
5351f55ac45SVincenzo Maffione {
536d6085e3aSStefan Hajnoczi     if (!nc || !nc->info->set_offload) {
5371f55ac45SVincenzo Maffione         return;
5381f55ac45SVincenzo Maffione     }
5391f55ac45SVincenzo Maffione 
5402ab0ec31SAndrew Melnychenko     nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo, uso4, uso6);
5411f55ac45SVincenzo Maffione }
5421f55ac45SVincenzo Maffione 
qemu_get_vnet_hdr_len(NetClientState * nc)543481c5232SAkihiko Odaki int qemu_get_vnet_hdr_len(NetClientState *nc)
544481c5232SAkihiko Odaki {
5454b52d632SAkihiko Odaki     return nc->vnet_hdr_len;
546481c5232SAkihiko Odaki }
547481c5232SAkihiko Odaki 
qemu_set_vnet_hdr_len(NetClientState * nc,int len)548d6085e3aSStefan Hajnoczi void qemu_set_vnet_hdr_len(NetClientState *nc, int len)
5491f55ac45SVincenzo Maffione {
550d6085e3aSStefan Hajnoczi     if (!nc || !nc->info->set_vnet_hdr_len) {
5511f55ac45SVincenzo Maffione         return;
5521f55ac45SVincenzo Maffione     }
5531f55ac45SVincenzo Maffione 
554a6775371SAkihiko Odaki     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
555a6775371SAkihiko Odaki            len == sizeof(struct virtio_net_hdr) ||
556a6775371SAkihiko Odaki            len == sizeof(struct virtio_net_hdr_v1_hash));
557a6775371SAkihiko Odaki 
558d6b732e9SZhang Chen     nc->vnet_hdr_len = len;
559d6085e3aSStefan Hajnoczi     nc->info->set_vnet_hdr_len(nc, len);
5601f55ac45SVincenzo Maffione }
5611f55ac45SVincenzo Maffione 
qemu_set_vnet_le(NetClientState * nc,bool is_le)562c80cd6bbSGreg Kurz int qemu_set_vnet_le(NetClientState *nc, bool is_le)
563c80cd6bbSGreg Kurz {
564e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
565c80cd6bbSGreg Kurz     if (!nc || !nc->info->set_vnet_le) {
566c80cd6bbSGreg Kurz         return -ENOSYS;
567c80cd6bbSGreg Kurz     }
568c80cd6bbSGreg Kurz 
569c80cd6bbSGreg Kurz     return nc->info->set_vnet_le(nc, is_le);
570052bd52fSMichael S. Tsirkin #else
571052bd52fSMichael S. Tsirkin     return 0;
572052bd52fSMichael S. Tsirkin #endif
573c80cd6bbSGreg Kurz }
574c80cd6bbSGreg Kurz 
qemu_set_vnet_be(NetClientState * nc,bool is_be)575c80cd6bbSGreg Kurz int qemu_set_vnet_be(NetClientState *nc, bool is_be)
576c80cd6bbSGreg Kurz {
577e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
578052bd52fSMichael S. Tsirkin     return 0;
579052bd52fSMichael S. Tsirkin #else
580c80cd6bbSGreg Kurz     if (!nc || !nc->info->set_vnet_be) {
581c80cd6bbSGreg Kurz         return -ENOSYS;
582c80cd6bbSGreg Kurz     }
583c80cd6bbSGreg Kurz 
584c80cd6bbSGreg Kurz     return nc->info->set_vnet_be(nc, is_be);
585052bd52fSMichael S. Tsirkin #endif
586c80cd6bbSGreg Kurz }
587c80cd6bbSGreg Kurz 
qemu_can_receive_packet(NetClientState * nc)588705df546SJason Wang int qemu_can_receive_packet(NetClientState *nc)
589705df546SJason Wang {
590705df546SJason Wang     if (nc->receive_disabled) {
591705df546SJason Wang         return 0;
592705df546SJason Wang     } else if (nc->info->can_receive &&
593705df546SJason Wang                !nc->info->can_receive(nc)) {
594705df546SJason Wang         return 0;
595705df546SJason Wang     }
596705df546SJason Wang     return 1;
597705df546SJason Wang }
598705df546SJason Wang 
qemu_can_send_packet(NetClientState * sender)599fd9400b3SPaolo Bonzini int qemu_can_send_packet(NetClientState *sender)
600fd9400b3SPaolo Bonzini {
601e1d64c08Szhanghailiang     int vm_running = runstate_is_running();
602e1d64c08Szhanghailiang 
603e1d64c08Szhanghailiang     if (!vm_running) {
604e1d64c08Szhanghailiang         return 0;
605e1d64c08Szhanghailiang     }
606e1d64c08Szhanghailiang 
607fd9400b3SPaolo Bonzini     if (!sender->peer) {
608fd9400b3SPaolo Bonzini         return 1;
609fd9400b3SPaolo Bonzini     }
610fd9400b3SPaolo Bonzini 
611705df546SJason Wang     return qemu_can_receive_packet(sender->peer);
612fd9400b3SPaolo Bonzini }
613fd9400b3SPaolo Bonzini 
filter_receive_iov(NetClientState * nc,NetFilterDirection direction,NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt,NetPacketSent * sent_cb)614e64c770dSYang Hongyang static ssize_t filter_receive_iov(NetClientState *nc,
615e64c770dSYang Hongyang                                   NetFilterDirection direction,
616e64c770dSYang Hongyang                                   NetClientState *sender,
617e64c770dSYang Hongyang                                   unsigned flags,
618e64c770dSYang Hongyang                                   const struct iovec *iov,
619e64c770dSYang Hongyang                                   int iovcnt,
620e64c770dSYang Hongyang                                   NetPacketSent *sent_cb)
621e64c770dSYang Hongyang {
622e64c770dSYang Hongyang     ssize_t ret = 0;
623e64c770dSYang Hongyang     NetFilterState *nf = NULL;
624e64c770dSYang Hongyang 
62525aaadf0SLi Zhijian     if (direction == NET_FILTER_DIRECTION_TX) {
626e64c770dSYang Hongyang         QTAILQ_FOREACH(nf, &nc->filters, next) {
627e64c770dSYang Hongyang             ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
628e64c770dSYang Hongyang                                          iovcnt, sent_cb);
629e64c770dSYang Hongyang             if (ret) {
630e64c770dSYang Hongyang                 return ret;
631e64c770dSYang Hongyang             }
632e64c770dSYang Hongyang         }
63325aaadf0SLi Zhijian     } else {
634eae3eb3eSPaolo Bonzini         QTAILQ_FOREACH_REVERSE(nf, &nc->filters, next) {
63525aaadf0SLi Zhijian             ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
63625aaadf0SLi Zhijian                                          iovcnt, sent_cb);
63725aaadf0SLi Zhijian             if (ret) {
63825aaadf0SLi Zhijian                 return ret;
63925aaadf0SLi Zhijian             }
64025aaadf0SLi Zhijian         }
64125aaadf0SLi Zhijian     }
642e64c770dSYang Hongyang 
643e64c770dSYang Hongyang     return ret;
644e64c770dSYang Hongyang }
645e64c770dSYang Hongyang 
filter_receive(NetClientState * nc,NetFilterDirection direction,NetClientState * sender,unsigned flags,const uint8_t * data,size_t size,NetPacketSent * sent_cb)646e64c770dSYang Hongyang static ssize_t filter_receive(NetClientState *nc,
647e64c770dSYang Hongyang                               NetFilterDirection direction,
648e64c770dSYang Hongyang                               NetClientState *sender,
649e64c770dSYang Hongyang                               unsigned flags,
650e64c770dSYang Hongyang                               const uint8_t *data,
651e64c770dSYang Hongyang                               size_t size,
652e64c770dSYang Hongyang                               NetPacketSent *sent_cb)
653e64c770dSYang Hongyang {
654e64c770dSYang Hongyang     struct iovec iov = {
655e64c770dSYang Hongyang         .iov_base = (void *)data,
656e64c770dSYang Hongyang         .iov_len = size
657e64c770dSYang Hongyang     };
658e64c770dSYang Hongyang 
659e64c770dSYang Hongyang     return filter_receive_iov(nc, direction, sender, flags, &iov, 1, sent_cb);
660e64c770dSYang Hongyang }
661e64c770dSYang Hongyang 
qemu_purge_queued_packets(NetClientState * nc)662fd9400b3SPaolo Bonzini void qemu_purge_queued_packets(NetClientState *nc)
663fd9400b3SPaolo Bonzini {
664fd9400b3SPaolo Bonzini     if (!nc->peer) {
665fd9400b3SPaolo Bonzini         return;
666fd9400b3SPaolo Bonzini     }
667fd9400b3SPaolo Bonzini 
668067404beSJan Kiszka     qemu_net_queue_purge(nc->peer->incoming_queue, nc);
669fd9400b3SPaolo Bonzini }
670fd9400b3SPaolo Bonzini 
qemu_flush_or_purge_queued_packets(NetClientState * nc,bool purge)671ca77d85eSMichael S. Tsirkin void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
672fd9400b3SPaolo Bonzini {
673fd9400b3SPaolo Bonzini     nc->receive_disabled = 0;
674fd9400b3SPaolo Bonzini 
675f394b2e2SEric Blake     if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_HUBPORT) {
676199ee608SLuigi Rizzo         if (net_hub_flush(nc->peer)) {
677199ee608SLuigi Rizzo             qemu_notify_event();
678199ee608SLuigi Rizzo         }
679199ee608SLuigi Rizzo     }
680067404beSJan Kiszka     if (qemu_net_queue_flush(nc->incoming_queue)) {
681fd9400b3SPaolo Bonzini         /* We emptied the queue successfully, signal to the IO thread to repoll
682fd9400b3SPaolo Bonzini          * the file descriptor (for tap, for example).
683fd9400b3SPaolo Bonzini          */
684fd9400b3SPaolo Bonzini         qemu_notify_event();
685ca77d85eSMichael S. Tsirkin     } else if (purge) {
686ca77d85eSMichael S. Tsirkin         /* Unable to empty the queue, purge remaining packets */
6875fe19fb8SJason Wang         qemu_net_queue_purge(nc->incoming_queue, nc->peer);
688fd9400b3SPaolo Bonzini     }
689fd9400b3SPaolo Bonzini }
690fd9400b3SPaolo Bonzini 
qemu_flush_queued_packets(NetClientState * nc)691ca77d85eSMichael S. Tsirkin void qemu_flush_queued_packets(NetClientState *nc)
692ca77d85eSMichael S. Tsirkin {
693ca77d85eSMichael S. Tsirkin     qemu_flush_or_purge_queued_packets(nc, false);
694ca77d85eSMichael S. Tsirkin }
695ca77d85eSMichael S. Tsirkin 
qemu_send_packet_async_with_flags(NetClientState * sender,unsigned flags,const uint8_t * buf,int size,NetPacketSent * sent_cb)696fd9400b3SPaolo Bonzini static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
697fd9400b3SPaolo Bonzini                                                  unsigned flags,
698fd9400b3SPaolo Bonzini                                                  const uint8_t *buf, int size,
699fd9400b3SPaolo Bonzini                                                  NetPacketSent *sent_cb)
700fd9400b3SPaolo Bonzini {
701fd9400b3SPaolo Bonzini     NetQueue *queue;
702e64c770dSYang Hongyang     int ret;
703fd9400b3SPaolo Bonzini 
704fd9400b3SPaolo Bonzini #ifdef DEBUG_NET
705fd9400b3SPaolo Bonzini     printf("qemu_send_packet_async:\n");
706b42581f5SPhilippe Mathieu-Daudé     qemu_hexdump(stdout, "net", buf, size);
707fd9400b3SPaolo Bonzini #endif
708fd9400b3SPaolo Bonzini 
709fd9400b3SPaolo Bonzini     if (sender->link_down || !sender->peer) {
710fd9400b3SPaolo Bonzini         return size;
711fd9400b3SPaolo Bonzini     }
712fd9400b3SPaolo Bonzini 
713e64c770dSYang Hongyang     /* Let filters handle the packet first */
714e64c770dSYang Hongyang     ret = filter_receive(sender, NET_FILTER_DIRECTION_TX,
715e64c770dSYang Hongyang                          sender, flags, buf, size, sent_cb);
716e64c770dSYang Hongyang     if (ret) {
717e64c770dSYang Hongyang         return ret;
718e64c770dSYang Hongyang     }
719e64c770dSYang Hongyang 
720e64c770dSYang Hongyang     ret = filter_receive(sender->peer, NET_FILTER_DIRECTION_RX,
721e64c770dSYang Hongyang                          sender, flags, buf, size, sent_cb);
722e64c770dSYang Hongyang     if (ret) {
723e64c770dSYang Hongyang         return ret;
724e64c770dSYang Hongyang     }
725e64c770dSYang Hongyang 
726067404beSJan Kiszka     queue = sender->peer->incoming_queue;
727fd9400b3SPaolo Bonzini 
728fd9400b3SPaolo Bonzini     return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
729fd9400b3SPaolo Bonzini }
730fd9400b3SPaolo Bonzini 
qemu_send_packet_async(NetClientState * sender,const uint8_t * buf,int size,NetPacketSent * sent_cb)731fd9400b3SPaolo Bonzini ssize_t qemu_send_packet_async(NetClientState *sender,
732fd9400b3SPaolo Bonzini                                const uint8_t *buf, int size,
733fd9400b3SPaolo Bonzini                                NetPacketSent *sent_cb)
734fd9400b3SPaolo Bonzini {
735fd9400b3SPaolo Bonzini     return qemu_send_packet_async_with_flags(sender, QEMU_NET_PACKET_FLAG_NONE,
736fd9400b3SPaolo Bonzini                                              buf, size, sent_cb);
737fd9400b3SPaolo Bonzini }
738fd9400b3SPaolo Bonzini 
qemu_send_packet(NetClientState * nc,const uint8_t * buf,int size)739625a526bSMarc-André Lureau ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size)
740fd9400b3SPaolo Bonzini {
741625a526bSMarc-André Lureau     return qemu_send_packet_async(nc, buf, size, NULL);
742fd9400b3SPaolo Bonzini }
743fd9400b3SPaolo Bonzini 
qemu_receive_packet(NetClientState * nc,const uint8_t * buf,int size)744705df546SJason Wang ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size)
745705df546SJason Wang {
746705df546SJason Wang     if (!qemu_can_receive_packet(nc)) {
747705df546SJason Wang         return 0;
748705df546SJason Wang     }
749705df546SJason Wang 
750705df546SJason Wang     return qemu_net_queue_receive(nc->incoming_queue, buf, size);
751705df546SJason Wang }
752705df546SJason Wang 
qemu_receive_packet_iov(NetClientState * nc,const struct iovec * iov,int iovcnt)753705df546SJason Wang ssize_t qemu_receive_packet_iov(NetClientState *nc, const struct iovec *iov,
754705df546SJason Wang                                 int iovcnt)
755705df546SJason Wang {
756705df546SJason Wang     if (!qemu_can_receive_packet(nc)) {
757705df546SJason Wang         return 0;
758705df546SJason Wang     }
759705df546SJason Wang 
760705df546SJason Wang     return qemu_net_queue_receive_iov(nc->incoming_queue, iov, iovcnt);
761705df546SJason Wang }
762705df546SJason Wang 
qemu_send_packet_raw(NetClientState * nc,const uint8_t * buf,int size)763fd9400b3SPaolo Bonzini ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
764fd9400b3SPaolo Bonzini {
765fd9400b3SPaolo Bonzini     return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW,
766fd9400b3SPaolo Bonzini                                              buf, size, NULL);
767fd9400b3SPaolo Bonzini }
768fd9400b3SPaolo Bonzini 
nc_sendv_compat(NetClientState * nc,const struct iovec * iov,int iovcnt,unsigned flags)769fd9400b3SPaolo Bonzini static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
770fefe2a78SYang Hongyang                                int iovcnt, unsigned flags)
771fd9400b3SPaolo Bonzini {
77274044c8fSPooja Dhannawat     uint8_t *buf = NULL;
773fefe2a78SYang Hongyang     uint8_t *buffer;
774fd9400b3SPaolo Bonzini     size_t offset;
77574044c8fSPooja Dhannawat     ssize_t ret;
776fd9400b3SPaolo Bonzini 
777fefe2a78SYang Hongyang     if (iovcnt == 1) {
778fefe2a78SYang Hongyang         buffer = iov[0].iov_base;
779fefe2a78SYang Hongyang         offset = iov[0].iov_len;
780fefe2a78SYang Hongyang     } else {
78147f9f158SPeter Lieven         offset = iov_size(iov, iovcnt);
78247f9f158SPeter Lieven         if (offset > NET_BUFSIZE) {
78347f9f158SPeter Lieven             return -1;
78447f9f158SPeter Lieven         }
78547f9f158SPeter Lieven         buf = g_malloc(offset);
786fefe2a78SYang Hongyang         buffer = buf;
78747f9f158SPeter Lieven         offset = iov_to_buf(iov, iovcnt, 0, buf, offset);
788fefe2a78SYang Hongyang     }
789fd9400b3SPaolo Bonzini 
79074044c8fSPooja Dhannawat     ret = nc->info->receive(nc, buffer, offset);
79174044c8fSPooja Dhannawat 
79274044c8fSPooja Dhannawat     g_free(buf);
79374044c8fSPooja Dhannawat     return ret;
794fefe2a78SYang Hongyang }
795fd9400b3SPaolo Bonzini 
qemu_deliver_packet_iov(NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt,void * opaque)79625c01bd1SJason Wang static ssize_t qemu_deliver_packet_iov(NetClientState *sender,
797fd9400b3SPaolo Bonzini                                        unsigned flags,
798fd9400b3SPaolo Bonzini                                        const struct iovec *iov,
799fd9400b3SPaolo Bonzini                                        int iovcnt,
800fd9400b3SPaolo Bonzini                                        void *opaque)
801fd9400b3SPaolo Bonzini {
8029050f976SAkihiko Odaki     MemReentrancyGuard *owned_reentrancy_guard;
803fd9400b3SPaolo Bonzini     NetClientState *nc = opaque;
804fd9400b3SPaolo Bonzini     int ret;
805b9ad513eSAkihiko Odaki     struct virtio_net_hdr_v1_hash vnet_hdr = { };
806b9ad513eSAkihiko Odaki     g_autofree struct iovec *iov_copy = NULL;
807fd9400b3SPaolo Bonzini 
8081592a994SJason Wang 
809fd9400b3SPaolo Bonzini     if (nc->link_down) {
81025c01bd1SJason Wang         return iov_size(iov, iovcnt);
811fd9400b3SPaolo Bonzini     }
812fd9400b3SPaolo Bonzini 
813fd9400b3SPaolo Bonzini     if (nc->receive_disabled) {
814fd9400b3SPaolo Bonzini         return 0;
815fd9400b3SPaolo Bonzini     }
816fd9400b3SPaolo Bonzini 
8179050f976SAkihiko Odaki     if (nc->info->type != NET_CLIENT_DRIVER_NIC ||
8189050f976SAkihiko Odaki         qemu_get_nic(nc)->reentrancy_guard->engaged_in_io) {
8199050f976SAkihiko Odaki         owned_reentrancy_guard = NULL;
8209050f976SAkihiko Odaki     } else {
8219050f976SAkihiko Odaki         owned_reentrancy_guard = qemu_get_nic(nc)->reentrancy_guard;
8229050f976SAkihiko Odaki         owned_reentrancy_guard->engaged_in_io = true;
8239050f976SAkihiko Odaki     }
8249050f976SAkihiko Odaki 
825b9ad513eSAkihiko Odaki     if ((flags & QEMU_NET_PACKET_FLAG_RAW) && nc->vnet_hdr_len) {
826b9ad513eSAkihiko Odaki         iov_copy = g_new(struct iovec, iovcnt + 1);
827b9ad513eSAkihiko Odaki         iov_copy[0].iov_base = &vnet_hdr;
828b9ad513eSAkihiko Odaki         iov_copy[0].iov_len =  nc->vnet_hdr_len;
829b9ad513eSAkihiko Odaki         memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
830b9ad513eSAkihiko Odaki         iov = iov_copy;
831b9ad513eSAkihiko Odaki     }
832b9ad513eSAkihiko Odaki 
833b9ad513eSAkihiko Odaki     if (nc->info->receive_iov) {
834fd9400b3SPaolo Bonzini         ret = nc->info->receive_iov(nc, iov, iovcnt);
835fd9400b3SPaolo Bonzini     } else {
836fefe2a78SYang Hongyang         ret = nc_sendv_compat(nc, iov, iovcnt, flags);
837fd9400b3SPaolo Bonzini     }
838fd9400b3SPaolo Bonzini 
8399050f976SAkihiko Odaki     if (owned_reentrancy_guard) {
8409050f976SAkihiko Odaki         owned_reentrancy_guard->engaged_in_io = false;
8419050f976SAkihiko Odaki     }
8429050f976SAkihiko Odaki 
843fd9400b3SPaolo Bonzini     if (ret == 0) {
844fd9400b3SPaolo Bonzini         nc->receive_disabled = 1;
845fd9400b3SPaolo Bonzini     }
846fd9400b3SPaolo Bonzini 
847fd9400b3SPaolo Bonzini     return ret;
848fd9400b3SPaolo Bonzini }
849fd9400b3SPaolo Bonzini 
qemu_sendv_packet_async(NetClientState * sender,const struct iovec * iov,int iovcnt,NetPacketSent * sent_cb)850fd9400b3SPaolo Bonzini ssize_t qemu_sendv_packet_async(NetClientState *sender,
851fd9400b3SPaolo Bonzini                                 const struct iovec *iov, int iovcnt,
852fd9400b3SPaolo Bonzini                                 NetPacketSent *sent_cb)
853fd9400b3SPaolo Bonzini {
854fd9400b3SPaolo Bonzini     NetQueue *queue;
85525c01bd1SJason Wang     size_t size = iov_size(iov, iovcnt);
856e64c770dSYang Hongyang     int ret;
857fd9400b3SPaolo Bonzini 
85825c01bd1SJason Wang     if (size > NET_BUFSIZE) {
85925c01bd1SJason Wang         return size;
86025c01bd1SJason Wang     }
86125c01bd1SJason Wang 
862fd9400b3SPaolo Bonzini     if (sender->link_down || !sender->peer) {
86325c01bd1SJason Wang         return size;
864fd9400b3SPaolo Bonzini     }
865fd9400b3SPaolo Bonzini 
866e64c770dSYang Hongyang     /* Let filters handle the packet first */
867e64c770dSYang Hongyang     ret = filter_receive_iov(sender, NET_FILTER_DIRECTION_TX, sender,
868e64c770dSYang Hongyang                              QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
869e64c770dSYang Hongyang     if (ret) {
870e64c770dSYang Hongyang         return ret;
871e64c770dSYang Hongyang     }
872e64c770dSYang Hongyang 
873e64c770dSYang Hongyang     ret = filter_receive_iov(sender->peer, NET_FILTER_DIRECTION_RX, sender,
874e64c770dSYang Hongyang                              QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
875e64c770dSYang Hongyang     if (ret) {
876e64c770dSYang Hongyang         return ret;
877e64c770dSYang Hongyang     }
878e64c770dSYang Hongyang 
879067404beSJan Kiszka     queue = sender->peer->incoming_queue;
880fd9400b3SPaolo Bonzini 
881fd9400b3SPaolo Bonzini     return qemu_net_queue_send_iov(queue, sender,
882fd9400b3SPaolo Bonzini                                    QEMU_NET_PACKET_FLAG_NONE,
883fd9400b3SPaolo Bonzini                                    iov, iovcnt, sent_cb);
884fd9400b3SPaolo Bonzini }
885fd9400b3SPaolo Bonzini 
886fd9400b3SPaolo Bonzini ssize_t
qemu_sendv_packet(NetClientState * nc,const struct iovec * iov,int iovcnt)887fd9400b3SPaolo Bonzini qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt)
888fd9400b3SPaolo Bonzini {
889fd9400b3SPaolo Bonzini     return qemu_sendv_packet_async(nc, iov, iovcnt, NULL);
890fd9400b3SPaolo Bonzini }
891fd9400b3SPaolo Bonzini 
qemu_find_netdev(const char * id)892fd9400b3SPaolo Bonzini NetClientState *qemu_find_netdev(const char *id)
893fd9400b3SPaolo Bonzini {
894fd9400b3SPaolo Bonzini     NetClientState *nc;
895fd9400b3SPaolo Bonzini 
896fd9400b3SPaolo Bonzini     QTAILQ_FOREACH(nc, &net_clients, next) {
897f394b2e2SEric Blake         if (nc->info->type == NET_CLIENT_DRIVER_NIC)
898fd9400b3SPaolo Bonzini             continue;
899fd9400b3SPaolo Bonzini         if (!strcmp(nc->name, id)) {
900fd9400b3SPaolo Bonzini             return nc;
901fd9400b3SPaolo Bonzini         }
902fd9400b3SPaolo Bonzini     }
903fd9400b3SPaolo Bonzini 
904fd9400b3SPaolo Bonzini     return NULL;
905fd9400b3SPaolo Bonzini }
906fd9400b3SPaolo Bonzini 
qemu_find_net_clients_except(const char * id,NetClientState ** ncs,NetClientDriver type,int max)9076c51ae73SJason Wang int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
908f394b2e2SEric Blake                                  NetClientDriver type, int max)
9096c51ae73SJason Wang {
9106c51ae73SJason Wang     NetClientState *nc;
9116c51ae73SJason Wang     int ret = 0;
9126c51ae73SJason Wang 
9136c51ae73SJason Wang     QTAILQ_FOREACH(nc, &net_clients, next) {
9146c51ae73SJason Wang         if (nc->info->type == type) {
9156c51ae73SJason Wang             continue;
9166c51ae73SJason Wang         }
91740d19394SHani Benhabiles         if (!id || !strcmp(nc->name, id)) {
9186c51ae73SJason Wang             if (ret < max) {
9196c51ae73SJason Wang                 ncs[ret] = nc;
9206c51ae73SJason Wang             }
9216c51ae73SJason Wang             ret++;
9226c51ae73SJason Wang         }
9236c51ae73SJason Wang     }
9246c51ae73SJason Wang 
9256c51ae73SJason Wang     return ret;
9266c51ae73SJason Wang }
9276c51ae73SJason Wang 
nic_get_free_idx(void)928fd9400b3SPaolo Bonzini static int nic_get_free_idx(void)
929fd9400b3SPaolo Bonzini {
930fd9400b3SPaolo Bonzini     int index;
931fd9400b3SPaolo Bonzini 
932fd9400b3SPaolo Bonzini     for (index = 0; index < MAX_NICS; index++)
933fd9400b3SPaolo Bonzini         if (!nd_table[index].used)
934fd9400b3SPaolo Bonzini             return index;
935fd9400b3SPaolo Bonzini     return -1;
936fd9400b3SPaolo Bonzini }
937fd9400b3SPaolo Bonzini 
qemu_get_nic_models(const char * device_type)938c6941b3bSThomas Huth GPtrArray *qemu_get_nic_models(const char *device_type)
939c6941b3bSThomas Huth {
940c6941b3bSThomas Huth     GPtrArray *nic_models = g_ptr_array_new();
941c6941b3bSThomas Huth     GSList *list = object_class_get_list_sorted(device_type, false);
942c6941b3bSThomas Huth 
943c6941b3bSThomas Huth     while (list) {
944c6941b3bSThomas Huth         DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data,
945c6941b3bSThomas Huth                                              TYPE_DEVICE);
946c6941b3bSThomas Huth         GSList *next;
947c6941b3bSThomas Huth         if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) &&
948c6941b3bSThomas Huth             dc->user_creatable) {
949c6941b3bSThomas Huth             const char *name = object_class_get_name(list->data);
950c6941b3bSThomas Huth             /*
951c6941b3bSThomas Huth              * A network device might also be something else than a NIC, see
952c6941b3bSThomas Huth              * e.g. the "rocker" device. Thus we have to look for the "netdev"
953c6941b3bSThomas Huth              * property, too. Unfortunately, some devices like virtio-net only
954c6941b3bSThomas Huth              * create this property during instance_init, so we have to create
955c6941b3bSThomas Huth              * a temporary instance here to be able to check it.
956c6941b3bSThomas Huth              */
957c6941b3bSThomas Huth             Object *obj = object_new_with_class(OBJECT_CLASS(dc));
958c6941b3bSThomas Huth             if (object_property_find(obj, "netdev")) {
959c6941b3bSThomas Huth                 g_ptr_array_add(nic_models, (gpointer)name);
960c6941b3bSThomas Huth             }
961c6941b3bSThomas Huth             object_unref(obj);
962c6941b3bSThomas Huth         }
963c6941b3bSThomas Huth         next = list->next;
964c6941b3bSThomas Huth         g_slist_free_1(list);
965c6941b3bSThomas Huth         list = next;
966c6941b3bSThomas Huth     }
967c6941b3bSThomas Huth     g_ptr_array_add(nic_models, NULL);
968c6941b3bSThomas Huth 
969c6941b3bSThomas Huth     return nic_models;
970c6941b3bSThomas Huth }
971c6941b3bSThomas Huth 
net_init_nic(const Netdev * netdev,const char * name,NetClientState * peer,Error ** errp)972cebea510SKővágó, Zoltán static int net_init_nic(const Netdev *netdev, const char *name,
973a30ecde6SMarkus Armbruster                         NetClientState *peer, Error **errp)
974fd9400b3SPaolo Bonzini {
975fd9400b3SPaolo Bonzini     int idx;
976fd9400b3SPaolo Bonzini     NICInfo *nd;
977fd9400b3SPaolo Bonzini     const NetLegacyNicOptions *nic;
978fd9400b3SPaolo Bonzini 
979f394b2e2SEric Blake     assert(netdev->type == NET_CLIENT_DRIVER_NIC);
980f394b2e2SEric Blake     nic = &netdev->u.nic;
981fd9400b3SPaolo Bonzini 
982fd9400b3SPaolo Bonzini     idx = nic_get_free_idx();
983fd9400b3SPaolo Bonzini     if (idx == -1 || nb_nics >= MAX_NICS) {
98466308868SMarkus Armbruster         error_setg(errp, "too many NICs");
985fd9400b3SPaolo Bonzini         return -1;
986fd9400b3SPaolo Bonzini     }
987fd9400b3SPaolo Bonzini 
988fd9400b3SPaolo Bonzini     nd = &nd_table[idx];
989fd9400b3SPaolo Bonzini 
990fd9400b3SPaolo Bonzini     memset(nd, 0, sizeof(*nd));
991fd9400b3SPaolo Bonzini 
9927480874aSMarkus Armbruster     if (nic->netdev) {
993fd9400b3SPaolo Bonzini         nd->netdev = qemu_find_netdev(nic->netdev);
994fd9400b3SPaolo Bonzini         if (!nd->netdev) {
99566308868SMarkus Armbruster             error_setg(errp, "netdev '%s' not found", nic->netdev);
996fd9400b3SPaolo Bonzini             return -1;
997fd9400b3SPaolo Bonzini         }
998fd9400b3SPaolo Bonzini     } else {
999fd9400b3SPaolo Bonzini         assert(peer);
1000fd9400b3SPaolo Bonzini         nd->netdev = peer;
1001fd9400b3SPaolo Bonzini     }
1002fd9400b3SPaolo Bonzini     nd->name = g_strdup(name);
10037480874aSMarkus Armbruster     if (nic->model) {
1004fd9400b3SPaolo Bonzini         nd->model = g_strdup(nic->model);
1005fd9400b3SPaolo Bonzini     }
10067480874aSMarkus Armbruster     if (nic->addr) {
1007fd9400b3SPaolo Bonzini         nd->devaddr = g_strdup(nic->addr);
1008fd9400b3SPaolo Bonzini     }
1009fd9400b3SPaolo Bonzini 
10107480874aSMarkus Armbruster     if (nic->macaddr &&
1011fd9400b3SPaolo Bonzini         net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) {
101266308868SMarkus Armbruster         error_setg(errp, "invalid syntax for ethernet address");
1013fd9400b3SPaolo Bonzini         return -1;
1014fd9400b3SPaolo Bonzini     }
10157480874aSMarkus Armbruster     if (nic->macaddr &&
1016d60b20cfSDmitry Krivenok         is_multicast_ether_addr(nd->macaddr.a)) {
101766308868SMarkus Armbruster         error_setg(errp,
101866308868SMarkus Armbruster                    "NIC cannot have multicast MAC address (odd 1st byte)");
1019d60b20cfSDmitry Krivenok         return -1;
1020d60b20cfSDmitry Krivenok     }
1021fd9400b3SPaolo Bonzini     qemu_macaddr_default_if_unset(&nd->macaddr);
1022fd9400b3SPaolo Bonzini 
1023fd9400b3SPaolo Bonzini     if (nic->has_vectors) {
1024fd9400b3SPaolo Bonzini         if (nic->vectors > 0x7ffffff) {
102566308868SMarkus Armbruster             error_setg(errp, "invalid # of vectors: %"PRIu32, nic->vectors);
1026fd9400b3SPaolo Bonzini             return -1;
1027fd9400b3SPaolo Bonzini         }
1028fd9400b3SPaolo Bonzini         nd->nvectors = nic->vectors;
1029fd9400b3SPaolo Bonzini     } else {
1030fd9400b3SPaolo Bonzini         nd->nvectors = DEV_NVECTORS_UNSPECIFIED;
1031fd9400b3SPaolo Bonzini     }
1032fd9400b3SPaolo Bonzini 
1033fd9400b3SPaolo Bonzini     nd->used = 1;
1034fd9400b3SPaolo Bonzini     nb_nics++;
1035fd9400b3SPaolo Bonzini 
1036fd9400b3SPaolo Bonzini     return idx;
1037fd9400b3SPaolo Bonzini }
1038fd9400b3SPaolo Bonzini 
add_nic_result(gpointer key,gpointer value,gpointer user_data)10392cdeca04SDavid Woodhouse static gboolean add_nic_result(gpointer key, gpointer value, gpointer user_data)
10402cdeca04SDavid Woodhouse {
10412cdeca04SDavid Woodhouse     GPtrArray *results = user_data;
10422cdeca04SDavid Woodhouse     GPtrArray *alias_list = value;
10432cdeca04SDavid Woodhouse     const char *model = key;
10442cdeca04SDavid Woodhouse     char *result;
10452cdeca04SDavid Woodhouse 
10462cdeca04SDavid Woodhouse     if (!alias_list) {
10472cdeca04SDavid Woodhouse         result = g_strdup(model);
10482cdeca04SDavid Woodhouse     } else {
10492cdeca04SDavid Woodhouse         GString *result_str = g_string_new(model);
10502cdeca04SDavid Woodhouse         int i;
10512cdeca04SDavid Woodhouse 
10522cdeca04SDavid Woodhouse         g_string_append(result_str, " (aka ");
10532cdeca04SDavid Woodhouse         for (i = 0; i < alias_list->len; i++) {
10542cdeca04SDavid Woodhouse             if (i) {
10552cdeca04SDavid Woodhouse                 g_string_append(result_str, ", ");
10562cdeca04SDavid Woodhouse             }
10572cdeca04SDavid Woodhouse             g_string_append(result_str, alias_list->pdata[i]);
10582cdeca04SDavid Woodhouse         }
10592cdeca04SDavid Woodhouse         g_string_append(result_str, ")");
10602cdeca04SDavid Woodhouse         result = result_str->str;
10612cdeca04SDavid Woodhouse         g_string_free(result_str, false);
10622cdeca04SDavid Woodhouse         g_ptr_array_unref(alias_list);
10632cdeca04SDavid Woodhouse     }
10642cdeca04SDavid Woodhouse     g_ptr_array_add(results, result);
10652cdeca04SDavid Woodhouse     return true;
10662cdeca04SDavid Woodhouse }
10672cdeca04SDavid Woodhouse 
model_cmp(char ** a,char ** b)10682cdeca04SDavid Woodhouse static int model_cmp(char **a, char **b)
10692cdeca04SDavid Woodhouse {
10702cdeca04SDavid Woodhouse     return strcmp(*a, *b);
10712cdeca04SDavid Woodhouse }
10722cdeca04SDavid Woodhouse 
show_nic_models(void)10732cdeca04SDavid Woodhouse static void show_nic_models(void)
10742cdeca04SDavid Woodhouse {
10752cdeca04SDavid Woodhouse     GPtrArray *results = g_ptr_array_new();
10762cdeca04SDavid Woodhouse     int i;
10772cdeca04SDavid Woodhouse 
10782cdeca04SDavid Woodhouse     g_hash_table_foreach_remove(nic_model_help, add_nic_result, results);
10792cdeca04SDavid Woodhouse     g_ptr_array_sort(results, (GCompareFunc)model_cmp);
10802cdeca04SDavid Woodhouse 
10812cdeca04SDavid Woodhouse     printf("Available NIC models for this configuration:\n");
10822cdeca04SDavid Woodhouse     for (i = 0 ; i < results->len; i++) {
10832cdeca04SDavid Woodhouse         printf("%s\n", (char *)results->pdata[i]);
10842cdeca04SDavid Woodhouse     }
10852cdeca04SDavid Woodhouse     g_hash_table_unref(nic_model_help);
10862cdeca04SDavid Woodhouse     nic_model_help = NULL;
10872cdeca04SDavid Woodhouse }
10882cdeca04SDavid Woodhouse 
add_nic_model_help(const char * model,const char * alias)10892cdeca04SDavid Woodhouse static void add_nic_model_help(const char *model, const char *alias)
10902cdeca04SDavid Woodhouse {
10912cdeca04SDavid Woodhouse     GPtrArray *alias_list = NULL;
10922cdeca04SDavid Woodhouse 
10932cdeca04SDavid Woodhouse     if (g_hash_table_lookup_extended(nic_model_help, model, NULL,
10942cdeca04SDavid Woodhouse                                      (gpointer *)&alias_list)) {
10952cdeca04SDavid Woodhouse         /* Already exists, no alias to add: return */
10962cdeca04SDavid Woodhouse         if (!alias) {
10972cdeca04SDavid Woodhouse             return;
10982cdeca04SDavid Woodhouse         }
10992cdeca04SDavid Woodhouse         if (alias_list) {
11002cdeca04SDavid Woodhouse             /* Check if this alias is already in the list. Add if not. */
11012cdeca04SDavid Woodhouse             if (!g_ptr_array_find_with_equal_func(alias_list, alias,
11022cdeca04SDavid Woodhouse                                                   g_str_equal, NULL)) {
11032cdeca04SDavid Woodhouse                 g_ptr_array_add(alias_list, g_strdup(alias));
11042cdeca04SDavid Woodhouse             }
11052cdeca04SDavid Woodhouse             return;
11062cdeca04SDavid Woodhouse         }
11072cdeca04SDavid Woodhouse     }
11082cdeca04SDavid Woodhouse     /* Either this model wasn't in the list already, or a first alias added */
11092cdeca04SDavid Woodhouse     if (alias) {
11102cdeca04SDavid Woodhouse         alias_list = g_ptr_array_new();
11112cdeca04SDavid Woodhouse         g_ptr_array_set_free_func(alias_list, g_free);
11122cdeca04SDavid Woodhouse         g_ptr_array_add(alias_list, g_strdup(alias));
11132cdeca04SDavid Woodhouse     }
11142cdeca04SDavid Woodhouse     g_hash_table_replace(nic_model_help, g_strdup(model), alias_list);
11152cdeca04SDavid Woodhouse }
11162cdeca04SDavid Woodhouse 
qemu_find_nic_info(const char * typename,bool match_default,const char * alias)111793e9d730SDavid Woodhouse NICInfo *qemu_find_nic_info(const char *typename, bool match_default,
111893e9d730SDavid Woodhouse                             const char *alias)
111993e9d730SDavid Woodhouse {
112093e9d730SDavid Woodhouse     NICInfo *nd;
112193e9d730SDavid Woodhouse     int i;
112293e9d730SDavid Woodhouse 
11232cdeca04SDavid Woodhouse     if (nic_model_help) {
11242cdeca04SDavid Woodhouse         add_nic_model_help(typename, alias);
11252cdeca04SDavid Woodhouse     }
11262cdeca04SDavid Woodhouse 
112793e9d730SDavid Woodhouse     for (i = 0; i < nb_nics; i++) {
112893e9d730SDavid Woodhouse         nd = &nd_table[i];
112993e9d730SDavid Woodhouse 
113093e9d730SDavid Woodhouse         if (!nd->used || nd->instantiated) {
113193e9d730SDavid Woodhouse             continue;
113293e9d730SDavid Woodhouse         }
113393e9d730SDavid Woodhouse 
113493e9d730SDavid Woodhouse         if ((match_default && !nd->model) || !g_strcmp0(nd->model, typename)
113593e9d730SDavid Woodhouse             || (alias && !g_strcmp0(nd->model, alias))) {
113693e9d730SDavid Woodhouse             return nd;
113793e9d730SDavid Woodhouse         }
113893e9d730SDavid Woodhouse     }
113993e9d730SDavid Woodhouse     return NULL;
114093e9d730SDavid Woodhouse }
114193e9d730SDavid Woodhouse 
is_nic_model_help_option(const char * model)114264f75f57SDavid Woodhouse static bool is_nic_model_help_option(const char *model)
114364f75f57SDavid Woodhouse {
114464f75f57SDavid Woodhouse     if (model && is_help_option(model)) {
114564f75f57SDavid Woodhouse         /*
114664f75f57SDavid Woodhouse          * Trigger the help output by instantiating the hash table which
114764f75f57SDavid Woodhouse          * will gather tha available models as they get registered.
114864f75f57SDavid Woodhouse          */
114964f75f57SDavid Woodhouse         if (!nic_model_help) {
115064f75f57SDavid Woodhouse             nic_model_help = g_hash_table_new_full(g_str_hash, g_str_equal,
115164f75f57SDavid Woodhouse                                                    g_free, NULL);
115264f75f57SDavid Woodhouse         }
115364f75f57SDavid Woodhouse         return true;
115464f75f57SDavid Woodhouse     }
115564f75f57SDavid Woodhouse     return false;
115664f75f57SDavid Woodhouse }
115793e9d730SDavid Woodhouse 
115893e9d730SDavid Woodhouse /* "I have created a device. Please configure it if you can" */
qemu_configure_nic_device(DeviceState * dev,bool match_default,const char * alias)115993e9d730SDavid Woodhouse bool qemu_configure_nic_device(DeviceState *dev, bool match_default,
116093e9d730SDavid Woodhouse                                const char *alias)
116193e9d730SDavid Woodhouse {
116293e9d730SDavid Woodhouse     NICInfo *nd = qemu_find_nic_info(object_get_typename(OBJECT(dev)),
116393e9d730SDavid Woodhouse                                      match_default, alias);
116493e9d730SDavid Woodhouse 
116593e9d730SDavid Woodhouse     if (nd) {
116693e9d730SDavid Woodhouse         qdev_set_nic_properties(dev, nd);
116793e9d730SDavid Woodhouse         return true;
116893e9d730SDavid Woodhouse     }
116993e9d730SDavid Woodhouse     return false;
117093e9d730SDavid Woodhouse }
117193e9d730SDavid Woodhouse 
117293e9d730SDavid Woodhouse /* "Please create a device, if you have a configuration for it" */
qemu_create_nic_device(const char * typename,bool match_default,const char * alias)117393e9d730SDavid Woodhouse DeviceState *qemu_create_nic_device(const char *typename, bool match_default,
117493e9d730SDavid Woodhouse                                     const char *alias)
117593e9d730SDavid Woodhouse {
117693e9d730SDavid Woodhouse     NICInfo *nd = qemu_find_nic_info(typename, match_default, alias);
117793e9d730SDavid Woodhouse     DeviceState *dev;
117893e9d730SDavid Woodhouse 
117993e9d730SDavid Woodhouse     if (!nd) {
118093e9d730SDavid Woodhouse         return NULL;
118193e9d730SDavid Woodhouse     }
118293e9d730SDavid Woodhouse 
118393e9d730SDavid Woodhouse     dev = qdev_new(typename);
118493e9d730SDavid Woodhouse     qdev_set_nic_properties(dev, nd);
118593e9d730SDavid Woodhouse     return dev;
118693e9d730SDavid Woodhouse }
1187fd9400b3SPaolo Bonzini 
qemu_create_nic_bus_devices(BusState * bus,const char * parent_type,const char * default_model,const char * alias,const char * alias_target)118893125e4bSDavid Woodhouse void qemu_create_nic_bus_devices(BusState *bus, const char *parent_type,
118993125e4bSDavid Woodhouse                                  const char *default_model,
119093125e4bSDavid Woodhouse                                  const char *alias, const char *alias_target)
119193125e4bSDavid Woodhouse {
119293125e4bSDavid Woodhouse     GPtrArray *nic_models = qemu_get_nic_models(parent_type);
119393125e4bSDavid Woodhouse     const char *model;
119493125e4bSDavid Woodhouse     DeviceState *dev;
119593125e4bSDavid Woodhouse     NICInfo *nd;
119693125e4bSDavid Woodhouse     int i;
119793125e4bSDavid Woodhouse 
119893125e4bSDavid Woodhouse     if (nic_model_help) {
119993125e4bSDavid Woodhouse         if (alias_target) {
120093125e4bSDavid Woodhouse             add_nic_model_help(alias_target, alias);
120193125e4bSDavid Woodhouse         }
120293125e4bSDavid Woodhouse         for (i = 0; i < nic_models->len - 1; i++) {
120393125e4bSDavid Woodhouse             add_nic_model_help(nic_models->pdata[i], NULL);
120493125e4bSDavid Woodhouse         }
120593125e4bSDavid Woodhouse     }
120693125e4bSDavid Woodhouse 
120793125e4bSDavid Woodhouse     /* Drop the NULL terminator which would make g_str_equal() unhappy */
120893125e4bSDavid Woodhouse     nic_models->len--;
120993125e4bSDavid Woodhouse 
121093125e4bSDavid Woodhouse     for (i = 0; i < nb_nics; i++) {
121193125e4bSDavid Woodhouse         nd = &nd_table[i];
121293125e4bSDavid Woodhouse 
121393125e4bSDavid Woodhouse         if (!nd->used || nd->instantiated) {
121493125e4bSDavid Woodhouse             continue;
121593125e4bSDavid Woodhouse         }
121693125e4bSDavid Woodhouse 
121793125e4bSDavid Woodhouse         model = nd->model ? nd->model : default_model;
121893125e4bSDavid Woodhouse         if (!model) {
121993125e4bSDavid Woodhouse             continue;
122093125e4bSDavid Woodhouse         }
122193125e4bSDavid Woodhouse 
122293125e4bSDavid Woodhouse         /* Each bus type is allowed *one* substitution */
122393125e4bSDavid Woodhouse         if (g_str_equal(model, alias)) {
122493125e4bSDavid Woodhouse             model = alias_target;
122593125e4bSDavid Woodhouse         }
122693125e4bSDavid Woodhouse 
122793125e4bSDavid Woodhouse         if (!g_ptr_array_find_with_equal_func(nic_models, model,
122893125e4bSDavid Woodhouse                                               g_str_equal, NULL)) {
122993125e4bSDavid Woodhouse             /* This NIC does not live on this bus. */
123093125e4bSDavid Woodhouse             continue;
123193125e4bSDavid Woodhouse         }
123293125e4bSDavid Woodhouse 
123393125e4bSDavid Woodhouse         dev = qdev_new(model);
123493125e4bSDavid Woodhouse         qdev_set_nic_properties(dev, nd);
123593125e4bSDavid Woodhouse         qdev_realize_and_unref(dev, bus, &error_fatal);
123693125e4bSDavid Woodhouse     }
123793125e4bSDavid Woodhouse 
123893125e4bSDavid Woodhouse     g_ptr_array_free(nic_models, true);
123993125e4bSDavid Woodhouse }
124093125e4bSDavid Woodhouse 
1241f394b2e2SEric Blake static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])(
1242cebea510SKővágó, Zoltán     const Netdev *netdev,
1243fd9400b3SPaolo Bonzini     const char *name,
1244a30ecde6SMarkus Armbruster     NetClientState *peer, Error **errp) = {
1245f394b2e2SEric Blake         [NET_CLIENT_DRIVER_NIC]       = net_init_nic,
1246fd9400b3SPaolo Bonzini #ifdef CONFIG_SLIRP
1247f394b2e2SEric Blake         [NET_CLIENT_DRIVER_USER]      = net_init_slirp,
1248fd9400b3SPaolo Bonzini #endif
1249f394b2e2SEric Blake         [NET_CLIENT_DRIVER_TAP]       = net_init_tap,
1250f394b2e2SEric Blake         [NET_CLIENT_DRIVER_SOCKET]    = net_init_socket,
12515166fe0aSLaurent Vivier         [NET_CLIENT_DRIVER_STREAM]    = net_init_stream,
12525166fe0aSLaurent Vivier         [NET_CLIENT_DRIVER_DGRAM]     = net_init_dgram,
1253fd9400b3SPaolo Bonzini #ifdef CONFIG_VDE
1254f394b2e2SEric Blake         [NET_CLIENT_DRIVER_VDE]       = net_init_vde,
1255fd9400b3SPaolo Bonzini #endif
125658952137SVincenzo Maffione #ifdef CONFIG_NETMAP
1257f394b2e2SEric Blake         [NET_CLIENT_DRIVER_NETMAP]    = net_init_netmap,
125858952137SVincenzo Maffione #endif
1259cb039ef3SIlya Maximets #ifdef CONFIG_AF_XDP
1260cb039ef3SIlya Maximets         [NET_CLIENT_DRIVER_AF_XDP]    = net_init_af_xdp,
1261cb039ef3SIlya Maximets #endif
1262fd9400b3SPaolo Bonzini #ifdef CONFIG_NET_BRIDGE
1263f394b2e2SEric Blake         [NET_CLIENT_DRIVER_BRIDGE]    = net_init_bridge,
1264fd9400b3SPaolo Bonzini #endif
1265f394b2e2SEric Blake         [NET_CLIENT_DRIVER_HUBPORT]   = net_init_hubport,
126656f41de7SPaolo Bonzini #ifdef CONFIG_VHOST_NET_USER
1267f394b2e2SEric Blake         [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
126803ce5744SNikolay Nikolaev #endif
12691e0a84eaSCindy Lu #ifdef CONFIG_VHOST_NET_VDPA
12701e0a84eaSCindy Lu         [NET_CLIENT_DRIVER_VHOST_VDPA] = net_init_vhost_vdpa,
12711e0a84eaSCindy Lu #endif
1272015a33bdSGonglei #ifdef CONFIG_L2TPV3
1273f394b2e2SEric Blake         [NET_CLIENT_DRIVER_L2TPV3]    = net_init_l2tpv3,
12743fb69aa1SAnton Ivanov #endif
127581ad2964SVladislav Yaroshchuk #ifdef CONFIG_VMNET
127681ad2964SVladislav Yaroshchuk         [NET_CLIENT_DRIVER_VMNET_HOST] = net_init_vmnet_host,
127781ad2964SVladislav Yaroshchuk         [NET_CLIENT_DRIVER_VMNET_SHARED] = net_init_vmnet_shared,
127881ad2964SVladislav Yaroshchuk         [NET_CLIENT_DRIVER_VMNET_BRIDGED] = net_init_vmnet_bridged,
127981ad2964SVladislav Yaroshchuk #endif /* CONFIG_VMNET */
1280fd9400b3SPaolo Bonzini };
1281fd9400b3SPaolo Bonzini 
1282fd9400b3SPaolo Bonzini 
net_client_init1(const Netdev * netdev,bool is_netdev,Error ** errp)128371830d84SThomas Huth static int net_client_init1(const Netdev *netdev, bool is_netdev, Error **errp)
1284fd9400b3SPaolo Bonzini {
12854ef0defbSStefan Hajnoczi     NetClientState *peer = NULL;
1286831734ccSMarkus Armbruster     NetClientState *nc;
1287fd9400b3SPaolo Bonzini 
1288fd9400b3SPaolo Bonzini     if (is_netdev) {
1289857d2087SThomas Huth         if (netdev->type == NET_CLIENT_DRIVER_NIC ||
1290f394b2e2SEric Blake             !net_client_init_fun[netdev->type]) {
12917d0e12afSDaniel P. Berrangé             error_setg(errp, "network backend '%s' is not compiled into this binary",
12927d0e12afSDaniel P. Berrangé                        NetClientDriver_str(netdev->type));
1293fd9400b3SPaolo Bonzini             return -1;
1294fd9400b3SPaolo Bonzini         }
1295fd9400b3SPaolo Bonzini     } else {
129671830d84SThomas Huth         if (netdev->type == NET_CLIENT_DRIVER_NONE) {
12971e81aba5SStefan Hajnoczi             return 0; /* nothing to do */
1298ca7eb184SMarkus Armbruster         }
12997d0e12afSDaniel P. Berrangé         if (netdev->type == NET_CLIENT_DRIVER_HUBPORT) {
13007d0e12afSDaniel P. Berrangé             error_setg(errp, "network backend '%s' is only supported with -netdev/-nic",
13017d0e12afSDaniel P. Berrangé                        NetClientDriver_str(netdev->type));
13027d0e12afSDaniel P. Berrangé             return -1;
13037d0e12afSDaniel P. Berrangé         }
13047d0e12afSDaniel P. Berrangé 
13057d0e12afSDaniel P. Berrangé         if (!net_client_init_fun[netdev->type]) {
13067d0e12afSDaniel P. Berrangé             error_setg(errp, "network backend '%s' is not compiled into this binary",
13077d0e12afSDaniel P. Berrangé                        NetClientDriver_str(netdev->type));
1308d139e9a6SStefan Hajnoczi             return -1;
1309d139e9a6SStefan Hajnoczi         }
1310fd9400b3SPaolo Bonzini 
1311af1a5c3eSThomas Huth         /* Do not add to a hub if it's a nic with a netdev= parameter. */
1312f394b2e2SEric Blake         if (netdev->type != NET_CLIENT_DRIVER_NIC ||
13137480874aSMarkus Armbruster             !netdev->u.nic.netdev) {
1314af1a5c3eSThomas Huth             peer = net_hub_add_port(0, NULL, NULL);
1315a2dbe135SThomas Huth         }
1316fd9400b3SPaolo Bonzini     }
1317fd9400b3SPaolo Bonzini 
1318831734ccSMarkus Armbruster     nc = qemu_find_netdev(netdev->id);
1319831734ccSMarkus Armbruster     if (nc) {
1320831734ccSMarkus Armbruster         error_setg(errp, "Duplicate ID '%s'", netdev->id);
1321831734ccSMarkus Armbruster         return -1;
1322831734ccSMarkus Armbruster     }
1323831734ccSMarkus Armbruster 
13249d903f30SThomas Huth     if (net_client_init_fun[netdev->type](netdev, netdev->id, peer, errp) < 0) {
1325a30ecde6SMarkus Armbruster         /* FIXME drop when all init functions store an Error */
1326a30ecde6SMarkus Armbruster         if (errp && !*errp) {
1327f820af87SMarkus Armbruster             error_setg(errp, "Device '%s' could not be initialized",
1328977c736fSMarkus Armbruster                        NetClientDriver_str(netdev->type));
1329a30ecde6SMarkus Armbruster         }
1330fd9400b3SPaolo Bonzini         return -1;
1331fd9400b3SPaolo Bonzini     }
133208712fcbSEric Blake 
133308712fcbSEric Blake     if (is_netdev) {
133408712fcbSEric Blake         nc = qemu_find_netdev(netdev->id);
133508712fcbSEric Blake         assert(nc);
133608712fcbSEric Blake         nc->is_netdev = true;
133708712fcbSEric Blake     }
133808712fcbSEric Blake 
1339fd9400b3SPaolo Bonzini     return 0;
1340fd9400b3SPaolo Bonzini }
1341fd9400b3SPaolo Bonzini 
show_netdevs(void)1342ad6f932fSPaolo Bonzini void show_netdevs(void)
1343547203eaSThomas Huth {
1344547203eaSThomas Huth     int idx;
1345547203eaSThomas Huth     const char *available_netdevs[] = {
1346547203eaSThomas Huth         "socket",
13475166fe0aSLaurent Vivier         "stream",
13485166fe0aSLaurent Vivier         "dgram",
1349547203eaSThomas Huth         "hubport",
1350547203eaSThomas Huth         "tap",
1351547203eaSThomas Huth #ifdef CONFIG_SLIRP
1352547203eaSThomas Huth         "user",
1353547203eaSThomas Huth #endif
1354547203eaSThomas Huth #ifdef CONFIG_L2TPV3
1355547203eaSThomas Huth         "l2tpv3",
1356547203eaSThomas Huth #endif
1357547203eaSThomas Huth #ifdef CONFIG_VDE
1358547203eaSThomas Huth         "vde",
1359547203eaSThomas Huth #endif
1360547203eaSThomas Huth #ifdef CONFIG_NET_BRIDGE
1361547203eaSThomas Huth         "bridge",
1362547203eaSThomas Huth #endif
1363547203eaSThomas Huth #ifdef CONFIG_NETMAP
1364547203eaSThomas Huth         "netmap",
1365547203eaSThomas Huth #endif
1366cb039ef3SIlya Maximets #ifdef CONFIG_AF_XDP
1367cb039ef3SIlya Maximets         "af-xdp",
1368cb039ef3SIlya Maximets #endif
1369547203eaSThomas Huth #ifdef CONFIG_POSIX
1370547203eaSThomas Huth         "vhost-user",
1371547203eaSThomas Huth #endif
13721bc211a1SCindy Lu #ifdef CONFIG_VHOST_VDPA
13731bc211a1SCindy Lu         "vhost-vdpa",
13741bc211a1SCindy Lu #endif
137581ad2964SVladislav Yaroshchuk #ifdef CONFIG_VMNET
137681ad2964SVladislav Yaroshchuk         "vmnet-host",
137781ad2964SVladislav Yaroshchuk         "vmnet-shared",
137881ad2964SVladislav Yaroshchuk         "vmnet-bridged",
137981ad2964SVladislav Yaroshchuk #endif
1380547203eaSThomas Huth     };
1381fd9400b3SPaolo Bonzini 
1382ad6f932fSPaolo Bonzini     qemu_printf("Available netdev backend types:\n");
1383547203eaSThomas Huth     for (idx = 0; idx < ARRAY_SIZE(available_netdevs); idx++) {
1384ad6f932fSPaolo Bonzini         qemu_printf("%s\n", available_netdevs[idx]);
1385547203eaSThomas Huth     }
1386547203eaSThomas Huth }
1387fd9400b3SPaolo Bonzini 
net_client_init(QemuOpts * opts,bool is_netdev,Error ** errp)1388aa09a485SThomas Huth static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp)
1389fd9400b3SPaolo Bonzini {
1390c1112b2dSStefano Garzarella     gchar **substrings = NULL;
139171830d84SThomas Huth     Netdev *object = NULL;
1392fd9400b3SPaolo Bonzini     int ret = -1;
139309204eacSEric Blake     Visitor *v = opts_visitor_new(opts);
1394fd9400b3SPaolo Bonzini 
13950a4a1512SMarkus Armbruster     /* Parse convenience option format ipv6-net=fec0::0[/64] */
1396891a2bb5SSamuel Thibault     const char *ip6_net = qemu_opt_get(opts, "ipv6-net");
13977aac531eSYann Bordenave 
13987aac531eSYann Bordenave     if (ip6_net) {
1399c1112b2dSStefano Garzarella         char *prefix_addr;
1400c1112b2dSStefano Garzarella         unsigned long prefix_len = 64; /* Default 64bit prefix length. */
14017aac531eSYann Bordenave 
1402c1112b2dSStefano Garzarella         substrings = g_strsplit(ip6_net, "/", 2);
1403c1112b2dSStefano Garzarella         if (!substrings || !substrings[0]) {
1404c1112b2dSStefano Garzarella             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "ipv6-net",
1405c1112b2dSStefano Garzarella                        "a valid IPv6 prefix");
1406c1112b2dSStefano Garzarella             goto out;
1407c1112b2dSStefano Garzarella         }
1408c1112b2dSStefano Garzarella 
1409c1112b2dSStefano Garzarella         prefix_addr = substrings[0];
1410c1112b2dSStefano Garzarella 
141133c9642fSVladimir Sementsov-Ogievskiy         /* Handle user-specified prefix length. */
141233c9642fSVladimir Sementsov-Ogievskiy         if (substrings[1] &&
141333c9642fSVladimir Sementsov-Ogievskiy             qemu_strtoul(substrings[1], NULL, 10, &prefix_len))
141433c9642fSVladimir Sementsov-Ogievskiy         {
14150a4a1512SMarkus Armbruster             error_setg(errp,
14160a4a1512SMarkus Armbruster                        "parameter 'ipv6-net' expects a number after '/'");
141721c520d0SStefano Garzarella             goto out;
14187aac531eSYann Bordenave         }
1419c1112b2dSStefano Garzarella 
1420c1112b2dSStefano Garzarella         qemu_opt_set(opts, "ipv6-prefix", prefix_addr, &error_abort);
1421c1112b2dSStefano Garzarella         qemu_opt_set_number(opts, "ipv6-prefixlen", prefix_len,
1422c1112b2dSStefano Garzarella                             &error_abort);
1423891a2bb5SSamuel Thibault         qemu_opt_unset(opts, "ipv6-net");
14247aac531eSYann Bordenave     }
14257aac531eSYann Bordenave 
142671830d84SThomas Huth     /* Create an ID for -net if the user did not specify one */
142771830d84SThomas Huth     if (!is_netdev && !qemu_opts_id(opts)) {
142827eb3722SThomas Huth         qemu_opts_set_id(opts, id_generate(ID_NET));
1429fd9400b3SPaolo Bonzini     }
1430fd9400b3SPaolo Bonzini 
143114217038SMarkus Armbruster     if (visit_type_Netdev(v, NULL, &object, errp)) {
143214217038SMarkus Armbruster         ret = net_client_init1(object, is_netdev, errp);
1433fd9400b3SPaolo Bonzini     }
1434fd9400b3SPaolo Bonzini 
143596a1616cSEric Blake     qapi_free_Netdev(object);
1436fd9400b3SPaolo Bonzini 
143721c520d0SStefano Garzarella out:
1438c1112b2dSStefano Garzarella     g_strfreev(substrings);
143909204eacSEric Blake     visit_free(v);
1440fd9400b3SPaolo Bonzini     return ret;
1441fd9400b3SPaolo Bonzini }
1442fd9400b3SPaolo Bonzini 
netdev_add(QemuOpts * opts,Error ** errp)1443fd9400b3SPaolo Bonzini void netdev_add(QemuOpts *opts, Error **errp)
1444fd9400b3SPaolo Bonzini {
14450e55c381SEric Blake     net_client_init(opts, true, errp);
1446fd9400b3SPaolo Bonzini }
1447fd9400b3SPaolo Bonzini 
qmp_netdev_add(Netdev * netdev,Error ** errp)1448db2a380cSEric Blake void qmp_netdev_add(Netdev *netdev, Error **errp)
1449fd9400b3SPaolo Bonzini {
1450e73b4317SPaolo Bonzini     if (!id_wellformed(netdev->id)) {
1451e73b4317SPaolo Bonzini         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
1452e73b4317SPaolo Bonzini         return;
1453e73b4317SPaolo Bonzini     }
1454e73b4317SPaolo Bonzini 
145508712fcbSEric Blake     net_client_init1(netdev, true, errp);
1456fd9400b3SPaolo Bonzini }
1457fd9400b3SPaolo Bonzini 
qmp_netdev_del(const char * id,Error ** errp)1458fd9400b3SPaolo Bonzini void qmp_netdev_del(const char *id, Error **errp)
1459fd9400b3SPaolo Bonzini {
1460fd9400b3SPaolo Bonzini     NetClientState *nc;
1461831734ccSMarkus Armbruster     QemuOpts *opts;
1462fd9400b3SPaolo Bonzini 
1463fd9400b3SPaolo Bonzini     nc = qemu_find_netdev(id);
1464fd9400b3SPaolo Bonzini     if (!nc) {
146575158ebbSMarkus Armbruster         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
146675158ebbSMarkus Armbruster                   "Device '%s' not found", id);
1467fd9400b3SPaolo Bonzini         return;
1468fd9400b3SPaolo Bonzini     }
1469fd9400b3SPaolo Bonzini 
147008712fcbSEric Blake     if (!nc->is_netdev) {
1471fd9400b3SPaolo Bonzini         error_setg(errp, "Device '%s' is not a netdev", id);
1472fd9400b3SPaolo Bonzini         return;
1473fd9400b3SPaolo Bonzini     }
1474fd9400b3SPaolo Bonzini 
1475fd9400b3SPaolo Bonzini     qemu_del_net_client(nc);
1476831734ccSMarkus Armbruster 
1477831734ccSMarkus Armbruster     /*
1478831734ccSMarkus Armbruster      * Wart: we need to delete the QemuOpts associated with netdevs
1479831734ccSMarkus Armbruster      * created via CLI or HMP, to avoid bogus "Duplicate ID" errors in
1480831734ccSMarkus Armbruster      * HMP netdev_add.
1481831734ccSMarkus Armbruster      */
1482831734ccSMarkus Armbruster     opts = qemu_opts_find(qemu_find_opts("netdev"), id);
1483831734ccSMarkus Armbruster     if (opts) {
1484831734ccSMarkus Armbruster         qemu_opts_del(opts);
1485831734ccSMarkus Armbruster     }
1486fd9400b3SPaolo Bonzini }
1487fd9400b3SPaolo Bonzini 
netfilter_print_info(Monitor * mon,NetFilterState * nf)1488aa9156f4Szhanghailiang static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
1489aa9156f4Szhanghailiang {
1490aa9156f4Szhanghailiang     char *str;
1491aa9156f4Szhanghailiang     ObjectProperty *prop;
1492aa9156f4Szhanghailiang     ObjectPropertyIterator iter;
14933b098d56SEric Blake     Visitor *v;
1494aa9156f4Szhanghailiang 
1495aa9156f4Szhanghailiang     /* generate info str */
1496aa9156f4Szhanghailiang     object_property_iter_init(&iter, OBJECT(nf));
1497aa9156f4Szhanghailiang     while ((prop = object_property_iter_next(&iter))) {
1498aa9156f4Szhanghailiang         if (!strcmp(prop->name, "type")) {
1499aa9156f4Szhanghailiang             continue;
1500aa9156f4Szhanghailiang         }
15013b098d56SEric Blake         v = string_output_visitor_new(false, &str);
15025325cc34SMarkus Armbruster         object_property_get(OBJECT(nf), prop->name, v, NULL);
15033b098d56SEric Blake         visit_complete(v, &str);
15043b098d56SEric Blake         visit_free(v);
1505aa9156f4Szhanghailiang         monitor_printf(mon, ",%s=%s", prop->name, str);
1506aa9156f4Szhanghailiang         g_free(str);
1507aa9156f4Szhanghailiang     }
1508aa9156f4Szhanghailiang     monitor_printf(mon, "\n");
1509aa9156f4Szhanghailiang }
1510aa9156f4Szhanghailiang 
print_net_client(Monitor * mon,NetClientState * nc)1511fd9400b3SPaolo Bonzini void print_net_client(Monitor *mon, NetClientState *nc)
1512fd9400b3SPaolo Bonzini {
1513a4960f52SYang Hongyang     NetFilterState *nf;
1514a4960f52SYang Hongyang 
15151ceef9f2SJason Wang     monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
15161ceef9f2SJason Wang                    nc->queue_index,
1517977c736fSMarkus Armbruster                    NetClientDriver_str(nc->info->type),
151856e6f594SJason Wang                    nc->info_str);
1519a4960f52SYang Hongyang     if (!QTAILQ_EMPTY(&nc->filters)) {
1520a4960f52SYang Hongyang         monitor_printf(mon, "filters:\n");
1521a4960f52SYang Hongyang     }
1522a4960f52SYang Hongyang     QTAILQ_FOREACH(nf, &nc->filters, next) {
15237a309cc9SMarkus Armbruster         monitor_printf(mon, "  - %s: type=%s",
15247a309cc9SMarkus Armbruster                        object_get_canonical_path_component(OBJECT(nf)),
1525aa9156f4Szhanghailiang                        object_get_typename(OBJECT(nf)));
1526aa9156f4Szhanghailiang         netfilter_print_info(mon, nf);
1527a4960f52SYang Hongyang     }
1528fd9400b3SPaolo Bonzini }
1529fd9400b3SPaolo Bonzini 
qmp_query_rx_filter(const char * name,Error ** errp)15307480874aSMarkus Armbruster RxFilterInfoList *qmp_query_rx_filter(const char *name, Error **errp)
1531b1be4280SAmos Kong {
1532b1be4280SAmos Kong     NetClientState *nc;
153395b3a8c8SEric Blake     RxFilterInfoList *filter_list = NULL, **tail = &filter_list;
1534b1be4280SAmos Kong 
1535b1be4280SAmos Kong     QTAILQ_FOREACH(nc, &net_clients, next) {
1536b1be4280SAmos Kong         RxFilterInfo *info;
1537b1be4280SAmos Kong 
15387480874aSMarkus Armbruster         if (name && strcmp(nc->name, name) != 0) {
1539b1be4280SAmos Kong             continue;
1540b1be4280SAmos Kong         }
1541b1be4280SAmos Kong 
1542b1be4280SAmos Kong         /* only query rx-filter information of NIC */
1543f394b2e2SEric Blake         if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
15447480874aSMarkus Armbruster             if (name) {
1545b1be4280SAmos Kong                 error_setg(errp, "net client(%s) isn't a NIC", name);
1546e9d635eaSEric Blake                 assert(!filter_list);
15479083da1dSMarkus Armbruster                 return NULL;
1548b1be4280SAmos Kong             }
1549b1be4280SAmos Kong             continue;
1550b1be4280SAmos Kong         }
1551b1be4280SAmos Kong 
15525320c2caSVladislav Yasevich         /* only query information on queue 0 since the info is per nic,
15535320c2caSVladislav Yasevich          * not per queue
15545320c2caSVladislav Yasevich          */
15555320c2caSVladislav Yasevich         if (nc->queue_index != 0)
15565320c2caSVladislav Yasevich             continue;
15575320c2caSVladislav Yasevich 
1558b1be4280SAmos Kong         if (nc->info->query_rx_filter) {
1559b1be4280SAmos Kong             info = nc->info->query_rx_filter(nc);
156095b3a8c8SEric Blake             QAPI_LIST_APPEND(tail, info);
15617480874aSMarkus Armbruster         } else if (name) {
1562b1be4280SAmos Kong             error_setg(errp, "net client(%s) doesn't support"
1563b1be4280SAmos Kong                        " rx-filter querying", name);
1564e9d635eaSEric Blake             assert(!filter_list);
15659083da1dSMarkus Armbruster             return NULL;
1566b1be4280SAmos Kong         }
1567638fb141SMarkus Armbruster 
15687480874aSMarkus Armbruster         if (name) {
1569638fb141SMarkus Armbruster             break;
1570638fb141SMarkus Armbruster         }
1571b1be4280SAmos Kong     }
1572b1be4280SAmos Kong 
15737480874aSMarkus Armbruster     if (filter_list == NULL && name) {
1574b1be4280SAmos Kong         error_setg(errp, "invalid net client name: %s", name);
1575b1be4280SAmos Kong     }
1576b1be4280SAmos Kong 
1577b1be4280SAmos Kong     return filter_list;
1578b1be4280SAmos Kong }
1579b1be4280SAmos Kong 
colo_notify_filters_event(int event,Error ** errp)15805fbba3d6SZhang Chen void colo_notify_filters_event(int event, Error **errp)
15815fbba3d6SZhang Chen {
15825fbba3d6SZhang Chen     NetClientState *nc;
15835fbba3d6SZhang Chen     NetFilterState *nf;
15845fbba3d6SZhang Chen     NetFilterClass *nfc = NULL;
15855fbba3d6SZhang Chen     Error *local_err = NULL;
15865fbba3d6SZhang Chen 
15875fbba3d6SZhang Chen     QTAILQ_FOREACH(nc, &net_clients, next) {
15885fbba3d6SZhang Chen         QTAILQ_FOREACH(nf, &nc->filters, next) {
15895fbba3d6SZhang Chen             nfc = NETFILTER_GET_CLASS(OBJECT(nf));
15905fbba3d6SZhang Chen             nfc->handle_event(nf, event, &local_err);
15915fbba3d6SZhang Chen             if (local_err) {
15925fbba3d6SZhang Chen                 error_propagate(errp, local_err);
15935fbba3d6SZhang Chen                 return;
15945fbba3d6SZhang Chen             }
15955fbba3d6SZhang Chen         }
15965fbba3d6SZhang Chen     }
15975fbba3d6SZhang Chen }
15985fbba3d6SZhang Chen 
qmp_set_link(const char * name,bool up,Error ** errp)1599fd9400b3SPaolo Bonzini void qmp_set_link(const char *name, bool up, Error **errp)
1600fd9400b3SPaolo Bonzini {
16011ceef9f2SJason Wang     NetClientState *ncs[MAX_QUEUE_NUM];
16021ceef9f2SJason Wang     NetClientState *nc;
16031ceef9f2SJason Wang     int queues, i;
1604fd9400b3SPaolo Bonzini 
16051ceef9f2SJason Wang     queues = qemu_find_net_clients_except(name, ncs,
1606f394b2e2SEric Blake                                           NET_CLIENT_DRIVER__MAX,
16071ceef9f2SJason Wang                                           MAX_QUEUE_NUM);
16081ceef9f2SJason Wang 
16091ceef9f2SJason Wang     if (queues == 0) {
161075158ebbSMarkus Armbruster         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
161175158ebbSMarkus Armbruster                   "Device '%s' not found", name);
1612fd9400b3SPaolo Bonzini         return;
1613fd9400b3SPaolo Bonzini     }
16141ceef9f2SJason Wang     nc = ncs[0];
1615fd9400b3SPaolo Bonzini 
16161ceef9f2SJason Wang     for (i = 0; i < queues; i++) {
16171ceef9f2SJason Wang         ncs[i]->link_down = !up;
16181ceef9f2SJason Wang     }
1619fd9400b3SPaolo Bonzini 
1620fd9400b3SPaolo Bonzini     if (nc->info->link_status_changed) {
1621fd9400b3SPaolo Bonzini         nc->info->link_status_changed(nc);
1622fd9400b3SPaolo Bonzini     }
1623fd9400b3SPaolo Bonzini 
162402d38fcbSVlad Yasevich     if (nc->peer) {
162502d38fcbSVlad Yasevich         /* Change peer link only if the peer is NIC and then notify peer.
162602d38fcbSVlad Yasevich          * If the peer is a HUBPORT or a backend, we do not change the
162702d38fcbSVlad Yasevich          * link status.
1628fd9400b3SPaolo Bonzini          *
1629af1a5c3eSThomas Huth          * This behavior is compatible with qemu hubs where there could be
1630fd9400b3SPaolo Bonzini          * multiple clients that can still communicate with each other in
163102d38fcbSVlad Yasevich          * disconnected mode. For now maintain this compatibility.
163202d38fcbSVlad Yasevich          */
1633f394b2e2SEric Blake         if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
163402d38fcbSVlad Yasevich             for (i = 0; i < queues; i++) {
163502d38fcbSVlad Yasevich                 ncs[i]->peer->link_down = !up;
163602d38fcbSVlad Yasevich             }
163702d38fcbSVlad Yasevich         }
163802d38fcbSVlad Yasevich         if (nc->peer->info->link_status_changed) {
1639fd9400b3SPaolo Bonzini             nc->peer->info->link_status_changed(nc->peer);
1640fd9400b3SPaolo Bonzini         }
1641fd9400b3SPaolo Bonzini     }
164202d38fcbSVlad Yasevich }
1643fd9400b3SPaolo Bonzini 
net_vm_change_state_handler(void * opaque,bool running,RunState state)1644538f0497SPhilippe Mathieu-Daudé static void net_vm_change_state_handler(void *opaque, bool running,
1645ca77d85eSMichael S. Tsirkin                                         RunState state)
1646ca77d85eSMichael S. Tsirkin {
1647ca77d85eSMichael S. Tsirkin     NetClientState *nc;
1648ca77d85eSMichael S. Tsirkin     NetClientState *tmp;
1649ca77d85eSMichael S. Tsirkin 
1650ca77d85eSMichael S. Tsirkin     QTAILQ_FOREACH_SAFE(nc, &net_clients, next, tmp) {
1651625de449SFam Zheng         if (running) {
1652625de449SFam Zheng             /* Flush queued packets and wake up backends. */
1653625de449SFam Zheng             if (nc->peer && qemu_can_send_packet(nc)) {
1654625de449SFam Zheng                 qemu_flush_queued_packets(nc->peer);
1655625de449SFam Zheng             }
1656625de449SFam Zheng         } else {
1657625de449SFam Zheng             /* Complete all queued packets, to guarantee we don't modify
1658625de449SFam Zheng              * state later when VM is not running.
1659625de449SFam Zheng              */
1660ca77d85eSMichael S. Tsirkin             qemu_flush_or_purge_queued_packets(nc, true);
1661ca77d85eSMichael S. Tsirkin         }
1662ca77d85eSMichael S. Tsirkin     }
1663ca77d85eSMichael S. Tsirkin }
1664ca77d85eSMichael S. Tsirkin 
net_cleanup(void)1665fd9400b3SPaolo Bonzini void net_cleanup(void)
1666fd9400b3SPaolo Bonzini {
166784f85eb9SDavid Woodhouse     NetClientState *nc, **p = &QTAILQ_FIRST(&net_clients);
1668fd9400b3SPaolo Bonzini 
16690c7af1a7SRao, Lei     /*cleanup colo compare module for COLO*/
16700c7af1a7SRao, Lei     colo_compare_cleanup();
16710c7af1a7SRao, Lei 
167284f85eb9SDavid Woodhouse     /*
167384f85eb9SDavid Woodhouse      * Walk the net_clients list and remove the netdevs but *not* any
167484f85eb9SDavid Woodhouse      * NET_CLIENT_DRIVER_NIC entries. The latter are owned by the device
167584f85eb9SDavid Woodhouse      * model which created them, and in some cases (e.g. xen-net-device)
167684f85eb9SDavid Woodhouse      * the device itself may do cleanup at exit and will be upset if we
167784f85eb9SDavid Woodhouse      * just delete its NIC from underneath it.
167884f85eb9SDavid Woodhouse      *
167984f85eb9SDavid Woodhouse      * Since qemu_del_net_client() may delete multiple entries, using
168084f85eb9SDavid Woodhouse      * QTAILQ_FOREACH_SAFE() is not safe here. The only safe pointer
168184f85eb9SDavid Woodhouse      * to keep as a bookmark is a NET_CLIENT_DRIVER_NIC entry, so keep
168284f85eb9SDavid Woodhouse      * 'p' pointing to either the head of the list, or the 'next' field
168384f85eb9SDavid Woodhouse      * of the latest NET_CLIENT_DRIVER_NIC, and operate on *p as we walk
168484f85eb9SDavid Woodhouse      * the list.
168584f85eb9SDavid Woodhouse      *
168684f85eb9SDavid Woodhouse      * The 'nc' variable isn't part of the list traversal; it's purely
168784f85eb9SDavid Woodhouse      * for convenience as too much '(*p)->' has a tendency to make the
168884f85eb9SDavid Woodhouse      * readers' eyes bleed.
16891ceef9f2SJason Wang      */
169084f85eb9SDavid Woodhouse     while (*p) {
169184f85eb9SDavid Woodhouse         nc = *p;
1692f394b2e2SEric Blake         if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
169384f85eb9SDavid Woodhouse             /* Skip NET_CLIENT_DRIVER_NIC entries */
169484f85eb9SDavid Woodhouse             p = &QTAILQ_NEXT(nc, next);
1695948ecf21SJason Wang         } else {
1696fd9400b3SPaolo Bonzini             qemu_del_net_client(nc);
1697fd9400b3SPaolo Bonzini         }
1698fd9400b3SPaolo Bonzini     }
1699ca77d85eSMichael S. Tsirkin 
1700ca77d85eSMichael S. Tsirkin     qemu_del_vm_change_state_handler(net_change_state_entry);
1701948ecf21SJason Wang }
1702fd9400b3SPaolo Bonzini 
net_check_clients(void)1703fd9400b3SPaolo Bonzini void net_check_clients(void)
1704fd9400b3SPaolo Bonzini {
1705fd9400b3SPaolo Bonzini     NetClientState *nc;
1706fd9400b3SPaolo Bonzini     int i;
1707fd9400b3SPaolo Bonzini 
17082cdeca04SDavid Woodhouse     if (nic_model_help) {
17092cdeca04SDavid Woodhouse         show_nic_models();
17102cdeca04SDavid Woodhouse         exit(0);
17112cdeca04SDavid Woodhouse     }
1712fd9400b3SPaolo Bonzini     net_hub_check_clients();
1713fd9400b3SPaolo Bonzini 
1714fd9400b3SPaolo Bonzini     QTAILQ_FOREACH(nc, &net_clients, next) {
1715fd9400b3SPaolo Bonzini         if (!nc->peer) {
17168297be80SAlistair Francis             warn_report("%s %s has no peer",
1717b62e39b4SAlistair Francis                         nc->info->type == NET_CLIENT_DRIVER_NIC
1718b62e39b4SAlistair Francis                         ? "nic" : "netdev",
1719b62e39b4SAlistair Francis                         nc->name);
1720fd9400b3SPaolo Bonzini         }
1721fd9400b3SPaolo Bonzini     }
1722fd9400b3SPaolo Bonzini 
1723fd9400b3SPaolo Bonzini     /* Check that all NICs requested via -net nic actually got created.
1724fd9400b3SPaolo Bonzini      * NICs created via -device don't need to be checked here because
1725fd9400b3SPaolo Bonzini      * they are always instantiated.
1726fd9400b3SPaolo Bonzini      */
1727fd9400b3SPaolo Bonzini     for (i = 0; i < MAX_NICS; i++) {
1728fd9400b3SPaolo Bonzini         NICInfo *nd = &nd_table[i];
1729fd9400b3SPaolo Bonzini         if (nd->used && !nd->instantiated) {
17308297be80SAlistair Francis             warn_report("requested NIC (%s, model %s) "
17318297be80SAlistair Francis                         "was not created (not supported by this machine?)",
1732fd9400b3SPaolo Bonzini                         nd->name ? nd->name : "anonymous",
1733fd9400b3SPaolo Bonzini                         nd->model ? nd->model : "unspecified");
1734fd9400b3SPaolo Bonzini         }
1735fd9400b3SPaolo Bonzini     }
1736fd9400b3SPaolo Bonzini }
1737fd9400b3SPaolo Bonzini 
net_init_client(void * dummy,QemuOpts * opts,Error ** errp)173828d0de7aSMarkus Armbruster static int net_init_client(void *dummy, QemuOpts *opts, Error **errp)
1739fd9400b3SPaolo Bonzini {
1740*fa62cb98SDavid Woodhouse     const char *model = qemu_opt_get(opts, "model");
174164f75f57SDavid Woodhouse 
174264f75f57SDavid Woodhouse     if (is_nic_model_help_option(model)) {
174364f75f57SDavid Woodhouse         return 0;
174464f75f57SDavid Woodhouse     }
174564f75f57SDavid Woodhouse 
174634f708b0SThomas Huth     return net_client_init(opts, false, errp);
1747fd9400b3SPaolo Bonzini }
1748fd9400b3SPaolo Bonzini 
net_init_netdev(void * dummy,QemuOpts * opts,Error ** errp)174928d0de7aSMarkus Armbruster static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp)
1750fd9400b3SPaolo Bonzini {
1751ad6f932fSPaolo Bonzini     const char *type = qemu_opt_get(opts, "type");
1752ad6f932fSPaolo Bonzini 
1753ad6f932fSPaolo Bonzini     if (type && is_help_option(type)) {
1754ad6f932fSPaolo Bonzini         show_netdevs();
1755ad6f932fSPaolo Bonzini         exit(0);
1756ad6f932fSPaolo Bonzini     }
175734f708b0SThomas Huth     return net_client_init(opts, true, errp);
1758fd9400b3SPaolo Bonzini }
1759fd9400b3SPaolo Bonzini 
176078cd6f7bSThomas Huth /* For the convenience "--nic" parameter */
net_param_nic(void * dummy,QemuOpts * opts,Error ** errp)176178cd6f7bSThomas Huth static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp)
176278cd6f7bSThomas Huth {
176378cd6f7bSThomas Huth     char *mac, *nd_id;
176478cd6f7bSThomas Huth     int idx, ret;
176578cd6f7bSThomas Huth     NICInfo *ni;
176678cd6f7bSThomas Huth     const char *type;
176778cd6f7bSThomas Huth 
176878cd6f7bSThomas Huth     type = qemu_opt_get(opts, "type");
176927c81924SThomas Huth     if (type) {
177027c81924SThomas Huth         if (g_str_equal(type, "none")) {
177178cd6f7bSThomas Huth             return 0;    /* Nothing to do, default_net is cleared in vl.c */
177278cd6f7bSThomas Huth         }
177327c81924SThomas Huth         if (is_help_option(type)) {
177427c81924SThomas Huth             GPtrArray *nic_models = qemu_get_nic_models(TYPE_DEVICE);
1775481434f9SDavid Woodhouse             int i;
177627c81924SThomas Huth             show_netdevs();
177727c81924SThomas Huth             printf("\n");
1778481434f9SDavid Woodhouse             printf("Available NIC models "
1779481434f9SDavid Woodhouse                    "(use -nic model=help for a filtered list):\n");
1780481434f9SDavid Woodhouse             for (i = 0 ; nic_models->pdata[i]; i++) {
1781481434f9SDavid Woodhouse                 printf("%s\n", (char *)nic_models->pdata[i]);
1782481434f9SDavid Woodhouse             }
178327c81924SThomas Huth             g_ptr_array_free(nic_models, true);
178427c81924SThomas Huth             exit(0);
178527c81924SThomas Huth         }
178627c81924SThomas Huth     }
178778cd6f7bSThomas Huth 
178878cd6f7bSThomas Huth     idx = nic_get_free_idx();
178978cd6f7bSThomas Huth     if (idx == -1 || nb_nics >= MAX_NICS) {
179078cd6f7bSThomas Huth         error_setg(errp, "no more on-board/default NIC slots available");
1791fd9400b3SPaolo Bonzini         return -1;
1792fd9400b3SPaolo Bonzini     }
1793fd9400b3SPaolo Bonzini 
179478cd6f7bSThomas Huth     if (!type) {
179578cd6f7bSThomas Huth         qemu_opt_set(opts, "type", "user", &error_abort);
179678cd6f7bSThomas Huth     }
179778cd6f7bSThomas Huth 
179878cd6f7bSThomas Huth     ni = &nd_table[idx];
179978cd6f7bSThomas Huth     memset(ni, 0, sizeof(*ni));
180078cd6f7bSThomas Huth     ni->model = qemu_opt_get_del(opts, "model");
180178cd6f7bSThomas Huth 
180264f75f57SDavid Woodhouse     if (is_nic_model_help_option(ni->model)) {
18032cdeca04SDavid Woodhouse         return 0;
18042cdeca04SDavid Woodhouse     }
18052cdeca04SDavid Woodhouse 
180678cd6f7bSThomas Huth     /* Create an ID if the user did not specify one */
180778cd6f7bSThomas Huth     nd_id = g_strdup(qemu_opts_id(opts));
180878cd6f7bSThomas Huth     if (!nd_id) {
180927eb3722SThomas Huth         nd_id = id_generate(ID_NET);
181078cd6f7bSThomas Huth         qemu_opts_set_id(opts, nd_id);
181178cd6f7bSThomas Huth     }
181278cd6f7bSThomas Huth 
181378cd6f7bSThomas Huth     /* Handle MAC address */
181478cd6f7bSThomas Huth     mac = qemu_opt_get_del(opts, "mac");
181578cd6f7bSThomas Huth     if (mac) {
181678cd6f7bSThomas Huth         ret = net_parse_macaddr(ni->macaddr.a, mac);
181778cd6f7bSThomas Huth         g_free(mac);
181878cd6f7bSThomas Huth         if (ret) {
181978cd6f7bSThomas Huth             error_setg(errp, "invalid syntax for ethernet address");
18209d946191SThomas Huth             goto out;
182178cd6f7bSThomas Huth         }
182278cd6f7bSThomas Huth         if (is_multicast_ether_addr(ni->macaddr.a)) {
182378cd6f7bSThomas Huth             error_setg(errp, "NIC cannot have multicast MAC address");
18249d946191SThomas Huth             ret = -1;
18259d946191SThomas Huth             goto out;
182678cd6f7bSThomas Huth         }
182778cd6f7bSThomas Huth     }
182878cd6f7bSThomas Huth     qemu_macaddr_default_if_unset(&ni->macaddr);
182978cd6f7bSThomas Huth 
183078cd6f7bSThomas Huth     ret = net_client_init(opts, true, errp);
183178cd6f7bSThomas Huth     if (ret == 0) {
183278cd6f7bSThomas Huth         ni->netdev = qemu_find_netdev(nd_id);
183378cd6f7bSThomas Huth         ni->used = true;
183478cd6f7bSThomas Huth         nb_nics++;
183578cd6f7bSThomas Huth     }
183678cd6f7bSThomas Huth 
18379d946191SThomas Huth out:
183878cd6f7bSThomas Huth     g_free(nd_id);
1839fd9400b3SPaolo Bonzini     return ret;
1840fd9400b3SPaolo Bonzini }
1841fd9400b3SPaolo Bonzini 
netdev_init_modern(void)1842f3eedcddSLaurent Vivier static void netdev_init_modern(void)
1843f3eedcddSLaurent Vivier {
1844f3eedcddSLaurent Vivier     while (!QSIMPLEQ_EMPTY(&nd_queue)) {
1845f3eedcddSLaurent Vivier         NetdevQueueEntry *nd = QSIMPLEQ_FIRST(&nd_queue);
1846f3eedcddSLaurent Vivier 
1847f3eedcddSLaurent Vivier         QSIMPLEQ_REMOVE_HEAD(&nd_queue, entry);
1848f3eedcddSLaurent Vivier         loc_push_restore(&nd->loc);
1849f3eedcddSLaurent Vivier         net_client_init1(nd->nd, true, &error_fatal);
1850f3eedcddSLaurent Vivier         loc_pop(&nd->loc);
1851f3eedcddSLaurent Vivier         qapi_free_Netdev(nd->nd);
1852f3eedcddSLaurent Vivier         g_free(nd);
1853f3eedcddSLaurent Vivier     }
1854f3eedcddSLaurent Vivier }
1855f3eedcddSLaurent Vivier 
net_init_clients(void)1856d63ef17bSLaurent Vivier void net_init_clients(void)
1857fd9400b3SPaolo Bonzini {
1858ca77d85eSMichael S. Tsirkin     net_change_state_entry =
1859ca77d85eSMichael S. Tsirkin         qemu_add_vm_change_state_handler(net_vm_change_state_handler, NULL);
1860ca77d85eSMichael S. Tsirkin 
1861fd9400b3SPaolo Bonzini     QTAILQ_INIT(&net_clients);
1862fd9400b3SPaolo Bonzini 
1863f3eedcddSLaurent Vivier     netdev_init_modern();
1864f3eedcddSLaurent Vivier 
1865d63ef17bSLaurent Vivier     qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL,
1866d63ef17bSLaurent Vivier                       &error_fatal);
1867fd9400b3SPaolo Bonzini 
1868d63ef17bSLaurent Vivier     qemu_opts_foreach(qemu_find_opts("nic"), net_param_nic, NULL,
1869d63ef17bSLaurent Vivier                       &error_fatal);
187078cd6f7bSThomas Huth 
1871d63ef17bSLaurent Vivier     qemu_opts_foreach(qemu_find_opts("net"), net_init_client, NULL,
1872d63ef17bSLaurent Vivier                       &error_fatal);
1873fd9400b3SPaolo Bonzini }
1874fd9400b3SPaolo Bonzini 
1875f3eedcddSLaurent Vivier /*
1876f3eedcddSLaurent Vivier  * Does this -netdev argument use modern rather than traditional syntax?
1877f3eedcddSLaurent Vivier  * Modern syntax is to be parsed with netdev_parse_modern().
1878f3eedcddSLaurent Vivier  * Traditional syntax is to be parsed with net_client_parse().
1879f3eedcddSLaurent Vivier  */
netdev_is_modern(const char * optstr)188073071f19SPhilippe Mathieu-Daudé bool netdev_is_modern(const char *optstr)
1881f3eedcddSLaurent Vivier {
18825166fe0aSLaurent Vivier     QemuOpts *opts;
18835166fe0aSLaurent Vivier     bool is_modern;
18845166fe0aSLaurent Vivier     const char *type;
18855166fe0aSLaurent Vivier     static QemuOptsList dummy_opts = {
18865166fe0aSLaurent Vivier         .name = "netdev",
18875166fe0aSLaurent Vivier         .implied_opt_name = "type",
18885166fe0aSLaurent Vivier         .head = QTAILQ_HEAD_INITIALIZER(dummy_opts.head),
18895166fe0aSLaurent Vivier         .desc = { { } },
18905166fe0aSLaurent Vivier     };
18915166fe0aSLaurent Vivier 
189273071f19SPhilippe Mathieu-Daudé     if (optstr[0] == '{') {
18935166fe0aSLaurent Vivier         /* This is JSON, which means it's modern syntax */
18945166fe0aSLaurent Vivier         return true;
18955166fe0aSLaurent Vivier     }
18965166fe0aSLaurent Vivier 
18975166fe0aSLaurent Vivier     opts = qemu_opts_create(&dummy_opts, NULL, false, &error_abort);
189873071f19SPhilippe Mathieu-Daudé     qemu_opts_do_parse(opts, optstr, dummy_opts.implied_opt_name,
18995166fe0aSLaurent Vivier                        &error_abort);
19005166fe0aSLaurent Vivier     type = qemu_opt_get(opts, "type");
19015166fe0aSLaurent Vivier     is_modern = !g_strcmp0(type, "stream") || !g_strcmp0(type, "dgram");
19025166fe0aSLaurent Vivier 
19035166fe0aSLaurent Vivier     qemu_opts_reset(&dummy_opts);
19045166fe0aSLaurent Vivier 
19055166fe0aSLaurent Vivier     return is_modern;
1906f3eedcddSLaurent Vivier }
1907f3eedcddSLaurent Vivier 
1908f3eedcddSLaurent Vivier /*
1909f3eedcddSLaurent Vivier  * netdev_parse_modern() uses modern, more expressive syntax than
1910f3eedcddSLaurent Vivier  * net_client_parse(), but supports only the -netdev option.
1911f3eedcddSLaurent Vivier  * netdev_parse_modern() appends to @nd_queue, whereas net_client_parse()
1912f3eedcddSLaurent Vivier  * appends to @qemu_netdev_opts.
1913f3eedcddSLaurent Vivier  */
netdev_parse_modern(const char * optstr)191473071f19SPhilippe Mathieu-Daudé void netdev_parse_modern(const char *optstr)
1915f3eedcddSLaurent Vivier {
1916f3eedcddSLaurent Vivier     Visitor *v;
1917f3eedcddSLaurent Vivier     NetdevQueueEntry *nd;
1918f3eedcddSLaurent Vivier 
191973071f19SPhilippe Mathieu-Daudé     v = qobject_input_visitor_new_str(optstr, "type", &error_fatal);
1920f3eedcddSLaurent Vivier     nd = g_new(NetdevQueueEntry, 1);
1921f3eedcddSLaurent Vivier     visit_type_Netdev(v, NULL, &nd->nd, &error_fatal);
1922f3eedcddSLaurent Vivier     visit_free(v);
1923f3eedcddSLaurent Vivier     loc_save(&nd->loc);
1924f3eedcddSLaurent Vivier 
1925f3eedcddSLaurent Vivier     QSIMPLEQ_INSERT_TAIL(&nd_queue, nd, entry);
1926f3eedcddSLaurent Vivier }
1927f3eedcddSLaurent Vivier 
net_client_parse(QemuOptsList * opts_list,const char * optstr)192873071f19SPhilippe Mathieu-Daudé void net_client_parse(QemuOptsList *opts_list, const char *optstr)
1929fd9400b3SPaolo Bonzini {
193073071f19SPhilippe Mathieu-Daudé     if (!qemu_opts_parse_noisily(opts_list, optstr, true)) {
193121fccb2cSLaurent Vivier         exit(1);
1932fd9400b3SPaolo Bonzini     }
1933fd9400b3SPaolo Bonzini }
1934fd9400b3SPaolo Bonzini 
1935fd9400b3SPaolo Bonzini /* From FreeBSD */
1936fd9400b3SPaolo Bonzini /* XXX: optimize */
net_crc32(const uint8_t * p,int len)1937eaba8f34SMark Cave-Ayland uint32_t net_crc32(const uint8_t *p, int len)
1938fd9400b3SPaolo Bonzini {
1939fd9400b3SPaolo Bonzini     uint32_t crc;
1940fd9400b3SPaolo Bonzini     int carry, i, j;
1941fd9400b3SPaolo Bonzini     uint8_t b;
1942fd9400b3SPaolo Bonzini 
1943fd9400b3SPaolo Bonzini     crc = 0xffffffff;
1944eaba8f34SMark Cave-Ayland     for (i = 0; i < len; i++) {
1945eaba8f34SMark Cave-Ayland         b = *p++;
1946fd9400b3SPaolo Bonzini         for (j = 0; j < 8; j++) {
1947fd9400b3SPaolo Bonzini             carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
1948fd9400b3SPaolo Bonzini             crc <<= 1;
1949fd9400b3SPaolo Bonzini             b >>= 1;
1950fd9400b3SPaolo Bonzini             if (carry) {
1951eaba8f34SMark Cave-Ayland                 crc = ((crc ^ POLYNOMIAL_BE) | carry);
1952fd9400b3SPaolo Bonzini             }
1953fd9400b3SPaolo Bonzini         }
1954fd9400b3SPaolo Bonzini     }
1955eaba8f34SMark Cave-Ayland 
1956eaba8f34SMark Cave-Ayland     return crc;
1957eaba8f34SMark Cave-Ayland }
1958eaba8f34SMark Cave-Ayland 
net_crc32_le(const uint8_t * p,int len)1959f1a7deb9SMark Cave-Ayland uint32_t net_crc32_le(const uint8_t *p, int len)
1960f1a7deb9SMark Cave-Ayland {
1961f1a7deb9SMark Cave-Ayland     uint32_t crc;
1962f1a7deb9SMark Cave-Ayland     int carry, i, j;
1963f1a7deb9SMark Cave-Ayland     uint8_t b;
1964f1a7deb9SMark Cave-Ayland 
1965f1a7deb9SMark Cave-Ayland     crc = 0xffffffff;
1966f1a7deb9SMark Cave-Ayland     for (i = 0; i < len; i++) {
1967f1a7deb9SMark Cave-Ayland         b = *p++;
1968f1a7deb9SMark Cave-Ayland         for (j = 0; j < 8; j++) {
1969f1a7deb9SMark Cave-Ayland             carry = (crc & 0x1) ^ (b & 0x01);
1970f1a7deb9SMark Cave-Ayland             crc >>= 1;
1971f1a7deb9SMark Cave-Ayland             b >>= 1;
1972f1a7deb9SMark Cave-Ayland             if (carry) {
1973f1a7deb9SMark Cave-Ayland                 crc ^= POLYNOMIAL_LE;
1974f1a7deb9SMark Cave-Ayland             }
1975f1a7deb9SMark Cave-Ayland         }
1976f1a7deb9SMark Cave-Ayland     }
1977f1a7deb9SMark Cave-Ayland 
1978f1a7deb9SMark Cave-Ayland     return crc;
1979f1a7deb9SMark Cave-Ayland }
1980f1a7deb9SMark Cave-Ayland 
19814d454574SPaolo Bonzini QemuOptsList qemu_netdev_opts = {
19824d454574SPaolo Bonzini     .name = "netdev",
19834d454574SPaolo Bonzini     .implied_opt_name = "type",
19844d454574SPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
19854d454574SPaolo Bonzini     .desc = {
19864d454574SPaolo Bonzini         /*
19874d454574SPaolo Bonzini          * no elements => accept any params
19884d454574SPaolo Bonzini          * validation will happen later
19894d454574SPaolo Bonzini          */
19904d454574SPaolo Bonzini         { /* end of list */ }
19914d454574SPaolo Bonzini     },
19924d454574SPaolo Bonzini };
19934d454574SPaolo Bonzini 
199478cd6f7bSThomas Huth QemuOptsList qemu_nic_opts = {
199578cd6f7bSThomas Huth     .name = "nic",
199678cd6f7bSThomas Huth     .implied_opt_name = "type",
199778cd6f7bSThomas Huth     .head = QTAILQ_HEAD_INITIALIZER(qemu_nic_opts.head),
199878cd6f7bSThomas Huth     .desc = {
199978cd6f7bSThomas Huth         /*
200078cd6f7bSThomas Huth          * no elements => accept any params
200178cd6f7bSThomas Huth          * validation will happen later
200278cd6f7bSThomas Huth          */
200378cd6f7bSThomas Huth         { /* end of list */ }
200478cd6f7bSThomas Huth     },
200578cd6f7bSThomas Huth };
200678cd6f7bSThomas Huth 
20074d454574SPaolo Bonzini QemuOptsList qemu_net_opts = {
20084d454574SPaolo Bonzini     .name = "net",
20094d454574SPaolo Bonzini     .implied_opt_name = "type",
20104d454574SPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
20114d454574SPaolo Bonzini     .desc = {
20124d454574SPaolo Bonzini         /*
20134d454574SPaolo Bonzini          * no elements => accept any params
20144d454574SPaolo Bonzini          * validation will happen later
20154d454574SPaolo Bonzini          */
20164d454574SPaolo Bonzini         { /* end of list */ }
20174d454574SPaolo Bonzini     },
20184d454574SPaolo Bonzini };
201916a3df40SZhang Chen 
net_socket_rs_init(SocketReadState * rs,SocketReadStateFinalize * finalize,bool vnet_hdr)202016a3df40SZhang Chen void net_socket_rs_init(SocketReadState *rs,
20213cde5ea2SZhang Chen                         SocketReadStateFinalize *finalize,
20223cde5ea2SZhang Chen                         bool vnet_hdr)
202316a3df40SZhang Chen {
202416a3df40SZhang Chen     rs->state = 0;
20253cde5ea2SZhang Chen     rs->vnet_hdr = vnet_hdr;
202616a3df40SZhang Chen     rs->index = 0;
202716a3df40SZhang Chen     rs->packet_len = 0;
20283cde5ea2SZhang Chen     rs->vnet_hdr_len = 0;
202916a3df40SZhang Chen     memset(rs->buf, 0, sizeof(rs->buf));
203016a3df40SZhang Chen     rs->finalize = finalize;
203116a3df40SZhang Chen }
203216a3df40SZhang Chen 
203316a3df40SZhang Chen /*
203416a3df40SZhang Chen  * Returns
2035e9e0a585SZhang Chen  * 0: success
2036e9e0a585SZhang Chen  * -1: error occurs
203716a3df40SZhang Chen  */
net_fill_rstate(SocketReadState * rs,const uint8_t * buf,int size)203816a3df40SZhang Chen int net_fill_rstate(SocketReadState *rs, const uint8_t *buf, int size)
203916a3df40SZhang Chen {
204016a3df40SZhang Chen     unsigned int l;
204116a3df40SZhang Chen 
204216a3df40SZhang Chen     while (size > 0) {
20433cde5ea2SZhang Chen         /* Reassemble a packet from the network.
20443cde5ea2SZhang Chen          * 0 = getting length.
20453cde5ea2SZhang Chen          * 1 = getting vnet header length.
20463cde5ea2SZhang Chen          * 2 = getting data.
20473cde5ea2SZhang Chen          */
20483cde5ea2SZhang Chen         switch (rs->state) {
204916a3df40SZhang Chen         case 0:
205016a3df40SZhang Chen             l = 4 - rs->index;
205116a3df40SZhang Chen             if (l > size) {
205216a3df40SZhang Chen                 l = size;
205316a3df40SZhang Chen             }
205416a3df40SZhang Chen             memcpy(rs->buf + rs->index, buf, l);
205516a3df40SZhang Chen             buf += l;
205616a3df40SZhang Chen             size -= l;
205716a3df40SZhang Chen             rs->index += l;
205816a3df40SZhang Chen             if (rs->index == 4) {
205916a3df40SZhang Chen                 /* got length */
206016a3df40SZhang Chen                 rs->packet_len = ntohl(*(uint32_t *)rs->buf);
206116a3df40SZhang Chen                 rs->index = 0;
20623cde5ea2SZhang Chen                 if (rs->vnet_hdr) {
206316a3df40SZhang Chen                     rs->state = 1;
20643cde5ea2SZhang Chen                 } else {
20653cde5ea2SZhang Chen                     rs->state = 2;
20663cde5ea2SZhang Chen                     rs->vnet_hdr_len = 0;
20673cde5ea2SZhang Chen                 }
206816a3df40SZhang Chen             }
206916a3df40SZhang Chen             break;
207016a3df40SZhang Chen         case 1:
20713cde5ea2SZhang Chen             l = 4 - rs->index;
20723cde5ea2SZhang Chen             if (l > size) {
20733cde5ea2SZhang Chen                 l = size;
20743cde5ea2SZhang Chen             }
20753cde5ea2SZhang Chen             memcpy(rs->buf + rs->index, buf, l);
20763cde5ea2SZhang Chen             buf += l;
20773cde5ea2SZhang Chen             size -= l;
20783cde5ea2SZhang Chen             rs->index += l;
20793cde5ea2SZhang Chen             if (rs->index == 4) {
20803cde5ea2SZhang Chen                 /* got vnet header length */
20813cde5ea2SZhang Chen                 rs->vnet_hdr_len = ntohl(*(uint32_t *)rs->buf);
20823cde5ea2SZhang Chen                 rs->index = 0;
20833cde5ea2SZhang Chen                 rs->state = 2;
20843cde5ea2SZhang Chen             }
20853cde5ea2SZhang Chen             break;
20863cde5ea2SZhang Chen         case 2:
208716a3df40SZhang Chen             l = rs->packet_len - rs->index;
208816a3df40SZhang Chen             if (l > size) {
208916a3df40SZhang Chen                 l = size;
209016a3df40SZhang Chen             }
209116a3df40SZhang Chen             if (rs->index + l <= sizeof(rs->buf)) {
209216a3df40SZhang Chen                 memcpy(rs->buf + rs->index, buf, l);
209316a3df40SZhang Chen             } else {
209416a3df40SZhang Chen                 fprintf(stderr, "serious error: oversized packet received,"
209516a3df40SZhang Chen                     "connection terminated.\n");
209616a3df40SZhang Chen                 rs->index = rs->state = 0;
209716a3df40SZhang Chen                 return -1;
209816a3df40SZhang Chen             }
209916a3df40SZhang Chen 
210016a3df40SZhang Chen             rs->index += l;
210116a3df40SZhang Chen             buf += l;
210216a3df40SZhang Chen             size -= l;
210316a3df40SZhang Chen             if (rs->index >= rs->packet_len) {
210416a3df40SZhang Chen                 rs->index = 0;
210516a3df40SZhang Chen                 rs->state = 0;
2106e79cd406SDaniel P. Berrange                 assert(rs->finalize);
210716a3df40SZhang Chen                 rs->finalize(rs);
210816a3df40SZhang Chen             }
210916a3df40SZhang Chen             break;
211016a3df40SZhang Chen         }
211116a3df40SZhang Chen     }
2112e9e0a585SZhang Chen 
2113e9e0a585SZhang Chen     assert(size == 0);
211416a3df40SZhang Chen     return 0;
211516a3df40SZhang Chen }
2116