D-Link Router SSH/Telnet Timeout Fix (2004)

I recently installed a DI-604 D-Link local ethernet router (model V1456VQE) at home. It mostly works great, except for one thing. It appears to have a TCP timeout after about 15 minutes of inactivity, and as far as I can tell there is no way to change this. This is fine for web browsing and other remote access, but it is extremely annoying for ssh or telnet connections when you may be inactive for a while, but still expect the connection to be up when you return -- instead, it is dead and must be re-started.

Thanks to Emile LeBlanc and Alan J Rosenthal, I now have the following solution to this problem. In my case, I am connecting from my home linux machine, to a work linux machine (running tcsh), by ssh. So in my .login file (in my home directory on the target machine at work) I include the lines:

if ($?term) then
  if ($?SSH_CLIENT) then
    unset autologout
    sendnulls &
  endif
endif
This calls the script "sendnulls" in the background, whenever I login using ssh.

And in my .logout file (again, in my home directory on the target machine at work) I include the lines:

if ($?SSH_CLIENT) then
  kill %sendnulls
endif
This kills the "sendnulls" script when I logout.

Here "sendnulls" is the following sh shell script (again, on the target machine at work):

:
set -e
while true
do
    dd if=/dev/zero count=1 bs=1 2>/dev/null
    sleep 300
done

The effect of this is that once every five minutes, the target machine uses the "dd" command to send a zero-value byte through the ssh connection's stdout, to my home computer. Since it is a zero-value byte, and since the stderr diagnostic output is redirected to /dev/null, this has no observable consequence. However, it is still enough to convince my router that the ssh connection is not idle, so the router does not disconnect it.

Phew!

-- Jeffrey Rosenthal (contact me)

Update: I later upgraded my D-Link Router firmware from 2.xx to 3.39 (by going to support.dlink.ca and selecting "DI" and "604" then "revision E" ...), and that appears to have solved the problem, i.e. I no longer need to run sendnulls (as above) to keep my connection open.



For bash users (and others), here is some follow-up information I received from Jeff Stern:

hi, jeffrey,

I wanted to thank you for putting up your page on the "D-Link Router
SSH/Telnet Timeout Fix"; it definitely did the trick for me.

[...]

I did convert your scripts a little for my uses. i use bash on redhat
linux (v's 8&9), and the syntax is a little different than sh. also i
guess this version (as configured/compiled by redhat) does not use
.login (or even .bash_login), though it apparently uses .bash_logout.

i also added a process id detection so that the kill script would kill
only the single PID with the particular sendnulls script started for
that session (since i might also theoretically be logged in from
behind another router, elsewhere, as well)..

also, i put some echo's in for feedback, which can of course be
commented back out for silent operation.

finally, i added another test so that ssh'ing in from a particular
location is what causes sendnulls to start (since i also ssh in from
other locations which *don't* have routers behind them..) the person
using these scripts will have to fill in with the current value of
their router where it says "[your-ip-here]"..

so, anyway, i offer these scripts to you, in case you want to post
them for fellow bash/rh-linux users..


These are my versions of your scripts, tailored to redhat linux
systems (v8 or 9) with bash as your shell. as with your original
scripts, these go in your directory on the server machine (the machine
you're ssh'ing *into*), not the machine behind the router you're
ssh'ing out of.  edit the ~/bin/detect_ssh_ip for your router's ip.

== somewhere in my .bashrc ========================================
# if incoming ssh connection is from home,
# send it nulls to keep it alive
if [ -f $HOME/bin/detect_ssh_ip ]; then
        . $HOME/bin/detect_ssh_ip
fi

== ~/bin/detect_ssh_ip ============================================
#!/bin/bash

# run script for keeping dlink di-604 router alive
if [[ -n $TERM ]]; then
#  echo "term is not null"
  if [[ -n $SSH_CLIENT ]]; then
#    echo "ssh_client is not null"
    SSH_IP=`echo $SSH_CLIENT | cut -f 1 -d ' '`
    # put your router's IP here (e.g. "128.200.16.20"). keep
    # the quotes.
    if [ "$SSH_IP" = "[your-ip-here]" ]; then
      sendnulls &
      export SENDNULLS_PID=$!
      echo "$SSH_IP: sendnulls started (pid $SENDNULLS_PID)"
    fi
  fi
fi

== ~/bin/sendnulls ================================================
#!/bin/bash

while true
do
  dd if=/dev/zero count=1 bs=1 2>/dev/null
  sleep 60
done

== ~/.bash_logout =================================================
# ~/.bash_logout

# kill script for keeping dlink di-604 router alive
if [[ -n $SENDNULLS_PID ]]; then
  # comment out next line if you don't feel like seeing the
  # feedback
  echo "killing sendnull (pid $SENDNULLS_PID)"
  kill -9 $SENDNULLS_PID
fi