diff options
Diffstat (limited to 'net/ipv4/tcp_cong.c')
-rw-r--r-- | net/ipv4/tcp_cong.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index d846d7b95e1f..343d6197c92e 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c | |||
@@ -123,6 +123,7 @@ int tcp_set_default_congestion_control(const char *name) | |||
123 | #endif | 123 | #endif |
124 | 124 | ||
125 | if (ca) { | 125 | if (ca) { |
126 | ca->non_restricted = 1; /* default is always allowed */ | ||
126 | list_move(&ca->list, &tcp_cong_list); | 127 | list_move(&ca->list, &tcp_cong_list); |
127 | ret = 0; | 128 | ret = 0; |
128 | } | 129 | } |
@@ -168,6 +169,64 @@ void tcp_get_default_congestion_control(char *name) | |||
168 | rcu_read_unlock(); | 169 | rcu_read_unlock(); |
169 | } | 170 | } |
170 | 171 | ||
172 | /* Built list of non-restricted congestion control values */ | ||
173 | void tcp_get_allowed_congestion_control(char *buf, size_t maxlen) | ||
174 | { | ||
175 | struct tcp_congestion_ops *ca; | ||
176 | size_t offs = 0; | ||
177 | |||
178 | *buf = '\0'; | ||
179 | rcu_read_lock(); | ||
180 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) { | ||
181 | if (!ca->non_restricted) | ||
182 | continue; | ||
183 | offs += snprintf(buf + offs, maxlen - offs, | ||
184 | "%s%s", | ||
185 | offs == 0 ? "" : " ", ca->name); | ||
186 | |||
187 | } | ||
188 | rcu_read_unlock(); | ||
189 | } | ||
190 | |||
191 | /* Change list of non-restricted congestion control */ | ||
192 | int tcp_set_allowed_congestion_control(char *val) | ||
193 | { | ||
194 | struct tcp_congestion_ops *ca; | ||
195 | char *clone, *name; | ||
196 | int ret = 0; | ||
197 | |||
198 | clone = kstrdup(val, GFP_USER); | ||
199 | if (!clone) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | spin_lock(&tcp_cong_list_lock); | ||
203 | /* pass 1 check for bad entries */ | ||
204 | while ((name = strsep(&clone, " ")) && *name) { | ||
205 | ca = tcp_ca_find(name); | ||
206 | if (!ca) { | ||
207 | ret = -ENOENT; | ||
208 | goto out; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* pass 2 clear */ | ||
213 | list_for_each_entry_rcu(ca, &tcp_cong_list, list) | ||
214 | ca->non_restricted = 0; | ||
215 | |||
216 | /* pass 3 mark as allowed */ | ||
217 | while ((name = strsep(&val, " ")) && *name) { | ||
218 | ca = tcp_ca_find(name); | ||
219 | WARN_ON(!ca); | ||
220 | if (ca) | ||
221 | ca->non_restricted = 1; | ||
222 | } | ||
223 | out: | ||
224 | spin_unlock(&tcp_cong_list_lock); | ||
225 | |||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | |||
171 | /* Change congestion control for socket */ | 230 | /* Change congestion control for socket */ |
172 | int tcp_set_congestion_control(struct sock *sk, const char *name) | 231 | int tcp_set_congestion_control(struct sock *sk, const char *name) |
173 | { | 232 | { |
@@ -183,6 +242,9 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) | |||
183 | if (!ca) | 242 | if (!ca) |
184 | err = -ENOENT; | 243 | err = -ENOENT; |
185 | 244 | ||
245 | else if (!(ca->non_restricted || capable(CAP_NET_ADMIN))) | ||
246 | err = -EPERM; | ||
247 | |||
186 | else if (!try_module_get(ca->owner)) | 248 | else if (!try_module_get(ca->owner)) |
187 | err = -EBUSY; | 249 | err = -EBUSY; |
188 | 250 | ||
@@ -284,6 +346,7 @@ EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd); | |||
284 | 346 | ||
285 | struct tcp_congestion_ops tcp_reno = { | 347 | struct tcp_congestion_ops tcp_reno = { |
286 | .name = "reno", | 348 | .name = "reno", |
349 | .non_restricted = 1, | ||
287 | .owner = THIS_MODULE, | 350 | .owner = THIS_MODULE, |
288 | .ssthresh = tcp_reno_ssthresh, | 351 | .ssthresh = tcp_reno_ssthresh, |
289 | .cong_avoid = tcp_reno_cong_avoid, | 352 | .cong_avoid = tcp_reno_cong_avoid, |