| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2017, Mellanox Technologies inc. All rights reserved. */ |
| |
| #include "mlx5_core.h" |
| #include "ipsec.h" |
| #include "lib/mlx5.h" |
| |
| u32 mlx5_ipsec_device_caps(struct mlx5_core_dev *mdev) |
| { |
| u32 caps = 0; |
| |
| if (!MLX5_CAP_GEN(mdev, ipsec_offload)) |
| return 0; |
| |
| if (!MLX5_CAP_GEN(mdev, log_max_dek)) |
| return 0; |
| |
| if (!(MLX5_CAP_GEN_64(mdev, general_obj_types) & |
| MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_IPSEC)) |
| return 0; |
| |
| if (!MLX5_CAP_FLOWTABLE_NIC_TX(mdev, ipsec_encrypt) || |
| !MLX5_CAP_FLOWTABLE_NIC_RX(mdev, ipsec_decrypt)) |
| return 0; |
| |
| if (!MLX5_CAP_IPSEC(mdev, ipsec_crypto_esp_aes_gcm_128_encrypt) || |
| !MLX5_CAP_IPSEC(mdev, ipsec_crypto_esp_aes_gcm_128_decrypt)) |
| return 0; |
| |
| if (MLX5_CAP_IPSEC(mdev, ipsec_crypto_offload) && |
| MLX5_CAP_ETH(mdev, insert_trailer) && MLX5_CAP_ETH(mdev, swp)) |
| caps |= MLX5_IPSEC_CAP_CRYPTO; |
| |
| if (!caps) |
| return 0; |
| |
| if (MLX5_CAP_IPSEC(mdev, ipsec_esn)) |
| caps |= MLX5_IPSEC_CAP_ESN; |
| |
| /* We can accommodate up to 2^24 different IPsec objects |
| * because we use up to 24 bit in flow table metadata |
| * to hold the IPsec Object unique handle. |
| */ |
| WARN_ON_ONCE(MLX5_CAP_IPSEC(mdev, log_max_ipsec_offload) > 24); |
| return caps; |
| } |
| EXPORT_SYMBOL_GPL(mlx5_ipsec_device_caps); |
| |
| static int mlx5_create_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) |
| { |
| struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs; |
| struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); |
| struct aes_gcm_keymat *aes_gcm = &attrs->aes_gcm; |
| u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; |
| u32 in[MLX5_ST_SZ_DW(create_ipsec_obj_in)] = {}; |
| void *obj, *salt_p, *salt_iv_p; |
| int err; |
| |
| obj = MLX5_ADDR_OF(create_ipsec_obj_in, in, ipsec_object); |
| |
| /* salt and seq_iv */ |
| salt_p = MLX5_ADDR_OF(ipsec_obj, obj, salt); |
| memcpy(salt_p, &aes_gcm->salt, sizeof(aes_gcm->salt)); |
| |
| MLX5_SET(ipsec_obj, obj, icv_length, MLX5_IPSEC_OBJECT_ICV_LEN_16B); |
| salt_iv_p = MLX5_ADDR_OF(ipsec_obj, obj, implicit_iv); |
| memcpy(salt_iv_p, &aes_gcm->seq_iv, sizeof(aes_gcm->seq_iv)); |
| /* esn */ |
| if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED) { |
| MLX5_SET(ipsec_obj, obj, esn_en, 1); |
| MLX5_SET(ipsec_obj, obj, esn_msb, attrs->esn); |
| if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) |
| MLX5_SET(ipsec_obj, obj, esn_overlap, 1); |
| } |
| |
| MLX5_SET(ipsec_obj, obj, dekn, sa_entry->enc_key_id); |
| |
| /* general object fields set */ |
| MLX5_SET(general_obj_in_cmd_hdr, in, opcode, |
| MLX5_CMD_OP_CREATE_GENERAL_OBJECT); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, |
| MLX5_GENERAL_OBJECT_TYPES_IPSEC); |
| |
| err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); |
| if (!err) |
| sa_entry->ipsec_obj_id = |
| MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); |
| |
| return err; |
| } |
| |
| static void mlx5_destroy_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry) |
| { |
| struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); |
| u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; |
| u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; |
| |
| MLX5_SET(general_obj_in_cmd_hdr, in, opcode, |
| MLX5_CMD_OP_DESTROY_GENERAL_OBJECT); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, |
| MLX5_GENERAL_OBJECT_TYPES_IPSEC); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sa_entry->ipsec_obj_id); |
| |
| mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); |
| } |
| |
| int mlx5_ipsec_create_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry) |
| { |
| struct aes_gcm_keymat *aes_gcm = &sa_entry->attrs.aes_gcm; |
| struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); |
| int err; |
| |
| /* key */ |
| err = mlx5_create_encryption_key(mdev, aes_gcm->aes_key, |
| aes_gcm->key_len / BITS_PER_BYTE, |
| MLX5_ACCEL_OBJ_IPSEC_KEY, |
| &sa_entry->enc_key_id); |
| if (err) { |
| mlx5_core_dbg(mdev, "Failed to create encryption key (err = %d)\n", err); |
| return err; |
| } |
| |
| err = mlx5_create_ipsec_obj(sa_entry); |
| if (err) { |
| mlx5_core_dbg(mdev, "Failed to create IPsec object (err = %d)\n", err); |
| goto err_enc_key; |
| } |
| |
| return 0; |
| |
| err_enc_key: |
| mlx5_destroy_encryption_key(mdev, sa_entry->enc_key_id); |
| return err; |
| } |
| |
| void mlx5_ipsec_free_sa_ctx(struct mlx5e_ipsec_sa_entry *sa_entry) |
| { |
| struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); |
| |
| mlx5_destroy_ipsec_obj(sa_entry); |
| mlx5_destroy_encryption_key(mdev, sa_entry->enc_key_id); |
| } |
| |
| static int mlx5_modify_ipsec_obj(struct mlx5e_ipsec_sa_entry *sa_entry, |
| const struct mlx5_accel_esp_xfrm_attrs *attrs) |
| { |
| struct mlx5_core_dev *mdev = mlx5e_ipsec_sa2dev(sa_entry); |
| u32 in[MLX5_ST_SZ_DW(modify_ipsec_obj_in)] = {}; |
| u32 out[MLX5_ST_SZ_DW(query_ipsec_obj_out)]; |
| u64 modify_field_select = 0; |
| u64 general_obj_types; |
| void *obj; |
| int err; |
| |
| if (!(attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_TRIGGERED)) |
| return 0; |
| |
| general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types); |
| if (!(general_obj_types & MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_IPSEC)) |
| return -EINVAL; |
| |
| /* general object fields set */ |
| MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_IPSEC); |
| MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sa_entry->ipsec_obj_id); |
| err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); |
| if (err) { |
| mlx5_core_err(mdev, "Query IPsec object failed (Object id %d), err = %d\n", |
| sa_entry->ipsec_obj_id, err); |
| return err; |
| } |
| |
| obj = MLX5_ADDR_OF(query_ipsec_obj_out, out, ipsec_object); |
| modify_field_select = MLX5_GET64(ipsec_obj, obj, modify_field_select); |
| |
| /* esn */ |
| if (!(modify_field_select & MLX5_MODIFY_IPSEC_BITMASK_ESN_OVERLAP) || |
| !(modify_field_select & MLX5_MODIFY_IPSEC_BITMASK_ESN_MSB)) |
| return -EOPNOTSUPP; |
| |
| obj = MLX5_ADDR_OF(modify_ipsec_obj_in, in, ipsec_object); |
| MLX5_SET64(ipsec_obj, obj, modify_field_select, |
| MLX5_MODIFY_IPSEC_BITMASK_ESN_OVERLAP | |
| MLX5_MODIFY_IPSEC_BITMASK_ESN_MSB); |
| MLX5_SET(ipsec_obj, obj, esn_msb, attrs->esn); |
| if (attrs->flags & MLX5_ACCEL_ESP_FLAGS_ESN_STATE_OVERLAP) |
| MLX5_SET(ipsec_obj, obj, esn_overlap, 1); |
| |
| /* general object fields set */ |
| MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT); |
| |
| return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); |
| } |
| |
| void mlx5_accel_esp_modify_xfrm(struct mlx5e_ipsec_sa_entry *sa_entry, |
| const struct mlx5_accel_esp_xfrm_attrs *attrs) |
| { |
| int err; |
| |
| err = mlx5_modify_ipsec_obj(sa_entry, attrs); |
| if (err) |
| return; |
| |
| memcpy(&sa_entry->attrs, attrs, sizeof(sa_entry->attrs)); |
| } |