5. Adding support to third-party software

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:

5.1. Modifying source code

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.

5.2. libkeepalive: library preloading

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/