diff mbox

[v9,03/10] netfilter: add netfilter_{add|del} commands

Message ID 1441098383-22585-4-git-send-email-yanghy@cn.fujitsu.com
State New
Headers show

Commit Message

Yang Hongyang Sept. 1, 2015, 9:06 a.m. UTC
add netfilter_{add|del} commands
This is mostly the same with netdev_{add|del} commands.

When we delete the netdev, we also delete the netfilter object
attached to it, because if the netdev is removed, the filters
which attached to it is useless.

Note that the buffer filter will be implemented in the following
patches.

Signed-off-by: Yang Hongyang <yanghy@cn.fujitsu.com>
CC: Luiz Capitulino <lcapitulino@redhat.com>
CC: Markus Armbruster <armbru@redhat.com>
CC: Eric Blake <eblake@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
---
v9: use buffer filter as an example in the command help
    cleanup qapi qmp command according to Markus&Eric's comment
v7: error msg fix
    move qmp_opts_del() into qemu_del_net_filter()
v6: add multiqueue support (qemu_del_net_filter)
v5: squash "net: delete netfilter object when delete netdev"
---
 hmp-commands.hx      | 30 ++++++++++++++++++++++++
 hmp.c                | 29 +++++++++++++++++++++++
 hmp.h                |  4 ++++
 include/net/filter.h |  2 ++
 monitor.c            | 33 ++++++++++++++++++++++++++
 net/filter.c         | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 net/net.c            |  7 ++++++
 qapi-schema.json     | 29 +++++++++++++++++++++++
 qapi/opts-visitor.c  | 16 +++++++++++++
 qmp-commands.hx      | 55 ++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 269 insertions(+), 1 deletion(-)

Comments

Stefan Hajnoczi Sept. 1, 2015, 2:37 p.m. UTC | #1
On Tue, Sep 01, 2015 at 05:06:16PM +0800, Yang Hongyang wrote:
> add netfilter_{add|del} commands
> This is mostly the same with netdev_{add|del} commands.

The QOM equivalent here is object-add/object-del.

