Skip to main content

ORA-12170: TNS:Connect timeout — resolved

If you're dealing with Oracle clients, you may be familiar with the error message
ERROR ORA-12170: TNS:Connect timed out occurred
I was recently asked to investigate such a problem where an application server was having trouble talking to a database server. This issue was blocking progress on a number of projects in our development environment, and our developers' agile post-it note progress note board had a red post-it saying 'Waiting for Cameron', so I thought I should promote it to the front of my rather long list of things I needed to do... it probably also helped that the problem domain was rather interesting to me, and so it ended being a late-night productivity session where I wasn't interrupted and my experimentation wouldn't disrupt others. I think my colleagues are still getting used to seeing email from me at the wee hours of the morning.

This can masquerade as a number of other error strings as well. Here's what you might see in the sqlnet.log file, which you can find in the directory instantclient_home/log/diag/clients/user_USER/host_client_ip_and_port/trace/sqlnet.log
Fatal NI connect error 12170.

Some Googling turned up a lot of hits, but none that definitively resolved the problem at hand. The key takeaway from that activity was that it was due to network issues preventing a TCP connection being made to the database listener.

So, how to go about diagnosting this issue? First, let's see what we can learn about this scenario from the reports I received and what we know about interconnectivity in this situation. The first piece of information is that this works fine for developers when connecting from their desktops, which are naturally in a different subnet from the database servers. Secondly, it was only impacting this particular application server, which was a different technology stack and thus a significant difference. Third, it also happened to be in the same subnet as the database server.

Connecting at a TCP level (with nc -z database-server-ip database-server-port || echo FAIL) manually, even multiple times very quickly, showed no problems. Perhaps it was due to the technology stack being used. I don't have enough Oracle nouse to use sqlplus to simulate the connection, which would have been useful in exempting or isolating the fault to the technology stack, but it seems that this problem is exhibited at the TCP layer, and is known to be transient, so I can't trust a manual test. It was only the scheduled tasks that were being impacted. Thus it can be considered to be an intermittent issue (the type that, when solved, gives the greatest amount of cerebral pleasure when solved... hence my investigation continuing to 3am).

Right, time to bring out the "big" guns: tcpdump on the application server to capture traffic known to exhibit the error (as indicated by logs in this case), followed by analysis on my workstation using Wireshark. Remember, Wireshark doesn't belong on servers. Before capturing, let's first consider how and what we want to capture.

The scheduled job that was most affected kicked off every ten minutes, and from the logs, it seems to fail to connect more often than not (about 2/3 probability), so I decided to run the capture for 20 minutes. Its a good thing to run tcpdump with a time or space limit, as distractions are common and it can be easy to forget about running capture sessions, and you don't want to create an issues for yourself or others later. Run the capture on the application server (at least), as its from there that the trouble is being experienced. I like to avoid running in promiscuous mode if not required, hence the -p option.

If your tcpdump supports it, you can use -G seconds -W rotations to put a time limit in the amount of time captured (note: not the amount of time the capture is running). This is a useful pattern to use when diagnosing intermittent issues. In this case, I would be capturing up to two files, each one covering a period to 20 minutes. If you wanted a space-limit, you could use -C instead of -G. Note that I'm not putting a capture filter in place (although if not capturing to a file not port ssh is very useful) because I'm running in non-promiscuous mode anyway, and don't want to miss anything that may help to find the root cause. Because I'm running with a time-based rotation, the filename argument needs to contain a formatting argument; in this case I'm asking for ISO8601 format in a form such as 2014-04-26T20:43:34.

tcpdump -p -i eth0 -G 1200 -W 2 -w /home/me/tnsconnect-%FT%T.pcap

Tailing the sqlnet.log file, I waited till I saw the error, and stopped tcpdump. I then copied the file to my desktop to analyse using Wireshark.

It's tempting to simply start scrolling to find the packets of interest, or apply a display filter if you think you know what to look for. However, Wireshark has something else of use, its Expert Info (menu Analyze > Expert Info). This shows various alerts that its various base protocol analysers pick up. Looking through this can be useful to give you a first glance. In this case, it did show up some ARP irregularities, namely that an address was claimed by multiple MAC addresses. Considering that one of the addresses was the database server, that was a nice red flag. It certainly helps explain the transient nature of the problem, any why it wasn't affecting connections through the router (it could have done, but routers tend to have a much longer ARP cache lifetime, so it kept the good result for longer). Time to look closer, just to see what was actually happening.

