xref: /linux/net/ipv4/tcp_ulp.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2734942ccSDave Watson /*
3734942ccSDave Watson  * Pluggable TCP upper layer protocol support.
4734942ccSDave Watson  *
5734942ccSDave Watson  * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
6734942ccSDave Watson  * Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
7734942ccSDave Watson  *
8734942ccSDave Watson  */
9734942ccSDave Watson 
10734942ccSDave Watson #include <linux/module.h>
11734942ccSDave Watson #include <linux/mm.h>
12734942ccSDave Watson #include <linux/types.h>
13734942ccSDave Watson #include <linux/list.h>
14734942ccSDave Watson #include <linux/gfp.h>
15734942ccSDave Watson #include <net/tcp.h>
16734942ccSDave Watson 
17734942ccSDave Watson static DEFINE_SPINLOCK(tcp_ulp_list_lock);
18734942ccSDave Watson static LIST_HEAD(tcp_ulp_list);
19734942ccSDave Watson 
20734942ccSDave Watson /* Simple linear search, don't expect many entries! */
tcp_ulp_find(const char * name)21734942ccSDave Watson static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
22734942ccSDave Watson {
23734942ccSDave Watson 	struct tcp_ulp_ops *e;
24734942ccSDave Watson 
25958a93c1SAmol Grover 	list_for_each_entry_rcu(e, &tcp_ulp_list, list,
26958a93c1SAmol Grover 				lockdep_is_held(&tcp_ulp_list_lock)) {
27734942ccSDave Watson 		if (strcmp(e->name, name) == 0)
28734942ccSDave Watson 			return e;
29734942ccSDave Watson 	}
30734942ccSDave Watson 
31734942ccSDave Watson 	return NULL;
32734942ccSDave Watson }
33734942ccSDave Watson 
__tcp_ulp_find_autoload(const char * name)34734942ccSDave Watson static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
35734942ccSDave Watson {
36734942ccSDave Watson 	const struct tcp_ulp_ops *ulp = NULL;
37734942ccSDave Watson 
38734942ccSDave Watson 	rcu_read_lock();
39734942ccSDave Watson 	ulp = tcp_ulp_find(name);
40734942ccSDave Watson 
41734942ccSDave Watson #ifdef CONFIG_MODULES
42734942ccSDave Watson 	if (!ulp && capable(CAP_NET_ADMIN)) {
43734942ccSDave Watson 		rcu_read_unlock();
44037b0b86SDaniel Borkmann 		request_module("tcp-ulp-%s", name);
45734942ccSDave Watson 		rcu_read_lock();
46734942ccSDave Watson 		ulp = tcp_ulp_find(name);
47734942ccSDave Watson 	}
48734942ccSDave Watson #endif
49734942ccSDave Watson 	if (!ulp || !try_module_get(ulp->owner))
50734942ccSDave Watson 		ulp = NULL;
51734942ccSDave Watson 
52734942ccSDave Watson 	rcu_read_unlock();
53734942ccSDave Watson 	return ulp;
54734942ccSDave Watson }
55734942ccSDave Watson 
56734942ccSDave Watson /* Attach new upper layer protocol to the list
57734942ccSDave Watson  * of available protocols.
58734942ccSDave Watson  */
tcp_register_ulp(struct tcp_ulp_ops * ulp)59734942ccSDave Watson int tcp_register_ulp(struct tcp_ulp_ops *ulp)
60734942ccSDave Watson {
61734942ccSDave Watson 	int ret = 0;
62734942ccSDave Watson 
63734942ccSDave Watson 	spin_lock(&tcp_ulp_list_lock);
64b11a632cSJohn Fastabend 	if (tcp_ulp_find(ulp->name))
65734942ccSDave Watson 		ret = -EEXIST;
66b11a632cSJohn Fastabend 	else
67734942ccSDave Watson 		list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
68734942ccSDave Watson 	spin_unlock(&tcp_ulp_list_lock);
69734942ccSDave Watson 
70734942ccSDave Watson 	return ret;
71734942ccSDave Watson }
72734942ccSDave Watson EXPORT_SYMBOL_GPL(tcp_register_ulp);
73734942ccSDave Watson 
tcp_unregister_ulp(struct tcp_ulp_ops * ulp)74734942ccSDave Watson void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
75734942ccSDave Watson {
76734942ccSDave Watson 	spin_lock(&tcp_ulp_list_lock);
77734942ccSDave Watson 	list_del_rcu(&ulp->list);
78734942ccSDave Watson 	spin_unlock(&tcp_ulp_list_lock);
79734942ccSDave Watson 
80734942ccSDave Watson 	synchronize_rcu();
81734942ccSDave Watson }
82734942ccSDave Watson EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
83734942ccSDave Watson 
84734942ccSDave Watson /* Build string with list of available upper layer protocl values */
tcp_get_available_ulp(char * buf,size_t maxlen)85734942ccSDave Watson void tcp_get_available_ulp(char *buf, size_t maxlen)
86734942ccSDave Watson {
87734942ccSDave Watson 	struct tcp_ulp_ops *ulp_ops;
88734942ccSDave Watson 	size_t offs = 0;
89734942ccSDave Watson 
90926f38e9SJakub Kicinski 	*buf = '\0';
91734942ccSDave Watson 	rcu_read_lock();
92734942ccSDave Watson 	list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
93734942ccSDave Watson 		offs += snprintf(buf + offs, maxlen - offs,
94734942ccSDave Watson 				 "%s%s",
95734942ccSDave Watson 				 offs == 0 ? "" : " ", ulp_ops->name);
969bb59a21SHangbin Liu 
979bb59a21SHangbin Liu 		if (WARN_ON_ONCE(offs >= maxlen))
989bb59a21SHangbin Liu 			break;
99734942ccSDave Watson 	}
100734942ccSDave Watson 	rcu_read_unlock();
101734942ccSDave Watson }
102734942ccSDave Watson 
tcp_update_ulp(struct sock * sk,struct proto * proto,void (* write_space)(struct sock * sk))10333bfe20dSJohn Fastabend void tcp_update_ulp(struct sock *sk, struct proto *proto,
10433bfe20dSJohn Fastabend 		    void (*write_space)(struct sock *sk))
10595fa1454SJohn Fastabend {
10695fa1454SJohn Fastabend 	struct inet_connection_sock *icsk = inet_csk(sk);
10795fa1454SJohn Fastabend 
10895fa1454SJohn Fastabend 	if (icsk->icsk_ulp_ops->update)
10933bfe20dSJohn Fastabend 		icsk->icsk_ulp_ops->update(sk, proto, write_space);
11095fa1454SJohn Fastabend }
11195fa1454SJohn Fastabend 
tcp_cleanup_ulp(struct sock * sk)112734942ccSDave Watson void tcp_cleanup_ulp(struct sock *sk)
113734942ccSDave Watson {
114734942ccSDave Watson 	struct inet_connection_sock *icsk = inet_csk(sk);
115734942ccSDave Watson 
116aadd4355SDaniel Borkmann 	/* No sock_owned_by_me() check here as at the time the
117aadd4355SDaniel Borkmann 	 * stack calls this function, the socket is dead and
118aadd4355SDaniel Borkmann 	 * about to be destroyed.
119aadd4355SDaniel Borkmann 	 */
120734942ccSDave Watson 	if (!icsk->icsk_ulp_ops)
121734942ccSDave Watson 		return;
122734942ccSDave Watson 
123734942ccSDave Watson 	if (icsk->icsk_ulp_ops->release)
124734942ccSDave Watson 		icsk->icsk_ulp_ops->release(sk);
125734942ccSDave Watson 	module_put(icsk->icsk_ulp_ops->owner);
12690545cdcSDaniel Borkmann 
12790545cdcSDaniel Borkmann 	icsk->icsk_ulp_ops = NULL;
128734942ccSDave Watson }
129734942ccSDave Watson 
__tcp_set_ulp(struct sock * sk,const struct tcp_ulp_ops * ulp_ops)1301243a51fSDaniel Borkmann static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
131734942ccSDave Watson {
132734942ccSDave Watson 	struct inet_connection_sock *icsk = inet_csk(sk);
1331243a51fSDaniel Borkmann 	int err;
1341243a51fSDaniel Borkmann 
1351243a51fSDaniel Borkmann 	err = -EEXIST;
1361243a51fSDaniel Borkmann 	if (icsk->icsk_ulp_ops)
1371243a51fSDaniel Borkmann 		goto out_err;
1381243a51fSDaniel Borkmann 
139e276d62dSPavel Begunkov 	if (sk->sk_socket)
140e276d62dSPavel Begunkov 		clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags);
141e276d62dSPavel Begunkov 
142*8ccc9936SPaolo Abeni 	err = -ENOTCONN;
1432c02d41dSPaolo Abeni 	if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN)
1442c02d41dSPaolo Abeni 		goto out_err;
1452c02d41dSPaolo Abeni 
1461243a51fSDaniel Borkmann 	err = ulp_ops->init(sk);
1471243a51fSDaniel Borkmann 	if (err)
1481243a51fSDaniel Borkmann 		goto out_err;
1491243a51fSDaniel Borkmann 
1501243a51fSDaniel Borkmann 	icsk->icsk_ulp_ops = ulp_ops;
1511243a51fSDaniel Borkmann 	return 0;
1521243a51fSDaniel Borkmann out_err:
1531243a51fSDaniel Borkmann 	module_put(ulp_ops->owner);
1541243a51fSDaniel Borkmann 	return err;
1551243a51fSDaniel Borkmann }
1561243a51fSDaniel Borkmann 
tcp_set_ulp(struct sock * sk,const char * name)1571243a51fSDaniel Borkmann int tcp_set_ulp(struct sock *sk, const char *name)
1581243a51fSDaniel Borkmann {
159734942ccSDave Watson 	const struct tcp_ulp_ops *ulp_ops;
160734942ccSDave Watson 
1618b9088f8SDaniel Borkmann 	sock_owned_by_me(sk);
162734942ccSDave Watson 
163734942ccSDave Watson 	ulp_ops = __tcp_ulp_find_autoload(name);
164734942ccSDave Watson 	if (!ulp_ops)
165539a06baSSabrina Dubroca 		return -ENOENT;
166734942ccSDave Watson 
1671243a51fSDaniel Borkmann 	return __tcp_set_ulp(sk, ulp_ops);
168734942ccSDave Watson }
169