> When we delete the netdev, we also delete the netfilter object
> attached to it, because if the netdev is removed, the filters
> which attached to it is useless.
> 
> Note that the buffer filter will be implemented in the following
> patches.
> 
> Signed-off-by: Yang Hongyang <yanghy@cn.fujitsu.com>
> CC: Luiz Capitulino <lcapitulino@redhat.com>
> CC: Markus Armbruster <armbru@redhat.com>
> CC: Eric Blake <eblake@redhat.com>
> Reviewed-by: Thomas Huth <thuth@redhat.com>
> ---
> v9: use buffer filter as an example in the command help
>     cleanup qapi qmp command according to Markus&Eric's comment
> v7: error msg fix
>     move qmp_opts_del() into qemu_del_net_filter()
> v6: add multiqueue support (qemu_del_net_filter)
> v5: squash "net: delete netfilter object when delete netdev"
> ---
>  hmp-commands.hx      | 30 ++++++++++++++++++++++++
>  hmp.c                | 29 +++++++++++++++++++++++
>  hmp.h                |  4 ++++
>  include/net/filter.h |  2 ++
>  monitor.c            | 33 ++++++++++++++++++++++++++
>  net/filter.c         | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  net/net.c            |  7 ++++++
>  qapi-schema.json     | 29 +++++++++++++++++++++++
>  qapi/opts-visitor.c  | 16 +++++++++++++
>  qmp-commands.hx      | 55 ++++++++++++++++++++++++++++++++++++++++++++
>  10 files changed, 269 insertions(+), 1 deletion(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index d3b7932..9e5f39d 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -1253,6 +1253,36 @@ Remove host network device.
>  ETEXI
>  
>      {
> +        .name       = "netfilter_add",
> +        .args_type  = "netfilter:O",
> +        .params     = "buffer,id=str,netdev=str[,chain=in|out|all,prop=value][,...]",
> +        .help       = "add netfilter",
> +        .mhandler.cmd = hmp_netfilter_add,
> +        .command_completion = netfilter_add_completion,
> +    },
> +
> +STEXI
> +@item netfilter_add
> +@findex netfilter_add
> +Add a netfilter to @var{netdev}, which captures the network packets on @var{netdev}.
> +ETEXI
> +
> +    {
> +        .name       = "netfilter_del",
> +        .args_type  = "id:s",
> +        .params     = "id",
> +        .help       = "remove netfilter",
> +        .mhandler.cmd = hmp_netfilter_del,
> +        .command_completion = netfilter_del_completion,
> +    },
> +
> +STEXI
> +@item netfilter_del
> +@findex netfilter_del
> +Remove the netfilter which named @var{id}.
> +ETEXI
> +
> +    {
>          .name       = "object_add",
>          .args_type  = "object:O",
>          .params     = "[qom-type=]type,id=str[,prop=value][,...]",
> diff --git a/hmp.c b/hmp.c
> index dcc66f1..09e3cda 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -15,6 +15,7 @@
>  
>  #include "hmp.h"
>  #include "net/net.h"
> +#include "net/filter.h"
>  #include "net/eth.h"
>  #include "sysemu/char.h"
>  #include "sysemu/block-backend.h"
> @@ -1599,6 +1600,34 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
>      hmp_handle_error(mon, &err);
>  }
>  
> +void hmp_netfilter_add(Monitor *mon, const QDict *qdict)
> +{
> +    Error *err = NULL;
> +    QemuOpts *opts;
> +
> +    opts = qemu_opts_from_qdict(qemu_find_opts("netfilter"), qdict, &err);
> +    if (err) {
> +        goto out;
> +    }
> +
> +    netfilter_add(opts, &err);
> +    if (err) {
> +        qemu_opts_del(opts);
> +    }
> +
> +out:
> +    hmp_handle_error(mon, &err);
> +}
> +
> +void hmp_netfilter_del(Monitor *mon, const QDict *qdict)
> +{
> +    const char *id = qdict_get_str(qdict, "id");
> +    Error *err = NULL;
> +
> +    qmp_netfilter_del(id, &err);
> +    hmp_handle_error(mon, &err);
> +}
> +
>  void hmp_object_add(Monitor *mon, const QDict *qdict)
>  {
>      Error *err = NULL;
> diff --git a/hmp.h b/hmp.h
> index 0cf4f2a..a21dbbb 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -85,6 +85,8 @@ void hmp_device_del(Monitor *mon, const QDict *qdict);
>  void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
>  void hmp_netdev_add(Monitor *mon, const QDict *qdict);
>  void hmp_netdev_del(Monitor *mon, const QDict *qdict);
> +void hmp_netfilter_add(Monitor *mon, const QDict *qdict);
> +void hmp_netfilter_del(Monitor *mon, const QDict *qdict);
>  void hmp_getfd(Monitor *mon, const QDict *qdict);
>  void hmp_closefd(Monitor *mon, const QDict *qdict);
>  void hmp_sendkey(Monitor *mon, const QDict *qdict);
> @@ -112,6 +114,8 @@ void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str);
>  void set_link_completion(ReadLineState *rs, int nb_args, const char *str);
>  void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str);
>  void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str);
> +void netfilter_add_completion(ReadLineState *rs, int nb_args, const char *str);
> +void netfilter_del_completion(ReadLineState *rs, int nb_args, const char *str);
>  void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str);
>  void watchdog_action_completion(ReadLineState *rs, int nb_args,
>                                  const char *str);
> diff --git a/include/net/filter.h b/include/net/filter.h
> index ce15f15..083083b 100644
> --- a/include/net/filter.h
> +++ b/include/net/filter.h
> @@ -53,5 +53,7 @@ NetFilterState *qemu_new_net_filter(NetFilterInfo *info,
>                                      NetClientState *netdev,
>                                      const char *name,
>                                      int chain);
> +void qemu_del_net_filter(NetFilterState *nf);
> +void netfilter_add(QemuOpts *opts, Error **errp);
>  
>  #endif /* QEMU_NET_FILTER_H */
> diff --git a/monitor.c b/monitor.c
> index fc32f12..10b1f77 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -31,6 +31,7 @@
>  #include "hw/loader.h"
>  #include "exec/gdbstub.h"
>  #include "net/net.h"
> +#include "net/filter.h"
>  #include "net/slirp.h"
>  #include "sysemu/char.h"
>  #include "ui/qemu-spice.h"
> @@ -4193,6 +4194,21 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
>      }
>  }
>  
> +void netfilter_add_completion(ReadLineState *rs, int nb_args, const char *str)
> +{
> +    size_t len;
> +    int i;
> +
> +    if (nb_args != 2) {
> +        return;
> +    }
> +    len = strlen(str);
> +    readline_set_completion_index(rs, len);
> +    for (i = 0; NetFilterType_lookup[i]; i++) {
> +        add_completion_option(rs, str, NetFilterType_lookup[i]);
> +    }
> +}
> +
>  void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
>  {
>      GSList *list, *elt;
> @@ -4429,6 +4445,23 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
>      }
>  }
>  
> +void netfilter_del_completion(ReadLineState *rs, int nb_args, const char *str)
> +{
> +    int len;
> +    QemuOpts *opts;
> +
> +    if (nb_args != 2) {
> +        return;
> +    }
> +
> +    len = strlen(str);
> +    readline_set_completion_index(rs, len);
> +    opts = qemu_opts_find(qemu_find_opts_err("netfilter", NULL), str);
> +    if (opts) {
> +        readline_add_completion(rs, str);
> +    }
> +}
> +
>  void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str)
>  {
>      int i;
> diff --git a/net/filter.c b/net/filter.c
> index 89fb089..904e5c7 100644
> --- a/net/filter.c
> +++ b/net/filter.c
> @@ -13,6 +13,7 @@
>  #include "qapi/opts-visitor.h"
>  #include "qapi/dealloc-visitor.h"
>  #include "qemu/config-file.h"
> +#include "qmp-commands.h"
>  
>  #include "net/filter.h"
>  #include "net/net.h"
> @@ -42,7 +43,7 @@ NetFilterState *qemu_new_net_filter(NetFilterInfo *info,
>      return nf;
>  }
>  
> -static inline void qemu_cleanup_net_filter(NetFilterState *nf)
> +static void qemu_cleanup_net_filter(NetFilterState *nf)
>  {
>      QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
>      QTAILQ_REMOVE(&net_filters, nf, global_list);
> @@ -55,6 +56,42 @@ static inline void qemu_cleanup_net_filter(NetFilterState *nf)
>      g_free(nf);
>  }
>  
> +static int qemu_find_netfilters_by_name(const char *id, NetFilterState **nfs,
> +                                        int max)
> +{
> +    NetFilterState *nf;
> +    int ret = 0;
> +
> +    QTAILQ_FOREACH(nf, &net_filters, global_list) {
> +        if (!strcmp(nf->name, id)) {
> +            if (ret < max) {
> +                nfs[ret] = nf;
> +            }
> +            ret++;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +void qemu_del_net_filter(NetFilterState *nf)
> +{
> +    NetFilterState *nfs[MAX_QUEUE_NUM];
> +    int queues, i;
> +    QemuOpts *opts;
> +
> +    opts = qemu_opts_find(qemu_find_opts_err("netfilter", NULL), nf->name);
> +
> +    queues = qemu_find_netfilters_by_name(nf->name, nfs, MAX_QUEUE_NUM);
> +    assert(queues != 0);
> +
> +    for (i = 0; i < queues; i++) {
> +        qemu_cleanup_net_filter(nfs[i]);
> +    }
> +
> +    qemu_opts_del(opts);
> +}
> +
>  static NetFilterState *qemu_find_netfilter(const char *id)
>  {
>      NetFilterState *nf;
> @@ -68,6 +105,32 @@ static NetFilterState *qemu_find_netfilter(const char *id)
>      return NULL;
>  }
>  
> +static int net_init_filter(void *dummy, QemuOpts *opts, Error **errp);
> +void netfilter_add(QemuOpts *opts, Error **errp)
> +{
> +    net_init_filter(NULL, opts, errp);
> +}
> +
> +static int net_filter_init1(const NetFilter *netfilter, Error **errp);
> +void qmp_netfilter_add(NetFilter *data, Error **errp)
> +{
> +    net_filter_init1(data, errp);
> +}
> +
> +void qmp_netfilter_del(const char *id, Error **errp)
> +{
> +    NetFilterState *nf;
> +
> +    nf = qemu_find_netfilter(id);
> +    if (!nf) {
> +        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
> +                  "Filter '%s' not found", id);
> +        return;
> +    }
> +
> +    qemu_del_net_filter(nf);
> +}
> +
>  typedef int (NetFilterInit)(const NetFilter *netfilter,
>                              const char *name, int chain,
>                              NetClientState *netdev, Error **errp);
> diff --git a/net/net.c b/net/net.c
> index d9b70cd..74f3592 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -28,6 +28,7 @@
>  #include "hub.h"
>  #include "net/slirp.h"
>  #include "net/eth.h"
> +#include "net/filter.h"
>  #include "util.h"
>  
>  #include "monitor/monitor.h"
> @@ -385,6 +386,7 @@ void qemu_del_net_client(NetClientState *nc)
>  {
>      NetClientState *ncs[MAX_QUEUE_NUM];
>      int queues, i;
> +    NetFilterState *nf, *next;
>  
>      assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
>  
> @@ -396,6 +398,11 @@ void qemu_del_net_client(NetClientState *nc)
>                                            MAX_QUEUE_NUM);
>      assert(queues != 0);
>  
> +    /* qemu_del_net_filter() will handle the multiqueue case */
> +    QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) {
> +        qemu_del_net_filter(nf);
> +    }
> +
>      /* If there is a peer NIC, delete and cleanup client, but do not free. */
>      if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
>          NICState *nic = qemu_get_nic(nc->peer);
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 750fc00..d961ac8 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2537,6 +2537,35 @@
>      'opts': 'NetClientOptions' } }
>  
>  ##
> +# @netfilter-add:
> +#
> +# Add a netfilter.
> +#
> +# @data: see NetFilterBase options and specific type's options.
> +#
> +# Since: 2.5
> +#
> +# Returns: Nothing on success
> +#          If it is not a valid netfilter, DeviceNotFound
> +##
> +{ 'command': 'netfilter-add',
> +  'data': { 'data': 'NetFilter' } }
> +
> +##
> +# @netfilter-del:
> +#
> +# Remove a netfilter.
> +#
> +# @id: the name of the netfilter to remove
> +#
> +# Returns: Nothing on success
> +#          If @id is not a valid netfilter, DeviceNotFound
> +#
> +# Since: 2.5
> +##
> +{ 'command': 'netfilter-del', 'data': {'id': 'str'} }
> +
> +##
>  # @NetFilterDummyOptions:
>  #
>  # An dummy filter for network backend
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 7ae33b3..1271fab 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -186,6 +186,20 @@ opts_end_struct(Visitor *v, Error **errp)
>  }
>  
>  
> +static void opts_start_implicit_struct(Visitor *v, void **obj,
> +                                       size_t size, Error **errp)
> +{
> +    if (obj) {
> +        *obj = g_malloc0(size);
> +    }
> +}
> +
> +
> +static void opts_end_implicit_struct(Visitor *v, Error **errp)
> +{
> +}
> +
> +
>  static GQueue *
>  lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>  {
> @@ -507,6 +521,8 @@ opts_visitor_new(const QemuOpts *opts)
>  
>      ov->visitor.start_struct = &opts_start_struct;
>      ov->visitor.end_struct   = &opts_end_struct;
> +    ov->visitor.start_implicit_struct = &opts_start_implicit_struct;
> +    ov->visitor.end_implicit_struct = &opts_end_implicit_struct;
>  
>      ov->visitor.start_list = &opts_start_list;
>      ov->visitor.next_list  = &opts_next_list;
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index ba630b1..bb73806 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -926,6 +926,61 @@ Example:
>  EQMP
>  
>      {
> +        .name       = "netfilter-add",
> +        .args_type  = "data:q",
> +        .mhandler.cmd_new = qmp_marshal_input_netfilter_add,
> +    },
> +
> +SQMP
> +netfilter-add
> +----------
> +
> +Add a netfilter.
> +
> +Arguments:
> +
> +- "data": filter options (json-string)
> +
> +Example:
> +
> +-> { "execute": "netfilter-add",
> +                "arguments": { "data": { "type": "buffer", "id": "nf0",
> +                               "netdev": "bn",
> +                               "chain": "in",
> +                               "interval": 1000 } } }
> +<- { "return": {} }
> +
> +Note: The supported filter options are the same ones supported by the
> +      '-netfilter' command-line argument, which are listed in the '-help'
> +      output or QEMU's manual
> +
> +EQMP
> +
> +    {
> +        .name       = "netfilter-del",
> +        .args_type  = "id:s",
> +        .mhandler.cmd_new = qmp_marshal_input_netfilter_del,
> +    },
> +
> +SQMP
> +netfilter-del
> +----------
> +
> +Remove a netfilter.
> +
> +Arguments:
> +
> +- "id": the netfilter's ID, must be unique (json-string)
> +
> +Example:
> +
> +-> { "execute": "netfilter_del", "arguments": { "id": "nf0" } }
> +<- { "return": {} }
> +
> +
> +EQMP
> +
> +    {
>          .name       = "object-add",
>          .args_type  = "qom-type:s,id:s,props:q?",
>          .mhandler.cmd_new = qmp_object_add,
> -- 
> 1.9.1
>
diff mbox

Patch

diff --git a/hmp-commands.hx b/hmp-commands.hx
index d3b7932..9e5f39d 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1253,6 +1253,36 @@  Remove host network device.
 ETEXI
 
     {
+        .name       = "netfilter_add",
+        .args_type  = "netfilter:O",
+        .params     = "buffer,id=str,netdev=str[,chain=in|out|all,prop=value][,...]",
+        .help       = "add netfilter",
+        .mhandler.cmd = hmp_netfilter_add,
+        .command_completion = netfilter_add_completion,
+    },
+
+STEXI
+@item netfilter_add
+@findex netfilter_add
+Add a netfilter to @var{netdev}, which captures the network packets on @var{netdev}.
+ETEXI
+
+    {
+        .name       = "netfilter_del",
+        .args_type  = "id:s",
+        .params     = "id",
+        .help       = "remove netfilter",
+        .mhandler.cmd = hmp_netfilter_del,
+        .command_completion = netfilter_del_completion,
+    },
+
+STEXI
+@item netfilter_del
+@findex netfilter_del
+Remove the netfilter which named @var{id}.
+ETEXI
+
+    {
         .name       = "object_add",
         .args_type  = "object:O",
         .params     = "[qom-type=]type,id=str[,prop=value][,...]",
diff --git a/hmp.c b/hmp.c
index dcc66f1..09e3cda 100644
--- a/hmp.c
+++ b/hmp.c
@@ -15,6 +15,7 @@ 
 
 #include "hmp.h"
 #include "net/net.h"
+#include "net/filter.h"
 #include "net/eth.h"
 #include "sysemu/char.h"
 #include "sysemu/block-backend.h"
@@ -1599,6 +1600,34 @@  void hmp_netdev_del(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &err);
 }
 
+void hmp_netfilter_add(Monitor *mon, const QDict *qdict)
+{
+    Error *err = NULL;
+    QemuOpts *opts;
+
+    opts = qemu_opts_from_qdict(qemu_find_opts("netfilter"), qdict, &err);
+    if (err) {
+        goto out;
+    }
+
+    netfilter_add(opts, &err);
+    if (err) {
+        qemu_opts_del(opts);
+    }
+
+out:
+    hmp_handle_error(mon, &err);
+}
+
+void hmp_netfilter_del(Monitor *mon, const QDict *qdict)
+{
+    const char *id = qdict_get_str(qdict, "id");
+    Error *err = NULL;
+
+    qmp_netfilter_del(id, &err);
+    hmp_handle_error(mon, &err);
+}
+
 void hmp_object_add(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
diff --git a/hmp.h b/hmp.h
index 0cf4f2a..a21dbbb 100644
--- a/hmp.h
+++ b/hmp.h
@@ -85,6 +85,8 @@  void hmp_device_del(Monitor *mon, const QDict *qdict);
 void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
 void hmp_netdev_add(Monitor *mon, const QDict *qdict);
 void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+void hmp_netfilter_add(Monitor *mon, const QDict *qdict);
+void hmp_netfilter_del(Monitor *mon, const QDict *qdict);
 void hmp_getfd(Monitor *mon, const QDict *qdict);
 void hmp_closefd(Monitor *mon, const QDict *qdict);
 void hmp_sendkey(Monitor *mon, const QDict *qdict);
@@ -112,6 +114,8 @@  void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void set_link_completion(ReadLineState *rs, int nb_args, const char *str);
 void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str);
+void netfilter_add_completion(ReadLineState *rs, int nb_args, const char *str);
+void netfilter_del_completion(ReadLineState *rs, int nb_args, const char *str);
 void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str);
 void watchdog_action_completion(ReadLineState *rs, int nb_args,
                                 const char *str);
diff --git a/include/net/filter.h b/include/net/filter.h
index ce15f15..083083b 100644
--- a/include/net/filter.h
+++ b/include/net/filter.h
@@ -53,5 +53,7 @@  NetFilterState *qemu_new_net_filter(NetFilterInfo *info,
                                     NetClientState *netdev,
                                     const char *name,
                                     int chain);
+void qemu_del_net_filter(NetFilterState *nf);
+void netfilter_add(QemuOpts *opts, Error **errp);
 
 #endif /* QEMU_NET_FILTER_H */
diff --git a/monitor.c b/monitor.c
index fc32f12..10b1f77 100644
--- a/monitor.c
+++ b/monitor.c
@@ -31,6 +31,7 @@ 
 #include "hw/loader.h"
 #include "exec/gdbstub.h"
 #include "net/net.h"
+#include "net/filter.h"
 #include "net/slirp.h"
 #include "sysemu/char.h"
 #include "ui/qemu-spice.h"
@@ -4193,6 +4194,21 @@  void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
     }
 }
 
