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                if (strcmp(e->name, name) == 0)
  27                        return e;
  28        }
  29
  30        return NULL;
  31}
  32
  33static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name)
  34{
  35        const struct tcp_ulp_ops *ulp = NULL;
  36
  37        rcu_read_lock();
  38        ulp = tcp_ulp_find(name);
  39
  40#ifdef CONFIG_MODULES
  41        if (!ulp && capable(CAP_NET_ADMIN)) {
  42                rcu_read_unlock();
  43                request_module("tcp-ulp-%s", name);
  44                rcu_read_lock();
  45                ulp = tcp_ulp_find(name);
  46        }
  47#endif
  48        if (!ulp || !try_module_get(ulp->owner))
  49                ulp = NULL;
  50
  51        rcu_read_unlock();
  52        return ulp;
  53}
  54
  55/* Attach new upper layer protocol to the list
  56 * of available protocols.
  57 */
  58int tcp_register_ulp(struct tcp_ulp_ops *ulp)
  59{
  60        int ret = 0;
  61
  62        spin_lock(&tcp_ulp_list_lock);
  63        if (tcp_ulp_find(ulp->name))
  64                ret = -EEXIST;
  65        else
  66                list_add_tail_rcu(&ulp->list, &tcp_ulp_list);
  67        spin_unlock(&tcp_ulp_list_lock);
  68
  69        return ret;
  70}
  71EXPORT_SYMBOL_GPL(tcp_register_ulp);
  72
  73void tcp_unregister_ulp(struct tcp_ulp_ops *ulp)
  74{
  75        spin_lock(&tcp_ulp_list_lock);
  76        list_del_rcu(&ulp->list);
  77        spin_unlock(&tcp_ulp_list_lock);
  78
  79        synchronize_rcu();
  80}
  81EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
  82
  83/* Build string with list of available upper layer protocl values */
  84void tcp_get_available_ulp(char *buf, size_t maxlen)
  85{
  86        struct tcp_ulp_ops *ulp_ops;
  87        size_t offs = 0;
  88
  89        *buf = '\0';
  90        rcu_read_lock();
  91        list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
  92                offs += snprintf(buf + offs, maxlen - offs,
  93                                 "%s%s",
  94                                 offs == 0 ? "" : " ", ulp_ops->name);
  95        }
  96        rcu_read_unlock();
  97}
  98
  99void tcp_update_ulp(struct sock *sk, struct proto *proto)
 100{
 101        struct inet_connection_sock *icsk = inet_csk(sk);
 102
 103        if (!icsk->icsk_ulp_ops) {
 104                sk->sk_prot = proto;
 105                return;
 106        }
 107
 108        if (icsk->icsk_ulp_ops->update)
 109                icsk->icsk_ulp_ops->update(sk, proto);
 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