Messages in this thread Patch in this message | | | Date | Mon, 15 Sep 1997 09:41:42 -0400 | From | Philip Gladstone <> | Subject | Re: ipv4 bind()ing bug |
| |
David S. Miller wrote: > > From: alan@lxorguk.ukuu.org.uk (Alan Cox) > Date: Sat, 13 Sep 1997 15:45:26 +0100 (BST) > > > zero. Normal un*x behavior is to assign a "random" port number > > between IPPORT_RESERVED (1024) and IPPORT_USERRESERVED (5000). > > Linux, however, assigns a port number between 1024 and 32767. > > This can be a > > Solaris does something similar as do SGI nowdays - its a big win to > do so, and 4000 user ports isnt enough. > > I think it's time somebody added the sysctls for these values.
Ok, Attached is a patch that allows the user to choose the range of local ports. /proc/sys/net/ipv4/local_port_range contains two integers -- the first local port and the last. These default to 1024 and 4999 (backwards compatibility). FOr good effects, you can try 32768 and 60000 -- though you should be careful if you use the masquerading stuff as it allocates ports at the high end (though above 60000 as I recall).
I've been running this for several months with no problems. This can be applied to 31-pre9 or thereabouts. Much before that you will find problems owing to the fact that the port numbers over 32768 were considered negative by some bits of the kernel.
Enjoy,
Philip
-- Philip Gladstone +1 617 487 7700 Raptor Systems, Waltham, MA http://www.raptor.com/ diff -u -r /export/home/users/philip/build/domestic/linux/intel/linux/include/linux/sysctl.h pre7/include/linux/sysctl.h --- /export/home/users/philip/build/domestic/linux/intel/linux/include/linux/sysctl.h Thu Aug 28 13:17:22 1997 +++ pre7/include/linux/sysctl.h Wed Aug 20 15:38:55 1997 @@ -100,6 +100,7 @@ #define NET_IPV4_ARP_CONFIRM_TIMEOUT 7 #define NET_IPV4_FORWARD 8 #define NET_IPV4_DYNADDR 9 +#define NET_IPV4_LOCAL_PORT_RANGE 10 /* /proc/sys/net/ipx */ diff -u -r /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/sysctl_net_ipv4.c pre7/net/ipv4/sysctl_net_ipv4.c --- /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/sysctl_net_ipv4.c Thu Aug 28 13:17:24 1997 +++ pre7/net/ipv4/sysctl_net_ipv4.c Wed Aug 20 15:38:55 1997 @@ -17,6 +17,7 @@ extern int sysctl_arp_check_interval; extern int sysctl_arp_confirm_interval; extern int sysctl_arp_confirm_timeout; +extern int sysctl_local_port_range[2]; extern int sysctl_ip_forward; extern int sysctl_ip_dynaddr; @@ -61,5 +62,8 @@ 0644, NULL, &proc_doipforward }, {NET_IPV4_DYNADDR, "ip_dynaddr", &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_IPV4_LOCAL_PORT_RANGE, "local_port_range", + &sysctl_local_port_range, sizeof(sysctl_local_port_range), 0644, NULL, + &proc_dointvec}, {0} }; diff -u -r /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/tcp.c pre7/net/ipv4/tcp.c --- /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/tcp.c Thu Aug 28 13:17:24 1997 +++ pre7/net/ipv4/tcp.c Wed Aug 20 15:41:47 1997 @@ -458,6 +458,14 @@ */ struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE]; +/* + * This array holds the first and last local port number. + * For high-usage systems, use sysctl to change this to + * 32768-61000 + */ + +int sysctl_local_port_range[2] = { 1024, 4999 }; + extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport); static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) @@ -522,13 +530,17 @@ */ unsigned short tcp_good_socknum(void) { - static int start = PROT_SOCK; + static int start = 0; static int binding_contour = 0; int best = 0; int size = 32767; /* a big num. */ int retval = 0, i, end, bc; SOCKHASH_LOCK(); + + if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0]) + start = sysctl_local_port_range[0]; + i = tcp_bhashfn(start); end = i + TCP_BHTABLE_SIZE; bc = binding_contour; @@ -570,8 +582,8 @@ best = retval; /* mark the starting point to avoid infinite loops */ while(tcp_lport_inuse(retval)) { retval = tcp_bhashnext(retval,i); - if (retval > 32767) /* Upper bound */ - retval = tcp_bhashnext(PROT_SOCK,i); + if (retval > sysctl_local_port_range[1]) /* Upper bound */ + retval = tcp_bhashnext(sysctl_local_port_range[0],i); if (retval == best) { /* This hash chain is full. No answer. */ retval = 0; @@ -581,8 +593,6 @@ done: start = (retval + 1); - if (start > 32767 || start < PROT_SOCK) - start = PROT_SOCK; SOCKHASH_UNLOCK(); return retval; diff -u -r /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/udp.c pre7/net/ipv4/udp.c --- /export/home/users/philip/build/domestic/linux/intel/linux/net/ipv4/udp.c Thu Aug 28 13:17:24 1997 +++ pre7/net/ipv4/udp.c Wed Aug 20 15:42:13 1997 @@ -170,22 +170,28 @@ int result; static int start = 0; int i, best, best_size_so_far; + extern int sysctl_local_port_range[]; SOCKHASH_LOCK(); - /* Select initial not-so-random "best" */ - best = PROT_SOCK + 1 + (start & 1023); + if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0]) + start = sysctl_local_port_range[0]; + best_size_so_far = 32767; /* "big" num */ - result = best; - for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { + best = result = start; + + for(i = 0; i < UDP_HTABLE_SIZE; i++, result++) { struct sock *sk; int size; sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; - /* No clashes - take it */ - if (!sk) + if(!sk) { + if (result > sysctl_local_port_range[1]) + result = sysctl_local_port_range[0] + + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); goto out; + } /* Is this one better than our best so far? */ size = 0; @@ -195,12 +201,19 @@ } while((sk = sk->next) != NULL); best_size_so_far = size; best = result; -next: + next: } - while (udp_lport_inuse(best)) - best += UDP_HTABLE_SIZE; result = best; + + for(;; result += UDP_HTABLE_SIZE) { + /* Get into range (but preserve hash bin)... */ + if (result > sysctl_local_port_range[1]) + result = sysctl_local_port_range[0] + + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); + if (!udp_lport_inuse(result)) + break; + } out: start = result; SOCKHASH_UNLOCK(); | |