+void netfilter_add_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    size_t len;
+    int i;
+
+    if (nb_args != 2) {
+        return;
+    }
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    for (i = 0; NetFilterType_lookup[i]; i++) {
+        add_completion_option(rs, str, NetFilterType_lookup[i]);
+    }
+}
+
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
 {
     GSList *list, *elt;
@@ -4429,6 +4445,23 @@  void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
     }
 }
 
+void netfilter_del_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    int len;
+    QemuOpts *opts;
+
+    if (nb_args != 2) {
+        return;
+    }
+
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    opts = qemu_opts_find(qemu_find_opts_err("netfilter", NULL), str);
+    if (opts) {
+        readline_add_completion(rs, str);
+    }
+}
+
 void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str)
 {
     int i;
diff --git a/net/filter.c b/net/filter.c
index 89fb089..904e5c7 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -13,6 +13,7 @@ 
 #include "qapi/opts-visitor.h"
 #include "qapi/dealloc-visitor.h"
 #include "qemu/config-file.h"
+#include "qmp-commands.h"
 
 #include "net/filter.h"
 #include "net/net.h"
@@ -42,7 +43,7 @@  NetFilterState *qemu_new_net_filter(NetFilterInfo *info,
     return nf;
 }
 
-static inline void qemu_cleanup_net_filter(NetFilterState *nf)
+static void qemu_cleanup_net_filter(NetFilterState *nf)
 {
     QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
     QTAILQ_REMOVE(&net_filters, nf, global_list);
@@ -55,6 +56,42 @@  static inline void qemu_cleanup_net_filter(NetFilterState *nf)
     g_free(nf);
 }
 
+static int qemu_find_netfilters_by_name(const char *id, NetFilterState **nfs,
+                                        int max)
+{
+    NetFilterState *nf;
+    int ret = 0;
+
+    QTAILQ_FOREACH(nf, &net_filters, global_list) {
+        if (!strcmp(nf->name, id)) {
+            if (ret < max) {
+                nfs[ret] = nf;
+            }
+            ret++;
+        }
+    }
+
+    return ret;
+}
+
+void qemu_del_net_filter(NetFilterState *nf)
+{
+    NetFilterState *nfs[MAX_QUEUE_NUM];
+    int queues, i;
+    QemuOpts *opts;
+
+    opts = qemu_opts_find(qemu_find_opts_err("netfilter", NULL), nf->name);
+
+    queues = qemu_find_netfilters_by_name(nf->name, nfs, MAX_QUEUE_NUM);
+    assert(queues != 0);
+
+    for (i = 0; i < queues; i++) {
+        qemu_cleanup_net_filter(nfs[i]);
+    }
+
+    qemu_opts_del(opts);
+}
+
 static NetFilterState *qemu_find_netfilter(const char *id)
 {
     NetFilterState *nf;
@@ -68,6 +105,32 @@  static NetFilterState *qemu_find_netfilter(const char *id)
     return NULL;
 }
 
+static int net_init_filter(void *dummy, QemuOpts *opts, Error **errp);
+void netfilter_add(QemuOpts *opts, Error **errp)
+{
+    net_init_filter(NULL, opts, errp);
+}
+
+static int net_filter_init1(const NetFilter *netfilter, Error **errp);
+void qmp_netfilter_add(NetFilter *data, Error **errp)
+{
+    net_filter_init1(data, errp);
+}
+
+void qmp_netfilter_del(const char *id, Error **errp)
+{
+    NetFilterState *nf;
+
+    nf = qemu_find_netfilter(id);
+    if (!nf) {
+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+                  "Filter '%s' not found", id);
+        return;
+    }
+
+    qemu_del_net_filter(nf);
+}
+
 typedef int (NetFilterInit)(const NetFilter *netfilter,
                             const char *name, int chain,
                             NetClientState *netdev, Error **errp);
