706 lines
37 KiB
Plaintext
706 lines
37 KiB
Plaintext
TCP Keepalive HOWTO
|
||
|
||
Fabio Busatto
|
||
|
||
<fabio.busatto@sikurezza.org>
|
||
|
||
2007-05-04
|
||
Revision History
|
||
Revision 1.0 2007-05-04 Revised by: FB
|
||
First release, reviewed by TM.
|
||
|
||
|
||
This document describes the TCP keepalive implementation in the linux
|
||
kernel, introduces the overall concept and points to both system
|
||
configuration and software development.
|
||
|
||
-----------------------------------------------------------------------------
|
||
Table of Contents
|
||
1. Introduction
|
||
1.1. Copyright and License
|
||
1.2. Disclaimer
|
||
1.3. Credits / Contributors
|
||
1.4. Feedback
|
||
1.5. Translations
|
||
|
||
|
||
2. TCP keepalive overview
|
||
2.1. What is TCP keepalive?
|
||
2.2. Why use TCP keepalive?
|
||
2.3. Checking for dead peers
|
||
2.4. Preventing disconnection due to network inactivity
|
||
|
||
|
||
3. Using TCP keepalive under Linux
|
||
3.1. Configuring the kernel
|
||
3.2. Making changes persistent to reboot
|
||
|
||
|
||
4. Programming applications
|
||
4.1. When your code needs keepalive support
|
||
4.2. The setsockopt function call
|
||
4.3. Code examples
|
||
|
||
|
||
5. Adding support to third-party software
|
||
5.1. Modifying source code
|
||
5.2. libkeepalive: library preloading
|
||
|
||
|
||
|
||
1. Introduction
|
||
|
||
Understanding TCP keepalive is not necessary in most cases, but it's a
|
||
subject that can be very useful under particular circumstances. You will need
|
||
to know basic TCP/IP networking concepts, and the C programming language to
|
||
understand all sections of this document.
|
||
|
||
The main purpose of this HOWTO is to describe TCP keepalive in detail and
|
||
demonstrate various application situations. After some initial theory, the
|
||
discussion focuses on the Linux implementation of TCP keepalive routines in
|
||
the modern Linux kernel releases (2.4.x, 2.6.x), and how system
|
||
administrators can take advantage of these routines, with specific
|
||
configuration examples and tricks.
|
||
|
||
The second part of the HOWTO involves the programming interface exposed by
|
||
the Linux kernel, and how to write TCP keepalive-enabled applications in the
|
||
C language. Pratical examples are presented, and there is an introduction to
|
||
the libkeepalive project, which permits legacy applications to benefit from
|
||
keepalive with no code modification.
|
||
-----------------------------------------------------------------------------
|
||
|
||
1.1. Copyright and License
|
||
|
||
This document, TCP Keepalive HOWTO, is copyrighted (c) 2007 by Fabio
|
||
Busatto. Permission is granted to copy, distribute and/or modify this
|
||
document under the terms of the GNU Free Documentation License, Version 1.1
|
||
or any later version published by the Free Software Foundation; with no
|
||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
|
||
A copy of the license is available at [http://www.gnu.org/copyleft/fdl.html]
|
||
http://www.gnu.org/copyleft/fdl.html.
|
||
|
||
Source code included in this document is released under the terms of the
|
||
GNU General Public License, Version 2 or any later version published by the
|
||
Free Software Foundation. A copy of the license is available at [http://
|
||
www.gnu.org/copyleft/gpl.html] http://www.gnu.org/copyleft/gpl.html.
|
||
|
||
Linux is a registered trademark of Linus Torvalds.
|
||
-----------------------------------------------------------------------------
|
||
|
||
1.2. Disclaimer
|
||
|
||
No liability for the contents of this document can be accepted. Use the
|
||
concepts, examples and information at your own risk. There may be errors and
|
||
inaccuracies that could be damaging to your system. Proceed with caution, and
|
||
although this is highly unlikely, the author does not take any
|
||
responsibility.
|
||
|
||
All copyrights are held by their by their respective owners, unless
|
||
specifically noted otherwise. Use of a term in this document should not be
|
||
regarded as affecting the validity of any trademark or service mark. Naming
|
||
of particular products or brands should not be seen as endorsements.
|
||
-----------------------------------------------------------------------------
|
||
|
||
1.3. Credits / Contributors
|
||
|
||
This work is not especially related to any people that I should thank. But
|
||
my life is, and my knowledge too: so, thanks to everyone that has supported
|
||
me, prior to my birth, now, and in the future. Really.
|
||
|
||
A special thank is due to Tabatha, the patient woman that read my work and
|
||
made the needed reviews.
|
||
-----------------------------------------------------------------------------
|
||
|
||
1.4. Feedback
|
||
|
||
Feedback is most certainly welcome for this document. Send your additions,
|
||
comments and criticisms to the following email address: <
|
||
fabio.busatto@sikurezza.org>.
|
||
-----------------------------------------------------------------------------
|
||
|
||
1.5. Translations
|
||
|
||
There are no translated versions of this HOWTO at the time of publication.
|
||
If you are interested in translating this HOWTO into other languages, please
|
||
feel free to contact me. Your contribution will be very welcome.
|
||
-----------------------------------------------------------------------------
|
||
|
||
2. TCP keepalive overview
|
||
|
||
In order to understand what TCP keepalive (which we will just call
|
||
keepalive) does, you need do nothing more than read the name: keep TCP alive.
|
||
This means that you will be able to check your connected socket (also known
|
||
as TCP sockets), and determine whether the connection is still up and running
|
||
or if it has broken.
|
||
-----------------------------------------------------------------------------
|
||
|
||
2.1. What is TCP keepalive?
|
||
|
||
The keepalive concept is very simple: when you set up a TCP connection, you
|
||
associate a set of timers. Some of these timers deal with the keepalive
|
||
procedure. When the keepalive timer reaches zero, you send your peer a
|
||
keepalive probe packet with no data in it and the ACK flag turned on. You can
|
||
do this because of the TCP/IP specifications, as a sort of duplicate ACK, and
|
||
the remote endpoint will have no arguments, as TCP is a stream-oriented
|
||
protocol. On the other hand, you will receive a reply from the remote host
|
||
(which doesn't need to support keepalive at all, just TCP/IP), with no data
|
||
and the ACK set.
|
||
|
||
If you receive a reply to your keepalive probe, you can assert that the
|
||
connection is still up and running without worrying about the user-level
|
||
implementation. In fact, TCP permits you to handle a stream, not packets, and
|
||
so a zero-length data packet is not dangerous for the user program.
|
||
|
||
This procedure is useful because if the other peers lose their connection
|
||
(for example by rebooting) you will notice that the connection is broken,
|
||
even if you don't have traffic on it. If the keepalive probes are not replied
|
||
to by your peer, you can assert that the connection cannot be considered
|
||
valid and then take the correct action.
|
||
-----------------------------------------------------------------------------
|
||
|
||
2.2. Why use TCP keepalive?
|
||
|
||
You can live quite happily without keepalive, so if you're reading this,
|
||
you may be trying to understand if keepalive is a possible solution for your
|
||
problems. Either that or you've really got nothing more interesting to do
|
||
instead, and that's okay too. :)
|
||
|
||
Keepalive is non-invasive, and in most cases, if you're in doubt, you can
|
||
turn it on without the risk of doing something wrong. But do remember that it
|
||
generates extra network traffic, which can have an impact on routers and
|
||
firewalls.
|
||
|
||
In short, use your brain and be careful.
|
||
|
||
In the next section we will distinguish between the two target tasks for
|
||
keepalive:
|
||
|
||
<EFBFBD><EFBFBD>*<2A>Checking for dead peers
|
||
|
||
<EFBFBD><EFBFBD>*<2A>Preventing disconnection due to network inactivity
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
2.3. Checking for dead peers
|
||
|
||
Keepalive can be used to advise you when your peer dies before it is able
|
||
to notify you. This could happen for several reasons, like kernel panic or a
|
||
brutal termination of the process handling that peer. Another scenario that
|
||
illustrates when you need keepalive to detect peer death is when the peer is
|
||
still alive but the network channel between it and you has gone down. In this
|
||
scenario, if the network doesn't become operational again, you have the
|
||
equivalent of peer death. This is one of those situations where normal TCP
|
||
operations aren't useful to check the connection status.
|
||
|
||
Think of a simple TCP connection between Peer A and Peer B: there is the
|
||
initial three-way handshake, with one SYN segment from A to B, the SYN/ACK
|
||
back from B to A, and the final ACK from A to B. At this time, we're in a
|
||
stable status: connection is established, and now we would normally wait for
|
||
someone to send data over the channel. And here comes the problem: unplug the
|
||
power supply from B and instantaneously it will go down, without sending
|
||
anything over the network to notify A that the connection is going to be
|
||
broken. A, from its side, is ready to receive data, and has no idea that B
|
||
has crashed. Now restore the power supply to B and wait for the system to
|
||
restart. A and B are now back again, but while A knows about a connection
|
||
still active with B, B has no idea. The situation resolves itself when A
|
||
tries to send data to B over the dead connection, and B replies with an RST
|
||
packet, causing A to finally to close the connection.
|
||
|
||
Keepalive can tell you when another peer becomes unreachable without the
|
||
risk of false-positives. In fact, if the problem is in the network between
|
||
two peers, the keepalive action is to wait some time and then retry, sending
|
||
the keepalive packet before marking the connection as broken.
|
||
|
||
|
||
_____ _____
|
||
| | | |
|
||
| A | | B |
|
||
|_____| |_____|
|
||
^ ^
|
||
|--->--->--->-------------- SYN -------------->--->--->---|
|
||
|---<---<---<------------ SYN/ACK ------------<---<---<---|
|
||
|--->--->--->-------------- ACK -------------->--->--->---|
|
||
| |
|
||
| system crash ---> X
|
||
|
|
||
| system restart ---> ^
|
||
| |
|
||
|--->--->--->-------------- PSH -------------->--->--->---|
|
||
|---<---<---<-------------- RST --------------<---<---<---|
|
||
| |
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
|
||
2.4. Preventing disconnection due to network inactivity
|
||
|
||
The other useful goal of keepalive is to prevent inactivity from
|
||
disconnecting the channel. It's a very common issue, when you are behind a
|
||
NAT proxy or a firewall, to be disconnected without a reason. This behavior
|
||
is caused by the connection tracking procedures implemented in proxies and
|
||
firewalls, which keep track of all connections that pass through them.
|
||
Because of the physical limits of these machines, they can only keep a finite
|
||
number of connections in their memory. The most common and logical policy is
|
||
to keep newest connections and to discard old and inactive connections first.
|
||
|
||
Returning to Peers A and B, reconnect them. Once the channel is open, wait
|
||
until an event occurs and then communicate this to the other peer. What if
|
||
the event verifies after a long period of time? Our connection has its scope,
|
||
but it's unknown to the proxy. So when we finally send data, the proxy isn't
|
||
able to correctly handle it, and the connection breaks up.
|
||
|
||
Because the normal implementation puts the connection at the top of the
|
||
list when one of its packets arrives and selects the last connection in the
|
||
queue when it needs to eliminate an entry, periodically sending packets over
|
||
the network is a good way to always be in a polar position with a minor risk
|
||
of deletion.
|
||
|
||
|
||
_____ _____ _____
|
||
| | | | | |
|
||
| A | | NAT | | B |
|
||
|_____| |_____| |_____|
|
||
^ ^ ^
|
||
|--->--->--->---|----------- SYN ------------->--->--->---|
|
||
|---<---<---<---|--------- SYN/ACK -----------<---<---<---|
|
||
|--->--->--->---|----------- ACK ------------->--->--->---|
|
||
| | |
|
||
| | <--- connection deleted from table |
|
||
| | |
|
||
|--->- PSH ->---| <--- invalid connection |
|
||
| | |
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
|
||
3. Using TCP keepalive under Linux
|
||
|
||
Linux has built-in support for keepalive. You need to enable TCP/IP
|
||
networking in order to use it. You also need procfs support and sysctl
|
||
support to be able to configure the kernel parameters at runtime.
|
||
|
||
The procedures involving keepalive use three user-driven variables:
|
||
|
||
tcp_keepalive_time
|
||
the interval between the last data packet sent (simple ACKs are not
|
||
considered data) and the first keepalive probe; after the connection is
|
||
marked to need keepalive, this counter is not used any further
|
||
|
||
tcp_keepalive_intvl
|
||
the interval between subsequential keepalive probes, regardless of what
|
||
the connection has exchanged in the meantime
|
||
|
||
tcp_keepalive_probes
|
||
the number of unacknowledged probes to send before considering the
|
||
connection dead and notifying the application layer
|
||
|
||
|
||
Remember that keepalive support, even if configured in the kernel, is not
|
||
the default behavior in Linux. Programs must request keepalive control for
|
||
their sockets using the setsockopt interface. There are relatively few
|
||
programs implementing keepalive, but you can easily add keepalive support for
|
||
most of them following the instructions explained later in this document.
|
||
-----------------------------------------------------------------------------
|
||
|
||
3.1. Configuring the kernel
|
||
|
||
There are two ways to configure keepalive parameters inside the kernel via
|
||
userspace commands:
|
||
|
||
<EFBFBD><EFBFBD>*<2A>procfs interface
|
||
|
||
<EFBFBD><EFBFBD>*<2A>sysctl interface
|
||
|
||
|
||
We mainly discuss how this is accomplished on the procfs interface because
|
||
it's the most used, recommended and the easiest to understand. The sysctl
|
||
interface, particularly regarding the sysctl(2) syscall and not the
|
||
sysctl(8) tool, is only here for the purpose of background knowledge.
|
||
-----------------------------------------------------------------------------
|
||
|
||
3.1.1. The procfs interface
|
||
|
||
This interface requires both sysctl and procfs to be built into the
|
||
kernel, and procfs mounted somewhere in the filesystem (usually on /proc,
|
||
as in the examples below). You can read the values for the actual parameters
|
||
by "catting" files in /proc/sys/net/ipv4/ directory:
|
||
|
||
|
||
# cat /proc/sys/net/ipv4/tcp_keepalive_time
|
||
7200
|
||
|
||
# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
|
||
75
|
||
|
||
# cat /proc/sys/net/ipv4/tcp_keepalive_probes
|
||
9
|
||
|
||
|
||
The first two parameters are expressed in seconds, and the last is the pure
|
||
number. This means that the keepalive routines wait for two hours (7200 secs)
|
||
before sending the first keepalive probe, and then resend it every 75
|
||
seconds. If no ACK response is received for nine consecutive times, the
|
||
connection is marked as broken.
|
||
|
||
Modifying this value is straightforward: you need to write new values into
|
||
the files. Suppose you decide to configure the host so that keepalive starts
|
||
after ten minutes of channel inactivity, and then send probes in intervals of
|
||
one minute. Because of the high instability of our network trunk and the low
|
||
value of the interval, suppose you also want to increase the number of probes
|
||
to 20.
|
||
|
||
Here's how we would change the settings:
|
||
|
||
|
||
# echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time
|
||
|
||
# echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl
|
||
|
||
# echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes
|
||
|
||
|
||
To be sure that all succeeds, recheck the files and confirm these new
|
||
values are showing in place of the old ones.
|
||
|
||
Remember that procfs handles special files, and you cannot perform any sort
|
||
of operation on them because they're just an interface within the kernel
|
||
space, not real files, so try your scripts before using them, and try to use
|
||
simple access methods as in the examples shown earlier.
|
||
|
||
You can access the interface through the sysctl(8) tool, specifying what
|
||
you want to read or write.
|
||
|
||
|
||
# sysctl \
|
||
> net.ipv4.tcp_keepalive_time \
|
||
> net.ipv4.tcp_keepalive_intvl \
|
||
> net.ipv4.tcp_keepalive_probes
|
||
net.ipv4.tcp_keepalive_time = 7200
|
||
net.ipv4.tcp_keepalive_intvl = 75
|
||
net.ipv4.tcp_keepalive_probes = 9
|
||
|
||
|
||
Note that sysctl names are very close to procfs paths. Write is performed
|
||
using the -w switch of sysctl (8):
|
||
|
||
|
||
# sysctl -w \
|
||
> net.ipv4.tcp_keepalive_time=600 \
|
||
> net.ipv4.tcp_keepalive_intvl=60 \
|
||
> net.ipv4.tcp_keepalive_probes=20
|
||
net.ipv4.tcp_keepalive_time = 600
|
||
net.ipv4.tcp_keepalive_intvl = 60
|
||
net.ipv4.tcp_keepalive_probes = 20
|
||
|
||
|
||
Note that sysctl (8) doesn't use sysctl(2) syscall, but reads and writes
|
||
directly in the procfs subtree, so you will need procfs enabled in the kernel
|
||
and mounted in the filesystem, just as you would if you directly accessed the
|
||
files within the procfs interface. Sysctl(8) is just a different way to do
|
||
the same thing.
|
||
-----------------------------------------------------------------------------
|
||
|
||
3.1.2. The sysctl interface
|
||
|
||
There is another way to access kernel variables: sysctl(2 ) syscall. It can
|
||
be useful when you don't have procfs available because the communication with
|
||
the kernel is performed directly via syscall and not through the procfs
|
||
subtree. There is currently no program that wraps this syscall (remember that
|
||
sysctl(8) doesn't use it).
|
||
|
||
For more details about using sysctl(2) refer to the manpage.
|
||
-----------------------------------------------------------------------------
|
||
|
||
3.2. Making changes persistent to reboot
|
||
|
||
There are several ways to reconfigure your system every time it boots up.
|
||
First, remember that every Linux distribution has its own set of init scripts
|
||
called by init (8). The most common configurations include the /etc/rc.d/
|
||
directory, or the alternative, /etc/init.d/. In any case, you can set the
|
||
parameters in any of the startup scripts, because keepalive rereads the
|
||
values every time its procedures need them. So if you change the value of
|
||
tcp_keepalive_intvl when the connection is still up, the kernel will use the
|
||
new value going forward.
|
||
|
||
There are three spots where the initialization commands should logically be
|
||
placed: the first is where your network is configured, the second is the
|
||
rc.local script, usually included in all distributions, which is known as the
|
||
place where user configuration setups are done. The third place may already
|
||
exist in your system. Referring back to the sysctl (8) tool, you can see that
|
||
the -p switch loads settings from the /etc/sysctl.conf configuration file.
|
||
In many cases your init script already performs the sysctl -p (you can "grep"
|
||
it in the configuration directory for confirmation), and so you just have to
|
||
add the lines in /etc/sysctl.conf to make them load at every boot. For more
|
||
information about the syntax of sysctl.conf(5), refer to the manpage.
|
||
-----------------------------------------------------------------------------
|
||
|
||
4. Programming applications
|
||
|
||
This section deals with programming code needed if you want to create
|
||
applications that use keepalive. This is not a programming manual, and it
|
||
requires that you have previous knowledge in C programming and in networking
|
||
concepts. I consider you familiar with sockets, and with everything
|
||
concerning the general aspects of your application.
|
||
-----------------------------------------------------------------------------
|
||
|
||
4.1. When your code needs keepalive support
|
||
|
||
Not all network applications need keepalive support. Remember that it is
|
||
TCP keepalive support. So, as you can imagine, only TCP sockets can take
|
||
advantage of it.
|
||
|
||
The most beautiful thing you can do when writing an application is to make
|
||
it as customizable as possible, and not to force decisions. If you want to
|
||
consider the happiness of your users, you should implement keepalive and let
|
||
the users decide if they want to use it or not by using a configuration
|
||
parameter or a switch on the command line.
|
||
-----------------------------------------------------------------------------
|
||
|
||
4.2. The setsockopt function call
|
||
|
||
All you need to enable keepalive for a specific socket is to set the
|
||
specific socket option on the socket itself. The prototype of the function is
|
||
as follows:
|
||
int setsockopt(int s, int level, int optname,
|
||
const void *optval, socklen_t optlen)
|
||
|
||
|
||
The first parameter is the socket, previously created with the socket(2);
|
||
the second one must be SOL_SOCKET, and the third must be SO_KEEPALIVE . The
|
||
fourth parameter must be a boolean integer value, indicating that we want to
|
||
enable the option, while the last is the size of the value passed before.
|
||
|
||
According to the manpage, 0 is returned upon success, and -1 is returned on
|
||
error (and errno is properly set).
|
||
|
||
There are also three other socket options you can set for keepalive when
|
||
you write your application. They all use the SOL_TCP level instead of
|
||
SOL_SOCKET, and they override system-wide variables only for the current
|
||
socket. If you read without writing first, the current system-wide parameters
|
||
will be returned.
|
||
|
||
<EFBFBD><EFBFBD>*<2A>TCP_KEEPCNT: overrides tcp_keepalive_probes
|
||
|
||
<EFBFBD><EFBFBD>*<2A>TCP_KEEPIDLE: overrides tcp_keepalive_time
|
||
|
||
<EFBFBD><EFBFBD>*<2A>TCP_KEEPINTVL: overrides tcp_keepalive_intvl
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
4.3. Code examples
|
||
|
||
This is a little example that creates a socket, shows that keepalive is
|
||
disabled, then enables it and checks that the option was effectively set.
|
||
|
||
|
||
/* --- begin of keepalive test program --- */
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
#include <sys/types.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
|
||
int main(void);
|
||
|
||
int main()
|
||
{
|
||
int s;
|
||
int optval;
|
||
socklen_t optlen = sizeof(optval);
|
||
|
||
/* Create the socket */
|
||
if((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
||
perror("socket()");
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
/* Check the status for the keepalive option */
|
||
if(getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
|
||
perror("getsockopt()");
|
||
close(s);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
printf("SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF"));
|
||
|
||
/* Set the option active */
|
||
optval = 1;
|
||
optlen = sizeof(optval);
|
||
if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
|
||
perror("setsockopt()");
|
||
close(s);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
printf("SO_KEEPALIVE set on socket\n");
|
||
|
||
/* Check the status again */
|
||
if(getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
|
||
perror("getsockopt()");
|
||
close(s);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
printf("SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF"));
|
||
|
||
close(s);
|
||
|
||
exit(EXIT_SUCCESS);
|
||
}
|
||
|
||
/* --- end of keepalive test program --- */
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
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:
|
||
|
||
<EFBFBD><EFBFBD>*<2A>source code modification of the original program
|
||
|
||
<EFBFBD><EFBFBD>*<2A>setsockopt (2) injection using the library preloading technique
|
||
|
||
|
||
-----------------------------------------------------------------------------
|
||
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/] 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/] http://libkeepalive.sourceforge.net/
|