-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1
 
 iQEcBAABAgAGBQJj7xKYAAoJEO8Ells5jWIRDZQH/Rao24sq3j97qE5RzekvANzq
 GnHUyLnl3yeOSNumv2BJInZTvgUpYL2etGQr3DtGRwOrr7w1vKB3zhY3V3jQefkh
 f4rsEGkamL/qM2N2cGUIUSqevo7OGnP8aQojpEi4MWWZ30B3L6jqd4NqyA1gyndV
 1eBkpR+BY2PjcLbgvFUZEXeAn/vapE5NKULXUGhg5mMvgwYH3CgZXpqqkxr876za
 S4rZMtReXKNeid14Z35SUjJdV2WKYmo/lN9+GQxF2YNLmDC3RtuFQVm038erSqvs
 uLVSg8tiIlCyOcSDpR/BARNrxVwzlJp5X6ocapHubS/i0Rp/Zo7ezSk/XWH1gfU=
 =UbzF
 -----END PGP SIGNATURE-----

Merge tag 'net-pull-request' of https://github.com/jasowang/qemu into staging

# -----BEGIN PGP SIGNATURE-----
# Version: GnuPG v1
#
# iQEcBAABAgAGBQJj7xKYAAoJEO8Ells5jWIRDZQH/Rao24sq3j97qE5RzekvANzq
# GnHUyLnl3yeOSNumv2BJInZTvgUpYL2etGQr3DtGRwOrr7w1vKB3zhY3V3jQefkh
# f4rsEGkamL/qM2N2cGUIUSqevo7OGnP8aQojpEi4MWWZ30B3L6jqd4NqyA1gyndV
# 1eBkpR+BY2PjcLbgvFUZEXeAn/vapE5NKULXUGhg5mMvgwYH3CgZXpqqkxr876za
# S4rZMtReXKNeid14Z35SUjJdV2WKYmo/lN9+GQxF2YNLmDC3RtuFQVm038erSqvs
# uLVSg8tiIlCyOcSDpR/BARNrxVwzlJp5X6ocapHubS/i0Rp/Zo7ezSk/XWH1gfU=
# =UbzF
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 17 Feb 2023 05:37:28 GMT
# gpg:                using RSA key EF04965B398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [marginal]
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* tag 'net-pull-request' of https://github.com/jasowang/qemu:
  vdpa: fix VHOST_BACKEND_F_IOTLB_ASID flag check
  net: stream: add a new option to automatically reconnect
  vmnet: stop recieving events when VM is stopped
  net: Increase L2TPv3 buffer to fit jumboframes
  hw/net/vmxnet3: allow VMXNET3_MAX_MTU itself as a value
  hw/net/lan9118: log [read|write]b when mode_16bit is enabled rather than abort
  net: Replace "Supported NIC models" with "Available NIC models"
  net: Restore printing of the help text with "-nic help"
  net: Move the code to collect available NIC models to a separate function

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2023-02-21 11:28:31 +00:00
commit 79b677d658
13 changed files with 272 additions and 61 deletions

View File

@ -15,7 +15,6 @@
#include "migration/vmstate.h" #include "migration/vmstate.h"
#include "net/net.h" #include "net/net.h"
#include "net/eth.h" #include "net/eth.h"
#include "hw/hw.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "hw/net/lan9118.h" #include "hw/net/lan9118.h"
#include "hw/ptimer.h" #include "hw/ptimer.h"
@ -32,12 +31,8 @@
#ifdef DEBUG_LAN9118 #ifdef DEBUG_LAN9118
#define DPRINTF(fmt, ...) \ #define DPRINTF(fmt, ...) \
do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
#define BADF(fmt, ...) \
do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#else #else
#define DPRINTF(fmt, ...) do {} while(0) #define DPRINTF(fmt, ...) do {} while(0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#endif #endif
/* The tx and rx fifo ports are a range of aliased 32-bit registers */ /* The tx and rx fifo ports are a range of aliased 32-bit registers */
@ -848,7 +843,8 @@ static uint32_t do_phy_read(lan9118_state *s, int reg)
case 30: /* Interrupt mask */ case 30: /* Interrupt mask */
return s->phy_int_mask; return s->phy_int_mask;
default: default:
BADF("PHY read reg %d\n", reg); qemu_log_mask(LOG_GUEST_ERROR,
"do_phy_read: PHY read reg %d\n", reg);
return 0; return 0;
} }
} }
@ -876,7 +872,8 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
phy_update_irq(s); phy_update_irq(s);
break; break;
default: default:
BADF("PHY write reg %d = 0x%04x\n", reg, val); qemu_log_mask(LOG_GUEST_ERROR,
"do_phy_write: PHY write reg %d = 0x%04x\n", reg, val);
} }
} }
@ -1209,7 +1206,8 @@ static void lan9118_16bit_mode_write(void *opaque, hwaddr offset,
return; return;
} }
hw_error("lan9118_write: Bad size 0x%x\n", size); qemu_log_mask(LOG_GUEST_ERROR,
"lan9118_16bit_mode_write: Bad size 0x%x\n", size);
} }
static uint64_t lan9118_readl(void *opaque, hwaddr offset, static uint64_t lan9118_readl(void *opaque, hwaddr offset,
@ -1324,7 +1322,8 @@ static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset,
return lan9118_readl(opaque, offset, size); return lan9118_readl(opaque, offset, size);
} }
hw_error("lan9118_read: Bad size 0x%x\n", size); qemu_log_mask(LOG_GUEST_ERROR,
"lan9118_16bit_mode_read: Bad size 0x%x\n", size);
return 0; return 0;
} }

