root/sys/ofed/drivers/infiniband/core/ib_uverbs_std_types_flow_action.c
/*
 * Copyright (c) 2018, Mellanox Technologies inc.  All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "rdma_core.h"
#include "uverbs.h"
#include <rdma/uverbs_std_types.h>

static int uverbs_free_flow_action(struct ib_uobject *uobject,
                                   enum rdma_remove_reason why,
                                   struct uverbs_attr_bundle *attrs)
{
        struct ib_flow_action *action = uobject->object;
        int ret;

        ret = ib_destroy_usecnt(&action->usecnt, why, uobject);
        if (ret)
                return ret;

        return action->device->destroy_flow_action(action);
}

static u64 esp_flags_uverbs_to_verbs(struct uverbs_attr_bundle *attrs,
                                     u32 flags, bool is_modify)
{
        u64 verbs_flags = flags;

        if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ESN))
                verbs_flags |= IB_FLOW_ACTION_ESP_FLAGS_ESN_TRIGGERED;

        if (is_modify && uverbs_attr_is_valid(attrs,
                                              UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS))
                verbs_flags |= IB_FLOW_ACTION_ESP_FLAGS_MOD_ESP_ATTRS;

        return verbs_flags;
};

static int validate_flow_action_esp_keymat_aes_gcm(struct ib_flow_action_attrs_esp_keymats *keymat)
{
        struct ib_uverbs_flow_action_esp_keymat_aes_gcm *aes_gcm =
                &keymat->keymat.aes_gcm;

        if (aes_gcm->iv_algo > IB_UVERBS_FLOW_ACTION_IV_ALGO_SEQ)
                return -EOPNOTSUPP;

        if (aes_gcm->key_len != 32 &&
            aes_gcm->key_len != 24 &&
            aes_gcm->key_len != 16)
                return -EINVAL;

        if (aes_gcm->icv_len != 16 &&
            aes_gcm->icv_len != 8 &&
            aes_gcm->icv_len != 12)
                return -EINVAL;

        return 0;
}

static int (* const flow_action_esp_keymat_validate[])(struct ib_flow_action_attrs_esp_keymats *keymat) = {
        [IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM] = validate_flow_action_esp_keymat_aes_gcm,
};

static int flow_action_esp_replay_none(struct ib_flow_action_attrs_esp_replays *replay,
                                       bool is_modify)
{
        /* This is used in order to modify an esp flow action with an enabled
         * replay protection to a disabled one. This is only supported via
         * modify, as in create verb we can simply drop the REPLAY attribute and
         * achieve the same thing.
         */
        return is_modify ? 0 : -EINVAL;
}

static int flow_action_esp_replay_def_ok(struct ib_flow_action_attrs_esp_replays *replay,
                                         bool is_modify)
{
        /* Some replay protections could always be enabled without validating
         * anything.
         */
        return 0;
}

static int (* const flow_action_esp_replay_validate[])(struct ib_flow_action_attrs_esp_replays *replay,
                                                       bool is_modify) = {
        [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_NONE] = flow_action_esp_replay_none,
        [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_BMP] = flow_action_esp_replay_def_ok,
};

