diff options
-rw-r--r-- | drivers/infiniband/core/cma.c | 22 | ||||
-rw-r--r-- | include/net/ip.h | 3 | ||||
-rw-r--r-- | net/ipv4/inet_connection_sock.c | 22 | ||||
-rw-r--r-- | net/ipv4/inet_hashtables.c | 13 | ||||
-rw-r--r-- | net/ipv4/sysctl_net_ipv4.c | 75 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 1 | ||||
-rw-r--r-- | net/ipv4/udp.c | 6 | ||||
-rw-r--r-- | net/ipv6/inet6_hashtables.c | 12 | ||||
-rw-r--r-- | net/sctp/socket.c | 11 | ||||
-rw-r--r-- | security/selinux/hooks.c | 39 |
10 files changed, 146 insertions, 58 deletions
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 9ffb9987450a..2e641b255db4 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c | |||
@@ -1866,13 +1866,14 @@ err1: | |||
1866 | static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) | 1866 | static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) |
1867 | { | 1867 | { |
1868 | struct rdma_bind_list *bind_list; | 1868 | struct rdma_bind_list *bind_list; |
1869 | int port, ret; | 1869 | int port, ret, low, high; |
1870 | 1870 | ||
1871 | bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL); | 1871 | bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL); |
1872 | if (!bind_list) | 1872 | if (!bind_list) |
1873 | return -ENOMEM; | 1873 | return -ENOMEM; |
1874 | 1874 | ||
1875 | retry: | 1875 | retry: |
1876 | /* FIXME: add proper port randomization per like inet_csk_get_port */ | ||
1876 | do { | 1877 | do { |
1877 | ret = idr_get_new_above(ps, bind_list, next_port, &port); | 1878 | ret = idr_get_new_above(ps, bind_list, next_port, &port); |
1878 | } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL)); | 1879 | } while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL)); |
@@ -1880,18 +1881,19 @@ retry: | |||
1880 | if (ret) | 1881 | if (ret) |
1881 | goto err1; | 1882 | goto err1; |
1882 | 1883 | ||
1883 | if (port > sysctl_local_port_range[1]) { | 1884 | inet_get_local_port_range(&low, &high); |
1884 | if (next_port != sysctl_local_port_range[0]) { | 1885 | if (port > high) { |
1886 | if (next_port != low) { | ||
1885 | idr_remove(ps, port); | 1887 | idr_remove(ps, port); |
1886 | next_port = sysctl_local_port_range[0]; | 1888 | next_port = low; |
1887 | goto retry; | 1889 | goto retry; |
1888 | } | 1890 | } |
1889 | ret = -EADDRNOTAVAIL; | 1891 | ret = -EADDRNOTAVAIL; |
1890 | goto err2; | 1892 | goto err2; |
1891 | } | 1893 | } |
1892 | 1894 | ||
1893 | if (port == sysctl_local_port_range[1]) | 1895 | if (port == high) |
1894 | next_port = sysctl_local_port_range[0]; | 1896 | next_port = low; |
1895 | else | 1897 | else |
1896 | next_port = port + 1; | 1898 | next_port = port + 1; |
1897 | 1899 | ||
@@ -2769,12 +2771,12 @@ static void cma_remove_one(struct ib_device *device) | |||
2769 | 2771 | ||
2770 | static int cma_init(void) | 2772 | static int cma_init(void) |
2771 | { | 2773 | { |
2772 | int ret; | 2774 | int ret, low, high; |
2773 | 2775 | ||
2774 | get_random_bytes(&next_port, sizeof next_port); | 2776 | get_random_bytes(&next_port, sizeof next_port); |
2775 | next_port = ((unsigned int) next_port % | 2777 | inet_get_local_port_range(&low, &high); |
2776 | (sysctl_local_port_range[1] - sysctl_local_port_range[0])) + | 2778 | next_port = ((unsigned int) next_port % (high - low)) + low; |
2777 | sysctl_local_port_range[0]; | 2779 | |
2778 | cma_wq = create_singlethread_workqueue("rdma_cm"); | 2780 | cma_wq = create_singlethread_workqueue("rdma_cm"); |
2779 | if (!cma_wq) | 2781 | if (!cma_wq) |
2780 | return -ENOMEM; | 2782 | return -ENOMEM; |
diff --git a/include/net/ip.h b/include/net/ip.h index abf2820a1125..3af3ed9d320b 100644 --- a/include/net/ip.h +++ b/include/net/ip.h | |||
@@ -171,7 +171,8 @@ extern unsigned long snmp_fold_field(void *mib[], int offt); | |||
171 | extern int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign); | 171 | extern int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign); |
172 | extern void snmp_mib_free(void *ptr[2]); | 172 | extern void snmp_mib_free(void *ptr[2]); |
173 | 173 | ||
174 | extern int sysctl_local_port_range[2]; | 174 | extern void inet_get_local_port_range(int *low, int *high); |
175 | |||
175 | extern int sysctl_ip_default_ttl; | 176 | extern int sysctl_ip_default_ttl; |
176 | extern int sysctl_ip_nonlocal_bind; | 177 | extern int sysctl_ip_nonlocal_bind; |
177 | 178 | ||
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index fbe7714f21d0..3cef12835c4b 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c | |||
@@ -33,6 +33,19 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); | |||
33 | * This array holds the first and last local port number. | 33 | * This array holds the first and last local port number. |
34 | */ | 34 | */ |
35 | int sysctl_local_port_range[2] = { 32768, 61000 }; | 35 | int sysctl_local_port_range[2] = { 32768, 61000 }; |
36 | DEFINE_SEQLOCK(sysctl_port_range_lock); | ||
37 | |||
38 | void inet_get_local_port_range(int *low, int *high) | ||
39 | { | ||
40 | unsigned seq; | ||
41 | do { | ||
42 | seq = read_seqbegin(&sysctl_port_range_lock); | ||
43 | |||
44 | *low = sysctl_local_port_range[0]; | ||
45 | *high = sysctl_local_port_range[1]; | ||
46 | } while (read_seqretry(&sysctl_port_range_lock, seq)); | ||
47 | } | ||
48 | EXPORT_SYMBOL(inet_get_local_port_range); | ||
36 | 49 | ||
37 | int inet_csk_bind_conflict(const struct sock *sk, | 50 | int inet_csk_bind_conflict(const struct sock *sk, |
38 | const struct inet_bind_bucket *tb) | 51 | const struct inet_bind_bucket *tb) |
@@ -77,10 +90,11 @@ int inet_csk_get_port(struct inet_hashinfo *hashinfo, | |||
77 | 90 | ||
78 | local_bh_disable(); | 91 | local_bh_disable(); |
79 | if (!snum) { | 92 | if (!snum) { |
80 | int low = sysctl_local_port_range[0]; | 93 | int remaining, rover, low, high; |
81 | int high = sysctl_local_port_range[1]; | 94 | |
82 | int remaining = (high - low) + 1; | 95 | inet_get_local_port_range(&low, &high); |
83 | int rover = net_random() % (high - low) + low; | 96 | remaining = high - low; |
97 | rover = net_random() % remaining + low; | ||
84 | 98 | ||
85 | do { | 99 | do { |
86 | head = &hashinfo->bhash[inet_bhashfn(rover, hashinfo->bhash_size)]; | 100 | head = &hashinfo->bhash[inet_bhashfn(rover, hashinfo->bhash_size)]; |
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index fb662621c54e..fac6398e4367 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c | |||
@@ -279,19 +279,18 @@ int inet_hash_connect(struct inet_timewait_death_row *death_row, | |||
279 | int ret; | 279 | int ret; |
280 | 280 | ||
281 | if (!snum) { | 281 | if (!snum) { |
282 | int low = sysctl_local_port_range[0]; | 282 | int i, remaining, low, high, port; |
283 | int high = sysctl_local_port_range[1]; | ||
284 | int range = high - low; | ||
285 | int i; | ||
286 | int port; | ||
287 | static u32 hint; | 283 | static u32 hint; |
288 | u32 offset = hint + inet_sk_port_offset(sk); | 284 | u32 offset = hint + inet_sk_port_offset(sk); |
289 | struct hlist_node *node; | 285 | struct hlist_node *node; |
290 | struct inet_timewait_sock *tw = NULL; | 286 | struct inet_timewait_sock *tw = NULL; |
291 | 287 | ||
288 | inet_get_local_port_range(&low, &high); | ||
289 | remaining = high - low; | ||
290 | |||
292 | local_bh_disable(); | 291 | local_bh_disable(); |
293 | for (i = 1; i <= range; i++) { | 292 | for (i = 1; i <= remaining; i++) { |
294 | port = low + (i + offset) % range; | 293 | port = low + (i + offset) % remaining; |
295 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; | 294 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; |
296 | spin_lock(&head->lock); | 295 | spin_lock(&head->lock); |
297 | 296 | ||
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 53ef0f4bbdaa..eb286abcf5dc 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/sysctl.h> | 12 | #include <linux/sysctl.h> |
13 | #include <linux/igmp.h> | 13 | #include <linux/igmp.h> |
14 | #include <linux/inetdevice.h> | 14 | #include <linux/inetdevice.h> |
15 | #include <linux/seqlock.h> | ||
15 | #include <net/snmp.h> | 16 | #include <net/snmp.h> |
16 | #include <net/icmp.h> | 17 | #include <net/icmp.h> |
17 | #include <net/ip.h> | 18 | #include <net/ip.h> |
@@ -89,6 +90,74 @@ static int ipv4_sysctl_forward_strategy(ctl_table *table, | |||
89 | return 1; | 90 | return 1; |
90 | } | 91 | } |
91 | 92 | ||
93 | extern seqlock_t sysctl_port_range_lock; | ||
94 | extern int sysctl_local_port_range[2]; | ||
95 | |||
96 | /* Update system visible IP port range */ | ||
97 | static void set_local_port_range(int range[2]) | ||
98 | { | ||
99 | write_seqlock(&sysctl_port_range_lock); | ||
100 | sysctl_local_port_range[0] = range[0]; | ||
101 | sysctl_local_port_range[1] = range[1]; | ||
102 | write_sequnlock(&sysctl_port_range_lock); | ||
103 | } | ||
104 | |||
105 | /* Validate changes from /proc interface. */ | ||
106 | static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp, | ||
107 | void __user *buffer, | ||
108 | size_t *lenp, loff_t *ppos) | ||
109 | { | ||
110 | int ret; | ||
111 | int range[2] = { sysctl_local_port_range[0], | ||
112 | sysctl_local_port_range[1] }; | ||
113 | ctl_table tmp = { | ||
114 | .data = &range, | ||
115 | .maxlen = sizeof(range), | ||
116 | .mode = table->mode, | ||
117 | .extra1 = &ip_local_port_range_min, | ||
118 | .extra2 = &ip_local_port_range_max, | ||
119 | }; | ||
120 | |||
121 | ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos); | ||
122 | |||
123 | if (write && ret == 0) { | ||
124 | if (range[1] <= range[0]) | ||
125 | ret = -EINVAL; | ||
126 | else | ||
127 | set_local_port_range(range); | ||
128 | } | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | /* Validate changes from sysctl interface. */ | ||
134 | static int ipv4_sysctl_local_port_range(ctl_table *table, int __user *name, | ||
135 | int nlen, void __user *oldval, | ||
136 | size_t __user *oldlenp, | ||
137 | void __user *newval, size_t newlen) | ||
138 | { | ||
139 | int ret; | ||
140 | int range[2] = { sysctl_local_port_range[0], | ||
141 | sysctl_local_port_range[1] }; | ||
142 | ctl_table tmp = { | ||
143 | .data = &range, | ||
144 | .maxlen = sizeof(range), | ||
145 | .mode = table->mode, | ||
146 | .extra1 = &ip_local_port_range_min, | ||
147 | .extra2 = &ip_local_port_range_max, | ||
148 | }; | ||
149 | |||
150 | ret = sysctl_intvec(&tmp, name, nlen, oldval, oldlenp, newval, newlen); | ||
151 | if (ret == 0 && newval && newlen) { | ||
152 | if (range[1] <= range[0]) | ||
153 | ret = -EINVAL; | ||
154 | else | ||
155 | set_local_port_range(range); | ||
156 | } | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | |||
92 | static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp, | 161 | static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp, |
93 | void __user *buffer, size_t *lenp, loff_t *ppos) | 162 | void __user *buffer, size_t *lenp, loff_t *ppos) |
94 | { | 163 | { |
@@ -427,10 +496,8 @@ ctl_table ipv4_table[] = { | |||
427 | .data = &sysctl_local_port_range, | 496 | .data = &sysctl_local_port_range, |
428 | .maxlen = sizeof(sysctl_local_port_range), | 497 | .maxlen = sizeof(sysctl_local_port_range), |
429 | .mode = 0644, | 498 | .mode = 0644, |
430 | .proc_handler = &proc_dointvec_minmax, | 499 | .proc_handler = &ipv4_local_port_range, |
431 | .strategy = &sysctl_intvec, | 500 | .strategy = &ipv4_sysctl_local_port_range, |
432 | .extra1 = ip_local_port_range_min, | ||
433 | .extra2 = ip_local_port_range_max | ||
434 | }, | 501 | }, |
435 | { | 502 | { |
436 | .ctl_name = NET_IPV4_ICMP_ECHO_IGNORE_ALL, | 503 | .ctl_name = NET_IPV4_ICMP_ECHO_IGNORE_ALL, |
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8855e640e958..38cf73a56731 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -2470,6 +2470,5 @@ EXPORT_SYMBOL(tcp_v4_syn_recv_sock); | |||
2470 | EXPORT_SYMBOL(tcp_proc_register); | 2470 | EXPORT_SYMBOL(tcp_proc_register); |
2471 | EXPORT_SYMBOL(tcp_proc_unregister); | 2471 | EXPORT_SYMBOL(tcp_proc_unregister); |
2472 | #endif | 2472 | #endif |
2473 | EXPORT_SYMBOL(sysctl_local_port_range); | ||
2474 | EXPORT_SYMBOL(sysctl_tcp_low_latency); | 2473 | EXPORT_SYMBOL(sysctl_tcp_low_latency); |
2475 | 2474 | ||
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index ef4d901ee9ad..cb9fc58efb2f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -147,11 +147,11 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum, | |||
147 | write_lock_bh(&udp_hash_lock); | 147 | write_lock_bh(&udp_hash_lock); |
148 | 148 | ||
149 | if (!snum) { | 149 | if (!snum) { |
150 | int i; | 150 | int i, low, high; |
151 | int low = sysctl_local_port_range[0]; | ||
152 | int high = sysctl_local_port_range[1]; | ||
153 | unsigned rover, best, best_size_so_far; | 151 | unsigned rover, best, best_size_so_far; |
154 | 152 | ||
153 | inet_get_local_port_range(&low, &high); | ||
154 | |||
155 | best_size_so_far = UINT_MAX; | 155 | best_size_so_far = UINT_MAX; |
156 | best = rover = net_random() % (high - low) + low; | 156 | best = rover = net_random() % (high - low) + low; |
157 | 157 | ||
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index ae6b0e7eb488..1c2c27655435 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c | |||
@@ -254,18 +254,18 @@ int inet6_hash_connect(struct inet_timewait_death_row *death_row, | |||
254 | int ret; | 254 | int ret; |
255 | 255 | ||
256 | if (snum == 0) { | 256 | if (snum == 0) { |
257 | const int low = sysctl_local_port_range[0]; | 257 | int i, port, low, high, remaining; |
258 | const int high = sysctl_local_port_range[1]; | ||
259 | const int range = high - low; | ||
260 | int i, port; | ||
261 | static u32 hint; | 258 | static u32 hint; |
262 | const u32 offset = hint + inet6_sk_port_offset(sk); | 259 | const u32 offset = hint + inet6_sk_port_offset(sk); |
263 | struct hlist_node *node; | 260 | struct hlist_node *node; |
264 | struct inet_timewait_sock *tw = NULL; | 261 | struct inet_timewait_sock *tw = NULL; |
265 | 262 | ||
263 | inet_get_local_port_range(&low, &high); | ||
264 | remaining = high - low; | ||
265 | |||
266 | local_bh_disable(); | 266 | local_bh_disable(); |
267 | for (i = 1; i <= range; i++) { | 267 | for (i = 1; i <= remaining; i++) { |
268 | port = low + (i + offset) % range; | 268 | port = low + (i + offset) % remaining; |
269 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; | 269 | head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)]; |
270 | spin_lock(&head->lock); | 270 | spin_lock(&head->lock); |
271 | 271 | ||
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 7cd58ef84eda..9c6a4b5f6264 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -5315,11 +5315,12 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) | |||
5315 | 5315 | ||
5316 | if (snum == 0) { | 5316 | if (snum == 0) { |
5317 | /* Search for an available port. */ | 5317 | /* Search for an available port. */ |
5318 | unsigned int low = sysctl_local_port_range[0]; | 5318 | int low, high, remaining, index; |
5319 | unsigned int high = sysctl_local_port_range[1]; | 5319 | unsigned int rover; |
5320 | unsigned int remaining = (high - low) + 1; | 5320 | |
5321 | unsigned int rover = net_random() % remaining + low; | 5321 | inet_get_local_port_range(&low, &high); |
5322 | int index; | 5322 | remaining = (high - low) + 1; |
5323 | rover = net_random() % remaining + low; | ||
5323 | 5324 | ||
5324 | do { | 5325 | do { |
5325 | rover++; | 5326 | rover++; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0753b20e23fe..3c3fff33d1ce 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -47,7 +47,7 @@ | |||
47 | #include <linux/netfilter_ipv6.h> | 47 | #include <linux/netfilter_ipv6.h> |
48 | #include <linux/tty.h> | 48 | #include <linux/tty.h> |
49 | #include <net/icmp.h> | 49 | #include <net/icmp.h> |
50 | #include <net/ip.h> /* for sysctl_local_port_range[] */ | 50 | #include <net/ip.h> /* for local_port_range[] */ |
51 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ | 51 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ |
52 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
53 | #include <asm/ioctls.h> | 53 | #include <asm/ioctls.h> |
@@ -3232,8 +3232,6 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
3232 | /* Range of port numbers used to automatically bind. | 3232 | /* Range of port numbers used to automatically bind. |
3233 | Need to determine whether we should perform a name_bind | 3233 | Need to determine whether we should perform a name_bind |
3234 | permission check between the socket and the port number. */ | 3234 | permission check between the socket and the port number. */ |
3235 | #define ip_local_port_range_0 sysctl_local_port_range[0] | ||
3236 | #define ip_local_port_range_1 sysctl_local_port_range[1] | ||
3237 | 3235 | ||
3238 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 3236 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
3239 | { | 3237 | { |
@@ -3276,20 +3274,27 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3276 | addrp = (char *)&addr6->sin6_addr.s6_addr; | 3274 | addrp = (char *)&addr6->sin6_addr.s6_addr; |
3277 | } | 3275 | } |
3278 | 3276 | ||
3279 | if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) || | 3277 | if (snum) { |
3280 | snum > ip_local_port_range_1)) { | 3278 | int low, high; |
3281 | err = security_port_sid(sk->sk_family, sk->sk_type, | 3279 | |
3282 | sk->sk_protocol, snum, &sid); | 3280 | inet_get_local_port_range(&low, &high); |
3283 | if (err) | 3281 | |
3284 | goto out; | 3282 | if (snum < max(PROT_SOCK, low) || snum > high) { |
3285 | AVC_AUDIT_DATA_INIT(&ad,NET); | 3283 | err = security_port_sid(sk->sk_family, |
3286 | ad.u.net.sport = htons(snum); | 3284 | sk->sk_type, |
3287 | ad.u.net.family = family; | 3285 | sk->sk_protocol, snum, |
3288 | err = avc_has_perm(isec->sid, sid, | 3286 | &sid); |
3289 | isec->sclass, | 3287 | if (err) |
3290 | SOCKET__NAME_BIND, &ad); | 3288 | goto out; |
3291 | if (err) | 3289 | AVC_AUDIT_DATA_INIT(&ad,NET); |
3292 | goto out; | 3290 | ad.u.net.sport = htons(snum); |
3291 | ad.u.net.family = family; | ||
3292 | err = avc_has_perm(isec->sid, sid, | ||
3293 | isec->sclass, | ||
3294 | SOCKET__NAME_BIND, &ad); | ||
3295 | if (err) | ||
3296 | goto out; | ||
3297 | } | ||
3293 | } | 3298 | } |
3294 | 3299 | ||
3295 | switch(isec->sclass) { | 3300 | switch(isec->sclass) { |