Patchwork [RFC] net: introduce monitor command to query mactables

login
register
mail settings
Submitter Amos Kong
Date April 11, 2013, 3:11 p.m.
Message ID <1365693118-6603-1-git-send-email-akong@redhat.com>
Download mbox | patch
Permalink /patch/235790/
State New
Headers show

Comments

Amos Kong - April 11, 2013, 3:11 p.m.
We want to do macvtap programming by libvirt, this patch added
a monitor command to query rx mode information. I will also
work on another patch to add QMP event notification for rx-mode
changing. Libvirt will sync the rx-mode change to macvtap
devices, there maybe exist an un-controlled delay, guests
normally expect rx mode updates immediately, it's another
separate issue.

Output examples:

(qemu) info mac_table
virtio-net-pci.0:
 \ promisc: on
 \ allmulti: off
 \ alluni: off
 \ nomulti: off
 \ nouni: off
 \ nobcast: off
 \ multi_overflow: off
 \ uni_overflow: off
 \ multicast:
    01:00:5e:00:00:01
    33:33:00:00:00:01
    33:33:ff:12:34:56

QMP example is in qmp-commands.hx

Signed-off-by: Amos Kong <akong@redhat.com>
---
Welcome your comments, thanks
---
 hmp-commands.hx   |    2 +
 hmp.c             |   63 +++++++++++++++++++++++++++++++++++++++++++++++
 hmp.h             |    1 +
 hw/virtio-net.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/net/net.h |    2 +
 monitor.c         |    7 +++++
 net/net.c         |   23 +++++++++++++++++
 qapi-schema.json  |   37 ++++++++++++++++++++++++++++
 qmp-commands.hx   |   45 ++++++++++++++++++++++++++++++++++
 9 files changed, 250 insertions(+), 0 deletions(-)
Michael S. Tsirkin - April 11, 2013, 3:33 p.m.
On Thu, Apr 11, 2013 at 11:11:58PM +0800, Amos Kong wrote:
> We want to do macvtap programming by libvirt, this patch added
> a monitor command to query rx mode information. I will also
> work on another patch to add QMP event notification for rx-mode
> changing. Libvirt will sync the rx-mode change to macvtap
> devices, there maybe exist an un-controlled delay, guests
> normally expect rx mode updates immediately, it's another
> separate issue.
> 
> Output examples:
> 
> (qemu) info mac_table
> virtio-net-pci.0:
>  \ promisc: on
>  \ allmulti: off
>  \ alluni: off
>  \ nomulti: off
>  \ nouni: off
>  \ nobcast: off
>  \ multi_overflow: off
>  \ uni_overflow: off
>  \ multicast:
>     01:00:5e:00:00:01
>     33:33:00:00:00:01
>     33:33:ff:12:34:56
> 
> QMP example is in qmp-commands.hx
> 
> Signed-off-by: Amos Kong <akong@redhat.com>

Looks good to me. Pls copy Marcus and libvirt guys for QMP
comments.

We also need an event so libvirt does not need to poll for
this information: maybe it's best to make it a
single patch so we can see how things work together.

