Hi, glibc 2.9 implement parallel IPv4/IPv6 DNS lookup. This caused lots of trouble in all kind of implementations, so all major Linux distributions removed _nss_dns_gethostbyname4_r in their glibc version, except for Debian Squeeze, see also "options single-request" for more information. Normally parallel DNS lookups works fine, first packet is received and forwarded, so conntrack is confirmed before second packet is received. However in combination with NFQUEUE, the second DNS requests is received while the first one is still in the queue and both DNS requests have an unconfirmed conntrack. So the second one will be dropped in nf_conntrack_confirm, which results in an DNS timeout and retransmit. Can be reproduced with: adnshost yahoo.com google.com My first idea was to re-lookup the conntrack in nf_conntrack_confirm, but at that time the seconds request was already NATed. So I moved that code to nf_nat_fn(). Of course this only works if nat is loaded... Any comments or ideas, how to address this problem? Cheers Ulrich -- Ulrich Weber | uweber@xxxxxxxxxx | Software Engineer Astaro GmbH & Co. KG | www.astaro.com | Phone +49-721-25516-0 | Fax –200 An der RaumFabrik 33a | 76227 Karlsruhe | Germany
>From 2f23d860cd34a006cf4a118536340d8cfcc0d8fa Mon Sep 17 00:00:00 2001 From: Ulrich Weber <uweber@xxxxxxxxxx> Date: Tue, 9 Nov 2010 16:03:08 +0100 Subject: [PATCH] nat: re-lookup unconfirmed UDP conntracks glibc 2.9 implemented DNS IPv4-IPv6 parallel lookup, where two DNS requests will be send at once with the same tuple information. Due nfqueue the second request will have another conntrack because the first is not confirmed yet and the second request will be dropped by conntrack_confirm afterwards. To avoid an DNS timeout, re-lookup the conntrack in nf_nat_fn and attach the real one... Signed-off-by: Ulrich Weber <uweber@xxxxxxxxxx> --- net/ipv4/netfilter/nf_nat_standalone.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-) diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 5f41d01..06ef2c5 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -124,6 +124,17 @@ nf_nat_fn(unsigned int hooknum, } /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ case IP_CT_NEW: + /* Nasty asynchronous DNS hack: Avoid NAT and conntrack_confirm race */ + if (!nf_ct_is_confirmed(ct) && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + struct nf_conntrack_tuple_hash *h = nf_conntrack_find_get(nf_ct_net(ct), + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if (h) { + ct = nf_ct_tuplehash_to_ctrack(h); + nf_conntrack_put(skb->nfct); + skb->nfct = &ct->ct_general; + } + } /* Seen it before? This can happen for loopback, retrans, or local packets.. */ -- 1.7.1