static int parse_esp_ip(enum ib_flow_spec_type proto,
                        const void __user *val_ptr,
                        size_t len, union ib_flow_spec *out)
{
        int ret;
        const struct ib_uverbs_flow_ipv4_filter ipv4 = {
                .src_ip = cpu_to_be32(0xffffffffUL),
                .dst_ip = cpu_to_be32(0xffffffffUL),
                .proto = 0xff,
                .tos = 0xff,
                .ttl = 0xff,
                .flags = 0xff,
        };
        const struct ib_uverbs_flow_ipv6_filter ipv6 = {
                .src_ip = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
                .dst_ip = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                           0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
                .flow_label = cpu_to_be32(0xffffffffUL),
                .next_hdr = 0xff,
                .traffic_class = 0xff,
                .hop_limit = 0xff,
        };
        union {
                struct ib_uverbs_flow_ipv4_filter ipv4;
                struct ib_uverbs_flow_ipv6_filter ipv6;
        } user_val = {};
        const void *user_pmask;
        size_t val_len;

        /* If the flow IPv4/IPv6 flow specifications are extended, the mask
         * should be changed as well.
         */
        BUILD_BUG_ON(offsetof(struct ib_uverbs_flow_ipv4_filter, flags) +
                     sizeof(ipv4.flags) != sizeof(ipv4));
        BUILD_BUG_ON(offsetof(struct ib_uverbs_flow_ipv6_filter, reserved) +
                     sizeof(ipv6.reserved) != sizeof(ipv6));

        switch (proto) {
        case IB_FLOW_SPEC_IPV4:
                if (len > sizeof(user_val.ipv4) &&
                    !ib_is_buffer_cleared((const u8 *)val_ptr + sizeof(user_val.ipv4),
                                          len - sizeof(user_val.ipv4)))
                        return -EOPNOTSUPP;

                val_len = min_t(size_t, len, sizeof(user_val.ipv4));
                ret = copy_from_user(&user_val.ipv4, val_ptr,
                                     val_len);
                if (ret)
                        return -EFAULT;

                user_pmask = &ipv4;
                break;
        case IB_FLOW_SPEC_IPV6:
                if (len > sizeof(user_val.ipv6) &&
                    !ib_is_buffer_cleared((const u8 *)val_ptr + sizeof(user_val.ipv6),
                                          len - sizeof(user_val.ipv6)))
                        return -EOPNOTSUPP;

                val_len = min_t(size_t, len, sizeof(user_val.ipv6));
                ret = copy_from_user(&user_val.ipv6, val_ptr,
                                     val_len);
                if (ret)
                        return -EFAULT;

                user_pmask = &ipv6;
                break;
        default:
                return -EOPNOTSUPP;
        }

        return ib_uverbs_kern_spec_to_ib_spec_filter(proto, user_pmask,
                                                     &user_val,
                                                     val_len, out);
}

static int flow_action_esp_get_encap(struct ib_flow_spec_list *out,
                                     struct uverbs_attr_bundle *attrs)
{
        struct ib_uverbs_flow_action_esp_encap uverbs_encap;
        int ret;

        ret = uverbs_copy_from(&uverbs_encap, attrs,
                               UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP);
        if (ret)
                return ret;

        /* We currently support only one encap */
        if (uverbs_encap.next_ptr)
                return -EOPNOTSUPP;

        if (uverbs_encap.type != IB_FLOW_SPEC_IPV4 &&
            uverbs_encap.type != IB_FLOW_SPEC_IPV6)
                return -EOPNOTSUPP;

        return parse_esp_ip(uverbs_encap.type,
                            u64_to_user_ptr(uverbs_encap.val_ptr),
                            uverbs_encap.len,
                            &out->spec);
}

struct ib_flow_action_esp_attr {
        struct  ib_flow_action_attrs_esp                hdr;
        struct  ib_flow_action_attrs_esp_keymats        keymat;
        struct  ib_flow_action_attrs_esp_replays        replay;
        /* We currently support only one spec */
        struct  ib_flow_spec_list                       encap;
};

