| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| |
| #include "fs_core.h" |
| #include "eswitch.h" |
| #include "en_accel/ipsec.h" |
| #include "esw/ipsec_fs.h" |
| #if IS_ENABLED(CONFIG_MLX5_CLS_ACT) |
| #include "en/tc_priv.h" |
| #endif |
| |
| enum { |
| MLX5_ESW_IPSEC_RX_POL_FT_LEVEL, |
| MLX5_ESW_IPSEC_RX_ESP_FT_LEVEL, |
| MLX5_ESW_IPSEC_RX_ESP_FT_CHK_LEVEL, |
| }; |
| |
| enum { |
| MLX5_ESW_IPSEC_TX_POL_FT_LEVEL, |
| MLX5_ESW_IPSEC_TX_ESP_FT_LEVEL, |
| MLX5_ESW_IPSEC_TX_ESP_FT_CNT_LEVEL, |
| }; |
| |
| void mlx5_esw_ipsec_rx_create_attr_set(struct mlx5e_ipsec *ipsec, |
| struct mlx5e_ipsec_rx_create_attr *attr) |
| { |
| attr->prio = FDB_CRYPTO_INGRESS; |
| attr->pol_level = MLX5_ESW_IPSEC_RX_POL_FT_LEVEL; |
| attr->sa_level = MLX5_ESW_IPSEC_RX_ESP_FT_LEVEL; |
| attr->status_level = MLX5_ESW_IPSEC_RX_ESP_FT_CHK_LEVEL; |
| attr->chains_ns = MLX5_FLOW_NAMESPACE_FDB; |
| } |
| |
| int mlx5_esw_ipsec_rx_status_pass_dest_get(struct mlx5e_ipsec *ipsec, |
| struct mlx5_flow_destination *dest) |
| { |
| dest->type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; |
| dest->ft = mlx5_chains_get_table(esw_chains(ipsec->mdev->priv.eswitch), 0, 1, 0); |
| |
| return 0; |
| } |
| |
| int mlx5_esw_ipsec_rx_setup_modify_header(struct mlx5e_ipsec_sa_entry *sa_entry, |
| struct mlx5_flow_act *flow_act) |
| { |
| u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; |
| struct mlx5e_ipsec *ipsec = sa_entry->ipsec; |
| struct mlx5_core_dev *mdev = ipsec->mdev; |
| struct mlx5_modify_hdr *modify_hdr; |
| u32 mapped_id; |
| int err; |
| |
| err = xa_alloc_bh(&ipsec->ipsec_obj_id_map, &mapped_id, |
| xa_mk_value(sa_entry->ipsec_obj_id), |
| XA_LIMIT(1, ESW_IPSEC_RX_MAPPED_ID_MASK), 0); |
| if (err) |
| return err; |
| |
| /* reuse tunnel bits for ipsec, |
| * tun_id is always 0 and tun_opts is mapped to ipsec_obj_id. |
| */ |
| MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); |
| MLX5_SET(set_action_in, action, field, |
| MLX5_ACTION_IN_FIELD_METADATA_REG_C_1); |
| MLX5_SET(set_action_in, action, offset, ESW_ZONE_ID_BITS); |
| MLX5_SET(set_action_in, action, length, |
| ESW_TUN_ID_BITS + ESW_TUN_OPTS_BITS); |
| MLX5_SET(set_action_in, action, data, mapped_id); |
| |
| modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_FDB, |
| 1, action); |
| if (IS_ERR(modify_hdr)) { |
| err = PTR_ERR(modify_hdr); |
| goto err_header_alloc; |
| } |
| |
| sa_entry->rx_mapped_id = mapped_id; |
| flow_act->modify_hdr = modify_hdr; |
| flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; |
| |
| return 0; |
| |
| err_header_alloc: |
| xa_erase_bh(&ipsec->ipsec_obj_id_map, mapped_id); |
| return err; |
| } |
| |
| void mlx5_esw_ipsec_rx_id_mapping_remove(struct mlx5e_ipsec_sa_entry *sa_entry) |
| { |
| struct mlx5e_ipsec *ipsec = sa_entry->ipsec; |
| |
| if (sa_entry->rx_mapped_id) |
| xa_erase_bh(&ipsec->ipsec_obj_id_map, |
| sa_entry->rx_mapped_id); |
| } |
| |
| int mlx5_esw_ipsec_rx_ipsec_obj_id_search(struct mlx5e_priv *priv, u32 id, |
| u32 *ipsec_obj_id) |
| { |
| struct mlx5e_ipsec *ipsec = priv->ipsec; |
| void *val; |
| |
| val = xa_load(&ipsec->ipsec_obj_id_map, id); |
| if (!val) |
| return -ENOENT; |
| |
| *ipsec_obj_id = xa_to_value(val); |
| |
| return 0; |
| } |
| |
| void mlx5_esw_ipsec_tx_create_attr_set(struct mlx5e_ipsec *ipsec, |
| struct mlx5e_ipsec_tx_create_attr *attr) |
| { |
| attr->prio = FDB_CRYPTO_EGRESS; |
| attr->pol_level = MLX5_ESW_IPSEC_TX_POL_FT_LEVEL; |
| attr->sa_level = MLX5_ESW_IPSEC_TX_ESP_FT_LEVEL; |
| attr->cnt_level = MLX5_ESW_IPSEC_TX_ESP_FT_CNT_LEVEL; |
| attr->chains_ns = MLX5_FLOW_NAMESPACE_FDB; |
| } |
| |
| #if IS_ENABLED(CONFIG_MLX5_CLS_ACT) |
| static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, |
| struct mlx5e_tc_flow *flow) |
| { |
| struct mlx5_esw_flow_attr *esw_attr; |
| struct mlx5_flow_attr *attr; |
| int err; |
| |
| attr = flow->attr; |
| esw_attr = attr->esw_attr; |
| if (esw_attr->out_count - esw_attr->split_count > 1) |
| return 0; |
| |
| err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, |
| esw_attr->out_count - 1); |
| |
| return err; |
| } |
| #endif |
| |
| void mlx5_esw_ipsec_restore_dest_uplink(struct mlx5_core_dev *mdev) |
| { |
| #if IS_ENABLED(CONFIG_MLX5_CLS_ACT) |
| struct mlx5_eswitch *esw = mdev->priv.eswitch; |
| struct mlx5_eswitch_rep *rep; |
| struct mlx5e_rep_priv *rpriv; |
| struct rhashtable_iter iter; |
| struct mlx5e_tc_flow *flow; |
| unsigned long i; |
| int err; |
| |
| xa_for_each(&esw->offloads.vport_reps, i, rep) { |
| rpriv = rep->rep_data[REP_ETH].priv; |
| if (!rpriv || !rpriv->netdev) |
| continue; |
| |
| rhashtable_walk_enter(&rpriv->tc_ht, &iter); |
| rhashtable_walk_start(&iter); |
| while ((flow = rhashtable_walk_next(&iter)) != NULL) { |
| if (IS_ERR(flow)) |
| continue; |
| |
| err = mlx5_esw_ipsec_modify_flow_dests(esw, flow); |
| if (err) |
| mlx5_core_warn_once(mdev, |
| "Failed to modify flow dests for IPsec"); |
| } |
| rhashtable_walk_stop(&iter); |
| rhashtable_walk_exit(&iter); |
| } |
| #endif |
| } |