Merge branch 'net-tls-add-socket-diag'

Davide Caratti says:

====================
net: tls: add socket diag

The current kernel does not provide any diagnostic tool, except
getsockopt(TCP_ULP), to know more about TCP sockets that have an upper
layer protocol (ULP) on top of them. This series extends the set of
information exported by INET_DIAG_INFO, to include data that are
specific to the ULP (and that might be meaningful for debug/testing
purposes).

patch 1/3 ensures that the control plane reads/updates ULP specific data
using RCU.

patch 2/3 extends INET_DIAG_INFO and allows knowing the ULP name for
each TCP socket that has done setsockopt(TCP_ULP) successfully.

patch 3/3 extends kTLS to let programs like 'ss' know the protocol
version and the cipher in use.

Changes since v2:
- remove unneeded #ifdef and fix reverse christmas tree in
  tls_get_info(), thanks to Jakub Kicinski

Changes since v1:
- don't worry about grace period when accessing ulp_ops, thanks to
  Jakub Kicinski and Eric Dumazet
- use rcu_dereference() to access ULP data in tls get_info(), and
  test against NULL value, thanks to Jakub Kicinski
- move RCU protected section inside tls get_info(), thanks to Jakub
  Kicinski

Changes since RFC:
- some coding style fixes, thanks to Jakub Kicinski
- add X_UNSPEC as lowest value of uAPI enums, thanks to Jakub Kicinski
- fix assignment of struct nlattr *start, thanks to Jakub Kicinski
- let tls dump RXCONF and TXCONF, suggested by Jakub Kicinski
- don't dump anything if TLS version or cipher are 0 (but still return a
  constant size in get_aux_size()), thanks to Boris Pismenny
- constify first argument of get_info() and get_size()
- use RCU to access access ulp_ops, like it's done for ca_ops
- add patch 1/3, from Jakub Kicinski
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-08-31 23:44:28 -07:00
commit 1b6ca07b68
9 changed files with 188 additions and 13 deletions

View File

@ -97,7 +97,7 @@ struct inet_connection_sock {
const struct tcp_congestion_ops *icsk_ca_ops;
const struct inet_connection_sock_af_ops *icsk_af_ops;
const struct tcp_ulp_ops *icsk_ulp_ops;
void *icsk_ulp_data;
void __rcu *icsk_ulp_data;
void (*icsk_clean_acked)(struct sock *sk, u32 acked_seq);
struct hlist_node icsk_listen_portaddr_node;
unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu);

View File