#define ESP_LAST_SUPPORTED_FLAG         IB_UVERBS_FLOW_ACTION_ESP_FLAGS_ESN_NEW_WINDOW
static int parse_flow_action_esp(struct ib_device *ib_dev,
                                 struct uverbs_attr_bundle *attrs,
                                 struct ib_flow_action_esp_attr *esp_attr,
                                 bool is_modify)
{
        struct ib_uverbs_flow_action_esp uverbs_esp = {};
        int ret;

        /* Optional param, if it doesn't exist, we get -ENOENT and skip it */
        ret = uverbs_copy_from(&esp_attr->hdr.esn, attrs,
                               UVERBS_ATTR_FLOW_ACTION_ESP_ESN);
        if (IS_UVERBS_COPY_ERR(ret))
                return ret;

        /* This can be called from FLOW_ACTION_ESP_MODIFY where
         * UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS is optional
         */
        if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS)) {
                ret = uverbs_copy_from_or_zero(&uverbs_esp, attrs,
                                               UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS);
                if (ret)
                        return ret;

                if (uverbs_esp.flags & ~((ESP_LAST_SUPPORTED_FLAG << 1) - 1))
                        return -EOPNOTSUPP;

                esp_attr->hdr.spi = uverbs_esp.spi;
                esp_attr->hdr.seq = uverbs_esp.seq;
                esp_attr->hdr.tfc_pad = uverbs_esp.tfc_pad;
                esp_attr->hdr.hard_limit_pkts = uverbs_esp.hard_limit_pkts;
        }
        esp_attr->hdr.flags = esp_flags_uverbs_to_verbs(attrs, uverbs_esp.flags,
                                                        is_modify);

        if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT)) {
                esp_attr->keymat.protocol =
                        uverbs_attr_get_enum_id(attrs,
                                                UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT);
                ret = uverbs_copy_from_or_zero(&esp_attr->keymat.keymat,
                                               attrs,
                                               UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT);
                if (ret)
                        return ret;

                ret = flow_action_esp_keymat_validate[esp_attr->keymat.protocol](&esp_attr->keymat);
                if (ret)
                        return ret;

                esp_attr->hdr.keymat = &esp_attr->keymat;
        }

        if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY)) {
                esp_attr->replay.protocol =
                        uverbs_attr_get_enum_id(attrs,
                                                UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY);

                ret = uverbs_copy_from_or_zero(&esp_attr->replay.replay,
                                               attrs,
                                               UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY);
                if (ret)
                        return ret;

                ret = flow_action_esp_replay_validate[esp_attr->replay.protocol](&esp_attr->replay,
                                                                                 is_modify);
                if (ret)
                        return ret;

                esp_attr->hdr.replay = &esp_attr->replay;
        }

        if (uverbs_attr_is_valid(attrs, UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP)) {
                ret = flow_action_esp_get_encap(&esp_attr->encap, attrs);
                if (ret)
                        return ret;

                esp_attr->hdr.encap = &esp_attr->encap;
        }

        return 0;
}

static int UVERBS_HANDLER(UVERBS_METHOD_FLOW_ACTION_ESP_CREATE)(
        struct uverbs_attr_bundle *attrs)
{
        struct ib_uobject *uobj = uverbs_attr_get_uobject(
                attrs, UVERBS_ATTR_CREATE_FLOW_ACTION_ESP_HANDLE);
        struct ib_device *ib_dev = attrs->context->device;
        int                               ret;
        struct ib_flow_action             *action;
        struct ib_flow_action_esp_attr    esp_attr = {};

        if (!ib_dev->create_flow_action_esp)
                return -EOPNOTSUPP;

        ret = parse_flow_action_esp(ib_dev, attrs, &esp_attr, false);
        if (ret)
                return ret;

        /* No need to check as this attribute is marked as MANDATORY */
        action = ib_dev->create_flow_action_esp(ib_dev, &esp_attr.hdr,
                                                    attrs);
        if (IS_ERR(action))
                return PTR_ERR(action);

        uverbs_flow_action_fill_action(action, uobj, ib_dev,
                                       IB_FLOW_ACTION_ESP);

        return 0;
}

static int UVERBS_HANDLER(UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY)(
        struct uverbs_attr_bundle *attrs)
{
        struct ib_uobject *uobj = uverbs_attr_get_uobject(
                attrs, UVERBS_ATTR_MODIFY_FLOW_ACTION_ESP_HANDLE);
        struct ib_flow_action *action = uobj->object;
        int                               ret;
        struct ib_flow_action_esp_attr    esp_attr = {};

        if (!action->device->modify_flow_action_esp)
                return -EOPNOTSUPP;

        ret = parse_flow_action_esp(action->device, attrs, &esp_attr, true);
        if (ret)
                return ret;

        if (action->type != IB_FLOW_ACTION_ESP)
                return -EINVAL;

        return action->device->modify_flow_action_esp(action,
                                                          &esp_attr.hdr,
                                                          attrs);
}

