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("%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 134 /* Change upper layer protocol for socket */ 135 int tcp_set_ulp(struct sock *sk, const char *name) 136 { 137 struct inet_connection_sock *icsk = inet_csk(sk); 138 const struct tcp_ulp_ops *ulp_ops; 139 int err = 0; 140 141 if (icsk->icsk_ulp_ops) 142 return -EEXIST; 143 144 ulp_ops = __tcp_ulp_find_autoload(name); 145 if (!ulp_ops) 146 return -ENOENT; 147 148 if (!ulp_ops->user_visible) { 149 module_put(ulp_ops->owner); 150 return -ENOENT; 151 } 152 153 err = ulp_ops->init(sk); 154 if (err) { 155 module_put(ulp_ops->owner); 156 return err; 157 } 158 159 icsk->icsk_ulp_ops = ulp_ops; 160 return 0; 161 } 162 163 int tcp_set_ulp_id(struct sock *sk, int ulp) 164 { 165 struct inet_connection_sock *icsk = inet_csk(sk); 166 const struct tcp_ulp_ops *ulp_ops; 167 int err; 168 169 if (icsk->icsk_ulp_ops) 170 return -EEXIST; 171 172 ulp_ops = __tcp_ulp_lookup(ulp); 173 if (!ulp_ops) 174 return -ENOENT; 175 176 err = ulp_ops->init(sk); 177 if (err) { 178 module_put(ulp_ops->owner); 179 return err; 180 } 181 182 icsk->icsk_ulp_ops = ulp_ops; 183 return 0; 184 } 185