@samitouri / QOSamiQemu / commits / ba78ac4ecd

net: add x-query-network QMP command

Add a structured x-query-network QMP command that returns network configuration as a NetworkInfo struct with pre-grouped hub information and a flat list of non-hub clients. Each hub includes its ports with inlined peer client info, and each client includes attached netfilter details. Refactor hmp_info_network() to consume only the QMP result instead of directly accessing internal net_clients state. This decouples HMP from internal data structures, making it possible to remove HMP support without losing programmatic network introspection. Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260828-qemu-no-hmp-v5-19-9227de146347@redhat.com>

Marc-André Lureau committed Aug 28, 2026 at 16:04 UTC ba78ac4ecd717c1c061f487bf571f130f31328e9
6 files changed +243 -44
include/net/net.h
+2 -1
@@ -274,7 +274,6 @@ DeviceState *qemu_create_nic_device(const char *typename, bool match_default,
274 void qemu_create_nic_bus_devices(BusState *bus, const char *parent_type,
275 const char *default_model,
276 const char *alias, const char *alias_target);
277 -void print_net_client(Monitor *mon, NetClientState *nc);
277 void net_socket_rs_init(SocketReadState *rs,
278 SocketReadStateFinalize *finalize,
279 bool vnet_hdr);
@@ -325,6 +324,8 @@ void netdev_add(QemuOpts *opts, Error **errp);
324
325 int net_hub_id_for_client(NetClientState *nc, int *id);
326
327 +NetworkClientInfo *net_client_info(NetClientState *nc);
328 +
329 #define DEFAULT_NETWORK_SCRIPT CONFIG_SYSCONFDIR "/qemu-ifup"
330 #define DEFAULT_NETWORK_DOWN_SCRIPT CONFIG_SYSCONFDIR "/qemu-ifdown"
331 #define DEFAULT_BRIDGE_HELPER CONFIG_QEMU_HELPERDIR "/qemu-bridge-helper"
net/hub.c
+17 -12
@@ -14,7 +14,7 @@
14
15 #include "qemu/osdep.h"
16 #include "qapi/error.h"
17 -#include "monitor/monitor.h"
17 +#include "qapi/util.h"
18 #include "net/net.h"
19 #include "clients.h"
20 #include "hub.h"
@@ -199,26 +199,31 @@ NetClientState *net_hub_add_port(int hub_id, const char *name,
199 return &port->nc;
200 }
201
202 -/**
203 - * Print hub configuration
204 - */
205 -void net_hub_info(Monitor *mon)
202 +NetHubInfoList *net_hub_query_info(void)
203 {
204 + NetHubInfoList *head = NULL, **tail = &head;
205 NetHub *hub;
208 - NetHubPort *port;
206
207 QLIST_FOREACH(hub, &hubs, next) {
211 - monitor_printf(mon, "hub %d\n", hub->id);
208 + NetHubInfo *hi = g_new0(NetHubInfo, 1);
209 + NetHubPortInfoList **ptail = &hi->ports;
210 + NetHubPort *port;
211 +
212 + hi->id = hub->id;
213 +
214 QLIST_FOREACH(port, &hub->ports, next) {
213 - monitor_printf(mon, " \\ %s", port->nc.name);
215 + NetHubPortInfo *pi = g_new0(NetHubPortInfo, 1);
216 + pi->name = g_strdup(port->nc.name);
217 if (port->nc.peer) {
215 - monitor_printf(mon, ": ");
216 - print_net_client(mon, port->nc.peer);
217 - } else {
218 - monitor_printf(mon, "\n");
218 + pi->peer = net_client_info(port->nc.peer);
219 }
220 + QAPI_LIST_APPEND(ptail, pi);
221 }
222 +
223 + QAPI_LIST_APPEND(tail, hi);
224 }
225 +
226 + return head;
227 }
228
229 /**
net/hub.h
+3 -1
@@ -15,10 +15,12 @@
15 #ifndef NET_HUB_H
16 #define NET_HUB_H
17
18 +#include "qapi/qapi-types-net.h"
19 +
20 NetClientState *net_hub_add_port(int hub_id, const char *name,
21 NetClientState *hubpeer);
20 -void net_hub_info(Monitor *mon);
22 void net_hub_check_clients(void);
23 bool net_hub_flush(NetClientState *nc);
24 +NetHubInfoList *net_hub_query_info(void);
25
26 #endif /* NET_HUB_H */
net/net-hmp-cmds.c
+44 -15
@@ -19,37 +19,66 @@
19 #include "monitor/hmp-completion.h"
20 #include "monitor/monitor.h"
21 #include "net/net.h"
22 -#include "net/hub.h"
22 #include "qapi/clone-visitor.h"
23 #include "qapi/qapi-commands-net.h"
24 #include "qapi/qapi-visit-net.h"
25 +#include "qapi/error.h"
26 #include "qobject/qdict.h"
27 #include "qemu/config-file.h"
28 #include "qemu/help_option.h"
29 #include "qemu/option.h"
30
31 -void hmp_info_network(Monitor *mon, const QDict *qdict)
31 +static void hmp_print_client_info(Monitor *mon, NetworkClientInfo *ci)
32 {
33 - NetClientState *nc, *peer;
34 - NetClientDriver type;
33 + NetFilterInfoList *f;
34 +
35 + monitor_printf(mon, "%s: index=%" PRIu32 ",type=%s,%s\n",
36 + ci->name, ci->queue_index,
37 + NetClientDriver_str(ci->type), ci->info_str);
38 + if (ci->filters) {
39 + monitor_printf(mon, "filters:\n");
40 + for (f = ci->filters; f; f = f->next) {
41 + monitor_printf(mon, " - %s: type=%s%s%s\n",
42 + f->value->name, f->value->type,
43 + f->value->info[0] ? "," : "", f->value->info);
44 + }
45 + }
46 +}
47
36 - net_hub_info(mon);
48 +void hmp_info_network(Monitor *mon, const QDict *qdict)
49 +{
50 + Error *err = NULL;
51 + g_autoptr(NetworkInfo) info = qmp_x_query_network(&err);
52 + NetHubInfoList *h;
53 + NetworkClientInfoList *entry;
54
38 - QTAILQ_FOREACH(nc, &net_clients, next) {
39 - peer = nc->peer;
40 - type = nc->info->type;
55 + if (hmp_handle_error(mon, err)) {
56 + return;
57 + }
58
42 - /* Skip if already printed in hub info */
43 - if (net_hub_id_for_client(nc, NULL) == 0) {
44 - continue;
59 + for (h = info->hubs; h; h = h->next) {
60 + NetHubPortInfoList *p;
61 +
62 + monitor_printf(mon, "hub %d\n", (int)h->value->id);
63 + for (p = h->value->ports; p; p = p->next) {
64 + if (p->value->peer) {
65 + monitor_printf(mon, " \\ %s: ", p->value->name);
66 + hmp_print_client_info(mon, p->value->peer);
67 + } else {
68 + monitor_printf(mon, " \\ %s\n", p->value->name);
69 + }
70 }
71 + }
72 +
73 + for (entry = info->clients; entry; entry = entry->next) {
74 + NetworkClientInfo *ci = entry->value;
75
47 - if (!peer || type == NET_CLIENT_DRIVER_NIC) {
48 - print_net_client(mon, nc);
76 + if (!ci->peer || ci->type == NET_CLIENT_DRIVER_NIC) {
77 + hmp_print_client_info(mon, ci);
78 } /* else it's a netdev connected to a NIC, printed with the NIC */
50 - if (peer && type == NET_CLIENT_DRIVER_NIC) {
79 + if (ci->peer && ci->type == NET_CLIENT_DRIVER_NIC) {
80 monitor_printf(mon, " \\ ");
52 - print_net_client(mon, peer);
81 + hmp_print_client_info(mon, ci->peer);
82 }
83 }
84 }
net/net.c
+55 -15
@@ -1525,14 +1525,14 @@ void qmp_netdev_del(const char *id, Error **errp)
1525 }
1526 }
1527
1528 -static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
1528 +static char *netfilter_get_info_str(NetFilterState *nf)
1529 {
1530 char *str;
1531 ObjectProperty *prop;
1532 ObjectPropertyIterator iter;
1533 Visitor *v;
1534 + GString *buf = g_string_new(NULL);
1535
1535 - /* generate info str */
1536 object_property_iter_init(&iter, OBJECT(nf));
1537 while ((prop = object_property_iter_next(&iter))) {
1538 if (!strcmp(prop->name, "type")) {
@@ -1542,29 +1542,69 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
1542 object_property_get(OBJECT(nf), prop->name, v, NULL);
1543 visit_complete(v, &str);
1544 visit_free(v);
1545 - monitor_printf(mon, ",%s=%s", prop->name, str);
1545 + if (buf->len > 0) {
1546 + g_string_append_c(buf, ',');
1547 + }
1548 + g_string_append_printf(buf, "%s=%s", prop->name, str);
1549 g_free(str);
1550 }
1548 - monitor_printf(mon, "\n");
1551 +
1552 + return g_string_free(buf, false);
1553 }
1554
1551 -void print_net_client(Monitor *mon, NetClientState *nc)
1555 +static NetworkClientInfo *net_client_info_no_peer(NetClientState *nc)
1556 {
1557 + NetworkClientInfo *info = g_new0(NetworkClientInfo, 1);
1558 NetFilterState *nf;
1559
1555 - monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
1556 - nc->queue_index,
1557 - NetClientDriver_str(nc->info->type),
1558 - nc->info_str);
1560 + info->name = g_strdup(nc->name);
1561 + info->queue_index = nc->queue_index;
1562 + info->type = nc->info->type;
1563 + info->info_str = g_strdup(nc->info_str);
1564 +
1565 if (!QTAILQ_EMPTY(&nc->filters)) {
1560 - monitor_printf(mon, "filters:\n");
1566 + NetFilterInfoList **ftail = &info->filters;
1567 + QTAILQ_FOREACH(nf, &nc->filters, next) {
1568 + NetFilterInfo *fi = g_new0(NetFilterInfo, 1);
1569 + fi->name = g_strdup(
1570 + object_get_canonical_path_component(OBJECT(nf)));
1571 + fi->type = g_strdup(object_get_typename(OBJECT(nf)));
1572 + fi->info = netfilter_get_info_str(nf);
1573 + QAPI_LIST_APPEND(ftail, fi);
1574 + }
1575 }
1562 - QTAILQ_FOREACH(nf, &nc->filters, next) {
1563 - monitor_printf(mon, " - %s: type=%s",
1564 - object_get_canonical_path_component(OBJECT(nf)),
1565 - object_get_typename(OBJECT(nf)));
1566 - netfilter_print_info(mon, nf);
1576 +
1577 + return info;
1578 +}
1579 +
1580 +NetworkClientInfo *net_client_info(NetClientState *nc)
1581 +{
1582 + NetworkClientInfo *info = net_client_info_no_peer(nc);
1583 +
1584 + if (nc->peer) {
1585 + info->peer = net_client_info_no_peer(nc->peer);
1586 }
1587 +
1588 + return info;
1589 +}
1590 +
1591 +NetworkInfo *qmp_x_query_network(Error **errp)
1592 +{
1593 + NetworkInfo *info = g_new0(NetworkInfo, 1);
1594 + NetworkClientInfoList **tail = &info->clients;
1595 + NetClientState *nc;
1596 +
1597 + info->hubs = net_hub_query_info();
1598 +
1599 + QTAILQ_FOREACH(nc, &net_clients, next) {
1600 + /* Skip if already gathered in hub info */
1601 + if (net_hub_id_for_client(nc, NULL) == 0) {
1602 + continue;
1603 + }
1604 + QAPI_LIST_APPEND(tail, net_client_info(nc));
1605 + }
1606 +
1607 + return info;
1608 }
1609
1610 RxFilterInfoList *qmp_query_rx_filter(const char *name, Error **errp)
qapi/net.json
+122
@@ -1305,3 +1305,125 @@
1305 'returns': ['UsernetInfo'],
1306 'if': 'CONFIG_SLIRP',
1307 'features': [ 'unstable' ] }
1308 +
1309 +##
1310 +# @NetFilterInfo:
1311 +#
1312 +# Information about a netfilter attached to a network client.
1313 +#
1314 +# @name: filter object name (QOM path component)
1315 +#
1316 +# @type: QOM type name (e.g. "filter-mirror")
1317 +#
1318 +# @info: filter properties as comma-separated key=value pairs
1319 +# (excluding @type)
1320 +#
1321 +# Since: 11.2
1322 +##
1323 +{ 'struct': 'NetFilterInfo',
1324 + 'data': {
1325 + 'name': 'str',
1326 + 'type': 'str',
1327 + 'info': 'str' } }
1328 +
1329 +##
1330 +# @NetworkClientInfo:
1331 +#
1332 +# Information about a network client.
1333 +#
1334 +# @name: unique network client identifier
1335 +#
1336 +# @queue-index: index of this queue (0 for single-queue clients)
1337 +#
1338 +# @type: network client driver type
1339 +#
1340 +# @info-str: driver-specific formatted information string (e.g.
1341 +# "model=e1000,macaddr=52:54:00:12:34:56")
1342 +#
1343 +# @peer: the connected peer client (always a leaf; the peer's own
1344 +# peer field is never populated)
1345 +#
1346 +# @filters: attached netfilters
1347 +#
1348 +# Since: 11.2
1349 +##
1350 +{ 'struct': 'NetworkClientInfo',
1351 + 'data': {
1352 + 'name': 'str',
1353 + 'queue-index': 'uint32',
1354 + 'type': 'NetClientDriver',
1355 + 'info-str': 'str',
1356 + '*peer': 'NetworkClientInfo',
1357 + 'filters': ['NetFilterInfo'] } }
1358 +
1359 +##
1360 +# @NetHubPortInfo:
1361 +#
1362 +# Information about a hub port.
1363 +#
1364 +# @name: hub port identifier
1365 +#
1366 +# @peer: the network client connected through this port
1367 +#
1368 +# Since: 11.2
1369 +##
1370 +{ 'struct': 'NetHubPortInfo',
1371 + 'data': {
1372 + 'name': 'str',
1373 + '*peer': 'NetworkClientInfo' } }
1374 +
1375 +##
1376 +# @NetHubInfo:
1377 +#
1378 +# Information about a network hub.
1379 +#
1380 +# @id: hub identifier
1381 +#
1382 +# @ports: list of ports on this hub
1383 +#
1384 +# Since: 11.2
1385 +##
1386 +{ 'struct': 'NetHubInfo',
1387 + 'data': {
1388 + 'id': 'int',
1389 + 'ports': ['NetHubPortInfo'] } }
1390 +
1391 +##
1392 +# @NetworkInfo:
1393 +#
1394 +# Information about the network configuration.
1395 +#
1396 +# @hubs: network hubs and their ports
1397 +#
1398 +# @clients: network clients not associated with a hub
1399 +#
1400 +# Since: 11.2
1401 +##
1402 +{ 'struct': 'NetworkInfo',
1403 + 'data': {
1404 + 'hubs': ['NetHubInfo'],
1405 + 'clients': ['NetworkClientInfo'] } }
1406 +
1407 +##
1408 +# @x-query-network:
1409 +#
1410 +# Query the network configuration including hubs and clients.
1411 +#
1412 +# Features:
1413 +#
1414 +# @unstable: This command is meant for debugging.
1415 +#
1416 +# Returns: @NetworkInfo describing hubs and clients.
1417 +#
1418 +# Since: 11.2
1419 +#
1420 +# .. qmp-example::
1421 +#
1422 +# -> { "execute": "x-query-network" }
1423 +# <- { "return": { "clients": [ { "name": "st0",
1424 +# "queue-index": 0, "type": "stream",
1425 +# "info-str": "listening" } ] } }
1426 +##
1427 +{ 'command': 'x-query-network',
1428 + 'returns': 'NetworkInfo',
1429 + 'features': [ 'unstable' ] }