/* * Copyright (c) 2017 Mellanox Technologies. 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. *
*/
if (!x->replay_esn->replay_window) {
seq_bottom = esn;
} else { if (esn >= x->replay_esn->replay_window)
seq_bottom = esn - x->replay_esn->replay_window + 1;
if (x->xso.type == XFRM_DEV_OFFLOAD_CRYPTO)
esn_msb = xfrm_replay_seqhi(x, htonl(seq_bottom));
}
if (sa_entry->esn_state.esn_msb)
sa_entry->esn_state.esn = esn; else /* According to RFC4303, section "3.3.3. Sequence Number Generation", * the first packet sent using a given SA will contain a sequence * number of 1.
*/
sa_entry->esn_state.esn = max_t(u32, esn, 1);
sa_entry->esn_state.esn_msb = esn_msb;
/* Compute hard limit initial value and number of rounds. * * The counting pattern of hardware counter goes: * value -> 2^31-1 * 2^31 | (2^31-1) -> 2^31-1 * 2^31 | (2^31-1) -> 2^31-1 * [..] * 2^31 | (2^31-1) -> 0 * * The pattern is created by using an ASO operation to atomically set * bit 31 after the down counter clears bit 31. This is effectively an * atomic addition of 2**31 to the counter. * * We wish to configure the counter, within the above pattern, so that * when it reaches 0, it has hit the hard limit. This is defined by this * system of equations: * * hard_limit == start_value + n * 2^31 * n >= 0 * start_value < 2^32, start_value >= 0 * * These equations are not single-solution, there are often two choices: * hard_limit == start_value + n * 2^31 * hard_limit == (start_value+2^31) + (n-1) * 2^31 * * The algorithm selects the solution that keeps the counter value * above 2^31 until the final iteration.
*/
/* Start by estimating n and compute start_value */
n = attrs->lft.hard_packet_limit / BIT_ULL(31);
start_value = attrs->lft.hard_packet_limit - n * BIT_ULL(31);
/* Choose the best of the two solutions: */ if (n >= 1)
n -= 1;
/* Computed values solve the system of equations: */
start_value = attrs->lft.hard_packet_limit - n * BIT_ULL(31);
/* The best solution means: when there are multiple iterations we must * start above 2^31 and count down to 2**31 to get the interrupt.
*/
attrs->lft.hard_packet_limit = lower_32_bits(start_value);
attrs->lft.numb_rounds_hard = (u64)n;
/* Compute soft limit initial value and number of rounds. * * The soft_limit is achieved by adjusting the counter's * interrupt_value. This is embedded in the counting pattern created by * hard packet calculations above. * * We wish to compute the interrupt_value for the soft_limit. This is * defined by this system of equations: * * soft_limit == start_value - soft_value + n * 2^31 * n >= 0 * soft_value < 2^32, soft_value >= 0 * for n == 0 start_value > soft_value * * As with compute_hard_n_value() the equations are not single-solution. * The algorithm selects the solution that has: * 2^30 <= soft_limit < 2^31 + 2^30 * for the interior iterations, which guarantees a large guard band * around the counter hard limit and next interrupt.
*/
/* Start by estimating n and compute soft_value */
n = (x->lft.soft_packet_limit - attrs->lft.hard_packet_limit) / BIT_ULL(31);
start_value = attrs->lft.hard_packet_limit + n * BIT_ULL(31) -
x->lft.soft_packet_limit;
/* Compare against constraints and adjust n */ if (n < 0)
n = 0; elseif (start_value >= BIT_ULL(32))
n -= 1; elseif (start_value < 0)
n += 1;
/* Choose the best of the two solutions: */
start_value = attrs->lft.hard_packet_limit + n * BIT_ULL(31) - start_value; if (n != attrs->lft.numb_rounds_hard && start_value < BIT_ULL(30))
n += 1;
/* Note that the upper limit of soft_value happens naturally because we * always select the lowest soft_value.
*/
/* Computed values solve the system of equations: */
start_value = attrs->lft.hard_packet_limit + n * BIT_ULL(31) - start_value;
/* The best solution means: when there are multiple iterations we must * not fall below 2^30 as that would get too close to the false * hard_limit and when we reach an interior iteration for soft_limit it * has to be far away from 2**32-1 which is the counter reset point * after the +2^31 to accommodate latency.
*/
attrs->lft.soft_packet_limit = lower_32_bits(start_value);
attrs->lft.numb_rounds_soft = (u64)n;
}
/* Destination can refer to a routed network, so perform FIB lookup * to resolve nexthop and get its MAC. Neighbour resolution is used as * fallback.
*/ switch (addrs->family) { case AF_INET:
rt = ip_route_output_key(dev_net(netdev), &fl4); if (IS_ERR(rt)) goto neigh;
if (rt->rt_type != RTN_UNICAST) {
ip_rt_put(rt); goto neigh;
}
rt_dst_entry = &rt->dst; break; case AF_INET6:
rt_dst_entry = ipv6_stub->ipv6_dst_lookup_flow(
dev_net(netdev), NULL, &fl6, NULL); if (IS_ERR(rt_dst_entry)) goto neigh; break; default: return;
}
n = dst_neigh_lookup(rt_dst_entry, pkey); if (!n) {
dst_release(rt_dst_entry); goto neigh;
}
neigh_ha_snapshot(addr, n, netdev);
ether_addr_copy(dst, addr);
dst_release(rt_dst_entry);
neigh_release(n); return;
neigh:
n = neigh_lookup(&arp_tbl, pkey, netdev); if (!n) {
n = neigh_create(&arp_tbl, pkey, netdev); if (IS_ERR(n)) return;
neigh_event_send(n, NULL);
attrs->drop = true;
} else {
neigh_ha_snapshot(addr, n, netdev);
ether_addr_copy(dst, addr);
}
neigh_release(n);
}
staticvoid mlx5e_ipsec_state_mask(struct mlx5e_ipsec_addr *addrs)
{ /* * State doesn't have subnet prefixes in outer headers. * The match is performed for exaxt source/destination addresses.
*/
memset(addrs->smask.m6, 0xFF, sizeof(__be32) * 4);
memset(addrs->dmask.m6, 0xFF, sizeof(__be32) * 4);
}
staticint mlx5e_xfrm_validate_state(struct mlx5_core_dev *mdev, struct xfrm_state *x, struct netlink_ext_ack *extack)
{ if (x->props.aalgo != SADB_AALG_NONE) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload authenticated xfrm states"); return -EINVAL;
} if (x->props.ealgo != SADB_X_EALG_AES_GCM_ICV16) {
NL_SET_ERR_MSG_MOD(extack, "Only AES-GCM-ICV16 xfrm state may be offloaded"); return -EINVAL;
} if (x->props.calgo != SADB_X_CALG_NONE) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload compressed xfrm states"); return -EINVAL;
} if (x->props.flags & XFRM_STATE_ESN &&
!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_ESN)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload ESN xfrm states"); return -EINVAL;
} if (x->props.family != AF_INET &&
x->props.family != AF_INET6) {
NL_SET_ERR_MSG_MOD(extack, "Only IPv4/6 xfrm states may be offloaded"); return -EINVAL;
} if (x->id.proto != IPPROTO_ESP) {
NL_SET_ERR_MSG_MOD(extack, "Only ESP xfrm state may be offloaded"); return -EINVAL;
} if (x->encap) { if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_ESPINUDP)) {
NL_SET_ERR_MSG_MOD(extack, "Encapsulation is not supported"); return -EINVAL;
}
if (x->encap->encap_type != UDP_ENCAP_ESPINUDP) {
NL_SET_ERR_MSG_MOD(extack, "Encapsulation other than UDP is not supported"); return -EINVAL;
}
if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET) {
NL_SET_ERR_MSG_MOD(extack, "Encapsulation is supported in packet offload mode only"); return -EINVAL;
}
if (x->props.mode != XFRM_MODE_TRANSPORT) {
NL_SET_ERR_MSG_MOD(extack, "Encapsulation is supported in transport mode only"); return -EINVAL;
}
} if (!x->aead) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states without aead"); return -EINVAL;
} if (x->aead->alg_icv_len != 128) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states with AEAD ICV length other than 128bit"); return -EINVAL;
} if ((x->aead->alg_key_len != 128 + 32) &&
(x->aead->alg_key_len != 256 + 32)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states with AEAD key length other than 128/256 bit"); return -EINVAL;
} if (x->tfcpad) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states with tfc padding"); return -EINVAL;
} if (!x->geniv) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states without geniv"); return -EINVAL;
} if (strcmp(x->geniv, "seqiv")) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload xfrm states with geniv other than seqiv"); return -EINVAL;
}
if (x->sel.proto != IPPROTO_IP && x->sel.proto != IPPROTO_UDP &&
x->sel.proto != IPPROTO_TCP) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support upper protocol other than TCP/UDP"); return -EINVAL;
}
if (x->props.mode != XFRM_MODE_TRANSPORT && x->props.mode != XFRM_MODE_TUNNEL) {
NL_SET_ERR_MSG_MOD(extack, "Only transport and tunnel xfrm states may be offloaded"); return -EINVAL;
}
switch (x->xso.type) { case XFRM_DEV_OFFLOAD_CRYPTO: if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_CRYPTO)) {
NL_SET_ERR_MSG_MOD(extack, "Crypto offload is not supported"); return -EINVAL;
}
break; case XFRM_DEV_OFFLOAD_PACKET: if (!(mlx5_ipsec_device_caps(mdev) &
MLX5_IPSEC_CAP_PACKET_OFFLOAD)) {
NL_SET_ERR_MSG_MOD(extack, "Packet offload is not supported"); return -EINVAL;
}
if (x->props.mode == XFRM_MODE_TUNNEL &&
!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_TUNNEL)) {
NL_SET_ERR_MSG_MOD(extack, "Packet offload is not supported for tunnel mode"); return -EINVAL;
}
switch (x->xso.type) { case XFRM_DEV_OFFLOAD_CRYPTO: if (!(x->props.flags & XFRM_STATE_ESN)) return 0; break; case XFRM_DEV_OFFLOAD_PACKET: if (x->props.mode != XFRM_MODE_TUNNEL) return 0; break; default: break;
}
work = kzalloc(sizeof(*work), GFP_KERNEL); if (!work) return -ENOMEM;
switch (x->xso.type) { case XFRM_DEV_OFFLOAD_CRYPTO:
data = kzalloc(sizeof(*sa_entry), GFP_KERNEL); if (!data) goto free_work;
INIT_WORK(&work->work, mlx5e_ipsec_modify_state); break; case XFRM_DEV_OFFLOAD_PACKET:
data = kzalloc(sizeof(struct mlx5e_ipsec_netevent_data),
GFP_KERNEL); if (!data) goto free_work;
sa_entry->x = x;
sa_entry->dev = dev;
sa_entry->ipsec = ipsec; /* Check if this SA is originated from acquire flow temporary SA */ if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) goto out;
err = mlx5e_xfrm_validate_state(priv->mdev, x, extack); if (err) goto err_xfrm;
if (!mlx5_eswitch_block_ipsec(priv->mdev)) {
err = -EBUSY; goto err_xfrm;
}
err = mlx5_eswitch_block_mode(priv->mdev); if (err) goto unblock_ipsec;
if (x->props.mode == XFRM_MODE_TUNNEL &&
x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
allow_tunnel_mode = mlx5e_ipsec_fs_tunnel_allowed(sa_entry); if (!allow_tunnel_mode) {
NL_SET_ERR_MSG_MOD(extack, "Packet offload tunnel mode is disabled due to encap settings");
err = -EINVAL; goto unblock_mode;
}
}
/* check esn */ if (x->props.flags & XFRM_STATE_ESN)
mlx5e_ipsec_update_esn_state(sa_entry); else /* According to RFC4303, section "3.3.3. Sequence Number Generation", * the first packet sent using a given SA will contain a sequence * number of 1.
*/
sa_entry->esn_state.esn = 1;
err = mlx5e_accel_ipsec_fs_add_rule(sa_entry); if (err) goto err_hw_ctx;
/* We use *_bh() variant because xfrm_timer_handler(), which runs * in softirq context, can reach our state delete logic and we need * xa_erase_bh() there.
*/
err = xa_insert_bh(&ipsec->sadb, sa_entry->ipsec_obj_id, sa_entry,
GFP_KERNEL); if (err) goto err_add_rule;
mlx5e_ipsec_set_esn_ops(sa_entry);
if (sa_entry->dwork)
queue_delayed_work(ipsec->wq, &sa_entry->dwork->dwork,
MLX5_IPSEC_RESCHED);
if (allow_tunnel_mode) {
xa_lock_bh(&ipsec->sadb);
__xa_set_mark(&ipsec->sadb, sa_entry->ipsec_obj_id,
MLX5E_IPSEC_TUNNEL_SA);
xa_unlock_bh(&ipsec->sadb);
}
out:
x->xso.offload_handle = (unsignedlong)sa_entry; if (allow_tunnel_mode)
mlx5_eswitch_unblock_encap(priv->mdev);
mlx5_eswitch_unblock_mode(priv->mdev);
return 0;
err_add_rule:
mlx5e_accel_ipsec_fs_del_rule(sa_entry);
err_hw_ctx:
mlx5_ipsec_free_sa_ctx(sa_entry);
release_dwork:
kfree(sa_entry->dwork);
release_work: if (sa_entry->work)
kfree(sa_entry->work->data);
kfree(sa_entry->work);
unblock_encap: if (allow_tunnel_mode)
mlx5_eswitch_unblock_encap(priv->mdev);
unblock_mode:
mlx5_eswitch_unblock_mode(priv->mdev);
unblock_ipsec:
mlx5_eswitch_unblock_ipsec(priv->mdev);
err_xfrm:
kfree(sa_entry);
NL_SET_ERR_MSG_WEAK_MOD(extack, "Device failed to offload this state"); return err;
}
mlx5_fc_query_cached(ipsec_rule->fc, &bytes, &packets, &lastuse);
success_packets = packets - auth_packets - trailer_packets - replay_packets;
x->curlft.packets += success_packets; /* NIC counts all bytes passed through flow steering and doesn't have * an ability to count payload data size which is needed for SA. * * To overcome HW limitestion, let's approximate the payload size * by removing always available headers.
*/
headers = sizeof(struct ethhdr); if (sa_entry->attrs.addrs.family == AF_INET)
headers += sizeof(struct iphdr); else
headers += sizeof(struct ipv6hdr);
/* Please pay attention that we support only one template */ if (x->xfrm_nr > 1) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload more than one template"); return -EINVAL;
}
if (!x->xfrm_vec[0].reqid && sel->proto == IPPROTO_IP &&
addr6_all_zero(sel->saddr.a6) && addr6_all_zero(sel->daddr.a6)) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported policy with reqid 0 without at least one of upper protocol or ip addr(s) different than 0"); return -EINVAL;
}
if (x->selector.proto != IPPROTO_IP &&
x->selector.proto != IPPROTO_UDP &&
x->selector.proto != IPPROTO_TCP) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support upper protocol other than TCP/UDP"); return -EINVAL;
}
if (x->priority) { if (!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PRIO)) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support policy priority"); return -EINVAL;
}
if (x->priority == U32_MAX) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support requested policy priority"); return -EINVAL;
}
}
if (x->xdo.type == XFRM_DEV_OFFLOAD_PACKET &&
!(mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD)) {
NL_SET_ERR_MSG_MOD(extack, "Packet offload is not supported"); return -EINVAL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.