cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tcp_ulp.c (3448B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Pluggable TCP upper layer protocol support.
      4 *
      5 * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
      6 * Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. All rights reserved.
      7 *
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/mm.h>
     12#include <linux/types.h>
     13#include <linux/list.h>
     14#include <linux/gfp.h>
     15#include <net/tcp.h>
     16
     17static DEFINE_SPINLOCK(tcp_ulp_list_lock);
     18static LIST_HEAD(tcp_ulp_list);
     19
     20/* Simple linear search, don't expect many entries! */
     21static struct tcp_ulp_ops *tcp_ulp_find(const char *name)
     22{
     23	struct tcp_ulp_ops *e;
     24
     25	list_for_each_entry_rcu(e, &tcp_ulp_list, list,
     26				lockdep_is_held(&tcp_ulp_list_lock)) {
     27		if (strcmp(e->name, name) == 0)
     28			return e;
     29	}
     30
     31	return NULL;
     32}
     33
     34static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
     35{
     36	const struct tcp_ulp_ops *ulp = NULL;
     37
     38	rcu_read_lock();
     39	ulp = tcp_ulp_find(name);
     40
     41#ifdef CONFIG_MODULES
     42	if (!ulp && capable(CAP_NET_ADMIN)) {
     43		rcu_read_unlock();
     44		request_module("tcp-ulp-%s", name);
     45		rcu_read_lock();
     46		ulp = tcp_ulp_find(name);
     47	}
     48#endif
     49	if (!ulp || !try_module_get(ulp->owner))
     50		ulp = NULL;
     51
     52	rcu_read_unlock();
     53	return ulp;
     54}
     55
     56/* Attach new upper layer protocol to the list
     57 * of available protocols.
     58 */
     59int tcp_register_ulp(struct tcp_ulp_ops *ulp)
     60{
     61	int ret = 0;
     62
     63	spin_lock(&tcp_ulp_list_lock);
     64	if (tcp_ulp_find(ulp->name))
     65		ret = -EEXIST;
     66	else
     67		list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
     68	spin_unlock(&tcp_ulp_list_lock);
     69
     70	return ret;
     71}
     72EXPORT_SYMBOL_GPL(tcp_register_ulp);
     73
     74void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
     75{
     76	spin_lock(&tcp_ulp_list_lock);
     77	list_del_rcu(&ulp->list);
     78	spin_unlock(&tcp_ulp_list_lock);
     79
     80	synchronize_rcu();
     81}
     82EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
     83
     84/* Build string with list of available upper layer protocl values */
     85void tcp_get_available_ulp(char *buf, size_t maxlen)
     86{
     87	struct tcp_ulp_ops *ulp_ops;
     88	size_t offs = 0;
     89
     90	*buf = '\0';
     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		if (WARN_ON_ONCE(offs >= maxlen))
     98			break;
     99	}
    100	rcu_read_unlock();
    101}
    102
    103void tcp_update_ulp(struct sock *sk, struct proto *proto,
    104		    void (*write_space)(struct sock *sk))
    105{
    106	struct inet_connection_sock *icsk = inet_csk(sk);
    107
    108	if (icsk->icsk_ulp_ops->update)
    109		icsk->icsk_ulp_ops->update(sk, proto, write_space);
    110}
    111
    112void tcp_cleanup_ulp(struct sock *sk)
    113{
    114	struct inet_connection_sock *icsk = inet_csk(sk);
    115
    116	/* No sock_owned_by_me() check here as at the time the
    117	 * stack calls this function, the socket is dead and
    118	 * about to be destroyed.
    119	 */
    120	if (!icsk->icsk_ulp_ops)
    121		return;
    122
    123	if (icsk->icsk_ulp_ops->release)
    124		icsk->icsk_ulp_ops->release(sk);
    125	module_put(icsk->icsk_ulp_ops->owner);
    126
    127	icsk->icsk_ulp_ops = NULL;
    128}
    129
    130static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
    131{
    132	struct inet_connection_sock *icsk = inet_csk(sk);
    133	int err;
    134
    135	err = -EEXIST;
    136	if (icsk->icsk_ulp_ops)
    137		goto out_err;
    138
    139	err = ulp_ops->init(sk);
    140	if (err)
    141		goto out_err;
    142
    143	icsk->icsk_ulp_ops = ulp_ops;
    144	return 0;
    145out_err:
    146	module_put(ulp_ops->owner);
    147	return err;
    148}
    149
    150int tcp_set_ulp(struct sock *sk, const char *name)
    151{
    152	const struct tcp_ulp_ops *ulp_ops;
    153
    154	sock_owned_by_me(sk);
    155
    156	ulp_ops = __tcp_ulp_find_autoload(name);
    157	if (!ulp_ops)
    158		return -ENOENT;
    159
    160	return __tcp_set_ulp(sk, ulp_ops);
    161}