Skip to content

Commit 734942c

Browse files
Dave Watsondavem330
authored andcommitted
tcp: ULP infrastructure
Add the infrustructure for attaching Upper Layer Protocols (ULPs) over TCP sockets. Based on a similar infrastructure in tcp_cong. The idea is that any ULP can add its own logic by changing the TCP proto_ops structure to its own methods. Example usage: setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); modules will call: tcp_register_ulp(&tcp_tls_ulp_ops); to register/unregister their ulp, with an init function and name. A list of registered ulps will be returned by tcp_get_available_ulp, which is hooked up to /proc. Example: $ cat /proc/sys/net/ipv4/tcp_available_ulp tls There is currently no functionality to remove or chain ULPs, but it should be possible to add these in the future if needed. Signed-off-by: Boris Pismenny <borisp@mellanox.com> Signed-off-by: Dave Watson <davejwatson@fb.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 206f60e commit 734942c

File tree

8 files changed

+220
-1
lines changed

8 files changed

+220
-1
lines changed

include/net/inet_connection_sock.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ struct inet_connection_sock_af_ops {
7575
* @icsk_pmtu_cookie Last pmtu seen by socket
7676
* @icsk_ca_ops Pluggable congestion control hook
7777
* @icsk_af_ops Operations which are AF_INET{4,6} specific
78+
* @icsk_ulp_ops Pluggable ULP control hook
79+
* @icsk_ulp_data ULP private data
7880
* @icsk_ca_state: Congestion control state
7981
* @icsk_retransmits: Number of unrecovered [RTO] timeouts
8082
* @icsk_pending: Scheduled timer event
@@ -97,6 +99,8 @@ struct inet_connection_sock {
9799
__u32 icsk_pmtu_cookie;
98100
const struct tcp_congestion_ops *icsk_ca_ops;
99101
const struct inet_connection_sock_af_ops *icsk_af_ops;
102+
const struct tcp_ulp_ops *icsk_ulp_ops;
103+
void *icsk_ulp_data;
100104
unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu);
101105
__u8 icsk_ca_state:6,
102106
icsk_ca_setsockopt:1,

include/net/tcp.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,4 +1991,29 @@ static inline void tcp_listendrop(const struct sock *sk)
19911991

19921992
enum hrtimer_restart tcp_pace_kick(struct hrtimer *timer);
19931993

1994+
/*
1995+
* Interface for adding Upper Level Protocols over TCP
1996+
*/
1997+
1998+
#define TCP_ULP_NAME_MAX 16
1999+
#define TCP_ULP_MAX 128
2000+
#define TCP_ULP_BUF_MAX (TCP_ULP_NAME_MAX*TCP_ULP_MAX)
2001+
2002+
struct tcp_ulp_ops {
2003+
struct list_head list;
2004+
2005+
/* initialize ulp */
2006+
int (*init)(struct sock *sk);
2007+
/* cleanup ulp */
2008+
void (*release)(struct sock *sk);
2009+
2010+
char name[TCP_ULP_NAME_MAX];
2011+
struct module *owner;
2012+
};
2013+
int tcp_register_ulp(struct tcp_ulp_ops *type);
2014+
void tcp_unregister_ulp(struct tcp_ulp_ops *type);
2015+
int tcp_set_ulp(struct sock *sk, const char *name);
2016+
void tcp_get_available_ulp(char *buf, size_t len);
2017+
void tcp_cleanup_ulp(struct sock *sk);
2018+
19942019
#endif /* _TCP_H */

include/uapi/linux/tcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ enum {
117117
#define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */
118118
#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */
119119
#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */
120+
#define TCP_ULP 31 /* Attach a ULP to a TCP connection */
120121

121122
struct tcp_repair_opt {
122123
__u32 opt_code;

net/ipv4/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ obj-y := route.o inetpeer.o protocol.o \
88
inet_timewait_sock.o inet_connection_sock.o \
99
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \
1010
tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \
11-
tcp_rate.o tcp_recovery.o \
11+
tcp_rate.o tcp_recovery.o tcp_ulp.o \
1212
tcp_offload.o datagram.o raw.o udp.o udplite.o \
1313
udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
1414
fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \

net/ipv4/sysctl_net_ipv4.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,25 @@ static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table,
360360
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
361361
if (write && ret == 0)
362362
tcp_fastopen_active_timeout_reset();
363+
364+
return ret;
365+
}
366+
367+
static int proc_tcp_available_ulp(struct ctl_table *ctl,
368+
int write,
369+
void __user *buffer, size_t *lenp,
370+
loff_t *ppos)
371+
{
372+
struct ctl_table tbl = { .maxlen = TCP_ULP_BUF_MAX, };
373+
int ret;
374+
375+
tbl.data = kmalloc(tbl.maxlen, GFP_USER);
376+
if (!tbl.data)
377+
return -ENOMEM;
378+
tcp_get_available_ulp(tbl.data, TCP_ULP_BUF_MAX);
379+
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
380+
kfree(tbl.data);
381+
363382
return ret;
364383
}
365384

@@ -685,6 +704,12 @@ static struct ctl_table ipv4_table[] = {
685704
.mode = 0644,
686705
.proc_handler = proc_dointvec_ms_jiffies,
687706
},
707+
{
708+
.procname = "tcp_available_ulp",
709+
.maxlen = TCP_ULP_BUF_MAX,
710+
.mode = 0444,
711+
.proc_handler = proc_tcp_available_ulp,
712+
},
688713
{
689714
.procname = "icmp_msgs_per_sec",
690715
.data = &sysctl_icmp_msgs_per_sec,

net/ipv4/tcp.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,6 +2482,24 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
24822482
release_sock(sk);
24832483
return err;
24842484
}
2485+
case TCP_ULP: {
2486+
char name[TCP_ULP_NAME_MAX];
2487+
2488+
if (optlen < 1)
2489+
return -EINVAL;
2490+
2491+
val = strncpy_from_user(name, optval,
2492+
min_t(long, TCP_ULP_NAME_MAX - 1,
2493+
optlen));
2494+
if (val < 0)
2495+
return -EFAULT;
2496+
name[val] = 0;
2497+
2498+
lock_sock(sk);
2499+
err = tcp_set_ulp(sk, name);
2500+
release_sock(sk);
2501+
return err;
2502+
}
24852503
default:
24862504
/* fallthru */
24872505
break;
@@ -3038,6 +3056,16 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
30383056
return -EFAULT;
30393057
return 0;
30403058

3059+
case TCP_ULP:
3060+
if (get_user(len, optlen))
3061+
return -EFAULT;
3062+
len = min_t(unsigned int, len, TCP_ULP_NAME_MAX);
3063+
if (put_user(len, optlen))
3064+
return -EFAULT;
3065+
if (copy_to_user(optval, icsk->icsk_ulp_ops->name, len))
3066+
return -EFAULT;
3067+
return 0;
3068+
30413069
case TCP_THIN_LINEAR_TIMEOUTS:
30423070
val = tp->thin_lto;
30433071
break;

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,8 @@ void tcp_v4_destroy_sock(struct sock *sk)
18601860

18611861
tcp_cleanup_congestion_control(sk);
18621862

1863+
tcp_cleanup_ulp(sk);
1864+
18631865
/* Cleanup up the write buffer. */
18641866
tcp_write_queue_purge(sk);
18651867

net/ipv4/tcp_ulp.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Pluggable TCP upper layer protocol support.
3+
*
4+
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
5+
* Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
6+
*
7+
*/
8+
9+
#include<linux/module.h>
10+
#include <linux/mm.h>
11+
#include <linux/types.h>
12+
#include <linux/list.h>
13+
#include <linux/gfp.h>
14+
#include <net/tcp.h>
15+
16+
static DEFINE_SPINLOCK(tcp_ulp_list_lock);
17+
static LIST_HEAD(tcp_ulp_list);
18+
19+
/* Simple linear search, don't expect many entries! */
20+
static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
21+
{
22+
struct tcp_ulp_ops *e;
23+
24+
list_for_each_entry_rcu(e, &tcp_ulp_list, list) {
25+
if (strcmp(e->name, name) == 0)
26+
return e;
27+
}
28+
29+
return NULL;
30+
}
31+
32+
static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
33+
{
34+
const struct tcp_ulp_ops *ulp = NULL;
35+
36+
rcu_read_lock();
37+
ulp = tcp_ulp_find(name);
38+
39+
#ifdef CONFIG_MODULES
40+
if (!ulp && capable(CAP_NET_ADMIN)) {
41+
rcu_read_unlock();
42+
request_module("%s", name);
43+
rcu_read_lock();
44+
ulp = tcp_ulp_find(name);
45+
}
46+
#endif
47+
if (!ulp || !try_module_get(ulp->owner))
48+
ulp = NULL;
49+
50+
rcu_read_unlock();
51+
return ulp;
52+
}
53+
54+
/* Attach new upper layer protocol to the list
55+
* of available protocols.
56+
*/
57+
int tcp_register_ulp(struct tcp_ulp_ops *ulp)
58+
{
59+
int ret = 0;
60+
61+
spin_lock(&tcp_ulp_list_lock);
62+
if (tcp_ulp_find(ulp->name)) {
63+
pr_notice("%s already registered or non-unique name\n",
64+
ulp->name);
65+
ret = -EEXIST;
66+
} else {
67+
list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
68+
}
69+
spin_unlock(&tcp_ulp_list_lock);
70+
71+
return ret;
72+
}
73+
EXPORT_SYMBOL_GPL(tcp_register_ulp);
74+
75+
void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
76+
{
77+
spin_lock(&tcp_ulp_list_lock);
78+
list_del_rcu(&ulp->list);
79+
spin_unlock(&tcp_ulp_list_lock);
80+
81+
synchronize_rcu();
82+
}
83+
EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
84+
85+
/* Build string with list of available upper layer protocl values */
86+
void tcp_get_available_ulp(char *buf, size_t maxlen)
87+
{
88+
struct tcp_ulp_ops *ulp_ops;
89+
size_t offs = 0;
90+
91+
rcu_read_lock();
92+
list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
93+
offs += snprintf(buf + offs, maxlen - offs,
94+
"%s%s",
95+
offs == 0 ? "" : " ", ulp_ops->name);
96+
}
97+
rcu_read_unlock();
98+
}
99+
100+
void tcp_cleanup_ulp(struct sock *sk)
101+
{
102+
struct inet_connection_sock *icsk = inet_csk(sk);
103+
104+
if (!icsk->icsk_ulp_ops)
105+
return;
106+
107+
if (icsk->icsk_ulp_ops->release)
108+
icsk->icsk_ulp_ops->release(sk);
109+
module_put(icsk->icsk_ulp_ops->owner);
110+
}
111+
112+
/* Change upper layer protocol for socket */
113+
int tcp_set_ulp(struct sock *sk, const char *name)
114+
{
115+
struct inet_connection_sock *icsk = inet_csk(sk);
116+
const struct tcp_ulp_ops *ulp_ops;
117+
int err = 0;
118+
119+
if (icsk->icsk_ulp_ops)
120+
return -EEXIST;
121+
122+
ulp_ops = __tcp_ulp_find_autoload(name);
123+
if (!ulp_ops)
124+
err = -ENOENT;
125+
else
126+
err = ulp_ops->init(sk);
127+
128+
if (err)
129+
goto out;
130+
131+
icsk->icsk_ulp_ops = ulp_ops;
132+
out:
133+
return err;
134+
}

0 commit comments

Comments
 (0)