diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-10-10 20:30:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-10-10 20:30:46 -0400 |
commit | 227b60f5102cda4e4ab792b526a59c8cb20cd9f8 (patch) | |
tree | 2c9e372601ba794894833b0618bc531a9f5d57c4 /drivers | |
parent | 06393009000779b00a558fd2f280882cc7dc2008 (diff) |
[INET]: local port range robustness
Expansion of original idea from Denis V. Lunev <den@openvz.org>
Add robustness and locking to the local_port_range sysctl.
1. Enforce that low < high when setting.
2. Use seqlock to ensure atomic update.
The locking might seem like overkill, but there are
cases where sysadmin might want to change value in the
middle of a DoS attack.
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/infiniband/core/cma.c | 22 |
1 files changed, 12 insertions, 10 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; |