Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 3052

kernel-2.6.18-238.el5.src.rpm

From: Jerome Marchand <jmarchan@redhat.com>
Date: Fri, 9 Jul 2010 13:24:56 -0400
Subject: [net] reserve ports for apps using fixed port numbers
Message-id: <4C372328.3070507@redhat.com>
Patchwork-id: 26803
O-Subject: [Patch RHEL5 2/4] net: reserve ports for applications using fixed
	port numbers
Bugzilla: 557884
RH-Acked-by: David S. Miller <davem@redhat.com>
RH-Acked-by: Amerigo Wang <amwang@redhat.com>

Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=557884

This patch introduces /proc/sys/net/ipv4/ip_local_reserved_ports which
allows users to reserve ports for third-party applications.

The reserved ports will not be used by automatic port assignments
(e.g. when calling connect() or bind() with port number 0). Explicit
port allocation behavior is unchanged.

Upstream status:
commit e3826f1e946e7d2354943232f1457be1455a29e2


diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 08ad56c..edef9a3 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -452,6 +452,37 @@ ip_local_port_range - 2 INTEGERS
 	(i.e. by default) range 1024-4999 is enough to issue up to
 	2000 connections per second to systems supporting timestamps.
 
+ip_local_reserved_ports - list of comma separated ranges
+	Specify the ports which are reserved for known third-party
+	applications. These ports will not be used by automatic port
+	assignments (e.g. when calling connect() or bind() with port
+	number 0). Explicit port allocation behavior is unchanged.
+
+	The format used for both input and output is a comma separated
+	list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and
+	10). Writing to the file will clear all previously reserved
+	ports and update the current list with the one given in the
+	input.
+
+	Note that ip_local_port_range and ip_local_reserved_ports
+	settings are independent and both are considered by the kernel
+	when determining which ports are available for automatic port
+	assignments.
+
+	You can reserve ports which are not in the current
+	ip_local_port_range, e.g.:
+
+	$ cat /proc/sys/net/ipv4/ip_local_port_range
+	32000	61000
+	$ cat /proc/sys/net/ipv4/ip_local_reserved_ports
+	8080,9148
+
+	although this is redundant. However such a setting is useful
+	if later the port range is changed to a value that will
+	include the reserved ports.
+
+	Default: Empty
+
 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/linux/sysctl.h b/include/linux/sysctl.h
index ec89739..88e94c0 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -451,6 +451,7 @@ enum
 	NET_UDP_MEM=122,
 	NET_UDP_RMEM_MIN=123,
 	NET_UDP_WMEM_MIN=124,
+	NET_IPV4_LOCAL_RESERVED_PORTS=125,
 };
 
 enum {
diff --git a/include/net/ip.h b/include/net/ip.h
index 9746fb4..f8c889c 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -169,6 +169,13 @@ DECLARE_SNMP_STAT(struct linux_mib, net_statistics);
 
 extern void inet_get_local_port_range(int *low, int *high);
 extern int sysctl_local_port_range[2];
+
+extern unsigned long *sysctl_local_reserved_ports;
+static inline int inet_is_reserved_local_port(int port)
+{
+	return test_bit(port, sysctl_local_reserved_ports);
+}
+
 extern int sysctl_ip_default_ttl;
 extern int sysctl_ip_nonlocal_bind;
 
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f2df5ce..27801d3 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1380,9 +1380,13 @@ static int __init inet_init(void)
 		goto out;
 	}
 
+	sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
+	if (!sysctl_local_reserved_ports)
+		goto out;
+
 	rc = proto_register(&tcp_prot, 1);
 	if (rc)
-		goto out;
+		goto out_free_reserved_ports;
 
 	rc = proto_register(&udp_prot, 1);
 	if (rc)
@@ -1475,6 +1479,8 @@ out_unregister_tcp_proto:
 	proto_unregister(&tcp_prot);
 out_unregister_udp_proto:
 	proto_unregister(&udp_prot);
+out_free_reserved_ports:
+	kfree(sysctl_local_reserved_ports);
 	goto out;
 }
 
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 45eab43..482d061 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -37,6 +37,9 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg);
 int sysctl_local_port_range[2] = { 32768, 61000 };
 DEFINE_SEQLOCK(sysctl_port_range_lock);
 
