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 16 static DEFINE_SPINLOCK(tcp_ulp_list_lock); 17 static LIST_HEAD(tcp_ulp_list); 18 19 /* Simple linear search, don't expect many entries! */ 20 static 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 32 static struct tcp_ulp_ops *tcp_ulp_find_id(const int ulp) 33 { 34 struct tcp_ulp_ops *e; 35 36 list_for_each_entry_rcu(e, &tcp_ulp_list, list) { 37 if (e->uid == ulp) 38 return e; 39 } 40 41 return NULL; 42 } 43 44 static const struct tcp_ulp_ops *__tcp_ulp_find_autoload(const char *name) 45 { 46 const struct tcp_ulp_ops *ulp = NULL; 47 48 rcu_read_lock(); 49 ulp = tcp_ulp_find(name); 50 51 #ifdef CONFIG_MODULES 52 if (!ulp && capable(CAP_NET_ADMIN)) { 53 rcu_read_unlock(); 54 request_module("tcp-ulp-%s", name); 55 rcu_read_lock(); 56 ulp = tcp_ulp_find(name); 57 } 58 #endif 59 if (!ulp || !try_module_get(ulp->owner)) 60 ulp = NULL; 61 62 rcu_read_unlock(); 63 return ulp; 64 } 65 66 static const struct tcp_ulp_ops *__tcp_ulp_lookup(const int uid) 67 { 68 const struct tcp_ulp_ops *ulp; 69 70 rcu_read_lock(); 71 ulp = tcp_ulp_find_id(uid); 72 if (!ulp || !try_module_get(ulp->owner)) 73 ulp = NULL; 74 rcu_read_unlock(); 75 return ulp; 76 } 77 78 /* Attach new upper layer protocol to the list 79 * of available protocols. 80 */ 81 int tcp_register_ulp(struct tcp_ulp_ops *ulp) 82 { 83 int ret = 0; 84 85 spin_lock(&tcp_ulp_list_lock); 86 if (tcp_ulp_find(ulp->name)) 87 ret = -EEXIST; 88 else 89 list_add_tail_rcu(&ulp->list, &tcp_ulp_list); 90 spin_unlock(&tcp_ulp_list_lock); 91 92 return ret; 93 } 94 EXPORT_SYMBOL_GPL(tcp_register_ulp); 95 96 void tcp_unregister_ulp(struct tcp_ulp_ops *ulp) 97 { 98 spin_lock(&tcp_ulp_list_lock); 99 list_del_rcu(&ulp->list); 100 spin_unlock(&tcp_ulp_list_lock); 101 102 synchronize_rcu(); 103 } 104 EXPORT_SYMBOL_GPL(tcp_unregister_ulp); 105 106 /* Build string with list of available upper layer protocl values */ 107 void tcp_get_available_ulp(char *buf, size_t maxlen) 108 { 109 struct tcp_ulp_ops *ulp_ops; 110 size_t offs = 0; 111 112 *buf = '\0'; 113 rcu_read_lock(); 114 list_for_each_entry_rcu(ulp_ops, &tcp_ulp_list, list) { 115 offs += snprintf(buf + offs, maxlen - offs, 116 "%s%s", 117 offs == 0 ? "" : " ", ulp_ops->name); 118 } 119 rcu_read_unlock(); 120 } 121 122 void tcp_cleanup_ulp(struct sock *sk) 123 { 124 struct inet_connection_sock *icsk = inet_csk(sk); 125 126 if (!icsk->icsk_ulp_ops) 127 return; 128 129 if (icsk->icsk_ulp_ops->release) 130 icsk->icsk_ulp_ops->release(sk); 131 module_put(icsk->icsk_ulp_ops->owner); 132 133 icsk->icsk_ulp_ops = NULL; 134 } 135 136 /* Change upper layer protocol for socket */ 137 int tcp_set_ulp(struct sock *sk, const char *name) 138 { 139 struct inet_connection_sock *icsk = inet_csk(sk); 140 const struct tcp_ulp_ops *ulp_ops; 141 int err = 0; 142 143 if (icsk->icsk_ulp_ops) 144 return -EEXIST; 145 146 ulp_ops = __tcp_ulp_find_autoload(name); 147 if (!ulp_ops) 148 return -ENOENT; 149 150 if (!ulp_ops->user_visible) { 151 module_put(ulp_ops->owner); 152 return -ENOENT; 153 } 154 155 err = ulp_ops->init(sk); 156 if (err) { 157 module_put(ulp_ops->owner); 158 return err; 159 } 160 161 icsk->icsk_ulp_ops = ulp_ops; 162 return 0; 163 } 164 165 int tcp_set_ulp_id(struct sock *sk, int ulp) 166 { 167 struct inet_connection_sock *icsk = inet_csk(sk); 168 const struct tcp_ulp_ops *ulp_ops; 169 int err; 170 171 if (icsk->icsk_ulp_ops) 172 return -EEXIST; 173 174 ulp_ops = __tcp_ulp_lookup(ulp); 175 if (!ulp_ops) 176 return -ENOENT; 177 178 err = ulp_ops->init(sk); 179 if (err) { 180 module_put(ulp_ops->owner); 181 return err; 182 } 183 184 icsk->icsk_ulp_ops = ulp_ops; 185 return 0; 186 } 187