static const struct uverbs_attr_spec uverbs_flow_action_esp_keymat[] = {
        [IB_UVERBS_FLOW_ACTION_ESP_KEYMAT_AES_GCM] = {
                .type = UVERBS_ATTR_TYPE_PTR_IN,
                UVERBS_ATTR_STRUCT(
                        struct ib_uverbs_flow_action_esp_keymat_aes_gcm,
                        aes_key),
        },
};

static const struct uverbs_attr_spec uverbs_flow_action_esp_replay[] = {
        [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_NONE] = {
                .type = UVERBS_ATTR_TYPE_PTR_IN,
                UVERBS_ATTR_NO_DATA(),
        },
        [IB_UVERBS_FLOW_ACTION_ESP_REPLAY_BMP] = {
                .type = UVERBS_ATTR_TYPE_PTR_IN,
                UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp_replay_bmp,
                                   size),
        },
};

DECLARE_UVERBS_NAMED_METHOD(
        UVERBS_METHOD_FLOW_ACTION_ESP_CREATE,
        UVERBS_ATTR_IDR(UVERBS_ATTR_CREATE_FLOW_ACTION_ESP_HANDLE,
                        UVERBS_OBJECT_FLOW_ACTION,
                        UVERBS_ACCESS_NEW,
                        UA_MANDATORY),
        UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS,
                           UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp,
                                              hard_limit_pkts),
                           UA_MANDATORY),
        UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ESN,
                           UVERBS_ATTR_TYPE(__u32),
                           UA_OPTIONAL),
        UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT,
                            uverbs_flow_action_esp_keymat,
                            UA_MANDATORY),
        UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY,
                            uverbs_flow_action_esp_replay,
                            UA_OPTIONAL),
        UVERBS_ATTR_PTR_IN(
                UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP,
                UVERBS_ATTR_TYPE(struct ib_uverbs_flow_action_esp_encap),
                UA_OPTIONAL));

DECLARE_UVERBS_NAMED_METHOD(
        UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY,
        UVERBS_ATTR_IDR(UVERBS_ATTR_MODIFY_FLOW_ACTION_ESP_HANDLE,
                        UVERBS_OBJECT_FLOW_ACTION,
                        UVERBS_ACCESS_WRITE,
                        UA_MANDATORY),
        UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ATTRS,
                           UVERBS_ATTR_STRUCT(struct ib_uverbs_flow_action_esp,
                                              hard_limit_pkts),
                           UA_OPTIONAL),
        UVERBS_ATTR_PTR_IN(UVERBS_ATTR_FLOW_ACTION_ESP_ESN,
                           UVERBS_ATTR_TYPE(__u32),
                           UA_OPTIONAL),
        UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_KEYMAT,
                            uverbs_flow_action_esp_keymat,
                            UA_OPTIONAL),
        UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_FLOW_ACTION_ESP_REPLAY,
                            uverbs_flow_action_esp_replay,
                            UA_OPTIONAL),
        UVERBS_ATTR_PTR_IN(
                UVERBS_ATTR_FLOW_ACTION_ESP_ENCAP,
                UVERBS_ATTR_TYPE(struct ib_uverbs_flow_action_esp_encap),
                UA_OPTIONAL));

DECLARE_UVERBS_NAMED_METHOD_DESTROY(
        UVERBS_METHOD_FLOW_ACTION_DESTROY,
        UVERBS_ATTR_IDR(UVERBS_ATTR_DESTROY_FLOW_ACTION_HANDLE,
                        UVERBS_OBJECT_FLOW_ACTION,
                        UVERBS_ACCESS_DESTROY,
                        UA_MANDATORY));

DECLARE_UVERBS_NAMED_OBJECT(
        UVERBS_OBJECT_FLOW_ACTION,
        UVERBS_TYPE_ALLOC_IDR(uverbs_free_flow_action),
        &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_CREATE),
        &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_DESTROY),
        &UVERBS_METHOD(UVERBS_METHOD_FLOW_ACTION_ESP_MODIFY));

const struct uapi_definition uverbs_def_obj_flow_action[] = {
        UAPI_DEF_CHAIN_OBJ_TREE_NAMED(
                UVERBS_OBJECT_FLOW_ACTION,
                UAPI_DEF_OBJ_NEEDS_FN(destroy_flow_action)),
        {}
};