qemu/net/filter.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 FUJITSU LIMITED
   3 * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
   4 *
   5 * This work is licensed under the terms of the GNU GPL, version 2 or
   6 * later.  See the COPYING file in the top-level directory.
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qapi/error.h"
  11#include "qemu-common.h"
  12#include "qapi/qmp/qerror.h"
  13#include "qemu/error-report.h"
  14
  15#include "net/filter.h"
  16#include "net/net.h"
  17#include "net/vhost_net.h"
  18#include "qom/object_interfaces.h"
  19#include "qemu/iov.h"
  20
  21static inline bool qemu_can_skip_netfilter(NetFilterState *nf)
  22{
  23    return !nf->on;
  24}
  25
  26ssize_t qemu_netfilter_receive(NetFilterState *nf,
  27                               NetFilterDirection direction,
  28                               NetClientState *sender,
  29                               unsigned flags,
  30                               const struct iovec *iov,
  31                               int iovcnt,
  32                               NetPacketSent *sent_cb)
  33{
  34    if (qemu_can_skip_netfilter(nf)) {
  35        return 0;
  36    }
  37    if (nf->direction == direction ||
  38        nf->direction == NET_FILTER_DIRECTION_ALL) {
  39        return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov(
  40                                   nf, sender, flags, iov, iovcnt, sent_cb);
  41    }
  42
  43    return 0;
  44}
  45
  46static NetFilterState *netfilter_next(NetFilterState *nf,
  47                                      NetFilterDirection dir)
  48{
  49    NetFilterState *next;
  50
  51    if (dir == NET_FILTER_DIRECTION_TX) {
  52        /* forward walk through filters */
  53        next = QTAILQ_NEXT(nf, next);
  54    } else {
  55        /* reverse order */
  56        next = QTAILQ_PREV(nf, NetFilterHead, next);
  57    }
  58
  59    return next;
  60}
  61
  62ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
  63                                    unsigned flags,
  64                                    const struct iovec *iov,
  65                                    int iovcnt,
  66                                    void *opaque)
  67{
  68    int ret = 0;
  69    int direction;
  70    NetFilterState *nf = opaque;
  71    NetFilterState *next = NULL;
  72
  73    if (!sender || !sender->peer) {
  74        /* no receiver, or sender been deleted, no need to pass it further */
  75        goto out;
  76    }
  77
  78    if (nf->direction == NET_FILTER_DIRECTION_ALL) {
  79        if (sender == nf->netdev) {
  80            /* This packet is sent by netdev itself */
  81            direction = NET_FILTER_DIRECTION_TX;
  82        } else {
  83            direction = NET_FILTER_DIRECTION_RX;
  84        }
  85    } else {
  86        direction = nf->direction;
  87    }
  88
  89    next = netfilter_next(nf, direction);
  90    while (next) {
  91        /*
  92         * if qemu_netfilter_pass_to_next been called, means that
  93         * the packet has been hold by filter and has already retured size
  94         * to the sender, so sent_cb shouldn't be called later, just
  95         * pass NULL to next.
  96         */
  97        ret = qemu_netfilter_receive(next, direction, sender, flags, iov,
  98                                     iovcnt, NULL);
  99        if (ret) {
 100            return ret;
 101        }
 102        next = netfilter_next(next, direction);
 103    }
 104
 105    /*
 106     * We have gone through all filters, pass it to receiver.
 107     * Do the valid check again incase sender or receiver been
 108     * deleted while we go through filters.
 109     */
 110    if (sender && sender->peer) {
 111        qemu_net_queue_send_iov(sender->peer->incoming_queue,
 112                                sender, flags, iov, iovcnt, NULL);
 113    }
 114
 115out:
 116    /* no receiver, or sender been deleted */
 117    return iov_size(iov, iovcnt);
 118}
 119
 120static char *netfilter_get_netdev_id(Object *obj, Error **errp)
 121{
 122    NetFilterState *nf = NETFILTER(obj);
 123
 124    return g_strdup(nf->netdev_id);
 125}
 126
 127static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp)
 128{
 129    NetFilterState *nf = NETFILTER(obj);
 130
 131    nf->netdev_id = g_strdup(str);
 132}
 133
 134static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED)
 135{
 136    NetFilterState *nf = NETFILTER(obj);
 137    return nf->direction;
 138}
 139
 140static void netfilter_set_direction(Object *obj, int direction, Error **errp)
 141{
 142    NetFilterState *nf = NETFILTER(obj);
 143    nf->direction = direction;
 144}
 145
 146static char *netfilter_get_status(Object *obj, Error **errp)
 147{
 148    NetFilterState *nf = NETFILTER(obj);
 149
 150    return nf->on ? g_strdup("on") : g_strdup("off");
 151}
 152
 153static void netfilter_set_status(Object *obj, const char *str, Error **errp)
 154{
 155    NetFilterState *nf = NETFILTER(obj);
 156    NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
 157
 158    if (strcmp(str, "on") && strcmp(str, "off")) {
 159        error_setg(errp, "Invalid value for netfilter status, "
 160                         "should be 'on' or 'off'");
 161        return;
 162    }
 163    if (nf->on == !strcmp(str, "on")) {
 164        return;
 165    }
 166    nf->on = !nf->on;
 167    if (nf->netdev && nfc->status_changed) {
 168        nfc->status_changed(nf, errp);
 169    }
 170}
 171
 172static void netfilter_init(Object *obj)
 173{
 174    NetFilterState *nf = NETFILTER(obj);
 175
 176    nf->on = true;
 177
 178    object_property_add_str(obj, "netdev",
 179                            netfilter_get_netdev_id, netfilter_set_netdev_id,
 180                            NULL);
 181    object_property_add_enum(obj, "queue", "NetFilterDirection",
 182                             &NetFilterDirection_lookup,
 183                             netfilter_get_direction, netfilter_set_direction,
 184                             NULL);
 185    object_property_add_str(obj, "status",
 186                            netfilter_get_status, netfilter_set_status,
 187                            NULL);
 188}
 189
 190static void netfilter_complete(UserCreatable *uc, Error **errp)
 191{
 192    NetFilterState *nf = NETFILTER(uc);
 193    NetClientState *ncs[MAX_QUEUE_NUM];
 194    NetFilterClass *nfc = NETFILTER_GET_CLASS(uc);
 195    int queues;
 196    Error *local_err = NULL;
 197
 198    if (!nf->netdev_id) {
 199        error_setg(errp, "Parameter 'netdev' is required");
 200        return;
 201    }
 202
 203    queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
 204                                          NET_CLIENT_DRIVER_NIC,
 205                                          MAX_QUEUE_NUM);
 206    if (queues < 1) {
 207        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
 208                   "a network backend id");
 209        return;
 210    } else if (queues > 1) {
 211        error_setg(errp, "multiqueue is not supported");
 212        return;
 213    }
 214
 215    if (get_vhost_net(ncs[0])) {
 216        error_setg(errp, "Vhost is not supported");
 217        return;
 218    }
 219
 220    nf->netdev = ncs[0];
 221
 222    if (nfc->setup) {
 223        nfc->setup(nf, &local_err);
 224        if (local_err) {
 225            error_propagate(errp, local_err);
 226            return;
 227        }
 228    }
 229    QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
 230}
 231
 232static void netfilter_finalize(Object *obj)
 233{
 234    NetFilterState *nf = NETFILTER(obj);
 235    NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
 236
 237    if (nfc->cleanup) {
 238        nfc->cleanup(nf);
 239    }
 240
 241    if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) &&
 242        QTAILQ_IN_USE(nf, next)) {
 243        QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
 244    }
 245    g_free(nf->netdev_id);
 246}
 247
 248static void netfilter_class_init(ObjectClass *oc, void *data)
 249{
 250    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 251
 252    ucc->complete = netfilter_complete;
 253}
 254
 255static const TypeInfo netfilter_info = {
 256    .name = TYPE_NETFILTER,
 257    .parent = TYPE_OBJECT,
 258    .abstract = true,
 259    .class_size = sizeof(NetFilterClass),
 260    .class_init = netfilter_class_init,
 261    .instance_size = sizeof(NetFilterState),
 262    .instance_init = netfilter_init,
 263    .instance_finalize = netfilter_finalize,
 264    .interfaces = (InterfaceInfo[]) {
 265        { TYPE_USER_CREATABLE },
 266        { }
 267    }
 268};
 269
 270static void register_types(void)
 271{
 272    type_register_static(&netfilter_info);
 273}
 274
 275type_init(register_types);
 276