Noting the time shown in the sqlnet.log file, I set Wireshark's time display format to show the time of day, and quickly found the traffic around the time of the connection. I saw it attempting to connect (SYN) but the SYN+ACK never eventuated. Looking more closely, I saw that it was attempting to connect to a different MAC address than the one I expected. Looking a little earlier, to the ARP communication, I saw that the application servers ARP request was being simultaneously answered by two inferfaces (this was mentioned in the Expert Info), and looking at what owned those MAC addresses, they were two interfaces on the same database server.

As would be expected, this database server has a management interface, and another interface over which application (database) traffic would go through... nothing out of the ordinary there. Each interface looked to be configured appropriately from the OS point of view, and was in a different interface.

To get a response from each interface, each interface must be getting the request... that would mean that those interfaces must be on the same layer-2 segment if the network... the same VLAN.

Hypothesis: management interface is on the wrong VLAN. That would make since due to the particular history of that machine.

Hmmm, how to verify what VLAN a port is on? I don't have visibility of that, and as its the middle of the night, no networking staff to ask. but I do have tcpdump and the knowledge that I would expect to see Cisco Discovery Protocol (CDP) frames. So, let's fire up tcpdump with (-v), which is sufficient to parse CDP frames, and wait till we see one. A little googling shows how to capture CDP frames:

tcpdump -nn -v -i eth0 -s 1500 -c 1 'ether[20:2] == 0x2000'

In the output, I was able to see the VLAN that the port was assigned to, and indeed it was incorrect (it was the same as the other interface). I don't expect that this would work for all such discoveries, but its useful... and it also told me which switch port needed to be reconfigured.

So I wrote a change request to the networking team, but naturally that wouldn't get actioned until the daylight kicks in, at the earliest. How can I simulate the fix? Two methods come to mind: the first, and simplest, would be to shut down the offending interfaces but let's say that would cause some other issue, or the conflicting interface is on a machine you don't have control over... then in that case, you can temporarily bypass ARP for connections from the application server to the database server, by putting in a static ARP entry on the application server (see the arp command).

That done I was able to continue tailing the logs and found that the problem ceased to occur, and stayed fixed until at least the morning, when the VLAN assignment could be changed.

Followup activities

One of my favourite things about solving problems like this is asking myself "how can I look for other instances of where this may be causing a problem for me?". In this case, I think it would be useful to deploy arpwatch strategically, looking for things like 'flip flops' in various VLANs, particularly in known troublespots. This is particularly useful if you run arpwatch without email reports, and centralise syslog logging to a remote server, where you could then do some analysis. ARP issues are a common way that devices transiently "fall off the network" or fail to connect.


  1. Thanks. We are having similar problems and will try this tomorrow.

  2. Thanks, i am struggling on the same problem.


Post a Comment

Popular posts from this blog

Use IPTables NOTRACK to implement stateless rules and reduce packet loss.

I recently struck a performance problem with a high-volume Linux DNS server and found a very satisfying way to overcome it. This post is not about DNS specifically, but useful also to services with a high rate of connections/sessions (UDP or TCP), but it is especially useful for UDP-based traffic, as the stateful firewall doesn't really buy you much with UDP. It is also applicable to services such as HTTP/HTTPS or anything where you have a lot of connections...

We observed times when DNS would not respond, but retrying very soon after would generally work. For TCP, you may find that you get a a connection timeout (or possibly a connection reset? I haven't checked that recently).

Observing logs, you might the following in kernel logs:
kernel: nf_conntrack: table full, dropping packet. You might be inclined to increase net.netfilter.nf_conntrack_max and net.nf_conntrack_max, but a better response might be found by looking at what is actually taking up those entries in your conne…

Getting MySQL server to run with SSL

I needed to get an old version of MySQL server running with SSL. Thankfully, that support has been there for a long time, although on my previous try I found it rather frustrating and gave it over for some other job that needed doing.

If securing client connections to a database server is a non-negotiable requirement, I would suggest that MySQL is perhaps a poor-fit and other options, such as PostgreSQL -- according to common web-consensus and my interactions with developers would suggest -- should be first considered. While MySQL can do SSL connections, it does so in a rather poor way that leaves much to be desired.

UPDATED 2014-04-28 for MySQL 5.0 (on ancient Debian Etch).

Here is the fast guide to getting SSL on MySQL server. I'm doing this on a Debian 7 ("Wheezy") server. To complete things, I'll test connectivity from a 5.1 client as well as a reasonably up-to-date MySQL Workbench 5.2 CE, plus a Python 2.6 client; just to see what sort of pain awaits.

UPDATE: 2014-0…