diff --git a/net/net.c b/net/net.c
index d9b70cd..74f3592 100644
--- a/net/net.c
+++ b/net/net.c
@@ -28,6 +28,7 @@ 
 #include "hub.h"
 #include "net/slirp.h"
 #include "net/eth.h"
+#include "net/filter.h"
 #include "util.h"
 
 #include "monitor/monitor.h"
@@ -385,6 +386,7 @@  void qemu_del_net_client(NetClientState *nc)
 {
     NetClientState *ncs[MAX_QUEUE_NUM];
     int queues, i;
+    NetFilterState *nf, *next;
 
     assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
 
@@ -396,6 +398,11 @@  void qemu_del_net_client(NetClientState *nc)
                                           MAX_QUEUE_NUM);
     assert(queues != 0);
 
+    /* qemu_del_net_filter() will handle the multiqueue case */
+    QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) {
+        qemu_del_net_filter(nf);
+    }
+
     /* If there is a peer NIC, delete and cleanup client, but do not free. */
     if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
         NICState *nic = qemu_get_nic(nc->peer);
diff --git a/qapi-schema.json b/qapi-schema.json
index 750fc00..d961ac8 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2537,6 +2537,35 @@ 
     'opts': 'NetClientOptions' } }
 
 ##
+# @netfilter-add:
+#
+# Add a netfilter.
+#
+# @data: see NetFilterBase options and specific type's options.
+#
+# Since: 2.5
+#
+# Returns: Nothing on success
+#          If it is not a valid netfilter, DeviceNotFound
+##
+{ 'command': 'netfilter-add',
+  'data': { 'data': 'NetFilter' } }
+
+##
+# @netfilter-del:
+#
+# Remove a netfilter.
+#
+# @id: the name of the netfilter to remove
+#
+# Returns: Nothing on success
+#          If @id is not a valid netfilter, DeviceNotFound
+#
+# Since: 2.5
+##
+{ 'command': 'netfilter-del', 'data': {'id': 'str'} }
+
+##
 # @NetFilterDummyOptions:
 #
 # An dummy filter for network backend
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 7ae33b3..1271fab 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -186,6 +186,20 @@  opts_end_struct(Visitor *v, Error **errp)
 }
 
 
