linux/net/ipv4/tcp_ulp.c
<<
>>
Prefs
   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}
 162