mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-28 01:21:58 +00:00
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>
This commit is contained in:
parent
206f60e145
commit
734942cc4e
8 changed files with 220 additions and 1 deletions
134
net/ipv4/tcp_ulp.c
Normal file
134
net/ipv4/tcp_ulp.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Pluggable TCP upper layer protocol support.
|
||||
*
|
||||
* Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016-2017, Dave Watson <davejwatson@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;
|
||||
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue