diff options
-rw-r--r-- | QMP/qmp-events.txt | 17 | ||||
-rw-r--r-- | hw/i386/pc.c | 2 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 133 | ||||
-rw-r--r-- | hw/pci-bridge/i82801b11.c | 2 | ||||
-rw-r--r-- | hw/pci/pci.c | 5 | ||||
-rw-r--r-- | hw/pci/pci_bridge.c | 2 | ||||
-rw-r--r-- | include/hw/i386/pc.h | 4 | ||||
-rw-r--r-- | include/hw/pci/pci_bridge.h | 1 | ||||
-rw-r--r-- | include/hw/pci/pci_ids.h | 2 | ||||
-rw-r--r-- | include/hw/virtio/virtio-net.h | 1 | ||||
-rw-r--r-- | include/hw/virtio/virtio.h | 2 | ||||
-rw-r--r-- | include/monitor/monitor.h | 1 | ||||
-rw-r--r-- | include/net/net.h | 3 | ||||
-rw-r--r-- | monitor.c | 1 | ||||
-rw-r--r-- | net/net.c | 48 | ||||
-rw-r--r-- | qapi-schema.json | 76 | ||||
-rw-r--r-- | qmp-commands.hx | 63 |
17 files changed, 353 insertions, 10 deletions
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt index 24e804e948..39b6016460 100644 --- a/QMP/qmp-events.txt +++ b/QMP/qmp-events.txt @@ -172,6 +172,23 @@ Data: }, "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +NIC_RX_FILTER_CHANGED +----------------- + +The event is emitted once until the query command is executed, +the first event will always be emitted. + +Data: + +- "name": net client name (json-string) +- "path": device path (json-string) + +{ "event": "NIC_RX_FILTER_CHANGED", + "data": { "name": "vnet0", + "path": "/machine/peripheral/vnet0/virtio-backend" }, + "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +} + RESET ----- diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c5d8570af1..2a8756321b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1005,7 +1005,7 @@ typedef struct PcRomPciInfo { static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info) { PcRomPciInfo *info; - if (!guest_info->has_pci_info) { + if (!guest_info->has_pci_info || !guest_info->fw_cfg) { return; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 1ea95564a5..679f50c33a 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -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); diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 5807a92d7f..b98bfb0664 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -69,7 +69,7 @@ static int i82801b11_bridge_initfn(PCIDevice *d) if (rc < 0) { goto err_bridge; } - pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB); + pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB); return 0; err_bridge: diff --git a/hw/pci/pci.c b/hw/pci/pci.c index dcc85ef0af..81cf5a958c 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -378,6 +378,7 @@ int pci_bus_num(PCIBus *s) static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) { PCIDevice *s = container_of(pv, PCIDevice, config); + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s); uint8_t *config; int i; @@ -395,6 +396,10 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) memcpy(s->config, config, size); pci_update_mappings(s); + if (pc->is_bridge) { + PCIBridge *b = container_of(s, PCIBridge, dev); + pci_bridge_update_mappings(b); + } memory_region_set_enabled(&s->bus_master_enable_region, pci_get_word(s->config + PCI_COMMAND) diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index ecdeab0d58..02a396b01c 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -224,7 +224,7 @@ static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) g_free(w); } -static void pci_bridge_update_mappings(PCIBridge *br) +void pci_bridge_update_mappings(PCIBridge *br) { PCIBridgeWindows *w = br->windows; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 61ff154c7d..7fb97b08a2 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -231,6 +231,10 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .driver = "Nehalem-" TYPE_X86_CPU,\ .property = "level",\ .value = stringify(2),\ + },{\ + .driver = "virtio-net-pci",\ + .property = "any_layout",\ + .value = "off",\ } #define PC_COMPAT_1_4 \ diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index 1868f7aea8..1d8f9973c7 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -37,6 +37,7 @@ PCIBus *pci_bridge_get_sec_bus(PCIBridge *br); pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type); pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type); +void pci_bridge_update_mappings(PCIBridge *br); void pci_bridge_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len); void pci_bridge_disable_base_limit(PCIDevice *dev); diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 08f8161524..d7933bfd16 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -39,7 +39,7 @@ #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCI_CLASS_BRIDGE_ISA 0x0601 #define PCI_CLASS_BRIDGE_PCI 0x0604 -#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 +#define PCI_CLASS_BRIDGE_PCI_INF_SUB 0x01 #define PCI_CLASS_BRIDGE_OTHER 0x0680 #define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index b315ac91a4..df60f16a3e 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -243,6 +243,7 @@ struct virtio_net_ctrl_mq { #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ + DEFINE_PROP_BIT("any_layout", _state, _field, VIRTIO_F_ANY_LAYOUT, true), \ DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index a6c5c5380c..5d1d2be295 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -43,6 +43,8 @@ /* We notify when the ring is completely used, even if the guest is suppressing * callbacks */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 +/* Can the device handle any descriptor layout? */ +#define VIRTIO_F_ANY_LAYOUT 27 /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 /* The Guest publishes the used index for which it expects an interrupt diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 1a6cfcf687..1942cc42fe 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -41,6 +41,7 @@ typedef enum MonitorEvent { QEVENT_BLOCK_JOB_READY, QEVENT_DEVICE_DELETED, QEVENT_DEVICE_TRAY_MOVED, + QEVENT_NIC_RX_FILTER_CHANGED, QEVENT_SUSPEND, QEVENT_SUSPEND_DISK, QEVENT_WAKEUP, diff --git a/include/net/net.h b/include/net/net.h index 43d85a16eb..30e4b04066 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -49,6 +49,7 @@ typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int); typedef void (NetCleanup) (NetClientState *); typedef void (LinkStatusChanged)(NetClientState *); typedef void (NetClientDestructor)(NetClientState *); +typedef RxFilterInfo *(QueryRxFilter)(NetClientState *); typedef struct NetClientInfo { NetClientOptionsKind type; @@ -59,6 +60,7 @@ typedef struct NetClientInfo { NetCanReceive *can_receive; NetCleanup *cleanup; LinkStatusChanged *link_status_changed; + QueryRxFilter *query_rx_filter; NetPoll *poll; } NetClientInfo; @@ -74,6 +76,7 @@ struct NetClientState { unsigned receive_disabled : 1; NetClientDestructor *destructor; unsigned int queue_index; + unsigned rxfilter_notify_enabled:1; }; typedef struct NICState { @@ -490,6 +490,7 @@ static const char *monitor_event_names[] = { [QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY", [QEVENT_DEVICE_DELETED] = "DEVICE_DELETED", [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", + [QEVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED", [QEVENT_SUSPEND] = "SUSPEND", [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", [QEVENT_WAKEUP] = "WAKEUP", @@ -961,6 +961,54 @@ void print_net_client(Monitor *mon, NetClientState *nc) nc->info_str); } +RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, + Error **errp) +{ + NetClientState *nc; + RxFilterInfoList *filter_list = NULL, *last_entry = NULL; + + QTAILQ_FOREACH(nc, &net_clients, next) { + RxFilterInfoList *entry; + RxFilterInfo *info; + + if (has_name && strcmp(nc->name, name) != 0) { + continue; + } + + /* only query rx-filter information of NIC */ + if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) { + if (has_name) { + error_setg(errp, "net client(%s) isn't a NIC", name); + break; + } + continue; + } + + if (nc->info->query_rx_filter) { + info = nc->info->query_rx_filter(nc); + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + + if (!filter_list) { + filter_list = entry; + } else { + last_entry->next = entry; + } + last_entry = entry; + } else if (has_name) { + error_setg(errp, "net client(%s) doesn't support" + " rx-filter querying", name); + break; + } + } + + if (filter_list == NULL && !error_is_set(errp) && has_name) { + error_setg(errp, "invalid net client name: %s", name); + } + + return filter_list; +} + void do_info_network(Monitor *mon, const QDict *qdict) { NetClientState *nc, *peer; diff --git a/qapi-schema.json b/qapi-schema.json index 7b9fef1bd1..8d33d527d3 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3679,3 +3679,79 @@ '*cpuid-input-ecx': 'int', 'cpuid-register': 'X86CPURegister32', 'features': 'int' } } + +## +# @RxState: +# +# Packets receiving state +# +# @normal: filter assigned packets according to the mac-table +# +# @none: don't receive any assigned packet +# +# @all: receive all assigned packets +# +# Since: 1.6 +## +{ 'enum': 'RxState', 'data': [ 'normal', 'none', 'all' ] } + +## +# @RxFilterInfo: +# +# Rx-filter information for a NIC. +# +# @name: net client name +# +# @promiscuous: whether promiscuous mode is enabled +# +# @multicast: multicast receive state +# +# @unicast: unicast receive state +# +# @broadcast-allowed: whether to receive broadcast +# +# @multicast-overflow: multicast table is overflowed or not +# +# @unicast-overflow: unicast table is overflowed or not +# +# @main-mac: the main macaddr string +# +# @vlan-table: a list of active vlan id +# +# @unicast-table: a list of unicast macaddr string +# +# @multicast-table: a list of multicast macaddr string +# +# Since 1.6 +## + +{ 'type': 'RxFilterInfo', + 'data': { + 'name': 'str', + 'promiscuous': 'bool', + 'multicast': 'RxState', + 'unicast': 'RxState', + 'broadcast-allowed': 'bool', + 'multicast-overflow': 'bool', + 'unicast-overflow': 'bool', + 'main-mac': 'str', + 'vlan-table': ['int'], + 'unicast-table': ['str'], + 'multicast-table': ['str'] }} + +## +# @query-rx-filter: +# +# Return rx-filter information for all NICs (or for the given NIC). +# +# @name: #optional net client name +# +# Returns: list of @RxFilterInfo for all NICs (or for the given NIC). +# Returns an error if the given @name doesn't exist, or given +# NIC doesn't support rx-filter querying, or given net client +# isn't a NIC. +# +# Since: 1.6 +## +{ 'command': 'query-rx-filter', 'data': { '*name': 'str' }, + 'returns': ['RxFilterInfo'] } diff --git a/qmp-commands.hx b/qmp-commands.hx index e075df423a..1529e354d1 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3047,3 +3047,66 @@ Example: <- { "return": {} } EQMP + { + .name = "query-rx-filter", + .args_type = "name:s?", + .mhandler.cmd_new = qmp_marshal_input_query_rx_filter, + }, + +SQMP +query-rx-filter +--------------- + +Show rx-filter information. + +Returns a json-array of rx-filter information for all NICs (or for the +given NIC), returning an error if the given NIC doesn't exist, or +given NIC doesn't support rx-filter querying, or given net client +isn't a NIC. + +The query will clear the event notification flag of each NIC, then qemu +will start to emit event to QMP monitor. + +Each array entry contains the following: + +- "name": net client name (json-string) +- "promiscuous": promiscuous mode is enabled (json-bool) +- "multicast": multicast receive state (one of 'normal', 'none', 'all') +- "unicast": unicast receive state (one of 'normal', 'none', 'all') +- "broadcast-allowed": allow to receive broadcast (json-bool) +- "multicast-overflow": multicast table is overflowed (json-bool) +- "unicast-overflow": unicast table is overflowed (json-bool) +- "main-mac": main macaddr string (json-string) +- "vlan-table": a json-array of active vlan id +- "unicast-table": a json-array of unicast macaddr string +- "multicast-table": a json-array of multicast macaddr string + +Example: + +-> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } } +<- { "return": [ + { + "promiscuous": true, + "name": "vnet0", + "main-mac": "52:54:00:12:34:56", + "unicast": "normal", + "vlan-table": [ + 4, + 0 + ], + "unicast-table": [ + ], + "multicast": "normal", + "multicast-overflow": false, + "unicast-overflow": false, + "multicast-table": [ + "01:00:5e:00:00:01", + "33:33:00:00:00:01", + "33:33:ff:12:34:56" + ], + "broadcast-allowed": false + } + ] + } + +EQMP |