linux/net/ipv4/tcp_ulp.c
<<
>>
Prefs
   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
  16static DEFINE_SPINLOCK(tcp_ulp_list_lock);
  17static LIST_HEAD(tcp_ulp_list);
  18
  19/* Simple linear search, don't expect many entries! */
  20static 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
  32static 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 */
  57int 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}
  73EXPORT_SYMBOL_GPL(tcp_register_ulp);
  74
  75void 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}
  83EXPORT_SYMBOL_GPL(tcp_unregister_ulp);
  84
  85/* Build string with list of available upper layer protocl values */
  86void tcp_get_available_ulp(char *buf, size_t maxlen)
  87{
  88        struct tcp_ulp_ops *ulp_ops;
  89        size_t offs = 0;
  90
  91        *buf = '\0';
  92        rcu_read_lock();
  93        list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) {
  94                offs += snprintf(buf + offs, maxlen - offs,
  95                                 "%s%s",
  96                                 offs == 0 ? "" : " ", ulp_ops->name);
  97        }
  98        rcu_read_unlock();
  99}
 100
 101void tcp_cleanup_ulp(struct sock *sk)
 102{
 103        struct inet_connection_sock *icsk = inet_csk(sk);
 104
 105        if (!icsk->icsk_ulp_ops)
 106                return;
 107
 108        if (icsk->icsk_ulp_ops->release)
 109                icsk->icsk_ulp_ops->release(sk);
 110        module_put(icsk->icsk_ulp_ops->owner);
 111}
 112
 113/* Change upper layer protocol for socket */
 114int tcp_set_ulp(struct sock *sk, const char *name)
 115{
 116        struct inet_connection_sock *icsk = inet_csk(sk);
 117        const struct tcp_ulp_ops *ulp_ops;
 118        int err = 0;
 119
 120        if (icsk->icsk_ulp_ops)
 121                return -EEXIST;
 122
 123        ulp_ops = __tcp_ulp_find_autoload(name);
 124        if (!ulp_ops)
 125                return -ENOENT;
 126
 127        err = ulp_ops->init(sk);
 128        if (err) {
 129                module_put(ulp_ops->owner);
 130                return err;
 131        }
 132
 133        icsk->icsk_ulp_ops = ulp_ops;
 134        return 0;
 135}
 136