@@ -, +, @@
---
C4/SIP/SIPServer.pm | 122 ++++++++++++++++++++++++++++++++-
debian/templates/SIPconfig.xml | 3 +
etc/SIPconfig.xml | 3 +
3 files changed, 127 insertions(+), 1 deletion(-)
--- a/C4/SIP/SIPServer.pm
+++ a/C4/SIP/SIPServer.pm
@@ -8,7 +8,7 @@ use FindBin qw($Bin);
use lib "$Bin";
use Net::Server::PreFork;
use IO::Socket::INET;
-use Socket qw(:DEFAULT :crlf);
+use Socket qw(:DEFAULT :crlf TCP_KEEPIDLE TCP_KEEPINTVL IPPROTO_TCP);
use Scalar::Util qw(blessed);
require UNIVERSAL::require;
@@ -90,6 +90,126 @@ push @parms, 'group=' . $>;
# This is the main event.
__PACKAGE__ ->run(@parms);
+#
+# Server
+#
+
+=head3 options
+
+As per Net::Server documentation, override "options" to provide your own
+custom options to the Net::Server* object. This allows us to use the Net::Server
+infrastructure for configuration rather than hacking our own configuration into the
+object.
+
+=cut
+
+sub options {
+ my $self = shift;
+ my $prop = $self->{'server'};
+ my $template = shift;
+
+ # setup options in the parent classes
+ $self->SUPER::options($template);
+
+ $prop->{'custom_tcp_keepalive'} ||= undef;
+ $template->{'custom_tcp_keepalive'} = \$prop->{'custom_tcp_keepalive'};
+
+ $prop->{'custom_tcp_keepalive_time'} ||= undef;
+ $template->{'custom_tcp_keepalive_time'} = \$prop->{'custom_tcp_keepalive_time'};
+
+ $prop->{'custom_tcp_keepalive_intvl'} ||= undef;
+ $template->{'custom_tcp_keepalive_intvl'} = \$prop->{'custom_tcp_keepalive_intvl'};
+}
+
+=head3 post_configure_hook
+
+As per Net::Server documentation, this method validates our custom configuration.
+
+=cut
+
+sub post_configure_hook {
+ my $self = shift;
+ my $prop = $self->{'server'};
+ if ( defined $prop->{'custom_tcp_keepalive'} && $prop->{'custom_tcp_keepalive'} ) {
+
+ #NOTE: Any true value defined is forced to 1 just for the sake of predictability
+ $prop->{'custom_tcp_keepalive'} = 1;
+ }
+
+ foreach my $key ( 'custom_tcp_keepalive_time', 'custom_tcp_keepalive_intvl' ) {
+ my $value = $prop->{$key};
+
+ #NOTE: A regex is used here to detect a positive integer, as int() returns integers but does not validate them
+ #NOTE: We do not allow zero as it can lead to problematic behaviour
+ if ( $value && $value =~ /^\d+$/ ) {
+
+ #NOTE: Strictly, you must convert into an integer as a string will cause setsockopt to fail
+ $prop->{$key} = int($value);
+ }
+ }
+}
+
+=head3 post_accept_hook
+
+This hook occurs after the client connection socket is created, which gives
+us an opportunity to enable support for TCP keepalives using the SO_KEEPALIVE
+socket option.
+
+By default, the kernel-level defaults (in seconds) are used. You can view these in the output of "sysctl -a":
+net.ipv4.tcp_keepalive_intvl = 75
+net.ipv4.tcp_keepalive_time = 7200
+
+Alternatively, you can use "custom_tcp_keepalive_time" and "custom_tcp_keepalive_intvl" to define
+your own custom values for the socket. Note that these parameters are defined at the top server-level
+and not on the listener-level.
+
+If you lower "custom_tcp_keepalive_time" below 75, you will also need to set "custom_tcp_keepalive_intvl".
+The "tcp_keepalive_time" is the initial time used for the keepalive timer, and the "tcp_keepalive_intvl"
+is the time used for subsequent keepalive timers. However, timers only send keepalive ACKs if the idle time
+elapsed is greater than "tcp_keepalive_time".
+
+Thus, if "tcp_keepalive_time = 10" and "tcp_keepalive_intvl = 5", a keepalive ACK will be sent every 10 seconds
+of idle time. If "tcp_keepalive_intvl = 10" and "tcp_keepalive_time = 5", a keepalive ACK will be sent after 5
+seconds of idle time, and the next keepalive ACK will be sent after 10 seconds of idle time. Generally speaking,
+it's best to set "tcp_keepalive_time" to be higher than "tcp_keepalive_intvl".
+
+Reminder: once these settings are set on the socket, they are handled by the operating system kernel, and not
+by the SIP server application. If you are having trouble with your settings, monitor your TCP traffic using
+a tool such as "tcpdump" to review and refine how they work.
+
+=cut
+
+sub post_accept_hook {
+ my $self = shift;
+ my $prop = $self->{'server'};
+ my $client = shift || $prop->{'client'};
+
+ my $tcp_keepalive = $prop->{custom_tcp_keepalive};
+ my $tcp_keepalive_time = $prop->{custom_tcp_keepalive_time};
+ my $tcp_keepalive_intvl = $prop->{custom_tcp_keepalive_intvl};
+
+ if ($tcp_keepalive) {
+
+ #NOTE: set the SO_KEEPALIVE option to enable TCP keepalives
+ setsockopt( $client, SOL_SOCKET, SO_KEEPALIVE, 1 )
+ or die "Unable to set SO_KEEPALIVE: $!";
+
+ if ($tcp_keepalive_time) {
+
+ #NOTE: override "net.ipv4.tcp_keepalive_time" kernel parameter for this socket
+ setsockopt( $client, IPPROTO_TCP, TCP_KEEPIDLE, $tcp_keepalive_time )
+ or die "Unable to set TCP_KEEPIDLE: $!";
+ }
+
+ if ($tcp_keepalive_intvl) {
+
+ #NOTE: override "net.ipv4.tcp_keepalive_intvl" kernel parameter for this socket
+ setsockopt( $client, IPPROTO_TCP, TCP_KEEPINTVL, $tcp_keepalive_intvl )
+ or die "Unable to set TCP_KEEPINTVL: $!";
+ }
+ }
+}
+
#
# Child
#
--- a/debian/templates/SIPconfig.xml
+++ a/debian/templates/SIPconfig.xml
@@ -10,6 +10,9 @@
--- a/etc/SIPconfig.xml
+++ a/etc/SIPconfig.xml
@@ -19,6 +19,9 @@
user='koha'
group='koha'
pid_file='/var/run/sipserver.pid'
+ custom_tcp_keepalive='0'
+ custom_tcp_keepalive_time='7200'
+ custom_tcp_keepalive_intvl='75'
/>
--