qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

filter.c (10798B)


      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 "qapi/qmp/qerror.h"
     12 #include "qemu/error-report.h"
     13 
     14 #include "net/filter.h"
     15 #include "net/net.h"
     16 #include "net/vhost_net.h"
     17 #include "qom/object_interfaces.h"
     18 #include "qemu/iov.h"
     19 #include "qemu/module.h"
     20 #include "net/colo.h"
     21 #include "migration/colo.h"
     22 
     23 static inline bool qemu_can_skip_netfilter(NetFilterState *nf)
     24 {
     25     return !nf->on;
     26 }
     27 
     28 ssize_t qemu_netfilter_receive(NetFilterState *nf,
     29                                NetFilterDirection direction,
     30                                NetClientState *sender,
     31                                unsigned flags,
     32                                const struct iovec *iov,
     33                                int iovcnt,
     34                                NetPacketSent *sent_cb)
     35 {
     36     if (qemu_can_skip_netfilter(nf)) {
     37         return 0;
     38     }
     39     if (nf->direction == direction ||
     40         nf->direction == NET_FILTER_DIRECTION_ALL) {
     41         return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov(
     42                                    nf, sender, flags, iov, iovcnt, sent_cb);
     43     }
     44 
     45     return 0;
     46 }
     47 
     48 static NetFilterState *netfilter_next(NetFilterState *nf,
     49                                       NetFilterDirection dir)
     50 {
     51     NetFilterState *next;
     52 
     53     if (dir == NET_FILTER_DIRECTION_TX) {
     54         /* forward walk through filters */
     55         next = QTAILQ_NEXT(nf, next);
     56     } else {
     57         /* reverse order */
     58         next = QTAILQ_PREV(nf, next);
     59     }
     60 
     61     return next;
     62 }
     63 
     64 ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
     65                                     unsigned flags,
     66                                     const struct iovec *iov,
     67                                     int iovcnt,
     68                                     void *opaque)
     69 {
     70     int ret = 0;
     71     int direction;
     72     NetFilterState *nf = opaque;
     73     NetFilterState *next = NULL;
     74 
     75     if (!sender || !sender->peer) {
     76         /* no receiver, or sender been deleted, no need to pass it further */
     77         goto out;
     78     }
     79 
     80     if (nf->direction == NET_FILTER_DIRECTION_ALL) {
     81         if (sender == nf->netdev) {
     82             /* This packet is sent by netdev itself */
     83             direction = NET_FILTER_DIRECTION_TX;
     84         } else {
     85             direction = NET_FILTER_DIRECTION_RX;
     86         }
     87     } else {
     88         direction = nf->direction;
     89     }
     90 
     91     next = netfilter_next(nf, direction);
     92     while (next) {
     93         /*
     94          * if qemu_netfilter_pass_to_next been called, means that
     95          * the packet has been hold by filter and has already retured size
     96          * to the sender, so sent_cb shouldn't be called later, just
     97          * pass NULL to next.
     98          */
     99         ret = qemu_netfilter_receive(next, direction, sender, flags, iov,
    100                                      iovcnt, NULL);
    101         if (ret) {
    102             return ret;
    103         }
    104         next = netfilter_next(next, direction);
    105     }
    106 
    107     /*
    108      * We have gone through all filters, pass it to receiver.
    109      * Do the valid check again incase sender or receiver been
    110      * deleted while we go through filters.
    111      */
    112     if (sender && sender->peer) {
    113         qemu_net_queue_send_iov(sender->peer->incoming_queue,
    114                                 sender, flags, iov, iovcnt, NULL);
    115     }
    116 
    117 out:
    118     /* no receiver, or sender been deleted */
    119     return iov_size(iov, iovcnt);
    120 }
    121 
    122 static char *netfilter_get_netdev_id(Object *obj, Error **errp)
    123 {
    124     NetFilterState *nf = NETFILTER(obj);
    125 
    126     return g_strdup(nf->netdev_id);
    127 }
    128 
    129 static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp)
    130 {
    131     NetFilterState *nf = NETFILTER(obj);
    132 
    133     nf->netdev_id = g_strdup(str);
    134 }
    135 
    136 static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED)
    137 {
    138     NetFilterState *nf = NETFILTER(obj);
    139     return nf->direction;
    140 }
    141 
    142 static void netfilter_set_direction(Object *obj, int direction, Error **errp)
    143 {
    144     NetFilterState *nf = NETFILTER(obj);
    145     nf->direction = direction;
    146 }
    147 
    148 static char *netfilter_get_status(Object *obj, Error **errp)
    149 {
    150     NetFilterState *nf = NETFILTER(obj);
    151 
    152     return nf->on ? g_strdup("on") : g_strdup("off");
    153 }
    154 
    155 static void netfilter_set_status(Object *obj, const char *str, Error **errp)
    156 {
    157     NetFilterState *nf = NETFILTER(obj);
    158     NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
    159 
    160     if (strcmp(str, "on") && strcmp(str, "off")) {
    161         error_setg(errp, "Invalid value for netfilter status, "
    162                          "should be 'on' or 'off'");
    163         return;
    164     }
    165     if (nf->on == !strcmp(str, "on")) {
    166         return;
    167     }
    168     nf->on = !nf->on;
    169     if (nf->netdev && nfc->status_changed) {
    170         nfc->status_changed(nf, errp);
    171     }
    172 }
    173 
    174 static char *netfilter_get_position(Object *obj, Error **errp)
    175 {
    176     NetFilterState *nf = NETFILTER(obj);
    177 
    178     return g_strdup(nf->position);
    179 }
    180 
    181 static void netfilter_set_position(Object *obj, const char *str, Error **errp)
    182 {
    183     NetFilterState *nf = NETFILTER(obj);
    184 
    185     nf->position = g_strdup(str);
    186 }
    187 
    188 static char *netfilter_get_insert(Object *obj, Error **errp)
    189 {
    190     NetFilterState *nf = NETFILTER(obj);
    191 
    192     return nf->insert_before_flag ? g_strdup("before") : g_strdup("behind");
    193 }
    194 
    195 static void netfilter_set_insert(Object *obj, const char *str, Error **errp)
    196 {
    197     NetFilterState *nf = NETFILTER(obj);
    198 
    199     if (strcmp(str, "before") && strcmp(str, "behind")) {
    200         error_setg(errp, "Invalid value for netfilter insert, "
    201                          "should be 'before' or 'behind'");
    202         return;
    203     }
    204 
    205     nf->insert_before_flag = !strcmp(str, "before");
    206 }
    207 
    208 static void netfilter_init(Object *obj)
    209 {
    210     NetFilterState *nf = NETFILTER(obj);
    211 
    212     nf->on = true;
    213     nf->insert_before_flag = false;
    214     nf->position = g_strdup("tail");
    215 }
    216 
    217 static void netfilter_complete(UserCreatable *uc, Error **errp)
    218 {
    219     NetFilterState *nf = NETFILTER(uc);
    220     NetFilterState *position = NULL;
    221     NetClientState *ncs[MAX_QUEUE_NUM];
    222     NetFilterClass *nfc = NETFILTER_GET_CLASS(uc);
    223     int queues;
    224     Error *local_err = NULL;
    225 
    226     if (!nf->netdev_id) {
    227         error_setg(errp, "Parameter 'netdev' is required");
    228         return;
    229     }
    230 
    231     queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
    232                                           NET_CLIENT_DRIVER_NIC,
    233                                           MAX_QUEUE_NUM);
    234     if (queues < 1) {
    235         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
    236                    "a network backend id");
    237         return;
    238     } else if (queues > 1) {
    239         error_setg(errp, "multiqueue is not supported");
    240         return;
    241     }
    242 
    243     if (get_vhost_net(ncs[0])) {
    244         error_setg(errp, "Vhost is not supported");
    245         return;
    246     }
    247 
    248     if (strcmp(nf->position, "head") && strcmp(nf->position, "tail")) {
    249         Object *container;
    250         Object *obj;
    251         char *position_id;
    252 
    253         if (!g_str_has_prefix(nf->position, "id=")) {
    254             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "position",
    255                        "'head', 'tail' or 'id=<id>'");
    256             return;
    257         }
    258 
    259         /* get the id from the string */
    260         position_id = g_strndup(nf->position + 3, strlen(nf->position) - 3);
    261 
    262         /* Search for the position to insert before/behind */
    263         container = object_get_objects_root();
    264         obj = object_resolve_path_component(container, position_id);
    265         if (!obj) {
    266             error_setg(errp, "filter '%s' not found", position_id);
    267             g_free(position_id);
    268             return;
    269         }
    270 
    271         position = NETFILTER(obj);
    272 
    273         if (position->netdev != ncs[0]) {
    274             error_setg(errp, "filter '%s' belongs to a different netdev",
    275                         position_id);
    276             g_free(position_id);
    277             return;
    278         }
    279 
    280         g_free(position_id);
    281     }
    282 
    283     nf->netdev = ncs[0];
    284 
    285     if (nfc->setup) {
    286         nfc->setup(nf, &local_err);
    287         if (local_err) {
    288             error_propagate(errp, local_err);
    289             return;
    290         }
    291     }
    292 
    293     if (position) {
    294         if (nf->insert_before_flag) {
    295             QTAILQ_INSERT_BEFORE(position, nf, next);
    296         } else {
    297             QTAILQ_INSERT_AFTER(&nf->netdev->filters, position, nf, next);
    298         }
    299     } else if (!strcmp(nf->position, "head")) {
    300         QTAILQ_INSERT_HEAD(&nf->netdev->filters, nf, next);
    301     } else if (!strcmp(nf->position, "tail")) {
    302         QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
    303     }
    304 }
    305 
    306 static void netfilter_finalize(Object *obj)
    307 {
    308     NetFilterState *nf = NETFILTER(obj);
    309     NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
    310 
    311     if (nfc->cleanup) {
    312         nfc->cleanup(nf);
    313     }
    314 
    315     if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters) &&
    316         QTAILQ_IN_USE(nf, next)) {
    317         QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
    318     }
    319     g_free(nf->netdev_id);
    320     g_free(nf->position);
    321 }
    322 
    323 static void default_handle_event(NetFilterState *nf, int event, Error **errp)
    324 {
    325     switch (event) {
    326     case COLO_EVENT_CHECKPOINT:
    327         break;
    328     case COLO_EVENT_FAILOVER:
    329         object_property_set_str(OBJECT(nf), "status", "off", errp);
    330         break;
    331     default:
    332         break;
    333     }
    334 }
    335 
    336 static void netfilter_class_init(ObjectClass *oc, void *data)
    337 {
    338     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
    339     NetFilterClass *nfc = NETFILTER_CLASS(oc);
    340 
    341     object_class_property_add_str(oc, "netdev",
    342                                   netfilter_get_netdev_id, netfilter_set_netdev_id);
    343     object_class_property_add_enum(oc, "queue", "NetFilterDirection",
    344                                    &NetFilterDirection_lookup,
    345                                    netfilter_get_direction, netfilter_set_direction);
    346     object_class_property_add_str(oc, "status",
    347                                   netfilter_get_status, netfilter_set_status);
    348     object_class_property_add_str(oc, "position",
    349                                   netfilter_get_position, netfilter_set_position);
    350     object_class_property_add_str(oc, "insert",
    351                                   netfilter_get_insert, netfilter_set_insert);
    352 
    353     ucc->complete = netfilter_complete;
    354     nfc->handle_event = default_handle_event;
    355 }
    356 
    357 static const TypeInfo netfilter_info = {
    358     .name = TYPE_NETFILTER,
    359     .parent = TYPE_OBJECT,
    360     .abstract = true,
    361     .class_size = sizeof(NetFilterClass),
    362     .class_init = netfilter_class_init,
    363     .instance_size = sizeof(NetFilterState),
    364     .instance_init = netfilter_init,
    365     .instance_finalize = netfilter_finalize,
    366     .interfaces = (InterfaceInfo[]) {
    367         { TYPE_USER_CREATABLE },
    368         { }
    369     }
    370 };
    371 
    372 static void register_types(void)
    373 {
    374     type_register_static(&netfilter_info);
    375 }
    376 
    377 type_init(register_types);