View File

@ -1441,7 +1441,7 @@ static void vmxnet3_activate_device(VMXNET3State *s)
vmxnet3_setup_rx_filtering(s); vmxnet3_setup_rx_filtering(s);
/* Cache fields from shared memory */ /* Cache fields from shared memory */
s->mtu = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.misc.mtu); s->mtu = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.misc.mtu);
assert(VMXNET3_MIN_MTU <= s->mtu && s->mtu < VMXNET3_MAX_MTU); assert(VMXNET3_MIN_MTU <= s->mtu && s->mtu <= VMXNET3_MAX_MTU);
VMW_CFPRN("MTU is %u", s->mtu); VMW_CFPRN("MTU is %u", s->mtu);
s->max_rx_frags = s->max_rx_frags =

View File

@ -1789,7 +1789,6 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
const char *default_devaddr) const char *default_devaddr)
{ {
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
GSList *list;
GPtrArray *pci_nic_models; GPtrArray *pci_nic_models;
PCIBus *bus; PCIBus *bus;
PCIDevice *pci_dev; PCIDevice *pci_dev;
@ -1804,33 +1803,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
nd->model = g_strdup("virtio-net-pci"); nd->model = g_strdup("virtio-net-pci");
} }
list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false); pci_nic_models = qemu_get_nic_models(TYPE_PCI_DEVICE);
pci_nic_models = g_ptr_array_new();
while (list) {
DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data,
TYPE_DEVICE);
GSList *next;
if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) &&
dc->user_creatable) {
const char *name = object_class_get_name(list->data);
/*
* A network device might also be something else than a NIC, see
* e.g. the "rocker" device. Thus we have to look for the "netdev"
* property, too. Unfortunately, some devices like virtio-net only
* create this property during instance_init, so we have to create
* a temporary instance here to be able to check it.
*/
Object *obj = object_new_with_class(OBJECT_CLASS(dc));
if (object_property_find(obj, "netdev")) {
g_ptr_array_add(pci_nic_models, (gpointer)name);
}
object_unref(obj);
}
next = list->next;
g_slist_free_1(list);
list = next;
}
g_ptr_array_add(pci_nic_models, NULL);
if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) { if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) {
exit(0); exit(0);

View File

@ -203,6 +203,20 @@ void net_socket_rs_init(SocketReadState *rs,
bool vnet_hdr); bool vnet_hdr);
NetClientState *qemu_get_peer(NetClientState *nc, int queue_index); NetClientState *qemu_get_peer(NetClientState *nc, int queue_index);
/**
* qemu_get_nic_models:
* @device_type: Defines which devices should be taken into consideration
* (e.g. TYPE_DEVICE for all devices, or TYPE_PCI_DEVICE for PCI)
*
* Get an array of pointers to names of NIC devices that are available in
* the QEMU binary. The array is terminated with a NULL pointer entry.
* The caller is responsible for freeing the memory when it is not required
* anymore, e.g. with g_ptr_array_free(..., true).
*
* Returns: Pointer to the array that contains the pointers to the names.
*/
GPtrArray *qemu_get_nic_models(const char *device_type);
/* NIC info */ /* NIC info */
#define MAX_NICS 8 #define MAX_NICS 8