@ -2122,6 +2122,9 @@ struct tcp_ulp_ops {
void (*update)(struct sock *sk, struct proto *p);
/* cleanup ulp */
void (*release)(struct sock *sk);
/* diagnostic */
int (*get_info)(const struct sock *sk, struct sk_buff *skb);
size_t (*get_info_size)(const struct sock *sk);
char name[TCP_ULP_NAME_MAX];
struct module *owner;

View File

@ -41,6 +41,7 @@
#include <linux/tcp.h>
#include <linux/skmsg.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
#include <net/tcp.h>
#include <net/strparser.h>
@ -290,6 +291,7 @@ struct tls_context {
struct list_head list;
refcount_t refcount;
struct rcu_head rcu;
};
enum tls_offload_ctx_dir {
@ -348,7 +350,7 @@ struct tls_offload_context_rx {
#define TLS_OFFLOAD_CONTEXT_SIZE_RX \
(sizeof(struct tls_offload_context_rx) + TLS_DRIVER_STATE_SIZE_RX)
void tls_ctx_free(struct tls_context *ctx);
void tls_ctx_free(struct sock *sk, struct tls_context *ctx);
int wait_on_pending_writer(struct sock *sk, long *timeo);
int tls_sk_query(struct sock *sk, int optname, char __user *optval,
int __user *optlen);
@ -429,6 +431,23 @@ static inline bool is_tx_ready(struct tls_sw_context_tx *ctx)
return READ_ONCE(rec->tx_ready);
}
static inline u16 tls_user_config(struct tls_context *ctx, bool tx)
{
u16 config = tx ? ctx->tx_conf : ctx->rx_conf;
switch (config) {
case TLS_BASE:
return TLS_CONF_BASE;
case TLS_SW:
return TLS_CONF_SW;
case TLS_HW:
return TLS_CONF_HW;
case TLS_HW_RECORD:
return TLS_CONF_HW_RECORD;
}
return 0;
}
struct sk_buff *
tls_validate_xmit_skb(struct sock *sk, struct net_device *dev,
struct sk_buff *skb);
@ -467,7 +486,10 @@ static inline struct tls_context *tls_get_ctx(const struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
return icsk->icsk_ulp_data;
/* Use RCU on icsk_ulp_data only for sock diag code,
* TLS data path doesn't need rcu_dereference().
*/
return (__force void *)icsk->icsk_ulp_data;
}
static inline void tls_advance_record_sn(struct sock *sk,

View File

@ -153,11 +153,20 @@ enum {
INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */
INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */
INET_DIAG_MD5SIG,
INET_DIAG_ULP_INFO,
__INET_DIAG_MAX,
};
#define INET_DIAG_MAX (__INET_DIAG_MAX - 1)
enum {
INET_ULP_INFO_UNSPEC,
INET_ULP_INFO_NAME,
INET_ULP_INFO_TLS,
__INET_ULP_INFO_MAX,
};
#define INET_ULP_INFO_MAX (__INET_ULP_INFO_MAX - 1)
/* INET_DIAG_MEM */
struct inet_diag_meminfo {

View File

@ -109,4 +109,19 @@ struct tls12_crypto_info_aes_ccm_128 {
unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE];
};
enum {
TLS_INFO_UNSPEC,
TLS_INFO_VERSION,
TLS_INFO_CIPHER,
TLS_INFO_TXCONF,
TLS_INFO_RXCONF,
__TLS_INFO_MAX,
};
#define TLS_INFO_MAX (__TLS_INFO_MAX - 1)
#define TLS_CONF_BASE 1
#define TLS_CONF_SW 2
#define TLS_CONF_HW 3
#define TLS_CONF_HW_RECORD 4
#endif /* _UAPI_LINUX_TLS_H */

View File

@ -345,7 +345,7 @@ static int sock_map_update_common(struct bpf_map *map, u32 idx,
return -EINVAL;
if (unlikely(idx >= map->max_entries))
return -E2BIG;
if (unlikely(icsk->icsk_ulp_data))
if (unlikely(rcu_access_pointer(icsk->icsk_ulp_data)))
return -EINVAL;
link = sk_psock_init_link();

View File

@ -81,13 +81,42 @@ static int tcp_diag_put_md5sig(struct sk_buff *skb,
}
#endif
static int tcp_diag_put_ulp(struct sk_buff *skb, struct sock *sk,
const struct tcp_ulp_ops *ulp_ops)
{
struct nlattr *nest;
int err;
nest = nla_nest_start_noflag(skb, INET_DIAG_ULP_INFO);
if (!nest)
return -EMSGSIZE;
err = nla_put_string(skb, INET_ULP_INFO_NAME, ulp_ops->name);
if (err)
goto nla_failure;
if (ulp_ops->get_info)
err = ulp_ops->get_info(sk, skb);
if (err)
goto nla_failure;
nla_nest_end(skb, nest);
return 0;
nla_failure:
nla_nest_cancel(skb, nest);
return err;
}
static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
struct sk_buff *skb)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int err = 0;
#ifdef CONFIG_TCP_MD5SIG
if (net_admin) {
struct tcp_md5sig_info *md5sig;
int err = 0;
rcu_read_lock();
md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
@ -99,11 +128,21 @@ static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
}
#endif
if (net_admin) {
const struct tcp_ulp_ops *ulp_ops;
ulp_ops = icsk->icsk_ulp_ops;
if (ulp_ops)
err = tcp_diag_put_ulp(skb, sk, ulp_ops);
if (err)
return err;
}
return 0;
}
static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
{
struct inet_connection_sock *icsk = inet_csk(sk);
size_t size = 0;
#ifdef CONFIG_TCP_MD5SIG
@ -124,6 +163,17 @@ static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
}
#endif
if (net_admin) {
const struct tcp_ulp_ops *ulp_ops;
ulp_ops = icsk->icsk_ulp_ops;
if (ulp_ops) {
size += nla_total_size(0) +
nla_total_size(TCP_ULP_NAME_MAX);
if (ulp_ops->get_info_size)
size += ulp_ops->get_info_size(sk);
}
}
return size;
}

View File

@ -61,7 +61,7 @@ static void tls_device_free_ctx(struct tls_context *ctx)
if (ctx->rx_conf == TLS_HW)
kfree(tls_offload_ctx_rx(ctx));
tls_ctx_free(ctx);
tls_ctx_free(NULL, ctx);
}
static void tls_device_gc_task(struct work_struct *work)

View File

