Not everyone is a software developer, and not everyone will rewrite software from scratch if it lacks just one feature. Maybe you want to add keepalive support to an existing application because, though the author might not have thought it important, you think it will be useful.
First, remember what was said about the situations where you need keepalive. Now you'll need to address connection-oriented TCP sockets.
Since Linux doesn't provide the functionality to enable keepalive support
via the kernel itself (as BSD-like operating systems often do), the only way
is to perform the setsockopt
(2) call
after socket creation. There are two solutions:
source code modification of the original program
setsockopt
(2) injection using
the library preloading technique
Remember that keepalive is not program-related, but socket-related, so if you have multiple sockets, you can handle keepalive for each of them separately. The first phase is to understand what the program does and then search the code for each socket in the program. This can be done using grep(1), as follows:
#
grep 'socket *(' *.c
This will more or less show you all sockets in the code. The next step is
to select only the right ones: you will need TCP sockets, so look for
PF_INET
(or AF_INET
),
SOCK_STREAM
and IPPROTO_TCP
(or more
commonly, 0
) in the parameters of your socket list,
and remove the non-matching ones.
Another way to create a socket is through
accept
(2). In this case, follow the TCP sockets identified and check
if any of these is a listening socket: if positive, keep in mind that
accept
(2) returns a socket descriptor, which
must be inserted in your socket list.
Once you've identified the sockets you can proceed with changes. The most
fast & furious patch can be done by simply adding the setsockopt
(2
) function just after the socket creation block.
Optionally, you may include additional calls in order to set the keepalive
parameters if you don't like the system defaults. Please be careful when
implementing error checks and handlers for the function, maybe by copying
the style from the original code around it. Remember to set the
optval
to a non-zero value and to initialize the optlen
before invoking the function.
If you have time or you think it would be really cool, try to add complete keepalive support to your program, including a switch on the command line or a configuration parameter to let the user choose whether or not to use keepalive.
There are often cases where you don't have the ability to modify the source code of an application, or when you have to enable keepalive for all your programs, so patching and recompiling everything is not recommended.
The libkeepalive project was born to help add keepalive support for applications since the Linux kernel doesn't provide the ability to do the same thing natively (like BSD does). The libkeepalive project homepage is http://libkeepalive.sourceforge.net/
It consists of a shared library that overrides the socket system call in
most binaries, without the need to recompile or modify them. The technique
is based on the preloading feature of the
ld.so(8) loader included in Linux, which
allows you to force the loading of shared libraries with higher priority
than normal. Programs usually use the
socket
(2) function call located in the glibc
shared library; with libkeepalive you can wrap
it and inject the setsockopt
(2) just
after the socket creation, returning a socket with keepalive already set
to the main program. Because of the mechanisms used to inject the system
call, this doesn't work when the socket function is statically compiled
into the binary, as in a program linked with the gcc(1
) flag -static
.
After downloading and installing libkeepalive,
you will able to add keepalive support to your programs without the
prerequisite of being root
, simply setting the
LD_PRELOAD
environment variable before executing the program. By
the way, the superuser can also force the preloading with a global
configuration, and the users can then decide to turn it off by setting the
KEEPALIVE
environment variable to off
.
The environment is also used to set specific values for keepalive
parameters, so you have the ability to handle each program differently,
setting KEEPCNT
, KEEPIDLE
and
KEEPINTVL
before starting the application.
Here's an example of libkeepalive usage:
$
test
SO_KEEPALIVE is OFF
$
LD_PRELOAD=libkeepalive.so \
>
KEEPCNT=20 \
>
KEEPIDLE=180 \
>
KEEPINTVL=60 \
>
test
SO_KEEPALIVE is ON TCP_KEEPCNT = 20 TCP_KEEPIDLE = 180 TCP_KEEPINTVL = 60
And you can use strace (1) to understand what happens:
$
strace test
execve("test", ["test"], [/* 26 vars */]) = 0 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [0], [4]) = 0 close(3) = 0 [..] _exit(0) = ?
$
LD_PRELOAD=libkeepalive.so \
>
strace test
execve("test", ["test"], [/* 27 vars */]) = 0 [..] open("/usr/local/lib/libkeepalive.so", O_RDONLY) = 3 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] open("/lib/libdl.so.2", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0 [..] getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], [4]) = 0 [..] close(3) = 0 [..] _exit(0) = ?
For more information, visit the libkeepalive project homepage: http://libkeepalive.sourceforge.net/