mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
601 lines
15 KiB
601 lines
15 KiB
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
|
/* Copyright (C) 2019 Netronome Systems, Inc. */ |
|
|
|
#include <linux/bitfield.h> |
|
#include <linux/ipv6.h> |
|
#include <linux/skbuff.h> |
|
#include <linux/string.h> |
|
#include <net/inet6_hashtables.h> |
|
#include <net/tls.h> |
|
|
|
#include "../ccm.h" |
|
#include "../nfp_net.h" |
|
#include "crypto.h" |
|
#include "fw.h" |
|
|
|
#define NFP_NET_TLS_CCM_MBOX_OPS_MASK \ |
|
(BIT(NFP_CCM_TYPE_CRYPTO_RESET) | \ |
|
BIT(NFP_CCM_TYPE_CRYPTO_ADD) | \ |
|
BIT(NFP_CCM_TYPE_CRYPTO_DEL) | \ |
|
BIT(NFP_CCM_TYPE_CRYPTO_UPDATE)) |
|
|
|
#define NFP_NET_TLS_OPCODE_MASK_RX \ |
|
BIT(NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_DEC) |
|
|
|
#define NFP_NET_TLS_OPCODE_MASK_TX \ |
|
BIT(NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_ENC) |
|
|
|
#define NFP_NET_TLS_OPCODE_MASK \ |
|
(NFP_NET_TLS_OPCODE_MASK_RX | NFP_NET_TLS_OPCODE_MASK_TX) |
|
|
|
static void nfp_net_crypto_set_op(struct nfp_net *nn, u8 opcode, bool on) |
|
{ |
|
u32 off, val; |
|
|
|
off = nn->tlv_caps.crypto_enable_off + round_down(opcode / 8, 4); |
|
|
|
val = nn_readl(nn, off); |
|
if (on) |
|
val |= BIT(opcode & 31); |
|
else |
|
val &= ~BIT(opcode & 31); |
|
nn_writel(nn, off, val); |
|
} |
|
|
|
static bool |
|
__nfp_net_tls_conn_cnt_changed(struct nfp_net *nn, int add, |
|
enum tls_offload_ctx_dir direction) |
|
{ |
|
u8 opcode; |
|
int cnt; |
|
|
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) { |
|
opcode = NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_ENC; |
|
nn->ktls_tx_conn_cnt += add; |
|
cnt = nn->ktls_tx_conn_cnt; |
|
nn->dp.ktls_tx = !!nn->ktls_tx_conn_cnt; |
|
} else { |
|
opcode = NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_DEC; |
|
nn->ktls_rx_conn_cnt += add; |
|
cnt = nn->ktls_rx_conn_cnt; |
|
} |
|
|
|
/* Care only about 0 -> 1 and 1 -> 0 transitions */ |
|
if (cnt > 1) |
|
return false; |
|
|
|
nfp_net_crypto_set_op(nn, opcode, cnt); |
|
return true; |
|
} |
|
|
|
static int |
|
nfp_net_tls_conn_cnt_changed(struct nfp_net *nn, int add, |
|
enum tls_offload_ctx_dir direction) |
|
{ |
|
int ret = 0; |
|
|
|
/* Use the BAR lock to protect the connection counts */ |
|
nn_ctrl_bar_lock(nn); |
|
if (__nfp_net_tls_conn_cnt_changed(nn, add, direction)) { |
|
ret = __nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_CRYPTO); |
|
/* Undo the cnt adjustment if failed */ |
|
if (ret) |
|
__nfp_net_tls_conn_cnt_changed(nn, -add, direction); |
|
} |
|
nn_ctrl_bar_unlock(nn); |
|
|
|
return ret; |
|
} |
|
|
|
static int |
|
nfp_net_tls_conn_add(struct nfp_net *nn, enum tls_offload_ctx_dir direction) |
|
{ |
|
return nfp_net_tls_conn_cnt_changed(nn, 1, direction); |
|
} |
|
|
|
static int |
|
nfp_net_tls_conn_remove(struct nfp_net *nn, enum tls_offload_ctx_dir direction) |
|
{ |
|
return nfp_net_tls_conn_cnt_changed(nn, -1, direction); |
|
} |
|
|
|
static struct sk_buff * |
|
nfp_net_tls_alloc_simple(struct nfp_net *nn, size_t req_sz, gfp_t flags) |
|
{ |
|
return nfp_ccm_mbox_msg_alloc(nn, req_sz, |
|
sizeof(struct nfp_crypto_reply_simple), |
|
flags); |
|
} |
|
|
|
static int |
|
nfp_net_tls_communicate_simple(struct nfp_net *nn, struct sk_buff *skb, |
|
const char *name, enum nfp_ccm_type type) |
|
{ |
|
struct nfp_crypto_reply_simple *reply; |
|
int err; |
|
|
|
err = __nfp_ccm_mbox_communicate(nn, skb, type, |
|
sizeof(*reply), sizeof(*reply), |
|
type == NFP_CCM_TYPE_CRYPTO_DEL); |
|
if (err) { |
|
nn_dp_warn(&nn->dp, "failed to %s TLS: %d\n", name, err); |
|
return err; |
|
} |
|
|
|
reply = (void *)skb->data; |
|
err = -be32_to_cpu(reply->error); |
|
if (err) |
|
nn_dp_warn(&nn->dp, "failed to %s TLS, fw replied: %d\n", |
|
name, err); |
|
dev_consume_skb_any(skb); |
|
|
|
return err; |
|
} |
|
|
|
static void nfp_net_tls_del_fw(struct nfp_net *nn, __be32 *fw_handle) |
|
{ |
|
struct nfp_crypto_req_del *req; |
|
struct sk_buff *skb; |
|
|
|
skb = nfp_net_tls_alloc_simple(nn, sizeof(*req), GFP_KERNEL); |
|
if (!skb) |
|
return; |
|
|
|
req = (void *)skb->data; |
|
req->ep_id = 0; |
|
memcpy(req->handle, fw_handle, sizeof(req->handle)); |
|
|
|
nfp_net_tls_communicate_simple(nn, skb, "delete", |
|
NFP_CCM_TYPE_CRYPTO_DEL); |
|
} |
|
|
|
static void |
|
nfp_net_tls_set_ipver_vlan(struct nfp_crypto_req_add_front *front, u8 ipver) |
|
{ |
|
front->ipver_vlan = cpu_to_be16(FIELD_PREP(NFP_NET_TLS_IPVER, ipver) | |
|
FIELD_PREP(NFP_NET_TLS_VLAN, |
|
NFP_NET_TLS_VLAN_UNUSED)); |
|
} |
|
|
|
static void |
|
nfp_net_tls_assign_conn_id(struct nfp_net *nn, |
|
struct nfp_crypto_req_add_front *front) |
|
{ |
|
u32 len; |
|
u64 id; |
|
|
|
id = atomic64_inc_return(&nn->ktls_conn_id_gen); |
|
len = front->key_len - NFP_NET_TLS_NON_ADDR_KEY_LEN; |
|
|
|
memcpy(front->l3_addrs, &id, sizeof(id)); |
|
memset(front->l3_addrs + sizeof(id), 0, len - sizeof(id)); |
|
} |
|
|
|
static struct nfp_crypto_req_add_back * |
|
nfp_net_tls_set_ipv4(struct nfp_net *nn, struct nfp_crypto_req_add_v4 *req, |
|
struct sock *sk, int direction) |
|
{ |
|
struct inet_sock *inet = inet_sk(sk); |
|
|
|
req->front.key_len += sizeof(__be32) * 2; |
|
|
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) { |
|
nfp_net_tls_assign_conn_id(nn, &req->front); |
|
} else { |
|
req->src_ip = inet->inet_daddr; |
|
req->dst_ip = inet->inet_saddr; |
|
} |
|
|
|
return &req->back; |
|
} |
|
|
|
static struct nfp_crypto_req_add_back * |
|
nfp_net_tls_set_ipv6(struct nfp_net *nn, struct nfp_crypto_req_add_v6 *req, |
|
struct sock *sk, int direction) |
|
{ |
|
#if IS_ENABLED(CONFIG_IPV6) |
|
struct ipv6_pinfo *np = inet6_sk(sk); |
|
|
|
req->front.key_len += sizeof(struct in6_addr) * 2; |
|
|
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) { |
|
nfp_net_tls_assign_conn_id(nn, &req->front); |
|
} else { |
|
memcpy(req->src_ip, &sk->sk_v6_daddr, sizeof(req->src_ip)); |
|
memcpy(req->dst_ip, &np->saddr, sizeof(req->dst_ip)); |
|
} |
|
|
|
#endif |
|
return &req->back; |
|
} |
|
|
|
static void |
|
nfp_net_tls_set_l4(struct nfp_crypto_req_add_front *front, |
|
struct nfp_crypto_req_add_back *back, struct sock *sk, |
|
int direction) |
|
{ |
|
struct inet_sock *inet = inet_sk(sk); |
|
|
|
front->l4_proto = IPPROTO_TCP; |
|
|
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) { |
|
back->src_port = 0; |
|
back->dst_port = 0; |
|
} else { |
|
back->src_port = inet->inet_dport; |
|
back->dst_port = inet->inet_sport; |
|
} |
|
} |
|
|
|
static u8 nfp_tls_1_2_dir_to_opcode(enum tls_offload_ctx_dir direction) |
|
{ |
|
switch (direction) { |
|
case TLS_OFFLOAD_CTX_DIR_TX: |
|
return NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_ENC; |
|
case TLS_OFFLOAD_CTX_DIR_RX: |
|
return NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_DEC; |
|
default: |
|
WARN_ON_ONCE(1); |
|
return 0; |
|
} |
|
} |
|
|
|
static bool |
|
nfp_net_cipher_supported(struct nfp_net *nn, u16 cipher_type, |
|
enum tls_offload_ctx_dir direction) |
|
{ |
|
u8 bit; |
|
|
|
switch (cipher_type) { |
|
case TLS_CIPHER_AES_GCM_128: |
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) |
|
bit = NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_ENC; |
|
else |
|
bit = NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_DEC; |
|
break; |
|
default: |
|
return false; |
|
} |
|
|
|
return nn->tlv_caps.crypto_ops & BIT(bit); |
|
} |
|
|
|
static int |
|
nfp_net_tls_add(struct net_device *netdev, struct sock *sk, |
|
enum tls_offload_ctx_dir direction, |
|
struct tls_crypto_info *crypto_info, |
|
u32 start_offload_tcp_sn) |
|
{ |
|
struct tls12_crypto_info_aes_gcm_128 *tls_ci; |
|
struct nfp_net *nn = netdev_priv(netdev); |
|
struct nfp_crypto_req_add_front *front; |
|
struct nfp_net_tls_offload_ctx *ntls; |
|
struct nfp_crypto_req_add_back *back; |
|
struct nfp_crypto_reply_add *reply; |
|
struct sk_buff *skb; |
|
size_t req_sz; |
|
void *req; |
|
bool ipv6; |
|
int err; |
|
|
|
BUILD_BUG_ON(sizeof(struct nfp_net_tls_offload_ctx) > |
|
TLS_DRIVER_STATE_SIZE_TX); |
|
BUILD_BUG_ON(offsetof(struct nfp_net_tls_offload_ctx, rx_end) > |
|
TLS_DRIVER_STATE_SIZE_RX); |
|
|
|
if (!nfp_net_cipher_supported(nn, crypto_info->cipher_type, direction)) |
|
return -EOPNOTSUPP; |
|
|
|
switch (sk->sk_family) { |
|
#if IS_ENABLED(CONFIG_IPV6) |
|
case AF_INET6: |
|
if (sk->sk_ipv6only || |
|
ipv6_addr_type(&sk->sk_v6_daddr) != IPV6_ADDR_MAPPED) { |
|
req_sz = sizeof(struct nfp_crypto_req_add_v6); |
|
ipv6 = true; |
|
break; |
|
} |
|
fallthrough; |
|
#endif |
|
case AF_INET: |
|
req_sz = sizeof(struct nfp_crypto_req_add_v4); |
|
ipv6 = false; |
|
break; |
|
default: |
|
return -EOPNOTSUPP; |
|
} |
|
|
|
err = nfp_net_tls_conn_add(nn, direction); |
|
if (err) |
|
return err; |
|
|
|
skb = nfp_ccm_mbox_msg_alloc(nn, req_sz, sizeof(*reply), GFP_KERNEL); |
|
if (!skb) { |
|
err = -ENOMEM; |
|
goto err_conn_remove; |
|
} |
|
|
|
front = (void *)skb->data; |
|
front->ep_id = 0; |
|
front->key_len = NFP_NET_TLS_NON_ADDR_KEY_LEN; |
|
front->opcode = nfp_tls_1_2_dir_to_opcode(direction); |
|
memset(front->resv, 0, sizeof(front->resv)); |
|
|
|
nfp_net_tls_set_ipver_vlan(front, ipv6 ? 6 : 4); |
|
|
|
req = (void *)skb->data; |
|
if (ipv6) |
|
back = nfp_net_tls_set_ipv6(nn, req, sk, direction); |
|
else |
|
back = nfp_net_tls_set_ipv4(nn, req, sk, direction); |
|
|
|
nfp_net_tls_set_l4(front, back, sk, direction); |
|
|
|
back->counter = 0; |
|
back->tcp_seq = cpu_to_be32(start_offload_tcp_sn); |
|
|
|
tls_ci = (struct tls12_crypto_info_aes_gcm_128 *)crypto_info; |
|
memcpy(back->key, tls_ci->key, TLS_CIPHER_AES_GCM_128_KEY_SIZE); |
|
memset(&back->key[TLS_CIPHER_AES_GCM_128_KEY_SIZE / 4], 0, |
|
sizeof(back->key) - TLS_CIPHER_AES_GCM_128_KEY_SIZE); |
|
memcpy(back->iv, tls_ci->iv, TLS_CIPHER_AES_GCM_128_IV_SIZE); |
|
memcpy(&back->salt, tls_ci->salt, TLS_CIPHER_AES_GCM_128_SALT_SIZE); |
|
memcpy(back->rec_no, tls_ci->rec_seq, sizeof(tls_ci->rec_seq)); |
|
|
|
/* Get an extra ref on the skb so we can wipe the key after */ |
|
skb_get(skb); |
|
|
|
err = nfp_ccm_mbox_communicate(nn, skb, NFP_CCM_TYPE_CRYPTO_ADD, |
|
sizeof(*reply), sizeof(*reply)); |
|
reply = (void *)skb->data; |
|
|
|
/* We depend on CCM MBOX code not reallocating skb we sent |
|
* so we can clear the key material out of the memory. |
|
*/ |
|
if (!WARN_ON_ONCE((u8 *)back < skb->head || |
|
(u8 *)back > skb_end_pointer(skb)) && |
|
!WARN_ON_ONCE((u8 *)&reply[1] > (u8 *)back)) |
|
memzero_explicit(back, sizeof(*back)); |
|
dev_consume_skb_any(skb); /* the extra ref from skb_get() above */ |
|
|
|
if (err) { |
|
nn_dp_warn(&nn->dp, "failed to add TLS: %d (%d)\n", |
|
err, direction == TLS_OFFLOAD_CTX_DIR_TX); |
|
/* communicate frees skb on error */ |
|
goto err_conn_remove; |
|
} |
|
|
|
err = -be32_to_cpu(reply->error); |
|
if (err) { |
|
if (err == -ENOSPC) { |
|
if (!atomic_fetch_inc(&nn->ktls_no_space)) |
|
nn_info(nn, "HW TLS table full\n"); |
|
} else { |
|
nn_dp_warn(&nn->dp, |
|
"failed to add TLS, FW replied: %d\n", err); |
|
} |
|
goto err_free_skb; |
|
} |
|
|
|
if (!reply->handle[0] && !reply->handle[1]) { |
|
nn_dp_warn(&nn->dp, "FW returned NULL handle\n"); |
|
err = -EINVAL; |
|
goto err_fw_remove; |
|
} |
|
|
|
ntls = tls_driver_ctx(sk, direction); |
|
memcpy(ntls->fw_handle, reply->handle, sizeof(ntls->fw_handle)); |
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) |
|
ntls->next_seq = start_offload_tcp_sn; |
|
dev_consume_skb_any(skb); |
|
|
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) |
|
return 0; |
|
|
|
if (!nn->tlv_caps.tls_resync_ss) |
|
tls_offload_rx_resync_set_type(sk, TLS_OFFLOAD_SYNC_TYPE_CORE_NEXT_HINT); |
|
|
|
return 0; |
|
|
|
err_fw_remove: |
|
nfp_net_tls_del_fw(nn, reply->handle); |
|
err_free_skb: |
|
dev_consume_skb_any(skb); |
|
err_conn_remove: |
|
nfp_net_tls_conn_remove(nn, direction); |
|
return err; |
|
} |
|
|
|
static void |
|
nfp_net_tls_del(struct net_device *netdev, struct tls_context *tls_ctx, |
|
enum tls_offload_ctx_dir direction) |
|
{ |
|
struct nfp_net *nn = netdev_priv(netdev); |
|
struct nfp_net_tls_offload_ctx *ntls; |
|
|
|
nfp_net_tls_conn_remove(nn, direction); |
|
|
|
ntls = __tls_driver_ctx(tls_ctx, direction); |
|
nfp_net_tls_del_fw(nn, ntls->fw_handle); |
|
} |
|
|
|
static int |
|
nfp_net_tls_resync(struct net_device *netdev, struct sock *sk, u32 seq, |
|
u8 *rcd_sn, enum tls_offload_ctx_dir direction) |
|
{ |
|
struct nfp_net *nn = netdev_priv(netdev); |
|
struct nfp_net_tls_offload_ctx *ntls; |
|
struct nfp_crypto_req_update *req; |
|
enum nfp_ccm_type type; |
|
struct sk_buff *skb; |
|
gfp_t flags; |
|
int err; |
|
|
|
flags = direction == TLS_OFFLOAD_CTX_DIR_TX ? GFP_KERNEL : GFP_ATOMIC; |
|
skb = nfp_net_tls_alloc_simple(nn, sizeof(*req), flags); |
|
if (!skb) |
|
return -ENOMEM; |
|
|
|
ntls = tls_driver_ctx(sk, direction); |
|
req = (void *)skb->data; |
|
req->ep_id = 0; |
|
req->opcode = nfp_tls_1_2_dir_to_opcode(direction); |
|
memset(req->resv, 0, sizeof(req->resv)); |
|
memcpy(req->handle, ntls->fw_handle, sizeof(ntls->fw_handle)); |
|
req->tcp_seq = cpu_to_be32(seq); |
|
memcpy(req->rec_no, rcd_sn, sizeof(req->rec_no)); |
|
|
|
type = NFP_CCM_TYPE_CRYPTO_UPDATE; |
|
if (direction == TLS_OFFLOAD_CTX_DIR_TX) { |
|
err = nfp_net_tls_communicate_simple(nn, skb, "sync", type); |
|
if (err) |
|
return err; |
|
ntls->next_seq = seq; |
|
} else { |
|
if (nn->tlv_caps.tls_resync_ss) |
|
type = NFP_CCM_TYPE_CRYPTO_RESYNC; |
|
nfp_ccm_mbox_post(nn, skb, type, |
|
sizeof(struct nfp_crypto_reply_simple)); |
|
atomic_inc(&nn->ktls_rx_resync_sent); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static const struct tlsdev_ops nfp_net_tls_ops = { |
|
.tls_dev_add = nfp_net_tls_add, |
|
.tls_dev_del = nfp_net_tls_del, |
|
.tls_dev_resync = nfp_net_tls_resync, |
|
}; |
|
|
|
int nfp_net_tls_rx_resync_req(struct net_device *netdev, |
|
struct nfp_net_tls_resync_req *req, |
|
void *pkt, unsigned int pkt_len) |
|
{ |
|
struct nfp_net *nn = netdev_priv(netdev); |
|
struct nfp_net_tls_offload_ctx *ntls; |
|
struct ipv6hdr *ipv6h; |
|
struct tcphdr *th; |
|
struct iphdr *iph; |
|
struct sock *sk; |
|
__be32 tcp_seq; |
|
int err; |
|
|
|
iph = pkt + req->l3_offset; |
|
ipv6h = pkt + req->l3_offset; |
|
th = pkt + req->l4_offset; |
|
|
|
if ((u8 *)&th[1] > (u8 *)pkt + pkt_len) { |
|
netdev_warn_once(netdev, "invalid TLS RX resync request (l3_off: %hhu l4_off: %hhu pkt_len: %u)\n", |
|
req->l3_offset, req->l4_offset, pkt_len); |
|
err = -EINVAL; |
|
goto err_cnt_ign; |
|
} |
|
|
|
switch (ipv6h->version) { |
|
case 4: |
|
sk = inet_lookup_established(dev_net(netdev), &tcp_hashinfo, |
|
iph->saddr, th->source, iph->daddr, |
|
th->dest, netdev->ifindex); |
|
break; |
|
#if IS_ENABLED(CONFIG_IPV6) |
|
case 6: |
|
sk = __inet6_lookup_established(dev_net(netdev), &tcp_hashinfo, |
|
&ipv6h->saddr, th->source, |
|
&ipv6h->daddr, ntohs(th->dest), |
|
netdev->ifindex, 0); |
|
break; |
|
#endif |
|
default: |
|
netdev_warn_once(netdev, "invalid TLS RX resync request (l3_off: %hhu l4_off: %hhu ipver: %u)\n", |
|
req->l3_offset, req->l4_offset, iph->version); |
|
err = -EINVAL; |
|
goto err_cnt_ign; |
|
} |
|
|
|
err = 0; |
|
if (!sk) |
|
goto err_cnt_ign; |
|
if (!tls_is_sk_rx_device_offloaded(sk) || |
|
sk->sk_shutdown & RCV_SHUTDOWN) |
|
goto err_put_sock; |
|
|
|
ntls = tls_driver_ctx(sk, TLS_OFFLOAD_CTX_DIR_RX); |
|
/* some FW versions can't report the handle and report 0s */ |
|
if (memchr_inv(&req->fw_handle, 0, sizeof(req->fw_handle)) && |
|
memcmp(&req->fw_handle, &ntls->fw_handle, sizeof(ntls->fw_handle))) |
|
goto err_put_sock; |
|
|
|
/* copy to ensure alignment */ |
|
memcpy(&tcp_seq, &req->tcp_seq, sizeof(tcp_seq)); |
|
tls_offload_rx_resync_request(sk, tcp_seq); |
|
atomic_inc(&nn->ktls_rx_resync_req); |
|
|
|
sock_gen_put(sk); |
|
return 0; |
|
|
|
err_put_sock: |
|
sock_gen_put(sk); |
|
err_cnt_ign: |
|
atomic_inc(&nn->ktls_rx_resync_ign); |
|
return err; |
|
} |
|
|
|
static int nfp_net_tls_reset(struct nfp_net *nn) |
|
{ |
|
struct nfp_crypto_req_reset *req; |
|
struct sk_buff *skb; |
|
|
|
skb = nfp_net_tls_alloc_simple(nn, sizeof(*req), GFP_KERNEL); |
|
if (!skb) |
|
return -ENOMEM; |
|
|
|
req = (void *)skb->data; |
|
req->ep_id = 0; |
|
|
|
return nfp_net_tls_communicate_simple(nn, skb, "reset", |
|
NFP_CCM_TYPE_CRYPTO_RESET); |
|
} |
|
|
|
int nfp_net_tls_init(struct nfp_net *nn) |
|
{ |
|
struct net_device *netdev = nn->dp.netdev; |
|
int err; |
|
|
|
if (!(nn->tlv_caps.crypto_ops & NFP_NET_TLS_OPCODE_MASK)) |
|
return 0; |
|
|
|
if ((nn->tlv_caps.mbox_cmsg_types & NFP_NET_TLS_CCM_MBOX_OPS_MASK) != |
|
NFP_NET_TLS_CCM_MBOX_OPS_MASK) |
|
return 0; |
|
|
|
if (!nfp_ccm_mbox_fits(nn, sizeof(struct nfp_crypto_req_add_v6))) { |
|
nn_warn(nn, "disabling TLS offload - mbox too small: %d\n", |
|
nn->tlv_caps.mbox_len); |
|
return 0; |
|
} |
|
|
|
err = nfp_net_tls_reset(nn); |
|
if (err) |
|
return err; |
|
|
|
nn_ctrl_bar_lock(nn); |
|
nn_writel(nn, nn->tlv_caps.crypto_enable_off, 0); |
|
err = __nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_CRYPTO); |
|
nn_ctrl_bar_unlock(nn); |
|
if (err) |
|
return err; |
|
|
|
if (nn->tlv_caps.crypto_ops & NFP_NET_TLS_OPCODE_MASK_RX) { |
|
netdev->hw_features |= NETIF_F_HW_TLS_RX; |
|
netdev->features |= NETIF_F_HW_TLS_RX; |
|
} |
|
if (nn->tlv_caps.crypto_ops & NFP_NET_TLS_OPCODE_MASK_TX) { |
|
netdev->hw_features |= NETIF_F_HW_TLS_TX; |
|
netdev->features |= NETIF_F_HW_TLS_TX; |
|
} |
|
|
|
netdev->tlsdev_ops = &nfp_net_tls_ops; |
|
|
|
return 0; |
|
}
|
|
|