> ---
> Welcome your comments, thanks
> ---
>  hmp-commands.hx   |    2 +
>  hmp.c             |   63 +++++++++++++++++++++++++++++++++++++++++++++++
>  hmp.h             |    1 +
>  hw/virtio-net.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/net/net.h |    2 +
>  monitor.c         |    7 +++++
>  net/net.c         |   23 +++++++++++++++++
>  qapi-schema.json  |   37 ++++++++++++++++++++++++++++
>  qmp-commands.hx   |   45 ++++++++++++++++++++++++++++++++++
>  9 files changed, 250 insertions(+), 0 deletions(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 3d98604..254969d 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1645,6 +1645,8 @@ show roms
>  show the TPM device
>  @item info cpu_max
>  show the number of CPUs supported by the machine being emulated.
> +@item info mac_table
> +show the mac tables of nic devices
>  @end table
>  ETEXI
>  
> diff --git a/hmp.c b/hmp.c
> index dbe9b90..8820224 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -653,6 +653,69 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
>      qapi_free_TPMInfoList(info_list);
>  }
>  
> +void hmp_info_mac_table(Monitor *mon, const QDict *qdict)
> +{
> +    MacTableInfoList *table_list, *table_entry;
> +    StringList *str_entry;
> +
> +    table_list = qmp_query_mac_table(NULL);
> +    table_entry = table_list;
> +    while (table_entry) {
> +        monitor_printf(mon, "%s:\n", table_entry->value->name);
> +        if (table_entry->value->has_promisc) {
> +            monitor_printf(mon, " \\ promisc: %s\n",
> +                           table_entry->value->promisc ? "on" : "off");
> +        }
> +        if (table_entry->value->has_allmulti) {
> +            monitor_printf(mon, " \\ allmulti: %s\n",
> +                           table_entry->value->allmulti ? "on" : "off");
> +        }
> +        if (table_entry->value->has_alluni) {
> +            monitor_printf(mon, " \\ alluni: %s\n",
> +                           table_entry->value->alluni ? "on" : "off");
> +        }
> +        if (table_entry->value->has_nomulti) {
> +            monitor_printf(mon, " \\ nomulti: %s\n",
> +                           table_entry->value->nomulti ? "on" : "off");
> +        }
> +        if (table_entry->value->has_nouni) {
> +            monitor_printf(mon, " \\ nouni: %s\n",
> +                           table_entry->value->nouni ? "on" : "off");
> +        }
> +        if (table_entry->value->has_nobcast) {
> +            monitor_printf(mon, " \\ nobcast: %s\n",
> +                           table_entry->value->nobcast ? "on" : "off");
> +        }
> +        if (table_entry->value->has_multi_overflow) {
> +            monitor_printf(mon, " \\ multi_overflow: %s\n",
> +                           table_entry->value->multi_overflow ? "on" : "off");
> +        }
> +        if (table_entry->value->has_uni_overflow) {
> +            monitor_printf(mon, " \\ uni_overflow: %s\n",
> +                           table_entry->value->uni_overflow ? "on" : "off");
> +        }
> +        if (table_entry->value->has_unicast) {
> +            str_entry = table_entry->value->unicast;
> +            monitor_printf(mon, " \\ unicast:\n");
> +            while (str_entry) {
> +                monitor_printf(mon, "    %s\n", str_entry->value->str);
> +                str_entry = str_entry->next;
> +            }
> +        }
> +
> +        if (table_entry->value->has_multicast) {
> +            str_entry = table_entry->value->multicast;
> +            monitor_printf(mon, " \\ multicast:\n");
> +            while (str_entry) {
> +                monitor_printf(mon, "    %s\n", str_entry->value->str);
> +                str_entry = str_entry->next;
> +            }
> +        }
> +        table_entry = table_entry->next;
> +    }
> +    qapi_free_MacTableInfoList(table_list);
> +}
> +
>  void hmp_quit(Monitor *mon, const QDict *qdict)
>  {
>      monitor_suspend(mon);
> diff --git a/hmp.h b/hmp.h
> index 80e8b41..286558b 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -37,6 +37,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict);
>  void hmp_info_pci(Monitor *mon, const QDict *qdict);
>  void hmp_info_block_jobs(Monitor *mon, const QDict *qdict);
>  void hmp_info_tpm(Monitor *mon, const QDict *qdict);
> +void hmp_info_mac_table(Monitor *mon, const QDict *qdict);
>  void hmp_quit(Monitor *mon, const QDict *qdict);
>  void hmp_stop(Monitor *mon, const QDict *qdict);
>  void hmp_system_reset(Monitor *mon, const QDict *qdict);
> diff --git a/hw/virtio-net.c b/hw/virtio-net.c
> index 5917740..8b15759 100644
> --- a/hw/virtio-net.c
> +++ b/hw/virtio-net.c
> @@ -193,6 +193,75 @@ static void virtio_net_set_link_status(NetClientState *nc)
>      virtio_net_set_status(&n->vdev, n->vdev.status);
>  }
>  
> +static MacTableInfo *virtio_net_query_mactable(NetClientState *nc)
> +{
> +    VirtIONet *n = qemu_get_nic_opaque(nc);
> +    MacTableInfo *info;
> +    StringList *str_list = NULL;
> +    StringList *entry;
> +    char str[12];
> +    int i;
> +
> +    info = g_malloc0(sizeof(*info));
> +    info->name = g_strdup(nc->name);
> +
> +    info->promisc = n->promisc;
> +    info->has_promisc = true;
> +    info->allmulti = n->allmulti;
> +    info->has_allmulti = true;
> +    info->alluni = n->alluni;
> +    info->has_alluni = true;
> +    info->nomulti = n->nomulti;
> +    info->has_nomulti = true;
> +    info->nouni = n->nouni;
> +    info->has_nouni = true;
> +    info->nobcast = n->nobcast;
> +    info->has_nobcast = true;
> +    info->multi_overflow = n->mac_table.multi_overflow;
> +    info->has_multi_overflow = true;
> +    info->uni_overflow = n->mac_table.uni_overflow;
> +    info->has_uni_overflow = true;
> +
> +    for (i = 0; i < n->mac_table.first_multi; i++) {
> +        info->has_unicast = true;
> +        entry = g_malloc0(sizeof(*entry));
> +        sprintf(str,
> +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> +                n->mac_table.macs[i * ETH_ALEN],
> +                n->mac_table.macs[i * ETH_ALEN + 1],
> +                n->mac_table.macs[i * ETH_ALEN + 2],
> +                n->mac_table.macs[i * ETH_ALEN + 3],
> +                n->mac_table.macs[i * ETH_ALEN + 4],
> +                n->mac_table.macs[i * ETH_ALEN + 5]);
> +        entry->value = g_malloc0(sizeof(String *));
> +        entry->value->str = g_strdup(str);
> +        entry->next = str_list;
> +        str_list = entry;
> +    }
> +    info->unicast = str_list;
> +
> +    str_list = NULL;
> +    for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
> +        info->has_multicast = true;
> +        entry = g_malloc0(sizeof(*entry));
> +        sprintf(str,
> +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> +                n->mac_table.macs[i * ETH_ALEN],
> +                n->mac_table.macs[i * ETH_ALEN + 1],
> +                n->mac_table.macs[i * ETH_ALEN + 2],
> +                n->mac_table.macs[i * ETH_ALEN + 3],
> +                n->mac_table.macs[i * ETH_ALEN + 4],
> +                n->mac_table.macs[i * ETH_ALEN + 5]);
> +        entry->value = g_malloc0(sizeof(String *));
> +        entry->value->str = g_strdup(str);
> +        entry->next = str_list;
> +        str_list = entry;
> +    }
> +    info->multicast = str_list;
> +
> +    return info;
> +}
> +
>  static void virtio_net_reset(VirtIODevice *vdev)
>  {
>      VirtIONet *n = to_virtio_net(vdev);
> @@ -1232,6 +1301,7 @@ static NetClientInfo net_virtio_info = {
>      .receive = virtio_net_receive,
>          .cleanup = virtio_net_cleanup,
>      .link_status_changed = virtio_net_set_link_status,
> +    .query_mac_table = virtio_net_query_mactable,
>  };
>  
>  static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
> diff --git a/include/net/net.h b/include/net/net.h
> index 43d85a1..866ec81 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 MacTableInfo * (QueryMacTable)(NetClientState *);
>  
>  typedef struct NetClientInfo {
>      NetClientOptionsKind type;
> @@ -59,6 +60,7 @@ typedef struct NetClientInfo {
>      NetCanReceive *can_receive;
>      NetCleanup *cleanup;
>      LinkStatusChanged *link_status_changed;
> +    QueryMacTable *query_mac_table;
>      NetPoll *poll;
>  } NetClientInfo;
>  
> diff --git a/monitor.c b/monitor.c
> index b4bda77..9846b1e 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -2772,6 +2772,13 @@ static mon_cmd_t info_cmds[] = {
>          .mhandler.cmd = hmp_query_cpu_max,
>      },
>      {
> +        .name       = "mac_table",
> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show the mac tables",
> +        .mhandler.cmd = hmp_info_mac_table,
> +    },
> +    {
>          .name       = NULL,
>      },
>  };
> diff --git a/net/net.c b/net/net.c
> index 7869161..2103e7f 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -964,6 +964,29 @@ void print_net_client(Monitor *mon, NetClientState *nc)
>                     nc->info_str);
>  }
>  
> +MacTableInfoList *qmp_query_mac_table(Error **errp)
> +{
> +    NetClientState *nc;
> +    MacTableInfoList *table_list = NULL;
> +
> +    QTAILQ_FOREACH(nc, &net_clients, next) {
> +        MacTableInfoList *entry;
> +        MacTableInfo *info;
> +
> +        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
> +            continue;
> +        }
> +        if (nc->info->query_mac_table) {
> +            info = nc->info->query_mac_table(nc);
> +            entry = g_malloc0(sizeof(*entry));
> +            entry->value = info;
> +            entry->next = table_list;
> +            table_list = entry;
> +        }
> +    }
> +    return table_list;
> +}
> +
>  void do_info_network(Monitor *mon, const QDict *qdict)
>  {
>      NetClientState *nc, *peer;
> diff --git a/qapi-schema.json b/qapi-schema.json
> index db542f6..febf62a 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3513,3 +3513,40 @@
>      '*asl_compiler_rev':  'uint32',
>      '*file':              'str',
>      '*data':              'str' }}
> +
> +# @MacTableInfo:
> +#
> +# Mac table information.
> +#
> +# @name: the name of nic device
> +#
> +# @unicast: #optional a list of unicast mac strings
> +#
> +# @multicast: #optional a list of multicast mac strings
> +#
> +# Since 1.5
> +##
> +{ 'type': 'MacTableInfo',
> +  'data': {
> +    'name':            'str',
> +    '*promisc':        'bool',
> +    '*allmulti':       'bool',
> +    '*alluni':         'bool',
> +    '*nomulti':        'bool',
> +    '*nouni':          'bool',
> +    '*nobcast':        'bool',
> +    '*multi_overflow': 'bool',
> +    '*uni_overflow':   'bool',
> +    '*unicast':        ['String'],
> +    '*multicast':      ['String'] }}
> +
> +##
> +# @query-mac-table:
> +#
> +# Return mac tables information of nic devices
> +#
> +# Returns: @MacTableInfo on success
> +#
> +# Since: 1.5
> +##
> +{ 'command': 'query-mac-table', 'returns': ['MacTableInfo'] }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 1e0e11e..6f79eb8 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2884,3 +2884,48 @@ Example:
>  <- { "return": {} }
>  
>  EQMP
> +    {
> +        .name       = "query-mac-table",
> +        .args_type  = "",
> +        .mhandler.cmd_new = qmp_marshal_input_query_mac_table,
> +    },
> +
> +SQMP
> +query-mac-table
> +---------------------
> +
> +Show mac tables of nic devices
> +
> +Returns a json-object, it's a list of mac tables, one mac table contains
> +"name", "unicast", "multicast". "name" is a json-str, "unicast" and "multicast"
> +are all a list that contains some mac strings.
> +
> +Example:
> +
> +-> { "execute": "query-mac-table"}
> +<- {"return": [
> +        {
> +            "name": "virtio-net-pci.0",
> +            "uni_overflow": false,
> +            "nobcast": false,
> +            "promisc": ,
> +            "nouni": false,
> +            "nomulti": false,
> +            "allmulti": false,
> +            "multi_overflow": false,
> +            "alluni": false
> +            "unicast": [
> +                {
> +                    "str": "33:33:ff:00:00:00"
> +                }
> +            ],
> +            "multicast": [
> +                {
> +                    "str": "01:80:c2:00:00:21"
> +                },
> +            ]
> +        }
> +    ]
> +  }
> +
> +EQMP
> -- 
> 1.7.1
Eric Blake - April 11, 2013, 3:36 p.m.
On 04/11/2013 09:11 AM, Amos Kong wrote:
> We want to do macvtap programming by libvirt, this patch added
> a monitor command to query rx mode information. I will also
> work on another patch to add QMP event notification for rx-mode
> changing. Libvirt will sync the rx-mode change to macvtap
> devices, there maybe exist an un-controlled delay, guests
> normally expect rx mode updates immediately, it's another
> separate issue.
> 

