diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index e37265db036a4ff439a2a1287322928a8da8b42c..98de9e0fb856b39c0bbf9e63e3c33387d1dc4565 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -698,6 +698,11 @@ ip_local_reserved_ports - list of comma separated ranges Default: Empty +reserved_port_bind - BOOLEAN + If set, allows explicit bind requests to applications requesting + any port within the range of ip_local_reserved_ports. + Default: 1 + ip_nonlocal_bind - BOOLEAN If set, allows processes to bind() to non-local IP addresses, which can be quite useful - but may break some applications. diff --git a/include/net/ip.h b/include/net/ip.h index 4afc28c1ae8a96e1c4d60deb974b36bf5d399f35..e7dcf99dedd3a83fa2c16a36b5470986ec73f5a3 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -209,6 +209,7 @@ static inline int inet_is_reserved_local_port(int port) return test_bit(port, sysctl_local_reserved_ports); } +extern int sysctl_reserved_port_bind; extern int sysctl_ip_nonlocal_bind; /* From inetpeer.c */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index b507a47acfb3427f531e8fe3216b84923effad25..f5b558606c54d3a49c0ffda5640db4f6f909d4cd 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -133,6 +133,8 @@ static inline int current_has_network(void) } #endif +int sysctl_reserved_port_bind __read_mostly = 1; + /* The inetsw table contains everything that inet_create needs to * build a new socket. */ diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 6dfec2f1821457df65d9f17cdba6748d063e8ae2..64198a381ddcadaa7793fdee03b42a47d2cdb0d6 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -181,6 +181,13 @@ have_snum: head = &hashinfo->bhash[inet_bhashfn(net, snum, hashinfo->bhash_size)]; spin_lock(&head->lock); + + if (inet_is_reserved_local_port(snum) && + !sysctl_reserved_port_bind) { + ret = 1; + goto fail_unlock; + } + inet_bind_bucket_for_each(tb, &head->chain) if (net_eq(ib_net(tb), net) && tb->port == snum) goto tb_found; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 2b31c2d6f68d134fe8e7cede1d223dc2cf0ae1be..aa54c1766bd03823b018a7c72d39b11f5b1e0d47 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -506,6 +506,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_do_large_bitmap, }, + { + .procname = "reserved_port_bind", + .data = &sysctl_reserved_port_bind, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "igmp_max_memberships", .data = &sysctl_igmp_max_memberships, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 1c9d2685de3f3a940a0432f8ebc760f6ff326c02..aac595f8d266267764622e743b2494738231a76a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -106,6 +106,7 @@ #include <net/route.h> #include <net/checksum.h> #include <net/xfrm.h> +#include <net/ip.h> #include <trace/events/udp.h> #include <linux/static_key.h> #include <trace/events/skb.h> @@ -254,6 +255,11 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, } else { hslot = udp_hashslot(udptable, net, snum); spin_lock_bh(&hslot->lock); + + if (inet_is_reserved_local_port(snum) && + !sysctl_reserved_port_bind) + goto fail_unlock; + if (hslot->count > 10) { int exist; unsigned int slot2 = udp_sk(sk)->udp_portaddr_hash ^ snum;