View File

@ -42,7 +42,7 @@
*/ */
#define BUFFER_ALIGN sysconf(_SC_PAGESIZE) #define BUFFER_ALIGN sysconf(_SC_PAGESIZE)
#define BUFFER_SIZE 2048 #define BUFFER_SIZE 16384
#define IOVSIZE 2 #define IOVSIZE 2
#define MAX_L2TPV3_MSGCNT 64 #define MAX_L2TPV3_MSGCNT 64
#define MAX_L2TPV3_IOVCNT (MAX_L2TPV3_MSGCNT * IOVSIZE) #define MAX_L2TPV3_IOVCNT (MAX_L2TPV3_MSGCNT * IOVSIZE)

View File

@ -899,6 +899,40 @@ static int nic_get_free_idx(void)
return -1; return -1;
} }
GPtrArray *qemu_get_nic_models(const char *device_type)
{
GPtrArray *nic_models = g_ptr_array_new();
GSList *list = object_class_get_list_sorted(device_type, false);
while (list) {
DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data,
TYPE_DEVICE);
GSList *next;
if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) &&
dc->user_creatable) {
const char *name = object_class_get_name(list->data);
/*
* A network device might also be something else than a NIC, see
* e.g. the "rocker" device. Thus we have to look for the "netdev"
* property, too. Unfortunately, some devices like virtio-net only
* create this property during instance_init, so we have to create
* a temporary instance here to be able to check it.
*/
Object *obj = object_new_with_class(OBJECT_CLASS(dc));
if (object_property_find(obj, "netdev")) {
g_ptr_array_add(nic_models, (gpointer)name);
}
object_unref(obj);
}
next = list->next;
g_slist_free_1(list);
list = next;
}
g_ptr_array_add(nic_models, NULL);
return nic_models;
}
int qemu_show_nic_models(const char *arg, const char *const *models) int qemu_show_nic_models(const char *arg, const char *const *models)
{ {
int i; int i;
@ -907,7 +941,7 @@ int qemu_show_nic_models(const char *arg, const char *const *models)
return 0; return 0;
} }
printf("Supported NIC models:\n"); printf("Available NIC models:\n");
for (i = 0 ; models[i]; i++) { for (i = 0 ; models[i]; i++) {
printf("%s\n", models[i]); printf("%s\n", models[i]);
} }
@ -1508,8 +1542,18 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp)
const char *type; const char *type;
type = qemu_opt_get(opts, "type"); type = qemu_opt_get(opts, "type");
if (type && g_str_equal(type, "none")) { if (type) {
return 0; /* Nothing to do, default_net is cleared in vl.c */ if (g_str_equal(type, "none")) {
return 0; /* Nothing to do, default_net is cleared in vl.c */
}
if (is_help_option(type)) {
GPtrArray *nic_models = qemu_get_nic_models(TYPE_DEVICE);
show_netdevs();
printf("\n");
qemu_show_nic_models(type, (const char **)nic_models->pdata);
g_ptr_array_free(nic_models, true);
exit(0);
}
} }
idx = nic_get_free_idx(); idx = nic_get_free_idx();

View File

