|
@@ -21,6 +21,8 @@
|
|
|
#include "hw/virtio/virtio-net.h"
|
|
|
#include "net/vhost_net.h"
|
|
|
#include "hw/virtio/virtio-bus.h"
|
|
|
+#include "qapi/qmp/qjson.h"
|
|
|
+#include "monitor/monitor.h"
|
|
|
|
|
|
#define VIRTIO_NET_VM_VERSION 11
|
|
|
|
|
@@ -192,6 +194,105 @@ static void virtio_net_set_link_status(NetClientState *nc)
|
|
|
virtio_net_set_status(vdev, vdev->status);
|
|
|
}
|
|
|
|
|
|
+static void rxfilter_notify(NetClientState *nc)
|
|
|
+{
|
|
|
+ QObject *event_data;
|
|
|
+ VirtIONet *n = qemu_get_nic_opaque(nc);
|
|
|
+
|
|
|
+ if (nc->rxfilter_notify_enabled) {
|
|
|
+ if (n->netclient_name) {
|
|
|
+ event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }",
|
|
|
+ n->netclient_name,
|
|
|
+ object_get_canonical_path(OBJECT(n->qdev)));
|
|
|
+ } else {
|
|
|
+ event_data = qobject_from_jsonf("{ 'path': %s }",
|
|
|
+ object_get_canonical_path(OBJECT(n->qdev)));
|
|
|
+ }
|
|
|
+ monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data);
|
|
|
+ qobject_decref(event_data);
|
|
|
+
|
|
|
+ /* disable event notification to avoid events flooding */
|
|
|
+ nc->rxfilter_notify_enabled = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static char *mac_strdup_printf(const uint8_t *mac)
|
|
|
+{
|
|
|
+ return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0],
|
|
|
+ mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
+}
|
|
|
+
|
|
|
+static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
|
|
|
+{
|
|
|
+ VirtIONet *n = qemu_get_nic_opaque(nc);
|
|
|
+ RxFilterInfo *info;
|
|
|
+ strList *str_list, *entry;
|
|
|
+ intList *int_list, *int_entry;
|
|
|
+ int i, j;
|
|
|
+
|
|
|
+ info = g_malloc0(sizeof(*info));
|
|
|
+ info->name = g_strdup(nc->name);
|
|
|
+ info->promiscuous = n->promisc;
|
|
|
+
|
|
|
+ if (n->nouni) {
|
|
|
+ info->unicast = RX_STATE_NONE;
|
|
|
+ } else if (n->alluni) {
|
|
|
+ info->unicast = RX_STATE_ALL;
|
|
|
+ } else {
|
|
|
+ info->unicast = RX_STATE_NORMAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (n->nomulti) {
|
|
|
+ info->multicast = RX_STATE_NONE;
|
|
|
+ } else if (n->allmulti) {
|
|
|
+ info->multicast = RX_STATE_ALL;
|
|
|
+ } else {
|
|
|
+ info->multicast = RX_STATE_NORMAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->broadcast_allowed = n->nobcast;
|
|
|
+ info->multicast_overflow = n->mac_table.multi_overflow;
|
|
|
+ info->unicast_overflow = n->mac_table.uni_overflow;
|
|
|
+
|
|
|
+ info->main_mac = mac_strdup_printf(n->mac);
|
|
|
+
|
|
|
+ str_list = NULL;
|
|
|
+ for (i = 0; i < n->mac_table.first_multi; i++) {
|
|
|
+ entry = g_malloc0(sizeof(*entry));
|
|
|
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
|
|
|
+ entry->next = str_list;
|
|
|
+ str_list = entry;
|
|
|
+ }
|
|
|
+ info->unicast_table = str_list;
|
|
|
+
|
|
|
+ str_list = NULL;
|
|
|
+ for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
|
|
|
+ entry = g_malloc0(sizeof(*entry));
|
|
|
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
|
|
|
+ entry->next = str_list;
|
|
|
+ str_list = entry;
|
|
|
+ }
|
|
|
+ info->multicast_table = str_list;
|
|
|
+
|
|
|
+ int_list = NULL;
|
|
|
+ for (i = 0; i < MAX_VLAN >> 5; i++) {
|
|
|
+ for (j = 0; n->vlans[i] && j < 0x1f; j++) {
|
|
|
+ if (n->vlans[i] & (1U << j)) {
|
|
|
+ int_entry = g_malloc0(sizeof(*int_entry));
|
|
|
+ int_entry->value = (i << 5) + j;
|
|
|
+ int_entry->next = int_list;
|
|
|
+ int_list = int_entry;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ info->vlan_table = int_list;
|
|
|
+
|
|
|
+ /* enable event notification after query */
|
|
|
+ nc->rxfilter_notify_enabled = 1;
|
|
|
+
|
|
|
+ return info;
|
|
|
+}
|
|
|
+
|
|
|
static void virtio_net_reset(VirtIODevice *vdev)
|
|
|
{
|
|
|
VirtIONet *n = VIRTIO_NET(vdev);
|
|
@@ -420,6 +521,7 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
|
|
|
{
|
|
|
uint8_t on;
|
|
|
size_t s;
|
|
|
+ NetClientState *nc = qemu_get_queue(n->nic);
|
|
|
|
|
|
s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
|
|
|
if (s != sizeof(on)) {
|
|
@@ -442,6 +544,8 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
|
|
|
return VIRTIO_NET_ERR;
|
|
|
}
|
|
|
|
|
|
+ rxfilter_notify(nc);
|
|
|
+
|
|
|
return VIRTIO_NET_OK;
|
|
|
}
|
|
|
|
|
@@ -487,6 +591,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
|
|
|
{
|
|
|
struct virtio_net_ctrl_mac mac_data;
|
|
|
size_t s;
|
|
|
+ NetClientState *nc = qemu_get_queue(n->nic);
|
|
|
|
|
|
if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
|
|
|
if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
|
|
@@ -495,6 +600,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
|
|
|
s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
|
|
|
assert(s == sizeof(n->mac));
|
|
|
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
|
|
|
+ rxfilter_notify(nc);
|
|
|
+
|
|
|
return VIRTIO_NET_OK;
|
|
|
}
|
|
|
|
|
@@ -512,19 +619,19 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
|
|
|
sizeof(mac_data.entries));
|
|
|
mac_data.entries = ldl_p(&mac_data.entries);
|
|
|
if (s != sizeof(mac_data.entries)) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
iov_discard_front(&iov, &iov_cnt, s);
|
|
|
|
|
|
if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
if (mac_data.entries <= MAC_TABLE_ENTRIES) {
|
|
|
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
|
|
|
mac_data.entries * ETH_ALEN);
|
|
|
if (s != mac_data.entries * ETH_ALEN) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
n->mac_table.in_use += mac_data.entries;
|
|
|
} else {
|
|
@@ -539,27 +646,33 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
|
|
|
sizeof(mac_data.entries));
|
|
|
mac_data.entries = ldl_p(&mac_data.entries);
|
|
|
if (s != sizeof(mac_data.entries)) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
iov_discard_front(&iov, &iov_cnt, s);
|
|
|
|
|
|
if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
|
|
|
if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
|
|
|
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
|
|
|
mac_data.entries * ETH_ALEN);
|
|
|
if (s != mac_data.entries * ETH_ALEN) {
|
|
|
- return VIRTIO_NET_ERR;
|
|
|
+ goto error;
|
|
|
}
|
|
|
n->mac_table.in_use += mac_data.entries;
|
|
|
} else {
|
|
|
n->mac_table.multi_overflow = 1;
|
|
|
}
|
|
|
|
|
|
+ rxfilter_notify(nc);
|
|
|
+
|
|
|
return VIRTIO_NET_OK;
|
|
|
+
|
|
|
+error:
|
|
|
+ rxfilter_notify(nc);
|
|
|
+ return VIRTIO_NET_ERR;
|
|
|
}
|
|
|
|
|
|
static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
|
|
@@ -567,6 +680,7 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
|
|
|
{
|
|
|
uint16_t vid;
|
|
|
size_t s;
|
|
|
+ NetClientState *nc = qemu_get_queue(n->nic);
|
|
|
|
|
|
s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
|
|
|
vid = lduw_p(&vid);
|
|
@@ -584,6 +698,8 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
|
|
|
else
|
|
|
return VIRTIO_NET_ERR;
|
|
|
|
|
|
+ rxfilter_notify(nc);
|
|
|
+
|
|
|
return VIRTIO_NET_OK;
|
|
|
}
|
|
|
|
|
@@ -1312,6 +1428,7 @@ static NetClientInfo net_virtio_info = {
|
|
|
.receive = virtio_net_receive,
|
|
|
.cleanup = virtio_net_cleanup,
|
|
|
.link_status_changed = virtio_net_set_link_status,
|
|
|
+ .query_rx_filter = virtio_net_query_rxfilter,
|
|
|
};
|
|
|
|
|
|
static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
|
|
@@ -1373,6 +1490,7 @@ static int virtio_net_device_init(VirtIODevice *vdev)
|
|
|
|
|
|
DeviceState *qdev = DEVICE(vdev);
|
|
|
VirtIONet *n = VIRTIO_NET(vdev);
|
|
|
+ NetClientState *nc;
|
|
|
|
|
|
virtio_init(VIRTIO_DEVICE(n), "virtio-net", VIRTIO_ID_NET,
|
|
|
n->config_size);
|
|
@@ -1439,6 +1557,9 @@ static int virtio_net_device_init(VirtIODevice *vdev)
|
|
|
|
|
|
n->vlans = g_malloc0(MAX_VLAN >> 3);
|
|
|
|
|
|
+ nc = qemu_get_queue(n->nic);
|
|
|
+ nc->rxfilter_notify_enabled = 1;
|
|
|
+
|
|
|
n->qdev = qdev;
|
|
|
register_savevm(qdev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
|
|
|
virtio_net_save, virtio_net_load, n);
|