From: Jaap Verhoeven (jaap_at_hinttech.nl)
Date: Thu 19 Feb 2004 - 00:06:36 GMT
Hi,
Our setup: vanilla kernel 2.4.24 with vserver 1.26. On it is
a vserver with two IP-adresses assigned to it:
IPROOT="$a $b"
These adresses are on the same interface. In this vserver, we
have the djbdns tool set running. (among others) Two processes
get started via the djbdns svscan daemon. Their (happenstance)
startup sequence:
- first, dnscache starts and binds (listens) to TCP address $b port 54
- next, axfrdns starts and binds (listens) to TCP address $a port 54
In this setup, axfrdns fails with error EADDRINUSE. Which it isn't:
a 'netstat -ln' shows that IP $a port 53 is still available.
Manually switching the startup sequence (first axfrdns and
next dnscache) shows different behaviour: both processes bind and
function properly.
This led us to change the IPROOT setting to:
IPROOT="$b $a"
The result was that both processes bound correctly from the svscan
daemon during normal startup.
Looking into the relevant code, which is a bit cryptic for me (no
offense!), the following bit from the vserver patch to net/ipv4/tcp_ipv4.c
may be important:
+/*
+ Return 1 if addr match the socket IP list
+ or the socket is INADDR_ANY
+*/
+static inline int tcp_in_list (struct sock *sk, u32 addr)
+{
+ struct iproot_info *ip_info = sk->ip_info;
+
+ if (ip_info) {
+ int n = ip_info->nbipv4;
+ int i;
+
+ for (i=0; i<n; i++)
+ if (ip_info->ipv4[i] == addr)
+ return 1;
+ }
+ else if (!sk->rcv_saddr || sk->rcv_saddr == addr)
+ return 1;
+ return 0;
+}
+
+/*
+ Check if the addresses in sk1 conflict with those in sk2
+*/
+int tcp_ipv4_addr_conflict (struct sock *sk1, struct sock *sk2)
+{
+ if (sk1->rcv_saddr) {
+ /* Bind to one address only */
+ return tcp_in_list (sk2,sk1->rcv_saddr);
+ } else if (sk1->ip_info) {
+ /* A restricted bind(any) */
+ struct iproot_info *ip_info = sk1->ip_info;
+ int n = ip_info->nbipv4;
+ int i;
+
+ for (i=0; i<n; i++)
+ if (tcp_in_list (sk2,ip_info->ipv4[i]))
+ return 1;
+ } else /* A bind(any) do not allow other bind on the same port */
+ return 1;
+ return 0;
+}
+
The way I read this (and the surrounding code)
In tcp_v4_get_port() the requested port to bind gets checked against the
already used sockets. This check is only on the port number, so if we request
a IP $a port 53 and process #1 is already bound to IP $b port 53, this will
lead to a match. This match triggers (via tcp_bind_conflict() ) to a call to
tcp_ipv4_addr_conflict() above. Which returns a false positive (which in turn
leads to the EADDRINUSE error).
I cannot exactly pinpoint where the ordering problem lies (needs more time),
but I hope that this (half) analysis is good enough to pinpoint the exact
cause by others...
BTW: it looks to me that the same code is in version 1.3.6
-j
_______________________________________________
Vserver mailing list
Vserver_at_list.linux-vserver.org
http://list.linux-vserver.org/mailman/listinfo/vserver