@ -39,6 +39,8 @@
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "io/net-listener.h" #include "io/net-listener.h"
#include "qapi/qapi-events-net.h" #include "qapi/qapi-events-net.h"
#include "qapi/qapi-visit-sockets.h"
#include "qapi/clone-visitor.h"
typedef struct NetStreamState { typedef struct NetStreamState {
NetClientState nc; NetClientState nc;
@ -49,11 +51,15 @@ typedef struct NetStreamState {
guint ioc_write_tag; guint ioc_write_tag;
SocketReadState rs; SocketReadState rs;
unsigned int send_index; /* number of bytes sent*/ unsigned int send_index; /* number of bytes sent*/
uint32_t reconnect;
guint timer_tag;
SocketAddress *addr;
} NetStreamState; } NetStreamState;
static void net_stream_listen(QIONetListener *listener, static void net_stream_listen(QIONetListener *listener,
QIOChannelSocket *cioc, QIOChannelSocket *cioc,
void *opaque); void *opaque);
static void net_stream_arm_reconnect(NetStreamState *s);
static gboolean net_stream_writable(QIOChannel *ioc, static gboolean net_stream_writable(QIOChannel *ioc,
GIOCondition condition, GIOCondition condition,
@ -170,6 +176,7 @@ static gboolean net_stream_send(QIOChannel *ioc,
qemu_set_info_str(&s->nc, "%s", ""); qemu_set_info_str(&s->nc, "%s", "");
qapi_event_send_netdev_stream_disconnected(s->nc.name); qapi_event_send_netdev_stream_disconnected(s->nc.name);
net_stream_arm_reconnect(s);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
@ -187,6 +194,14 @@ static gboolean net_stream_send(QIOChannel *ioc,
static void net_stream_cleanup(NetClientState *nc) static void net_stream_cleanup(NetClientState *nc)
{ {
NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc);
if (s->timer_tag) {
g_source_remove(s->timer_tag);
s->timer_tag = 0;
}
if (s->addr) {
qapi_free_SocketAddress(s->addr);
s->addr = NULL;
}
if (s->ioc) { if (s->ioc) {
if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) { if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) {
if (s->ioc_read_tag) { if (s->ioc_read_tag) {
@ -346,12 +361,37 @@ static void net_stream_client_connected(QIOTask *task, gpointer opaque)
error: error:
object_unref(OBJECT(s->ioc)); object_unref(OBJECT(s->ioc));
s->ioc = NULL; s->ioc = NULL;
net_stream_arm_reconnect(s);
}
static gboolean net_stream_reconnect(gpointer data)
{
NetStreamState *s = data;
QIOChannelSocket *sioc;
s->timer_tag = 0;
sioc = qio_channel_socket_new();
s->ioc = QIO_CHANNEL(sioc);
qio_channel_socket_connect_async(sioc, s->addr,
net_stream_client_connected, s,
NULL, NULL);
return G_SOURCE_REMOVE;
}
static void net_stream_arm_reconnect(NetStreamState *s)
{
if (s->reconnect && s->timer_tag == 0) {
s->timer_tag = g_timeout_add_seconds(s->reconnect,
net_stream_reconnect, s);
}
} }
static int net_stream_client_init(NetClientState *peer, static int net_stream_client_init(NetClientState *peer,
const char *model, const char *model,
const char *name, const char *name,
SocketAddress *addr, SocketAddress *addr,
uint32_t reconnect,
Error **errp) Error **errp)
{ {
NetStreamState *s; NetStreamState *s;
@ -364,6 +404,10 @@ static int net_stream_client_init(NetClientState *peer,
s->ioc = QIO_CHANNEL(sioc); s->ioc = QIO_CHANNEL(sioc);
s->nc.link_down = true; s->nc.link_down = true;
s->reconnect = reconnect;
if (reconnect) {
s->addr = QAPI_CLONE(SocketAddress, addr);
}
qio_channel_socket_connect_async(sioc, addr, qio_channel_socket_connect_async(sioc, addr,
net_stream_client_connected, s, net_stream_client_connected, s,
NULL, NULL); NULL, NULL);
@ -380,7 +424,14 @@ int net_init_stream(const Netdev *netdev, const char *name,
sock = &netdev->u.stream; sock = &netdev->u.stream;
if (!sock->has_server || !sock->server) { if (!sock->has_server || !sock->server) {
return net_stream_client_init(peer, "stream", name, sock->addr, errp); return net_stream_client_init(peer, "stream", name, sock->addr,
sock->has_reconnect ? sock->reconnect : 0,
errp);
}
if (sock->has_reconnect) {
error_setg(errp, "'reconnect' option is incompatible with "
"socket in server mode");
return -1;
} }
return net_stream_server_init(peer, "stream", name, sock->addr, errp); return net_stream_server_init(peer, "stream", name, sock->addr, errp);
} }

View File

@ -384,7 +384,7 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc)
g_strerror(errno), errno); g_strerror(errno), errno);
return -1; return -1;
} }
if (!(backend_features & VHOST_BACKEND_F_IOTLB_ASID) || if (!(backend_features & BIT_ULL(VHOST_BACKEND_F_IOTLB_ASID)) ||
!vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) { !vhost_vdpa_net_valid_svq_features(v->dev->features, NULL)) {
return 0; return 0;
} }

View File

@ -17,6 +17,7 @@
#include "clients.h" #include "clients.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "sysemu/runstate.h"
#include <vmnet/vmnet.h> #include <vmnet/vmnet.h>
#include <dispatch/dispatch.h> #include <dispatch/dispatch.h>
@ -242,6 +243,35 @@ static void vmnet_bufs_init(VmnetState *s)
} }
} }
/**
* Called on state change to un-register/re-register handlers
*/
static void vmnet_vm_state_change_cb(void *opaque, bool running, RunState state)
{
VmnetState *s = opaque;
if (running) {
vmnet_interface_set_event_callback(
s->vmnet_if,
VMNET_INTERFACE_PACKETS_AVAILABLE,
s->if_queue,
^(interface_event_t event_id, xpc_object_t event) {
assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE);
/*
* This function is being called from a non qemu thread, so
* we only schedule a BH, and do the rest of the io completion
* handling from vmnet_send_bh() which runs in a qemu context.
*/
qemu_bh_schedule(s->send_bh);
});
} else {
vmnet_interface_set_event_callback(
s->vmnet_if,
VMNET_INTERFACE_PACKETS_AVAILABLE,
NULL,
NULL);
}
}
int vmnet_if_create(NetClientState *nc, int vmnet_if_create(NetClientState *nc,
xpc_object_t if_desc, xpc_object_t if_desc,
@ -329,19 +359,9 @@ int vmnet_if_create(NetClientState *nc,
s->packets_send_current_pos = 0; s->packets_send_current_pos = 0;
s->packets_send_end_pos = 0; s->packets_send_end_pos = 0;
vmnet_interface_set_event_callback( vmnet_vm_state_change_cb(s, 1, RUN_STATE_RUNNING);
s->vmnet_if,
VMNET_INTERFACE_PACKETS_AVAILABLE, s->change = qemu_add_vm_change_state_handler(vmnet_vm_state_change_cb, s);
s->if_queue,
^(interface_event_t event_id, xpc_object_t event) {
assert(event_id == VMNET_INTERFACE_PACKETS_AVAILABLE);
/*
* This function is being called from a non qemu thread, so
* we only schedule a BH, and do the rest of the io completion
* handling from vmnet_send_bh() which runs in a qemu context.
*/
qemu_bh_schedule(s->send_bh);
});
return 0; return 0;
} }
@ -356,6 +376,8 @@ void vmnet_cleanup_common(NetClientState *nc)
return; return;
} }
vmnet_vm_state_change_cb(s, 0, RUN_STATE_SHUTDOWN);
qemu_del_vm_change_state_handler(s->change);
if_stopped_sem = dispatch_semaphore_create(0); if_stopped_sem = dispatch_semaphore_create(0);
vmnet_stop_interface( vmnet_stop_interface(
s->vmnet_if, s->vmnet_if,

View File

@ -45,6 +45,8 @@ typedef struct VmnetState {
int packets_send_end_pos; int packets_send_end_pos;
struct iovec iov_buf[VMNET_PACKETS_LIMIT]; struct iovec iov_buf[VMNET_PACKETS_LIMIT];
VMChangeStateEntry *change;
} VmnetState; } VmnetState;
const char *vmnet_status_map_str(vmnet_return_t status); const char *vmnet_status_map_str(vmnet_return_t status);

View File

@ -585,6 +585,10 @@
# @addr: socket address to listen on (server=true) # @addr: socket address to listen on (server=true)
# or connect to (server=false) # or connect to (server=false)
# @server: create server socket (default: false) # @server: create server socket (default: false)
# @reconnect: For a client socket, if a socket is disconnected,
# then attempt a reconnect after the given number of seconds.
# Setting this to zero disables this function. (default: 0)
# (since 8.0)
# #
# Only SocketAddress types 'unix', 'inet' and 'fd' are supported. # Only SocketAddress types 'unix', 'inet' and 'fd' are supported.
# #
@ -593,7 +597,8 @@
{ 'struct': 'NetdevStreamOptions', { 'struct': 'NetdevStreamOptions',
'data': { 'data': {
'addr': 'SocketAddress', 'addr': 'SocketAddress',
'*server': 'bool' } } '*server': 'bool',
'*reconnect': 'uint32' } }
## ##
# @NetdevDgramOptions: # @NetdevDgramOptions:

View File

@ -2762,9 +2762,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
"-netdev socket,id=str[,fd=h][,udp=host:port][,localaddr=host:port]\n" "-netdev socket,id=str[,fd=h][,udp=host:port][,localaddr=host:port]\n"
" configure a network backend to connect to another network\n" " configure a network backend to connect to another network\n"
" using an UDP tunnel\n" " using an UDP tunnel\n"
"-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off]\n" "-netdev stream,id=str[,server=on|off],addr.type=inet,addr.host=host,addr.port=port[,to=maxport][,numeric=on|off][,keep-alive=on|off][,mptcp=on|off][,addr.ipv4=on|off][,addr.ipv6=on|off][,reconnect=seconds]\n"
"-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off]\n" "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect=seconds]\n"
"-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor\n" "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect=seconds]\n"
" configure a network backend to connect to another network\n" " configure a network backend to connect to another network\n"
" using a socket connection in stream mode.\n" " using a socket connection in stream mode.\n"
"-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]\n" "-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]\n"

View File

@ -11,6 +11,10 @@
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include "../unit/socket-helpers.h" #include "../unit/socket-helpers.h"
#include "libqtest.h" #include "libqtest.h"
#include "qapi/qmp/qstring.h"
#include "qemu/sockets.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-sockets.h"
#define CONNECTION_TIMEOUT 60 #define CONNECTION_TIMEOUT 60
@ -142,6 +146,101 @@ static void test_stream_inet_ipv4(void)
qtest_quit(qts0); qtest_quit(qts0);
} }
static void wait_stream_connected(QTestState *qts, const char *id,
SocketAddress **addr)
{
QDict *resp, *data;
QString *qstr;
QObject *obj;
Visitor *v = NULL;
resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_CONNECTED");
g_assert_nonnull(resp);
data = qdict_get_qdict(resp, "data");
g_assert_nonnull(data);
qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
g_assert_nonnull(data);
g_assert(!strcmp(qstring_get_str(qstr), id));
obj = qdict_get(data, "addr");
v = qobject_input_visitor_new(obj);
visit_type_SocketAddress(v, NULL, addr, NULL);
visit_free(v);
qobject_unref(resp);
}
static void wait_stream_disconnected(QTestState *qts, const char *id)
{
QDict *resp, *data;
QString *qstr;
resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_DISCONNECTED");
g_assert_nonnull(resp);
data = qdict_get_qdict(resp, "data");
g_assert_nonnull(data);
qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
g_assert_nonnull(data);
g_assert(!strcmp(qstring_get_str(qstr), id));
qobject_unref(resp);
}
static void test_stream_inet_reconnect(void)
{
QTestState *qts0, *qts1;
int port;
SocketAddress *addr;
port = inet_get_free_port(false);
qts0 = qtest_initf("-nodefaults -M none "
"-netdev stream,id=st0,server=true,addr.type=inet,"
"addr.ipv4=on,addr.ipv6=off,"
"addr.host=127.0.0.1,addr.port=%d", port);
EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
qts1 = qtest_initf("-nodefaults -M none "
"-netdev stream,server=false,id=st0,addr.type=inet,"
"addr.ipv4=on,addr.ipv6=off,reconnect=1,"
"addr.host=127.0.0.1,addr.port=%d", port);
wait_stream_connected(qts0, "st0", &addr);
g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET);
g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1");
qapi_free_SocketAddress(addr);
/* kill server */
qtest_quit(qts0);
/* check client has been disconnected */
wait_stream_disconnected(qts1, "st0");
/* restart server */
qts0 = qtest_initf("-nodefaults -M none "
"-netdev stream,id=st0,server=true,addr.type=inet,"
"addr.ipv4=on,addr.ipv6=off,"
"addr.host=127.0.0.1,addr.port=%d", port);
/* wait connection events*/
wait_stream_connected(qts0, "st0", &addr);
g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET);
g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1");
qapi_free_SocketAddress(addr);
wait_stream_connected(qts1, "st0", &addr);
g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_INET);
g_assert_cmpstr(addr->u.inet.host, ==, "127.0.0.1");
g_assert_cmpint(atoi(addr->u.inet.port), ==, port);
qapi_free_SocketAddress(addr);
qtest_quit(qts1);
qtest_quit(qts0);
}
static void test_stream_inet_ipv6(void) static void test_stream_inet_ipv6(void)
{ {
QTestState *qts0, *qts1; QTestState *qts0, *qts1;
@ -418,6 +517,8 @@ int main(int argc, char **argv)
#ifndef _WIN32 #ifndef _WIN32
qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
#endif #endif
qtest_add_func("/netdev/stream/inet/reconnect",
test_stream_inet_reconnect);
} }
if (has_ipv6) { if (has_ipv6) {
qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6); qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);