cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     23static inline bool qemu_can_skip_netfilter(NetFilterState *nf)
     24{
     25    return !nf->on;
     26}
     27
     28ssize_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
     48static 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
     64ssize_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
    117out:
    118    /* no receiver, or sender been deleted */
    119    return iov_size(iov, iovcnt);
    120}
    121
    122static 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
    129static 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
    136static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED)
    137{
    138    NetFilterState *nf = NETFILTER(obj);
    139    return nf->direction;
    140}
    141
    142static void netfilter_set_direction(Object *obj, int direction, Error **errp)
    143{
    144    NetFilterState *nf = NETFILTER(obj);
    145    nf->direction = direction;
    146}
    147
    148static 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
    155static 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
    174static char *netfilter_get_position(Object *obj, Error **errp)
    175{
    176    NetFilterState *nf = NETFILTER(obj);
    177
    178    return g_strdup(nf->position);
    179}
    180
    181static 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
    188static 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
    195static 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
    208static 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
    217static 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
    306static 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
    323static 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
    336static 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
    357static 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
    372static void register_types(void)
    373{
    374    type_register_static(&netfilter_info);
    375}
    376
    377type_init(register_types);