| /* |
| * 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; |
| |
| if (atomic_read(&action->usecnt)) |
| return -EBUSY; |
| |
| return action->device->ops.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(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(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->ops.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->ops.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->ops.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->ops.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)), |
| {} |
| }; |