> +++ b/qapi-schema.json
> @@ -3513,3 +3513,40 @@
>      '*asl_compiler_rev':  'uint32',
>      '*file':              'str',
>      '*data':              'str' }}
> +
> +# @MacTableInfo:
> +#
> +# Mac table information.
> +#
> +# @name: the name of nic device
> +#
> +# @unicast: #optional a list of unicast mac strings
> +#
> +# @multicast: #optional a list of multicast mac strings

Quite a few undocumented fields in relation to...

> +#
> +# Since 1.5
> +##
> +{ 'type': 'MacTableInfo',
> +  'data': {
> +    'name':            'str',
> +    '*promisc':        'bool',
> +    '*allmulti':       'bool',
> +    '*alluni':         'bool',
> +    '*nomulti':        'bool',
> +    '*nouni':          'bool',
> +    '*nobcast':        'bool',
> +    '*multi_overflow': 'bool',
> +    '*uni_overflow':   'bool',
> +    '*unicast':        ['String'],
> +    '*multicast':      ['String'] }}

...the size of the struct itself.

Why ['String'] instead of the simpler ['str']?  That's just adding
additional JSON structure for no discernible gain (unless it is a
workaround for a weakness in the code generation tool not allowing an
array of a native JSON type - in which case, let's fix that).

s/_overflow/-overflow/2 - we prefer '-' over '_' in new QMP interfaces.

> +
> +##
> +# @query-mac-table:
> +#
> +# Return mac tables information of nic devices
> +#
> +# Returns: @MacTableInfo on success
> +#
> +# Since: 1.5
> +##
> +{ 'command': 'query-mac-table', 'returns': ['MacTableInfo'] }

Seems useful.

> +-> { "execute": "query-mac-table"}
> +<- {"return": [
> +        {
> +            "name": "virtio-net-pci.0",
> +            "uni_overflow": false,
> +            "nobcast": false,
> +            "promisc": ,

Invalid JSON.

> +            "nouni": false,
> +            "nomulti": false,
> +            "allmulti": false,
> +            "multi_overflow": false,
> +            "alluni": false
> +            "unicast": [
> +                {
> +                    "str": "33:33:ff:00:00:00"
> +                }

If you use 'str' instead of 'String', this would be:

"unicast": [ "33:33:ff:00:00:00" ]
Amos Kong - April 11, 2013, 3:44 p.m.
On Thu, Apr 11, 2013 at 06:33:02PM +0300, Michael S. Tsirkin wrote:
> On Thu, Apr 11, 2013 at 11:11:58PM +0800, Amos Kong wrote:
> > We want to do macvtap programming by libvirt, this patch added
> > a monitor command to query rx mode information. I will also
> > work on another patch to add QMP event notification for rx-mode
> > changing. Libvirt will sync the rx-mode change to macvtap
> > devices, there maybe exist an un-controlled delay, guests
> > normally expect rx mode updates immediately, it's another
> > separate issue.
> > 
> > Output examples:
> > 
> > (qemu) info mac_table
> > virtio-net-pci.0:
> >  \ promisc: on
> >  \ allmulti: off
> >  \ alluni: off
> >  \ nomulti: off
> >  \ nouni: off
> >  \ nobcast: off
> >  \ multi_overflow: off
> >  \ uni_overflow: off
> >  \ multicast:
> >     01:00:5e:00:00:01
> >     33:33:00:00:00:01
> >     33:33:ff:12:34:56
> > 
> > QMP example is in qmp-commands.hx
> > 
> > Signed-off-by: Amos Kong <akong@redhat.com>
> 
> Looks good to me. Pls copy Marcus and libvirt guys for QMP
> comments.

CC: Markus, Osier
 
> We also need an event so libvirt does not need to poll for
> this information: maybe it's best to make it a
> single patch so we can see how things work together.
> 
> > ---
> > Welcome your comments, thanks
> > ---
> >  hmp-commands.hx   |    2 +
> >  hmp.c             |   63 +++++++++++++++++++++++++++++++++++++++++++++++
> >  hmp.h             |    1 +
> >  hw/virtio-net.c   |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  include/net/net.h |    2 +
> >  monitor.c         |    7 +++++
> >  net/net.c         |   23 +++++++++++++++++
> >  qapi-schema.json  |   37 ++++++++++++++++++++++++++++
> >  qmp-commands.hx   |   45 ++++++++++++++++++++++++++++++++++
> >  9 files changed, 250 insertions(+), 0 deletions(-)
> > 
> > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > index 3d98604..254969d 100644
> > --- a/hmp-commands.hx
> > +++ b/hmp-commands.hx
> > @@ -1645,6 +1645,8 @@ show roms
> >  show the TPM device
> >  @item info cpu_max
> >  show the number of CPUs supported by the machine being emulated.
> > +@item info mac_table
> > +show the mac tables of nic devices
> >  @end table
> >  ETEXI
> >  
> > diff --git a/hmp.c b/hmp.c
> > index dbe9b90..8820224 100644
> > --- a/hmp.c
> > +++ b/hmp.c
> > @@ -653,6 +653,69 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
> >      qapi_free_TPMInfoList(info_list);
> >  }
> >  
> > +void hmp_info_mac_table(Monitor *mon, const QDict *qdict)
> > +{
> > +    MacTableInfoList *table_list, *table_entry;
> > +    StringList *str_entry;
> > +
> > +    table_list = qmp_query_mac_table(NULL);
> > +    table_entry = table_list;
> > +    while (table_entry) {
> > +        monitor_printf(mon, "%s:\n", table_entry->value->name);
> > +        if (table_entry->value->has_promisc) {
> > +            monitor_printf(mon, " \\ promisc: %s\n",
> > +                           table_entry->value->promisc ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_allmulti) {
> > +            monitor_printf(mon, " \\ allmulti: %s\n",
> > +                           table_entry->value->allmulti ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_alluni) {
> > +            monitor_printf(mon, " \\ alluni: %s\n",
> > +                           table_entry->value->alluni ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_nomulti) {
> > +            monitor_printf(mon, " \\ nomulti: %s\n",
> > +                           table_entry->value->nomulti ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_nouni) {
> > +            monitor_printf(mon, " \\ nouni: %s\n",
> > +                           table_entry->value->nouni ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_nobcast) {
> > +            monitor_printf(mon, " \\ nobcast: %s\n",
> > +                           table_entry->value->nobcast ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_multi_overflow) {
> > +            monitor_printf(mon, " \\ multi_overflow: %s\n",
> > +                           table_entry->value->multi_overflow ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_uni_overflow) {
> > +            monitor_printf(mon, " \\ uni_overflow: %s\n",
> > +                           table_entry->value->uni_overflow ? "on" : "off");
> > +        }
> > +        if (table_entry->value->has_unicast) {
> > +            str_entry = table_entry->value->unicast;
> > +            monitor_printf(mon, " \\ unicast:\n");
> > +            while (str_entry) {
> > +                monitor_printf(mon, "    %s\n", str_entry->value->str);
> > +                str_entry = str_entry->next;
> > +            }
> > +        }
> > +
> > +        if (table_entry->value->has_multicast) {
> > +            str_entry = table_entry->value->multicast;
> > +            monitor_printf(mon, " \\ multicast:\n");
> > +            while (str_entry) {
> > +                monitor_printf(mon, "    %s\n", str_entry->value->str);
> > +                str_entry = str_entry->next;
> > +            }
> > +        }
> > +        table_entry = table_entry->next;
> > +    }
> > +    qapi_free_MacTableInfoList(table_list);
> > +}
> > +
> >  void hmp_quit(Monitor *mon, const QDict *qdict)
> >  {
> >      monitor_suspend(mon);
> > diff --git a/hmp.h b/hmp.h
> > index 80e8b41..286558b 100644
> > --- a/hmp.h
> > +++ b/hmp.h
> > @@ -37,6 +37,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict);
> >  void hmp_info_pci(Monitor *mon, const QDict *qdict);
> >  void hmp_info_block_jobs(Monitor *mon, const QDict *qdict);
> >  void hmp_info_tpm(Monitor *mon, const QDict *qdict);
> > +void hmp_info_mac_table(Monitor *mon, const QDict *qdict);
> >  void hmp_quit(Monitor *mon, const QDict *qdict);
> >  void hmp_stop(Monitor *mon, const QDict *qdict);
> >  void hmp_system_reset(Monitor *mon, const QDict *qdict);
> > diff --git a/hw/virtio-net.c b/hw/virtio-net.c
> > index 5917740..8b15759 100644
> > --- a/hw/virtio-net.c
> > +++ b/hw/virtio-net.c
> > @@ -193,6 +193,75 @@ static void virtio_net_set_link_status(NetClientState *nc)
> >      virtio_net_set_status(&n->vdev, n->vdev.status);
> >  }
> >  
> > +static MacTableInfo *virtio_net_query_mactable(NetClientState *nc)
> > +{
> > +    VirtIONet *n = qemu_get_nic_opaque(nc);
> > +    MacTableInfo *info;
> > +    StringList *str_list = NULL;
> > +    StringList *entry;
> > +    char str[12];
> > +    int i;
> > +
> > +    info = g_malloc0(sizeof(*info));
> > +    info->name = g_strdup(nc->name);
> > +
> > +    info->promisc = n->promisc;
> > +    info->has_promisc = true;
> > +    info->allmulti = n->allmulti;
> > +    info->has_allmulti = true;
> > +    info->alluni = n->alluni;
> > +    info->has_alluni = true;
> > +    info->nomulti = n->nomulti;
> > +    info->has_nomulti = true;
> > +    info->nouni = n->nouni;
> > +    info->has_nouni = true;
> > +    info->nobcast = n->nobcast;
> > +    info->has_nobcast = true;
> > +    info->multi_overflow = n->mac_table.multi_overflow;
> > +    info->has_multi_overflow = true;
> > +    info->uni_overflow = n->mac_table.uni_overflow;
> > +    info->has_uni_overflow = true;
> > +
> > +    for (i = 0; i < n->mac_table.first_multi; i++) {
> > +        info->has_unicast = true;
> > +        entry = g_malloc0(sizeof(*entry));
> > +        sprintf(str,
> > +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> > +                n->mac_table.macs[i * ETH_ALEN],
> > +                n->mac_table.macs[i * ETH_ALEN + 1],
> > +                n->mac_table.macs[i * ETH_ALEN + 2],
> > +                n->mac_table.macs[i * ETH_ALEN + 3],
> > +                n->mac_table.macs[i * ETH_ALEN + 4],
> > +                n->mac_table.macs[i * ETH_ALEN + 5]);
> > +        entry->value = g_malloc0(sizeof(String *));
> > +        entry->value->str = g_strdup(str);
> > +        entry->next = str_list;
> > +        str_list = entry;
> > +    }
> > +    info->unicast = str_list;
> > +
> > +    str_list = NULL;
> > +    for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
> > +        info->has_multicast = true;
> > +        entry = g_malloc0(sizeof(*entry));
> > +        sprintf(str,
> > +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> > +                n->mac_table.macs[i * ETH_ALEN],
> > +                n->mac_table.macs[i * ETH_ALEN + 1],
> > +                n->mac_table.macs[i * ETH_ALEN + 2],
> > +                n->mac_table.macs[i * ETH_ALEN + 3],
> > +                n->mac_table.macs[i * ETH_ALEN + 4],
> > +                n->mac_table.macs[i * ETH_ALEN + 5]);
> > +        entry->value = g_malloc0(sizeof(String *));
> > +        entry->value->str = g_strdup(str);
> > +        entry->next = str_list;
> > +        str_list = entry;
> > +    }
> > +    info->multicast = str_list;
> > +
> > +    return info;
> > +}
> > +
> >  static void virtio_net_reset(VirtIODevice *vdev)
> >  {
> >      VirtIONet *n = to_virtio_net(vdev);
> > @@ -1232,6 +1301,7 @@ static NetClientInfo net_virtio_info = {
> >      .receive = virtio_net_receive,
> >          .cleanup = virtio_net_cleanup,
> >      .link_status_changed = virtio_net_set_link_status,
> > +    .query_mac_table = virtio_net_query_mactable,
> >  };
> >  
> >  static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
> > diff --git a/include/net/net.h b/include/net/net.h
> > index 43d85a1..866ec81 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 MacTableInfo * (QueryMacTable)(NetClientState *);
> >  
> >  typedef struct NetClientInfo {
> >      NetClientOptionsKind type;
> > @@ -59,6 +60,7 @@ typedef struct NetClientInfo {
> >      NetCanReceive *can_receive;
> >      NetCleanup *cleanup;
> >      LinkStatusChanged *link_status_changed;
> > +    QueryMacTable *query_mac_table;
> >      NetPoll *poll;
> >  } NetClientInfo;
> >  
> > diff --git a/monitor.c b/monitor.c
> > index b4bda77..9846b1e 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -2772,6 +2772,13 @@ static mon_cmd_t info_cmds[] = {
> >          .mhandler.cmd = hmp_query_cpu_max,
> >      },
> >      {
> > +        .name       = "mac_table",
> > +        .args_type  = "",
> > +        .params     = "",
> > +        .help       = "show the mac tables",
> > +        .mhandler.cmd = hmp_info_mac_table,
> > +    },
> > +    {
> >          .name       = NULL,
> >      },
> >  };
> > diff --git a/net/net.c b/net/net.c
> > index 7869161..2103e7f 100644
> > --- a/net/net.c
> > +++ b/net/net.c
> > @@ -964,6 +964,29 @@ void print_net_client(Monitor *mon, NetClientState *nc)
> >                     nc->info_str);
> >  }
> >  
> > +MacTableInfoList *qmp_query_mac_table(Error **errp)
> > +{
> > +    NetClientState *nc;
> > +    MacTableInfoList *table_list = NULL;
> > +
> > +    QTAILQ_FOREACH(nc, &net_clients, next) {
> > +        MacTableInfoList *entry;
> > +        MacTableInfo *info;
> > +
> > +        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
> > +            continue;
> > +        }
> > +        if (nc->info->query_mac_table) {
> > +            info = nc->info->query_mac_table(nc);
> > +            entry = g_malloc0(sizeof(*entry));
> > +            entry->value = info;
> > +            entry->next = table_list;
> > +            table_list = entry;
> > +        }
> > +    }
> > +    return table_list;
> > +}
> > +
> >  void do_info_network(Monitor *mon, const QDict *qdict)
> >  {
> >      NetClientState *nc, *peer;
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index db542f6..febf62a 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -3513,3 +3513,40 @@
> >      '*asl_compiler_rev':  'uint32',
> >      '*file':              'str',
> >      '*data':              'str' }}
> > +
> > +# @MacTableInfo:
> > +#
> > +# Mac table information.
> > +#
> > +# @name: the name of nic device
> > +#
> > +# @unicast: #optional a list of unicast mac strings
> > +#
> > +# @multicast: #optional a list of multicast mac strings
> > +#
> > +# Since 1.5
> > +##
> > +{ 'type': 'MacTableInfo',
> > +  'data': {
> > +    'name':            'str',
> > +    '*promisc':        'bool',
> > +    '*allmulti':       'bool',
> > +    '*alluni':         'bool',
> > +    '*nomulti':        'bool',
> > +    '*nouni':          'bool',
> > +    '*nobcast':        'bool',
> > +    '*multi_overflow': 'bool',
> > +    '*uni_overflow':   'bool',
> > +    '*unicast':        ['String'],
> > +    '*multicast':      ['String'] }}
> > +
> > +##
> > +# @query-mac-table:
> > +#
> > +# Return mac tables information of nic devices
> > +#
> > +# Returns: @MacTableInfo on success
> > +#
> > +# Since: 1.5
> > +##
> > +{ 'command': 'query-mac-table', 'returns': ['MacTableInfo'] }
> > diff --git a/qmp-commands.hx b/qmp-commands.hx
> > index 1e0e11e..6f79eb8 100644
> > --- a/qmp-commands.hx
> > +++ b/qmp-commands.hx
> > @@ -2884,3 +2884,48 @@ Example:
> >  <- { "return": {} }
> >  
> >  EQMP
> > +    {
> > +        .name       = "query-mac-table",
> > +        .args_type  = "",
> > +        .mhandler.cmd_new = qmp_marshal_input_query_mac_table,
> > +    },
> > +
> > +SQMP
> > +query-mac-table
> > +---------------------
> > +
> > +Show mac tables of nic devices
> > +
> > +Returns a json-object, it's a list of mac tables, one mac table contains
> > +"name", "unicast", "multicast". "name" is a json-str, "unicast" and "multicast"
> > +are all a list that contains some mac strings.
> > +
> > +Example:
> > +
> > +-> { "execute": "query-mac-table"}
> > +<- {"return": [
> > +        {
> > +            "name": "virtio-net-pci.0",
> > +            "uni_overflow": false,
> > +            "nobcast": false,
> > +            "promisc": ,
> > +            "nouni": false,
> > +            "nomulti": false,
> > +            "allmulti": false,
> > +            "multi_overflow": false,
> > +            "alluni": false
> > +            "unicast": [
> > +                {
> > +                    "str": "33:33:ff:00:00:00"
> > +                }
> > +            ],
> > +            "multicast": [
> > +                {
> > +                    "str": "01:80:c2:00:00:21"
> > +                },
> > +            ]
> > +        }
> > +    ]
> > +  }
> > +
> > +EQMP
> > -- 
> > 1.7.1
Amos Kong - April 11, 2013, 11:25 p.m.
Hi Eric,

On Thu, Apr 11, 2013 at 09:36:40AM -0600, Eric Blake wrote:
> On 04/11/2013 09:11 AM, Amos Kong wrote:
> > We want to do macvtap programming by libvirt, this patch added
> > a monitor command to query rx mode information. I will also
> > work on another patch to add QMP event notification for rx-mode
> > changing. Libvirt will sync the rx-mode change to macvtap
> > devices, there maybe exist an un-controlled delay, guests
> > normally expect rx mode updates immediately, it's another
> > separate issue.
> > 
> 
> > +++ b/qapi-schema.json
> > @@ -3513,3 +3513,40 @@
> >      '*asl_compiler_rev':  'uint32',
> >      '*file':              'str',
> >      '*data':              'str' }}
> > +
> > +# @MacTableInfo:
> > +#
> > +# Mac table information.
> > +#
> > +# @name: the name of nic device
> > +#
> > +# @unicast: #optional a list of unicast mac strings
> > +#
> > +# @multicast: #optional a list of multicast mac strings
> 
> Quite a few undocumented fields in relation to...
> 
> > +#
> > +# Since 1.5
> > +##
> > +{ 'type': 'MacTableInfo',
> > +  'data': {
> > +    'name':            'str',
> > +    '*promisc':        'bool',
> > +    '*allmulti':       'bool',
> > +    '*alluni':         'bool',
> > +    '*nomulti':        'bool',
> > +    '*nouni':          'bool',
> > +    '*nobcast':        'bool',
> > +    '*multi_overflow': 'bool',
> > +    '*uni_overflow':   'bool',
> > +    '*unicast':        ['String'],
> > +    '*multicast':      ['String'] }}
> 
> ...the size of the struct itself.

Ok.
 
> Why ['String'] instead of the simpler ['str']?  That's just adding
> additional JSON structure for no discernible gain (unless it is a
> workaround for a weakness in the code generation tool not allowing an
> array of a native JSON type - in which case, let's fix that).

Yes, 'str' is the native JSON type, but strList is not defined
by default, I will fix it in next version.
 
> s/_overflow/-overflow/2 - we prefer '-' over '_' in new QMP interfaces.

got it.
 
> > +
> > +##
> > +# @query-mac-table:
> > +#
> > +# Return mac tables information of nic devices
> > +#
> > +# Returns: @MacTableInfo on success
> > +#
> > +# Since: 1.5
> > +##
> > +{ 'command': 'query-mac-table', 'returns': ['MacTableInfo'] }
> 
> Seems useful.
> 
> > +-> { "execute": "query-mac-table"}
> > +<- {"return": [
> > +        {
> > +            "name": "virtio-net-pci.0",
> > +            "uni_overflow": false,
> > +            "nobcast": false,
> > +            "promisc": ,
> 
> Invalid JSON.
 
I wanted to edit it to true manually ;)

> > +            "nouni": false,
> > +            "nomulti": false,
> > +            "allmulti": false,
> > +            "multi_overflow": false,
> > +            "alluni": false
> > +            "unicast": [
> > +                {
> > +                    "str": "33:33:ff:00:00:00"
> > +                }
> 
> If you use 'str' instead of 'String', this would be:
> 
> "unicast": [ "33:33:ff:00:00:00" ]

Yeah, the expected format should be(after fixed the native type 'str'):
  "unicast": [ "33:33:ff:00:00:00",
               "33:33:ff:00:00:01",
               "33:33:ff:00:00:02" ]
 
Thanks.
Stefan Hajnoczi - April 16, 2013, 8:11 a.m.
On Thu, Apr 11, 2013 at 11:11:58PM +0800, Amos Kong wrote:
> +static MacTableInfo *virtio_net_query_mactable(NetClientState *nc)
> +{
> +    VirtIONet *n = qemu_get_nic_opaque(nc);
> +    MacTableInfo *info;
> +    StringList *str_list = NULL;
> +    StringList *entry;
> +    char str[12];
> +    int i;
> +
> +    info = g_malloc0(sizeof(*info));
> +    info->name = g_strdup(nc->name);
> +
> +    info->promisc = n->promisc;
> +    info->has_promisc = true;
> +    info->allmulti = n->allmulti;
> +    info->has_allmulti = true;
> +    info->alluni = n->alluni;
> +    info->has_alluni = true;
> +    info->nomulti = n->nomulti;
> +    info->has_nomulti = true;
> +    info->nouni = n->nouni;
> +    info->has_nouni = true;
> +    info->nobcast = n->nobcast;
> +    info->has_nobcast = true;
> +    info->multi_overflow = n->mac_table.multi_overflow;
> +    info->has_multi_overflow = true;
> +    info->uni_overflow = n->mac_table.uni_overflow;
> +    info->has_uni_overflow = true;
> +
> +    for (i = 0; i < n->mac_table.first_multi; i++) {
> +        info->has_unicast = true;
> +        entry = g_malloc0(sizeof(*entry));
> +        sprintf(str,
> +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> +                n->mac_table.macs[i * ETH_ALEN],
> +                n->mac_table.macs[i * ETH_ALEN + 1],
> +                n->mac_table.macs[i * ETH_ALEN + 2],
> +                n->mac_table.macs[i * ETH_ALEN + 3],
> +                n->mac_table.macs[i * ETH_ALEN + 4],
> +                n->mac_table.macs[i * ETH_ALEN + 5]);

Buffer overflow, char str[12], but luckily...

> +        entry->value = g_malloc0(sizeof(String *));
> +        entry->value->str = g_strdup(str);

...these lines can be replaced with g_strdup_printf():
https://developer.gnome.org/glib/2.28/glib-String-Utility-Functions.html#g-strdup-printf

> diff --git a/net/net.c b/net/net.c
> index 7869161..2103e7f 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -964,6 +964,29 @@ void print_net_client(Monitor *mon, NetClientState *nc)
>                     nc->info_str);
>  }
>  
> +MacTableInfoList *qmp_query_mac_table(Error **errp)
> +{
> +    NetClientState *nc;
> +    MacTableInfoList *table_list = NULL;
> +
> +    QTAILQ_FOREACH(nc, &net_clients, next) {
> +        MacTableInfoList *entry;
> +        MacTableInfo *info;
> +
> +        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
> +            continue;
> +        }
> +        if (nc->info->query_mac_table) {
> +            info = nc->info->query_mac_table(nc);
> +            entry = g_malloc0(sizeof(*entry));
> +            entry->value = info;
> +            entry->next = table_list;
> +            table_list = entry;
> +        }
> +    }
> +    return table_list;
> +}

Please add an optional net client name argument so the user can query
just a single NIC.  This saves users from having to parse out a specific
NIC when they just want to query one.

Stefan
Amos Kong - April 18, 2013, 2:51 a.m.
Hi Stefan,

On Tue, Apr 16, 2013 at 10:11:50AM +0200, Stefan Hajnoczi wrote:
> On Thu, Apr 11, 2013 at 11:11:58PM +0800, Amos Kong wrote:
> > +static MacTableInfo *virtio_net_query_mactable(NetClientState *nc)
> > +{

....
> > +    for (i = 0; i < n->mac_table.first_multi; i++) {
> > +        info->has_unicast = true;
> > +        entry = g_malloc0(sizeof(*entry));
> > +        sprintf(str,
> > +                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
> > +                n->mac_table.macs[i * ETH_ALEN],
> > +                n->mac_table.macs[i * ETH_ALEN + 1],
> > +                n->mac_table.macs[i * ETH_ALEN + 2],
> > +                n->mac_table.macs[i * ETH_ALEN + 3],
> > +                n->mac_table.macs[i * ETH_ALEN + 4],
> > +                n->mac_table.macs[i * ETH_ALEN + 5]);
> 
> Buffer overflow, char str[12], but luckily...
> 
> > +        entry->value = g_malloc0(sizeof(String *));
> > +        entry->value->str = g_strdup(str);
> 
> ...these lines can be replaced with g_strdup_printf():
> https://developer.gnome.org/glib/2.28/glib-String-Utility-Functions.html#g-strdup-printf

Fixed, thanks.
 
> > diff --git a/net/net.c b/net/net.c
> > index 7869161..2103e7f 100644
> > --- a/net/net.c
> > +++ b/net/net.c
> > @@ -964,6 +964,29 @@ void print_net_client(Monitor *mon, NetClientState *nc)
> >                     nc->info_str);

....

> Please add an optional net client name argument so the user can query
> just a single NIC.  This saves users from having to parse out a specific
> NIC when they just want to query one.

The reason why I didn't add argument is:
  All current info/query commands don't have arguments, Is there a
  special reason?
  (info network, info qtree, etc) output info of all devices.

But we have interface to support this (.args_type), I will add the
useful argument for info/query mac-table.
Stefan Hajnoczi - April 18, 2013, 7:27 a.m.
On Thu, Apr 18, 2013 at 10:51:14AM +0800, Amos Kong wrote:
> Hi Stefan,
> 
> On Tue, Apr 16, 2013 at 10:11:50AM +0200, Stefan Hajnoczi wrote:
> > On Thu, Apr 11, 2013 at 11:11:58PM +0800, Amos Kong wrote:
> > Please add an optional net client name argument so the user can query
> > just a single NIC.  This saves users from having to parse out a specific
> > NIC when they just want to query one.
> 
> The reason why I didn't add argument is:
>   All current info/query commands don't have arguments, Is there a
>   special reason?
>   (info network, info qtree, etc) output info of all devices.
> 
> But we have interface to support this (.args_type), I will add the
> useful argument for info/query mac-table.

I think arguments to info/query commands were only added recently.

Stefan

Patch

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 3d98604..254969d 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1645,6 +1645,8 @@  show roms
 show the TPM device
 @item info cpu_max
 show the number of CPUs supported by the machine being emulated.
+@item info mac_table
+show the mac tables of nic devices
 @end table
 ETEXI
 
diff --git a/hmp.c b/hmp.c
index dbe9b90..8820224 100644
--- a/hmp.c
+++ b/hmp.c
@@ -653,6 +653,69 @@  void hmp_info_tpm(Monitor *mon, const QDict *qdict)
     qapi_free_TPMInfoList(info_list);
 }
 
+void hmp_info_mac_table(Monitor *mon, const QDict *qdict)
+{
+    MacTableInfoList *table_list, *table_entry;
+    StringList *str_entry;
+
+    table_list = qmp_query_mac_table(NULL);
+    table_entry = table_list;
+    while (table_entry) {
+        monitor_printf(mon, "%s:\n", table_entry->value->name);
+        if (table_entry->value->has_promisc) {
+            monitor_printf(mon, " \\ promisc: %s\n",
+                           table_entry->value->promisc ? "on" : "off");
+        }
+        if (table_entry->value->has_allmulti) {
+            monitor_printf(mon, " \\ allmulti: %s\n",
+                           table_entry->value->allmulti ? "on" : "off");
+        }
+        if (table_entry->value->has_alluni) {
+            monitor_printf(mon, " \\ alluni: %s\n",
+                           table_entry->value->alluni ? "on" : "off");
+        }
+        if (table_entry->value->has_nomulti) {
+            monitor_printf(mon, " \\ nomulti: %s\n",
+                           table_entry->value->nomulti ? "on" : "off");
+        }
+        if (table_entry->value->has_nouni) {
+            monitor_printf(mon, " \\ nouni: %s\n",
+                           table_entry->value->nouni ? "on" : "off");
+        }
+        if (table_entry->value->has_nobcast) {
+            monitor_printf(mon, " \\ nobcast: %s\n",
+                           table_entry->value->nobcast ? "on" : "off");
+        }
+        if (table_entry->value->has_multi_overflow) {
+            monitor_printf(mon, " \\ multi_overflow: %s\n",
+                           table_entry->value->multi_overflow ? "on" : "off");
+        }
+        if (table_entry->value->has_uni_overflow) {
+            monitor_printf(mon, " \\ uni_overflow: %s\n",
+                           table_entry->value->uni_overflow ? "on" : "off");
+        }
+        if (table_entry->value->has_unicast) {
+            str_entry = table_entry->value->unicast;
+            monitor_printf(mon, " \\ unicast:\n");
+            while (str_entry) {
+                monitor_printf(mon, "    %s\n", str_entry->value->str);
+                str_entry = str_entry->next;
+            }
+        }
+
+        if (table_entry->value->has_multicast) {
+            str_entry = table_entry->value->multicast;
+            monitor_printf(mon, " \\ multicast:\n");
+            while (str_entry) {
+                monitor_printf(mon, "    %s\n", str_entry->value->str);
+                str_entry = str_entry->next;
+            }
+        }
+        table_entry = table_entry->next;
+    }
+    qapi_free_MacTableInfoList(table_list);
+}
+
 void hmp_quit(Monitor *mon, const QDict *qdict)
 {
     monitor_suspend(mon);
diff --git a/hmp.h b/hmp.h
index 80e8b41..286558b 100644
--- a/hmp.h
+++ b/hmp.h
@@ -37,6 +37,7 @@  void hmp_info_balloon(Monitor *mon, const QDict *qdict);
 void hmp_info_pci(Monitor *mon, const QDict *qdict);
 void hmp_info_block_jobs(Monitor *mon, const QDict *qdict);
 void hmp_info_tpm(Monitor *mon, const QDict *qdict);
+void hmp_info_mac_table(Monitor *mon, const QDict *qdict);
 void hmp_quit(Monitor *mon, const QDict *qdict);
 void hmp_stop(Monitor *mon, const QDict *qdict);
 void hmp_system_reset(Monitor *mon, const QDict *qdict);
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 5917740..8b15759 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -193,6 +193,75 @@  static void virtio_net_set_link_status(NetClientState *nc)
     virtio_net_set_status(&n->vdev, n->vdev.status);
 }
 
+static MacTableInfo *virtio_net_query_mactable(NetClientState *nc)
+{
+    VirtIONet *n = qemu_get_nic_opaque(nc);
+    MacTableInfo *info;
+    StringList *str_list = NULL;
+    StringList *entry;
+    char str[12];
+    int i;
+
+    info = g_malloc0(sizeof(*info));
+    info->name = g_strdup(nc->name);
+
+    info->promisc = n->promisc;
+    info->has_promisc = true;
+    info->allmulti = n->allmulti;
+    info->has_allmulti = true;
+    info->alluni = n->alluni;
+    info->has_alluni = true;
+    info->nomulti = n->nomulti;
+    info->has_nomulti = true;
+    info->nouni = n->nouni;
+    info->has_nouni = true;
+    info->nobcast = n->nobcast;
+    info->has_nobcast = true;
+    info->multi_overflow = n->mac_table.multi_overflow;
+    info->has_multi_overflow = true;
+    info->uni_overflow = n->mac_table.uni_overflow;
+    info->has_uni_overflow = true;
+
+    for (i = 0; i < n->mac_table.first_multi; i++) {
+        info->has_unicast = true;
+        entry = g_malloc0(sizeof(*entry));
+        sprintf(str,
+                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+                n->mac_table.macs[i * ETH_ALEN],
+                n->mac_table.macs[i * ETH_ALEN + 1],
+                n->mac_table.macs[i * ETH_ALEN + 2],
+                n->mac_table.macs[i * ETH_ALEN + 3],
+                n->mac_table.macs[i * ETH_ALEN + 4],
+                n->mac_table.macs[i * ETH_ALEN + 5]);
+        entry->value = g_malloc0(sizeof(String *));
+        entry->value->str = g_strdup(str);
+        entry->next = str_list;
+        str_list = entry;
+    }
+    info->unicast = str_list;
+
+    str_list = NULL;
+    for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+        info->has_multicast = true;
+        entry = g_malloc0(sizeof(*entry));
+        sprintf(str,
+                "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+                n->mac_table.macs[i * ETH_ALEN],
+                n->mac_table.macs[i * ETH_ALEN + 1],
+                n->mac_table.macs[i * ETH_ALEN + 2],
+                n->mac_table.macs[i * ETH_ALEN + 3],
+                n->mac_table.macs[i * ETH_ALEN + 4],
+                n->mac_table.macs[i * ETH_ALEN + 5]);
+        entry->value = g_malloc0(sizeof(String *));
+        entry->value->str = g_strdup(str);
+        entry->next = str_list;
+        str_list = entry;
+    }
+    info->multicast = str_list;
+
+    return info;
+}
+
 static void virtio_net_reset(VirtIODevice *vdev)
 {
     VirtIONet *n = to_virtio_net(vdev);
@@ -1232,6 +1301,7 @@  static NetClientInfo net_virtio_info = {
     .receive = virtio_net_receive,
         .cleanup = virtio_net_cleanup,
     .link_status_changed = virtio_net_set_link_status,
+    .query_mac_table = virtio_net_query_mactable,
 };
 
 static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
diff --git a/include/net/net.h b/include/net/net.h
index 43d85a1..866ec81 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 MacTableInfo * (QueryMacTable)(NetClientState *);
 
 typedef struct NetClientInfo {
     NetClientOptionsKind type;
@@ -59,6 +60,7 @@  typedef struct NetClientInfo {
     NetCanReceive *can_receive;
     NetCleanup *cleanup;
     LinkStatusChanged *link_status_changed;
+    QueryMacTable *query_mac_table;
     NetPoll *poll;
 } NetClientInfo;
 
diff --git a/monitor.c b/monitor.c
index b4bda77..9846b1e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2772,6 +2772,13 @@  static mon_cmd_t info_cmds[] = {
         .mhandler.cmd = hmp_query_cpu_max,
     },
     {
+        .name       = "mac_table",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show the mac tables",
+        .mhandler.cmd = hmp_info_mac_table,
+    },
+    {
         .name       = NULL,
     },
 };
diff --git a/net/net.c b/net/net.c
index 7869161..2103e7f 100644
--- a/net/net.c
+++ b/net/net.c
@@ -964,6 +964,29 @@  void print_net_client(Monitor *mon, NetClientState *nc)
                    nc->info_str);
 }
 
+MacTableInfoList *qmp_query_mac_table(Error **errp)
+{
+    NetClientState *nc;
+    MacTableInfoList *table_list = NULL;
+
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        MacTableInfoList *entry;
+        MacTableInfo *info;
+
+        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+            continue;
+        }
+        if (nc->info->query_mac_table) {
+            info = nc->info->query_mac_table(nc);
+            entry = g_malloc0(sizeof(*entry));
+            entry->value = info;
+            entry->next = table_list;
+            table_list = entry;
+        }
+    }
+    return table_list;
+}
+
 void do_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
diff --git a/qapi-schema.json b/qapi-schema.json
index db542f6..febf62a 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3513,3 +3513,40 @@ 
     '*asl_compiler_rev':  'uint32',
     '*file':              'str',
     '*data':              'str' }}
+
+# @MacTableInfo:
+#
+# Mac table information.
+#
+# @name: the name of nic device
+#
+# @unicast: #optional a list of unicast mac strings
+#
+# @multicast: #optional a list of multicast mac strings
+#
+# Since 1.5
+##
+{ 'type': 'MacTableInfo',
+  'data': {
+    'name':            'str',
+    '*promisc':        'bool',
+    '*allmulti':       'bool',
+    '*alluni':         'bool',
+    '*nomulti':        'bool',
+    '*nouni':          'bool',
+    '*nobcast':        'bool',
+    '*multi_overflow': 'bool',
+    '*uni_overflow':   'bool',
+    '*unicast':        ['String'],
+    '*multicast':      ['String'] }}
+
+##
+# @query-mac-table:
+#
+# Return mac tables information of nic devices
+#
+# Returns: @MacTableInfo on success
+#
+# Since: 1.5
+##
+{ 'command': 'query-mac-table', 'returns': ['MacTableInfo'] }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 1e0e11e..6f79eb8 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2884,3 +2884,48 @@  Example:
 <- { "return": {} }
 
 EQMP
+    {
+        .name       = "query-mac-table",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_query_mac_table,
+    },
+
+SQMP
+query-mac-table
+---------------------
+
+Show mac tables of nic devices
+
+Returns a json-object, it's a list of mac tables, one mac table contains
+"name", "unicast", "multicast". "name" is a json-str, "unicast" and "multicast"
+are all a list that contains some mac strings.
+
+Example:
+
+-> { "execute": "query-mac-table"}
+<- {"return": [
+        {
+            "name": "virtio-net-pci.0",
+            "uni_overflow": false,
+            "nobcast": false,
+            "promisc": ,
+            "nouni": false,
+            "nomulti": false,
+            "allmulti": false,
+            "multi_overflow": false,
+            "alluni": false
+            "unicast": [
+                {
+                    "str": "33:33:ff:00:00:00"
+                }
+            ],
+            "multicast": [
+                {
+                    "str": "01:80:c2:00:00:21"
+                },
+            ]
+        }
+    ]
+  }
+
+EQMP