@ -39,6 +39,7 @@
#include <linux/netdevice.h>
#include <linux/sched/signal.h>
#include <linux/inetdevice.h>
#include <linux/inet_diag.h>
#include <net/tls.h>
@ -251,14 +252,26 @@ static void tls_write_space(struct sock *sk)
ctx->sk_write_space(sk);
}
void tls_ctx_free(struct tls_context *ctx)
/**
* tls_ctx_free() - free TLS ULP context
* @sk: socket to with @ctx is attached
* @ctx: TLS context structure
*
* Free TLS context. If @sk is %NULL caller guarantees that the socket
* to which @ctx was attached has no outstanding references.
*/
void tls_ctx_free(struct sock *sk, struct tls_context *ctx)
{
if (!ctx)
return;
memzero_explicit(&ctx->crypto_send, sizeof(ctx->crypto_send));
memzero_explicit(&ctx->crypto_recv, sizeof(ctx->crypto_recv));
kfree(ctx);
if (sk)
kfree_rcu(ctx, rcu);
else
kfree(ctx);
}
static void tls_sk_proto_cleanup(struct sock *sk,
@ -306,7 +319,7 @@ static void tls_sk_proto_close(struct sock *sk, long timeout)
write_lock_bh(&sk->sk_callback_lock);
if (free_ctx)
icsk->icsk_ulp_data = NULL;
rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
sk->sk_prot = ctx->sk_proto;
if (sk->sk_write_space == tls_write_space)
sk->sk_write_space = ctx->sk_write_space;
@ -321,7 +334,7 @@ static void tls_sk_proto_close(struct sock *sk, long timeout)
ctx->sk_proto_close(sk, timeout);
if (free_ctx)
tls_ctx_free(ctx);
tls_ctx_free(sk, ctx);
}
static int do_tls_getsockopt_tx(struct sock *sk, char __user *optval,
@ -610,7 +623,7 @@ static struct tls_context *create_ctx(struct sock *sk)
if (!ctx)
return NULL;
icsk->icsk_ulp_data = ctx;
rcu_assign_pointer(icsk->icsk_ulp_data, ctx);
ctx->setsockopt = sk->sk_prot->setsockopt;
ctx->getsockopt = sk->sk_prot->getsockopt;
ctx->sk_proto_close = sk->sk_prot->close;
@ -651,8 +664,8 @@ static void tls_hw_sk_destruct(struct sock *sk)
ctx->sk_destruct(sk);
/* Free ctx */
tls_ctx_free(ctx);
icsk->icsk_ulp_data = NULL;
rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
tls_ctx_free(sk, ctx);
}
static int tls_hw_prot(struct sock *sk)
@ -823,6 +836,67 @@ static void tls_update(struct sock *sk, struct proto *p)
}
}
static int tls_get_info(const struct sock *sk, struct sk_buff *skb)
{
u16 version, cipher_type;
struct tls_context *ctx;
struct nlattr *start;
int err;
start = nla_nest_start_noflag(skb, INET_ULP_INFO_TLS);
if (!start)
return -EMSGSIZE;
rcu_read_lock();
ctx = rcu_dereference(inet_csk(sk)->icsk_ulp_data);
if (!ctx) {
err = 0;
goto nla_failure;
}
version = ctx->prot_info.version;
if (version) {
err = nla_put_u16(skb, TLS_INFO_VERSION, version);
if (err)
goto nla_failure;
}
cipher_type = ctx->prot_info.cipher_type;
if (cipher_type) {
err = nla_put_u16(skb, TLS_INFO_CIPHER, cipher_type);
if (err)
goto nla_failure;
}
err = nla_put_u16(skb, TLS_INFO_TXCONF, tls_user_config(ctx, true));
if (err)
goto nla_failure;
err = nla_put_u16(skb, TLS_INFO_RXCONF, tls_user_config(ctx, false));
if (err)
goto nla_failure;
rcu_read_unlock();
nla_nest_end(skb, start);
return 0;
nla_failure:
rcu_read_unlock();
nla_nest_cancel(skb, start);
return err;
}
static size_t tls_get_info_size(const struct sock *sk)
{
size_t size = 0;
size += nla_total_size(0) + /* INET_ULP_INFO_TLS */
nla_total_size(sizeof(u16)) + /* TLS_INFO_VERSION */
nla_total_size(sizeof(u16)) + /* TLS_INFO_CIPHER */
nla_total_size(sizeof(u16)) + /* TLS_INFO_RXCONF */
nla_total_size(sizeof(u16)) + /* TLS_INFO_TXCONF */
0;
return size;
}
void tls_register_device(struct tls_device *device)
{
spin_lock_bh(&device_spinlock);
@ -844,6 +918,8 @@ static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = {
.owner = THIS_MODULE,
.init = tls_init,
.update = tls_update,
.get_info = tls_get_info,
.get_info_size = tls_get_info_size,
};
static int __init tls_register(void)