Fix TCP and TLS to use the newer ULP infrastructure in sockets. Tested-by: Dave Watson <davejwat...@fb.com> Signed-off-by: Tom Herbert <t...@quantonium.net> --- Documentation/networking/tls.txt | 6 +- include/net/inet_connection_sock.h | 4 -- include/net/tcp.h | 25 ------- include/net/tls.h | 4 +- net/ipv4/Makefile | 2 +- net/ipv4/sysctl_net_ipv4.c | 9 ++- net/ipv4/tcp.c | 40 ++++++----- net/ipv4/tcp_ipv4.c | 2 - net/ipv4/tcp_ulp.c | 135 ------------------------------------- net/tls/Kconfig | 1 + net/tls/tls_main.c | 24 ++++--- 11 files changed, 51 insertions(+), 201 deletions(-) delete mode 100644 net/ipv4/tcp_ulp.c
diff --git a/Documentation/networking/tls.txt b/Documentation/networking/tls.txt index 77ed00631c12..b70309df4709 100644 --- a/Documentation/networking/tls.txt +++ b/Documentation/networking/tls.txt @@ -12,8 +12,12 @@ Creating a TLS connection First create a new TCP socket and set the TLS ULP. + struct ulp_config ulpc = { + .ulp_name = "tls", + }; + sock = socket(AF_INET, SOCK_STREAM, 0); - setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); + setsockopt(sock, SOL_SOCKET, SO_ULP, &ulpc, sizeof(ulpc)) Setting the TLS ULP allows us to set/get TLS socket options. Currently only the symmetric encryption is handled in the kernel. After the TLS diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 13e4c89a8231..c7a577976bec 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -75,8 +75,6 @@ struct inet_connection_sock_af_ops { * @icsk_pmtu_cookie Last pmtu seen by socket * @icsk_ca_ops Pluggable congestion control hook * @icsk_af_ops Operations which are AF_INET{4,6} specific - * @icsk_ulp_ops Pluggable ULP control hook - * @icsk_ulp_data ULP private data * @icsk_ca_state: Congestion control state * @icsk_retransmits: Number of unrecovered [RTO] timeouts * @icsk_pending: Scheduled timer event @@ -99,8 +97,6 @@ struct inet_connection_sock { __u32 icsk_pmtu_cookie; 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; unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu); __u8 icsk_ca_state:6, icsk_ca_setsockopt:1, diff --git a/include/net/tcp.h b/include/net/tcp.h index bb1881b4ce48..65c462da3740 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1968,31 +1968,6 @@ static inline void tcp_listendrop(const struct sock *sk) enum hrtimer_restart tcp_pace_kick(struct hrtimer *timer); -/* - * Interface for adding Upper Level Protocols over TCP - */ - -#define TCP_ULP_NAME_MAX 16 -#define TCP_ULP_MAX 128 -#define TCP_ULP_BUF_MAX (TCP_ULP_NAME_MAX*TCP_ULP_MAX) - -struct tcp_ulp_ops { - struct list_head list; - - /* initialize ulp */ - int (*init)(struct sock *sk); - /* cleanup ulp */ - void (*release)(struct sock *sk); - - char name[TCP_ULP_NAME_MAX]; - struct module *owner; -}; -int tcp_register_ulp(struct tcp_ulp_ops *type); -void tcp_unregister_ulp(struct tcp_ulp_ops *type); -int tcp_set_ulp(struct sock *sk, const char *name); -void tcp_get_available_ulp(char *buf, size_t len); -void tcp_cleanup_ulp(struct sock *sk); - /* Call BPF_SOCK_OPS program that returns an int. If the return value * is < 0, then the BPF op failed (for example if the loaded BPF * program does not support the chosen operation or there is no BPF diff --git a/include/net/tls.h b/include/net/tls.h index b89d397dd62f..7d88a6e2f5a7 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -214,9 +214,7 @@ static inline void tls_fill_prepend(struct tls_context *ctx, 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; + return sk->sk_ulp_data; } static inline struct tls_sw_context *tls_sw_ctx( diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index afcb435adfbe..f83de23a30e7 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -8,7 +8,7 @@ obj-y := route.o inetpeer.o protocol.o \ inet_timewait_sock.o inet_connection_sock.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \ - tcp_rate.o tcp_recovery.o tcp_ulp.o \ + tcp_rate.o tcp_recovery.o \ tcp_offload.o datagram.o raw.o udp.o udplite.o \ udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 0d3c038d7b04..9ab0c278b7ba 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -21,6 +21,7 @@ #include <net/route.h> #include <net/tcp.h> #include <net/udp.h> +#include <net/ulp_sock.h> #include <net/cipso_ipv4.h> #include <net/inet_frag.h> #include <net/ping.h> @@ -372,13 +373,15 @@ static int proc_tcp_available_ulp(struct ctl_table *ctl, void __user *buffer, size_t *lenp, loff_t *ppos) { - struct ctl_table tbl = { .maxlen = TCP_ULP_BUF_MAX, }; + struct ctl_table tbl = { .maxlen = ULP_BUF_MAX, }; int ret; tbl.data = kmalloc(tbl.maxlen, GFP_USER); if (!tbl.data) return -ENOMEM; - tcp_get_available_ulp(tbl.data, TCP_ULP_BUF_MAX); + + /* Just return all ULPs for compatibility */ + ulp_get_available(tbl.data, ULP_BUF_MAX); ret = proc_dostring(&tbl, write, buffer, lenp, ppos); kfree(tbl.data); @@ -709,7 +712,7 @@ static struct ctl_table ipv4_table[] = { }, { .procname = "tcp_available_ulp", - .maxlen = TCP_ULP_BUF_MAX, + .maxlen = ULP_BUF_MAX, .mode = 0444, .proc_handler = proc_tcp_available_ulp, }, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9dd6f4dba9b1..26f0d9821e6d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2404,24 +2404,24 @@ static int do_tcp_setsockopt(struct sock *sk, int level, release_sock(sk); return err; } + case TCP_ULP: { - char name[TCP_ULP_NAME_MAX]; + struct ulp_config ulpc; if (optlen < 1) return -EINVAL; - val = strncpy_from_user(name, optval, - min_t(long, TCP_ULP_NAME_MAX - 1, + val = strncpy_from_user(ulpc.ulp_name, optval, + min_t(long, ULP_NAME_MAX - 1, optlen)); if (val < 0) return -EFAULT; - name[val] = 0; + ulpc.ulp_name[val] = 0; - lock_sock(sk); - err = tcp_set_ulp(sk, name); - release_sock(sk); - return err; + return kernel_setsockopt(sk->sk_socket, SOL_SOCKET, SO_ULP, + (char *)&ulpc, sizeof(ulpc)); } + default: /* fallthru */ break; @@ -2993,20 +2993,28 @@ static int do_tcp_getsockopt(struct sock *sk, int level, return -EFAULT; return 0; - case TCP_ULP: + case TCP_ULP: { + struct ulp_config ulpc; + int ulen, ret; + if (get_user(len, optlen)) return -EFAULT; - len = min_t(unsigned int, len, TCP_ULP_NAME_MAX); - if (!icsk->icsk_ulp_ops) { - if (put_user(0, optlen)) - return -EFAULT; - return 0; - } + len = min_t(unsigned int, len, ULP_NAME_MAX); + + ulen = sizeof(ulpc); + + /* Backwards compatbility */ + ret = kernel_getsockopt(sk->sk_socket, SOL_SOCKET, SO_ULP, + (char *)&ulpc, &ulen); + if (ret) + return ret; + if (put_user(len, optlen)) return -EFAULT; - if (copy_to_user(optval, icsk->icsk_ulp_ops->name, len)) + if (copy_to_user(optval, ulpc.ulp_name, len)) return -EFAULT; return 0; + } case TCP_THIN_LINEAR_TIMEOUTS: val = tp->thin_lto; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9b51663cd5a4..89fafd2c5d36 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1858,8 +1858,6 @@ void tcp_v4_destroy_sock(struct sock *sk) tcp_cleanup_congestion_control(sk); - tcp_cleanup_ulp(sk); - /* Cleanup up the write buffer. */ tcp_write_queue_purge(sk); diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c deleted file mode 100644 index 2417f55374c5..000000000000 --- a/net/ipv4/tcp_ulp.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Pluggable TCP upper layer protocol support. - * - * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. - * Copyright (c) 2016-2017, Dave Watson <davejwat...@fb.com>. All rights reserved. - * - */ - -#include<linux/module.h> -#include <linux/mm.h> -#include <linux/types.h> -#include <linux/list.h> -#include <linux/gfp.h> -#include <net/tcp.h> - -static DEFINE_SPINLOCK(tcp_ulp_list_lock); -static LIST_HEAD(tcp_ulp_list); - -/* Simple linear search, don't expect many entries! */ -static struct tcp_ulp_ops *tcp_ulp_find(const char *name) -{ - struct tcp_ulp_ops *e; - - list_for_each_entry_rcu(e, &tcp_ulp_list, list) { - if (strcmp(e->name, name) == 0) - return e; - } - - return NULL; -} - -static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name) -{ - const struct tcp_ulp_ops *ulp = NULL; - - rcu_read_lock(); - ulp = tcp_ulp_find(name); - -#ifdef CONFIG_MODULES - if (!ulp && capable(CAP_NET_ADMIN)) { - rcu_read_unlock(); - request_module("%s", name); - rcu_read_lock(); - ulp = tcp_ulp_find(name); - } -#endif - if (!ulp || !try_module_get(ulp->owner)) - ulp = NULL; - - rcu_read_unlock(); - return ulp; -} - -/* Attach new upper layer protocol to the list - * of available protocols. - */ -int tcp_register_ulp(struct tcp_ulp_ops *ulp) -{ - int ret = 0; - - spin_lock(&tcp_ulp_list_lock); - if (tcp_ulp_find(ulp->name)) { - pr_notice("%s already registered or non-unique name\n", - ulp->name); - ret = -EEXIST; - } else { - list_add_tail_rcu(&ulp->list, &tcp_ulp_list); - } - spin_unlock(&tcp_ulp_list_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(tcp_register_ulp); - -void tcp_unregister_ulp(struct tcp_ulp_ops *ulp) -{ - spin_lock(&tcp_ulp_list_lock); - list_del_rcu(&ulp->list); - spin_unlock(&tcp_ulp_list_lock); - - synchronize_rcu(); -} -EXPORT_SYMBOL_GPL(tcp_unregister_ulp); - -/* Build string with list of available upper layer protocl values */ -void tcp_get_available_ulp(char *buf, size_t maxlen) -{ - struct tcp_ulp_ops *ulp_ops; - size_t offs = 0; - - *buf = '\0'; - rcu_read_lock(); - list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) { - offs += snprintf(buf + offs, maxlen - offs, - "%s%s", - offs == 0 ? "" : " ", ulp_ops->name); - } - rcu_read_unlock(); -} - -void tcp_cleanup_ulp(struct sock *sk) -{ - struct inet_connection_sock *icsk = inet_csk(sk); - - if (!icsk->icsk_ulp_ops) - return; - - if (icsk->icsk_ulp_ops->release) - icsk->icsk_ulp_ops->release(sk); - module_put(icsk->icsk_ulp_ops->owner); -} - -/* Change upper layer protocol for socket */ -int tcp_set_ulp(struct sock *sk, const char *name) -{ - struct inet_connection_sock *icsk = inet_csk(sk); - const struct tcp_ulp_ops *ulp_ops; - int err = 0; - - if (icsk->icsk_ulp_ops) - return -EEXIST; - - ulp_ops = __tcp_ulp_find_autoload(name); - if (!ulp_ops) - err = -ENOENT; - else - err = ulp_ops->init(sk); - - if (err) - goto out; - - icsk->icsk_ulp_ops = ulp_ops; - out: - return err; -} diff --git a/net/tls/Kconfig b/net/tls/Kconfig index eb583038c67e..60ae4e9b257e 100644 --- a/net/tls/Kconfig +++ b/net/tls/Kconfig @@ -7,6 +7,7 @@ config TLS select CRYPTO select CRYPTO_AES select CRYPTO_GCM + select ULP_SOCK default n ---help--- Enable kernel support for TLS protocol. This allows symmetric diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 60aff60e30ad..1ae5b50f7b7a 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -31,15 +31,15 @@ * SOFTWARE. */ -#include <linux/module.h> - -#include <net/tcp.h> -#include <net/inet_common.h> #include <linux/highmem.h> +#include <linux/module.h> #include <linux/netdevice.h> #include <linux/sched/signal.h> - +#include <net/inet_common.h> +#include <net/sock.h> +#include <net/tcp.h> #include <net/tls.h> +#include <net/ulp_sock.h> MODULE_AUTHOR("Mellanox Technologies"); MODULE_DESCRIPTION("Transport Layer Security Support"); @@ -438,19 +438,21 @@ static int tls_setsockopt(struct sock *sk, int level, int optname, return do_tls_setsockopt(sk, optname, optval, optlen); } -static int tls_init(struct sock *sk) +static int tls_init(struct sock *sk, char __user *optval, int len) { - struct inet_connection_sock *icsk = inet_csk(sk); struct tls_context *ctx; int rc = 0; + if (sk->sk_protocol != IPPROTO_TCP) + return -EAFNOSUPPORT; + /* allocate tls context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { rc = -ENOMEM; goto out; } - icsk->icsk_ulp_data = ctx; + sk->sk_ulp_data = ctx; ctx->setsockopt = sk->sk_prot->setsockopt; ctx->getsockopt = sk->sk_prot->getsockopt; sk->sk_prot = &tls_base_prot; @@ -458,7 +460,7 @@ static int tls_init(struct sock *sk) return rc; } -static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = { +static struct ulp_ops ulp_tls_ops __read_mostly = { .name = "tls", .owner = THIS_MODULE, .init = tls_init, @@ -475,14 +477,14 @@ static int __init tls_register(void) tls_sw_prot.sendpage = tls_sw_sendpage; tls_sw_prot.close = tls_sk_proto_close; - tcp_register_ulp(&tcp_tls_ulp_ops); + ulp_register(&ulp_tls_ops); return 0; } static void __exit tls_unregister(void) { - tcp_unregister_ulp(&tcp_tls_ulp_ops); + ulp_unregister(&ulp_tls_ops); } module_init(tls_register); -- 2.11.0