+unsigned long *sysctl_local_reserved_ports;
+EXPORT_SYMBOL(sysctl_local_reserved_ports);
+
 void inet_get_local_port_range(int *low, int *high)
 {
 	unsigned seq;
@@ -99,6 +102,8 @@ int inet_csk_get_port(struct inet_hashinfo *hashinfo,
 		rover = net_random() % remaining + low;
 
 		do {
+			if (inet_is_reserved_local_port(rover))
+				goto next_nolock;
 			head = &hashinfo->bhash[inet_bhashfn(rover, hashinfo->bhash_size)];
 			spin_lock(&head->lock);
 			inet_bind_bucket_for_each(tb, node, &head->chain)
@@ -107,6 +112,7 @@ int inet_csk_get_port(struct inet_hashinfo *hashinfo,
 			break;
 		next:
 			spin_unlock(&head->lock);
+		next_nolock:
 			if (++rover > high)
 				rover = low;
 		} while (--remaining > 0);
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 7494861..cbb324b 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -262,6 +262,8 @@ int inet_hash_connect(struct inet_timewait_death_row *death_row,
  		local_bh_disable();
 		for (i = 1; i <= remaining; i++) {
 			port = low + (i + offset) % remaining;
+			if (inet_is_reserved_local_port(port))
+				continue;
  			head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
  			spin_lock(&head->lock);
 
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 8c93708..039f744 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -451,6 +451,14 @@ ctl_table ipv4_table[] = {
 		.strategy	= &ipv4_sysctl_local_port_range,
 	},
 	{
+		.ctl_name	= NET_IPV4_LOCAL_RESERVED_PORTS,
+		.procname	= "ip_local_reserved_ports",
+		.data		= NULL, /* initialized in sysctl_ipv4_init */
+		.maxlen		= 65536,
+		.mode		= 0644,
+		.proc_handler	= &proc_do_large_bitmap,
+	},
+	{
 		.ctl_name	= NET_IPV4_ICMP_ECHO_IGNORE_ALL,
 		.procname	= "icmp_echo_ignore_all",
 		.data		= &sysctl_icmp_echo_ignore_all,
@@ -842,3 +850,21 @@ ctl_table ipv4_table[] = {
 #endif /* CONFIG_SYSCTL */
 
 EXPORT_SYMBOL(ipv4_config);
+
+static __init int sysctl_ipv4_init(void)
+{
+	struct ctl_table *i;
+
+	for (i = ipv4_table; i->procname; i++) {
+		if (strcmp(i->procname, "ip_local_reserved_ports") == 0) {
+			i->data = sysctl_local_reserved_ports;
+			break;
+		}
+	}
+	if (!i->procname)
+		return -EINVAL;
+
+	return 0;
+}
+
+__initcall(sysctl_ipv4_init);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index d9f6108..b58f579 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -148,7 +148,8 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
 		best_size_so_far = UINT_MAX;
 		best = rover = net_random() % remaining + low;
 
-		if (!udp_lport_inuse(rover))
+		if (!udp_lport_inuse(rover) &&
+		    !inet_is_reserved_local_port(rover))
 			goto gotit;
 
 		/* 1st pass: look for empty (or shortest) hash chain */
@@ -157,7 +158,8 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
 			int size = 0;
 
 			list = &udp_hash[rover & (UDP_HTABLE_SIZE - 1)];
-			if (hlist_empty(list))
+			if (hlist_empty(list) &&
+			    !inet_is_reserved_local_port(rover))
 				goto gotit;
 
 			sk_for_each(sk2, node, list)
@@ -174,7 +176,8 @@ static int udp_v4_get_port(struct sock *sk, unsigned short snum)
 		/* 2nd pass: find hole in shortest hash chain */
 		rover = best;
 		for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
-			if (!udp_lport_inuse(rover))
+			if (!udp_lport_inuse(rover) &&
+			    !inet_is_reserved_local_port(rover))
 				goto gotit;
 			rover += UDP_HTABLE_SIZE;
 			if (rover > high)
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 32d4750..aa3c527 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4689,6 +4689,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 			rover++;
 			if ((rover < low) || (rover > high))
 				rover = low;
+			if (inet_is_reserved_local_port(rover))
+				continue;
 			index = sctp_phashfn(rover);
 			head = &sctp_port_hashtable[index];
 			sctp_spin_lock(&head->lock);