tls: rx: add sockopt for enabling optimistic decrypt with TLS 1.3
Since optimisitic decrypt may add extra load in case of retries require socket owner to explicitly opt-in. Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ce61327ce9
commit
88527790c0
8 changed files with 122 additions and 7 deletions
|
@ -239,6 +239,19 @@ for the original TCP transmission and TCP retransmissions. To the receiver
|
||||||
this will look like TLS records had been tampered with and will result
|
this will look like TLS records had been tampered with and will result
|
||||||
in record authentication failures.
|
in record authentication failures.
|
||||||
|
|
||||||
|
TLS_RX_EXPECT_NO_PAD
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
TLS 1.3 only. Expect the sender to not pad records. This allows the data
|
||||||
|
to be decrypted directly into user space buffers with TLS 1.3.
|
||||||
|
|
||||||
|
This optimization is safe to enable only if the remote end is trusted,
|
||||||
|
otherwise it is an attack vector to doubling the TLS processing cost.
|
||||||
|
|
||||||
|
If the record decrypted turns out to had been padded or is not a data
|
||||||
|
record it will be decrypted again into a kernel buffer without zero copy.
|
||||||
|
Such events are counted in the ``TlsDecryptRetry`` statistic.
|
||||||
|
|
||||||
Statistics
|
Statistics
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
@ -264,3 +277,8 @@ TLS implementation exposes the following per-namespace statistics
|
||||||
|
|
||||||
- ``TlsDeviceRxResync`` -
|
- ``TlsDeviceRxResync`` -
|
||||||
number of RX resyncs sent to NICs handling cryptography
|
number of RX resyncs sent to NICs handling cryptography
|
||||||
|
|
||||||
|
- ``TlsDecryptRetry`` -
|
||||||
|
number of RX records which had to be re-decrypted due to
|
||||||
|
``TLS_RX_EXPECT_NO_PAD`` mis-prediction. Note that this counter will
|
||||||
|
also increment for non-data records.
|
||||||
|
|
|
@ -102,4 +102,12 @@ static inline long strncpy_from_sockptr(char *dst, sockptr_t src, size_t count)
|
||||||
return strncpy_from_user(dst, src.user, count);
|
return strncpy_from_user(dst, src.user, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int check_zeroed_sockptr(sockptr_t src, size_t offset,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
if (!sockptr_is_kernel(src))
|
||||||
|
return check_zeroed_user(src.user + offset, size);
|
||||||
|
return memchr_inv(src.kernel + offset, 0, size) == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _LINUX_SOCKPTR_H */
|
#endif /* _LINUX_SOCKPTR_H */
|
||||||
|
|
|
@ -149,6 +149,7 @@ struct tls_sw_context_rx {
|
||||||
|
|
||||||
struct sk_buff *recv_pkt;
|
struct sk_buff *recv_pkt;
|
||||||
u8 async_capable:1;
|
u8 async_capable:1;
|
||||||
|
u8 zc_capable:1;
|
||||||
atomic_t decrypt_pending;
|
atomic_t decrypt_pending;
|
||||||
/* protect crypto_wait with decrypt_pending*/
|
/* protect crypto_wait with decrypt_pending*/
|
||||||
spinlock_t decrypt_compl_lock;
|
spinlock_t decrypt_compl_lock;
|
||||||
|
@ -239,6 +240,7 @@ struct tls_context {
|
||||||
u8 tx_conf:3;
|
u8 tx_conf:3;
|
||||||
u8 rx_conf:3;
|
u8 rx_conf:3;
|
||||||
u8 zerocopy_sendfile:1;
|
u8 zerocopy_sendfile:1;
|
||||||
|
u8 rx_no_pad:1;
|
||||||
|
|
||||||
int (*push_pending_record)(struct sock *sk, int flags);
|
int (*push_pending_record)(struct sock *sk, int flags);
|
||||||
void (*sk_write_space)(struct sock *sk);
|
void (*sk_write_space)(struct sock *sk);
|
||||||
|
@ -358,6 +360,7 @@ int tls_sk_attach(struct sock *sk, int optname, char __user *optval,
|
||||||
void tls_err_abort(struct sock *sk, int err);
|
void tls_err_abort(struct sock *sk, int err);
|
||||||
|
|
||||||
int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx);
|
int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx);
|
||||||
|
void tls_update_rx_zc_capable(struct tls_context *tls_ctx);
|
||||||
void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx);
|
void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx);
|
||||||
void tls_sw_strparser_done(struct tls_context *tls_ctx);
|
void tls_sw_strparser_done(struct tls_context *tls_ctx);
|
||||||
int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
||||||
|
|
|
@ -344,6 +344,7 @@ enum
|
||||||
LINUX_MIB_TLSRXDEVICE, /* TlsRxDevice */
|
LINUX_MIB_TLSRXDEVICE, /* TlsRxDevice */
|
||||||
LINUX_MIB_TLSDECRYPTERROR, /* TlsDecryptError */
|
LINUX_MIB_TLSDECRYPTERROR, /* TlsDecryptError */
|
||||||
LINUX_MIB_TLSRXDEVICERESYNC, /* TlsRxDeviceResync */
|
LINUX_MIB_TLSRXDEVICERESYNC, /* TlsRxDeviceResync */
|
||||||
|
LINUX_MIN_TLSDECRYPTRETRY, /* TlsDecryptRetry */
|
||||||
__LINUX_MIB_TLSMAX
|
__LINUX_MIB_TLSMAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#define TLS_TX 1 /* Set transmit parameters */
|
#define TLS_TX 1 /* Set transmit parameters */
|
||||||
#define TLS_RX 2 /* Set receive parameters */
|
#define TLS_RX 2 /* Set receive parameters */
|
||||||
#define TLS_TX_ZEROCOPY_RO 3 /* TX zerocopy (only sendfile now) */
|
#define TLS_TX_ZEROCOPY_RO 3 /* TX zerocopy (only sendfile now) */
|
||||||
|
#define TLS_RX_EXPECT_NO_PAD 4 /* Attempt opportunistic zero-copy */
|
||||||
|
|
||||||
/* Supported versions */
|
/* Supported versions */
|
||||||
#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF)
|
#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF)
|
||||||
|
@ -162,6 +163,7 @@ enum {
|
||||||
TLS_INFO_TXCONF,
|
TLS_INFO_TXCONF,
|
||||||
TLS_INFO_RXCONF,
|
TLS_INFO_RXCONF,
|
||||||
TLS_INFO_ZC_RO_TX,
|
TLS_INFO_ZC_RO_TX,
|
||||||
|
TLS_INFO_RX_NO_PAD,
|
||||||
__TLS_INFO_MAX,
|
__TLS_INFO_MAX,
|
||||||
};
|
};
|
||||||
#define TLS_INFO_MAX (__TLS_INFO_MAX - 1)
|
#define TLS_INFO_MAX (__TLS_INFO_MAX - 1)
|
||||||
|
|
|
@ -533,6 +533,37 @@ static int do_tls_getsockopt_tx_zc(struct sock *sk, char __user *optval,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
|
||||||
|
int __user *optlen)
|
||||||
|
{
|
||||||
|
struct tls_context *ctx = tls_get_ctx(sk);
|
||||||
|
unsigned int value;
|
||||||
|
int err, len;
|
||||||
|
|
||||||
|
if (ctx->prot_info.version != TLS_1_3_VERSION)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (get_user(len, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
if (len < sizeof(value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
err = -EINVAL;
|
||||||
|
if (ctx->rx_conf == TLS_SW || ctx->rx_conf == TLS_HW)
|
||||||
|
value = ctx->rx_no_pad;
|
||||||
|
release_sock(sk);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (put_user(sizeof(value), optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
if (copy_to_user(optval, &value, sizeof(value)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_tls_getsockopt(struct sock *sk, int optname,
|
static int do_tls_getsockopt(struct sock *sk, int optname,
|
||||||
char __user *optval, int __user *optlen)
|
char __user *optval, int __user *optlen)
|
||||||
{
|
{
|
||||||
|
@ -547,6 +578,9 @@ static int do_tls_getsockopt(struct sock *sk, int optname,
|
||||||
case TLS_TX_ZEROCOPY_RO:
|
case TLS_TX_ZEROCOPY_RO:
|
||||||
rc = do_tls_getsockopt_tx_zc(sk, optval, optlen);
|
rc = do_tls_getsockopt_tx_zc(sk, optval, optlen);
|
||||||
break;
|
break;
|
||||||
|
case TLS_RX_EXPECT_NO_PAD:
|
||||||
|
rc = do_tls_getsockopt_no_pad(sk, optval, optlen);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -ENOPROTOOPT;
|
rc = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
@ -718,6 +752,38 @@ static int do_tls_setsockopt_tx_zc(struct sock *sk, sockptr_t optval,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_tls_setsockopt_no_pad(struct sock *sk, sockptr_t optval,
|
||||||
|
unsigned int optlen)
|
||||||
|
{
|
||||||
|
struct tls_context *ctx = tls_get_ctx(sk);
|
||||||
|
u32 val;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (ctx->prot_info.version != TLS_1_3_VERSION ||
|
||||||
|
sockptr_is_null(optval) || optlen < sizeof(val))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rc = copy_from_sockptr(&val, optval, sizeof(val));
|
||||||
|
if (rc)
|
||||||
|
return -EFAULT;
|
||||||
|
if (val > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
rc = check_zeroed_sockptr(optval, sizeof(val), optlen - sizeof(val));
|
||||||
|
if (rc < 1)
|
||||||
|
return rc == 0 ? -EINVAL : rc;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
rc = -EINVAL;
|
||||||
|
if (ctx->rx_conf == TLS_SW || ctx->rx_conf == TLS_HW) {
|
||||||
|
ctx->rx_no_pad = val;
|
||||||
|
tls_update_rx_zc_capable(ctx);
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
||||||
unsigned int optlen)
|
unsigned int optlen)
|
||||||
{
|
{
|
||||||
|
@ -736,6 +802,9 @@ static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval,
|
||||||
rc = do_tls_setsockopt_tx_zc(sk, optval, optlen);
|
rc = do_tls_setsockopt_tx_zc(sk, optval, optlen);
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
break;
|
break;
|
||||||
|
case TLS_RX_EXPECT_NO_PAD:
|
||||||
|
rc = do_tls_setsockopt_no_pad(sk, optval, optlen);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -ENOPROTOOPT;
|
rc = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
@ -976,6 +1045,11 @@ static int tls_get_info(const struct sock *sk, struct sk_buff *skb)
|
||||||
if (err)
|
if (err)
|
||||||
goto nla_failure;
|
goto nla_failure;
|
||||||
}
|
}
|
||||||
|
if (ctx->rx_no_pad) {
|
||||||
|
err = nla_put_flag(skb, TLS_INFO_RX_NO_PAD);
|
||||||
|
if (err)
|
||||||
|
goto nla_failure;
|
||||||
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
nla_nest_end(skb, start);
|
nla_nest_end(skb, start);
|
||||||
|
@ -997,6 +1071,7 @@ static size_t tls_get_info_size(const struct sock *sk)
|
||||||
nla_total_size(sizeof(u16)) + /* TLS_INFO_RXCONF */
|
nla_total_size(sizeof(u16)) + /* TLS_INFO_RXCONF */
|
||||||
nla_total_size(sizeof(u16)) + /* TLS_INFO_TXCONF */
|
nla_total_size(sizeof(u16)) + /* TLS_INFO_TXCONF */
|
||||||
nla_total_size(0) + /* TLS_INFO_ZC_RO_TX */
|
nla_total_size(0) + /* TLS_INFO_ZC_RO_TX */
|
||||||
|
nla_total_size(0) + /* TLS_INFO_RX_NO_PAD */
|
||||||
0;
|
0;
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
|
|
|
@ -18,6 +18,7 @@ static const struct snmp_mib tls_mib_list[] = {
|
||||||
SNMP_MIB_ITEM("TlsRxDevice", LINUX_MIB_TLSRXDEVICE),
|
SNMP_MIB_ITEM("TlsRxDevice", LINUX_MIB_TLSRXDEVICE),
|
||||||
SNMP_MIB_ITEM("TlsDecryptError", LINUX_MIB_TLSDECRYPTERROR),
|
SNMP_MIB_ITEM("TlsDecryptError", LINUX_MIB_TLSDECRYPTERROR),
|
||||||
SNMP_MIB_ITEM("TlsRxDeviceResync", LINUX_MIB_TLSRXDEVICERESYNC),
|
SNMP_MIB_ITEM("TlsRxDeviceResync", LINUX_MIB_TLSRXDEVICERESYNC),
|
||||||
|
SNMP_MIB_ITEM("TlsDecryptRetry", LINUX_MIN_TLSDECRYPTRETRY),
|
||||||
SNMP_MIB_SENTINEL
|
SNMP_MIB_SENTINEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1601,6 +1601,7 @@ static int decrypt_skb_update(struct sock *sk, struct sk_buff *skb,
|
||||||
if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION &&
|
if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION &&
|
||||||
darg->tail != TLS_RECORD_TYPE_DATA)) {
|
darg->tail != TLS_RECORD_TYPE_DATA)) {
|
||||||
darg->zc = false;
|
darg->zc = false;
|
||||||
|
TLS_INC_STATS(sock_net(sk), LINUX_MIN_TLSDECRYPTRETRY);
|
||||||
return decrypt_skb_update(sk, skb, dest, darg);
|
return decrypt_skb_update(sk, skb, dest, darg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1787,7 +1788,7 @@ int tls_sw_recvmsg(struct sock *sk,
|
||||||
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
|
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
|
||||||
|
|
||||||
zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek &&
|
zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek &&
|
||||||
prot->version != TLS_1_3_VERSION;
|
ctx->zc_capable;
|
||||||
decrypted = 0;
|
decrypted = 0;
|
||||||
while (len && (decrypted + copied < target || ctx->recv_pkt)) {
|
while (len && (decrypted + copied < target || ctx->recv_pkt)) {
|
||||||
struct tls_decrypt_arg darg = {};
|
struct tls_decrypt_arg darg = {};
|
||||||
|
@ -2269,6 +2270,14 @@ void tls_sw_strparser_arm(struct sock *sk, struct tls_context *tls_ctx)
|
||||||
strp_check_rcv(&rx_ctx->strp);
|
strp_check_rcv(&rx_ctx->strp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tls_update_rx_zc_capable(struct tls_context *tls_ctx)
|
||||||
|
{
|
||||||
|
struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(tls_ctx);
|
||||||
|
|
||||||
|
rx_ctx->zc_capable = tls_ctx->rx_no_pad ||
|
||||||
|
tls_ctx->prot_info.version != TLS_1_3_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
|
int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
|
||||||
{
|
{
|
||||||
struct tls_context *tls_ctx = tls_get_ctx(sk);
|
struct tls_context *tls_ctx = tls_get_ctx(sk);
|
||||||
|
@ -2504,12 +2513,10 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
|
||||||
if (sw_ctx_rx) {
|
if (sw_ctx_rx) {
|
||||||
tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv);
|
tfm = crypto_aead_tfm(sw_ctx_rx->aead_recv);
|
||||||
|
|
||||||
if (crypto_info->version == TLS_1_3_VERSION)
|
tls_update_rx_zc_capable(ctx);
|
||||||
sw_ctx_rx->async_capable = 0;
|
sw_ctx_rx->async_capable =
|
||||||
else
|
crypto_info->version != TLS_1_3_VERSION &&
|
||||||
sw_ctx_rx->async_capable =
|
!!(tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC);
|
||||||
!!(tfm->__crt_alg->cra_flags &
|
|
||||||
CRYPTO_ALG_ASYNC);
|
|
||||||
|
|
||||||
/* Set up strparser */
|
/* Set up strparser */
|
||||||
memset(&cb, 0, sizeof(cb));
|
memset(&cb, 0, sizeof(cb));
|
||||||
|
|
Loading…
Add table
Reference in a new issue