+static void opts_start_implicit_struct(Visitor *v, void **obj,
+                                       size_t size, Error **errp)
+{
+    if (obj) {
+        *obj = g_malloc0(size);
+    }
+}
+
+
+static void opts_end_implicit_struct(Visitor *v, Error **errp)
+{
+}
+
+
 static GQueue *
 lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
 {
@@ -507,6 +521,8 @@  opts_visitor_new(const QemuOpts *opts)
 
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;
+    ov->visitor.start_implicit_struct = &opts_start_implicit_struct;
+    ov->visitor.end_implicit_struct = &opts_end_implicit_struct;
 
     ov->visitor.start_list = &opts_start_list;
     ov->visitor.next_list  = &opts_next_list;
diff --git a/qmp-commands.hx b/qmp-commands.hx
index ba630b1..bb73806 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -926,6 +926,61 @@  Example:
 EQMP
 
     {
+        .name       = "netfilter-add",
+        .args_type  = "data:q",
+        .mhandler.cmd_new = qmp_marshal_input_netfilter_add,
+    },
+
+SQMP
+netfilter-add
+----------
+
+Add a netfilter.
+
+Arguments:
+
+- "data": filter options (json-string)
+
+Example:
+
+-> { "execute": "netfilter-add",
+                "arguments": { "data": { "type": "buffer", "id": "nf0",
+                               "netdev": "bn",
+                               "chain": "in",
+                               "interval": 1000 } } }
+<- { "return": {} }
+
+Note: The supported filter options are the same ones supported by the
+      '-netfilter' command-line argument, which are listed in the '-help'
+      output or QEMU's manual
+
+EQMP
+
+    {
+        .name       = "netfilter-del",
+        .args_type  = "id:s",
+        .mhandler.cmd_new = qmp_marshal_input_netfilter_del,
+    },
+
+SQMP
+netfilter-del
+----------
+
+Remove a netfilter.
+
+Arguments:
+
+- "id": the netfilter's ID, must be unique (json-string)
+
+Example:
+
+-> { "execute": "netfilter_del", "arguments": { "id": "nf0" } }
+<- { "return": {} }
+
+
+EQMP
+
+    {
         .name       = "object-add",
         .args_type  = "qom-type:s,id:s,props:q?",
         .mhandler.cmd_new = qmp_object_add,