Forráskód Böngészése

modules/tcpops: new module to tweak TCP options

This module adds the ability to control TCP options (currently only keepalive)
through the kamailio cfg, on demand, and on a per-socket basis.
Camille Oudot 10 éve
szülő
commit
5193ff2544

+ 14 - 0
modules/tcpops/Makefile

@@ -0,0 +1,14 @@
+#
+# TCP keepelive module
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=tcpops.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 115 - 0
modules/tcpops/README

@@ -0,0 +1,115 @@
+TCP Ops module
+
+Camille Oudot
+
+   Orange
+
+   Copyright © 2015 Orange
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Parameters
+        3. Functions
+
+              3.1. tcp_keepalive_enable([conid], idle, count, interval)
+              3.2. tcp_keepalive_disable([conid])
+
+   List of Examples
+
+   1.1. tcp_keepalive_enable usage
+   1.2. tcp_keepalive_disable usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Parameters
+   3. Functions
+
+        3.1. tcp_keepalive_enable([conid], idle, count, interval)
+        3.2. tcp_keepalive_disable([conid])
+
+1. Overview
+
+   This modules allows kamailio to control the TCP options (such as the
+   keepalive mechanism), on demand, and on a per-socket basis.
+
+   Note: the keepalive functions only work on systems with the
+   HAVE_TCP_KEEPIDLE, HAVE_TCP_KEEPCNT and HAVE_TCP_KEEPINTVL macros
+   defined (currently only Linux).
+
+2. Parameters
+
+3. Functions
+
+   3.1. tcp_keepalive_enable([conid], idle, count, interval)
+   3.2. tcp_keepalive_disable([conid])
+
+3.1.  tcp_keepalive_enable([conid], idle, count, interval)
+
+   Enables keepalive on a TCP connection.
+
+   Meaning of the parameters is as follows:
+     * conid (optionnal): the kamailio internal connection id on which TCP
+       keepalive will be enabled. If no parameter is given, the keepalive
+       mechanism will be enabled on the current message source connection.
+     * idle (seconds): the time before the first keepalive packet is sent
+       out.
+     * count: number of non-acked keepalive before reseting the
+       connection.
+     * interval (seconds): time between two keepalive probes.
+
+   Retuns 1 on success, -1 on failure.
+
+   Example 1.1. tcp_keepalive_enable usage
+request_route {
+        if (is_method("INVITE")) {
+                $avp(caller_conid) = $conid;
+        }
+        ...
+}
+
+onreply_route {
+        if (is_method("INVITE") && status == 200) {
+                # enable on callee's connection
+                tcp_keepalive_enable("60", "5", "5");
+                # enable on caller's connection
+                tcp_keepalive_enable("$avp(caller_conid)", "60", "5", "2");
+        }
+        ...
+}
+
+3.2.  tcp_keepalive_disable([conid])
+
+   Disables keepalive on a TCP connection.
+
+   Meaning of the parameters is as follows:
+     * conid (optionnal): the kamailio internal connection id on which TCP
+       keepalive will be disabled. If no parameter is given, the keepalive
+       mechanism will be disabled on the current message source
+       connection.
+
+   Retuns 1 on success, -1 on failure.
+
+   Example 1.2. tcp_keepalive_disable usage
+request_route {
+        ...
+        if (is_method("BYE")) {
+                $avp(bye_conid) = $conid;
+        }
+        ...
+}
+
+onreply_route {
+        ...
+        if (is_method("BYE") && status == 200) {
+                tcp_keepalive_disable();
+                tcp_keepalive_disable("$avp(bye_conid)");
+        }
+        ...
+}

+ 4 - 0
modules/tcpops/doc/Makefile

@@ -0,0 +1,4 @@
+docs = tcpops.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module

+ 107 - 0
modules/tcpops/doc/functions.xml

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="print.functions" xmlns:xi="http://www.w3.org/2001/XInclude">
+	<sectioninfo>
+	</sectioninfo>
+
+	<title>Functions</title>
+
+	<section id="tcpops.f.tcp_keepalive_enable">
+		<title>
+			<function>tcp_keepalive_enable([conid], idle, count, interval)</function>
+		</title>
+		<para>
+				Enables keepalive on a TCP connection.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+			<listitem>
+				<para><emphasis>conid</emphasis> (optionnal): the kamailio internal
+				connection id on which TCP keepalive will be enabled. If no parameter
+				is given, the keepalive mechanism will be enabled on the current message
+				source connection.
+				</para>
+			</listitem>
+			<listitem>
+				<para><emphasis>idle</emphasis> (seconds): the time before the first
+				keepalive packet is sent out.
+				</para>
+			</listitem>
+			<listitem>
+				<para><emphasis>count</emphasis>: number of non-acked keepalive before
+				reseting the connection. 
+				</para>
+			</listitem>
+			<listitem>
+				<para><emphasis>interval</emphasis> (seconds): time between two keepalive
+				probes. 
+				</para>
+			</listitem>
+		</itemizedlist>
+		<para>Retuns 1 on success, -1 on failure.</para>
+		<example>
+			<title><function>tcp_keepalive_enable</function> usage</title>
+			<programlisting><![CDATA[
+request_route {
+	if (is_method("INVITE")) {
+		$avp(caller_conid) = $conid;
+	}
+	...
+}
+
+onreply_route {
+	if (is_method("INVITE") && status == 200) {
+		# enable on callee's connection
+		tcp_keepalive_enable("60", "5", "5");
+		# enable on caller's connection
+		tcp_keepalive_enable("$avp(caller_conid)", "60", "5", "2");
+	}
+	...
+}
+			]]></programlisting>
+		</example>
+	</section>
+	
+	 <section id="tcpops.f.tcp_keepalive_disable">
+		<title>
+			<function>tcp_keepalive_disable([conid])</function>
+		</title>
+		<para>
+				Disables keepalive on a TCP connection.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+			<listitem>
+				<para><emphasis>conid</emphasis> (optionnal): the kamailio internal
+				connection id on which TCP keepalive will be disabled. If no parameter
+				is given, the keepalive mechanism will be disabled on the current message
+				source connection.
+				</para>
+			</listitem>
+		</itemizedlist>
+		<para>Retuns 1 on success, -1 on failure.</para>
+		<example>
+			<title><function>tcp_keepalive_disable</function> usage</title>
+			<programlisting><![CDATA[
+request_route {
+	...
+	if (is_method("BYE")) {
+		$avp(bye_conid) = $conid;
+	}
+	...
+}
+
+onreply_route {
+	...
+	if (is_method("BYE") && status == 200) {
+		tcp_keepalive_disable();
+		tcp_keepalive_disable("$avp(bye_conid)");
+	}
+	...
+}
+			]]></programlisting>
+		</example>
+	</section>
+</section>

+ 13 - 0
modules/tcpops/doc/params.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="print.parameters" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+    </sectioninfo>
+
+    <title>Parameters</title>
+
+
+
+</section>

+ 44 - 0
modules/tcpops/doc/tcpops.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<book id="print" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+        <title>TCP Ops module</title>
+	<authorgroup>
+	    <author>
+		<firstname>Camille</firstname>
+		<surname>Oudot</surname>
+		<affiliation><orgname>Orange</orgname></affiliation>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </author>
+	</authorgroup>
+	<copyright>
+	    <year>2015</year>
+	    <holder>Orange</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <chapter>
+	<title>Admin Guide</title>
+    <section id="tcpops.overview">
+	<title>Overview</title>
+	<para>
+	    This modules allows kamailio to control the TCP options (such as the keepalive
+	    mechanism), on demand, and on a per-socket basis.
+	</para>
+	<para>
+	<emphasis>Note</emphasis>: the keepalive functions only work on systems with the
+	HAVE_TCP_KEEPIDLE, HAVE_TCP_KEEPCNT and HAVE_TCP_KEEPINTVL macros defined
+	(currently only Linux).
+	</para>
+    </section>
+    
+    <xi:include href="params.xml"/>
+    <xi:include href="functions.xml"/>
+    </chapter>
+</book>
+

+ 171 - 0
modules/tcpops/tcpops.c

@@ -0,0 +1,171 @@
+/**
+ * Copyright 2015 (C) Orange
+ * <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+
+#include "../../dprint.h"
+#include "../../tcp_options.h"
+#include "../../tcp_conn.h"
+#include "../../globals.h"
+#include "../../pass_fd.h"
+
+/**
+ * gets the fd of the current message source connection
+ *
+ * @param conid - connection id
+ * @param fd - placeholder to return the fd
+ * @return 1 on success, 0 on failure
+ *
+ */
+int get_current_fd(int conid, int *fd)
+{
+	struct tcp_connection *s_con;
+	if (unlikely((s_con = tcpconn_get(conid, 0, 0, 0, 0)) == NULL)) {
+		LM_ERR("invalid connection id %d, (must be a TCP connid)\n", conid);
+		return 0;
+	}
+	LM_DBG("got fd=%d from id=%d\n", s_con->fd, conid);
+
+	*fd = s_con->fd;
+	tcpconn_put(s_con);
+	return 1;
+}
+
+/**
+ * Request the fd corresponding to the given connection id to the TCP main process.
+ * You may want to close() the fd after use.
+ *
+ * @param conid - connection id
+ * @param fd - placeholder to return the fd
+ * @return 1 on success, 0 on failure
+ *
+ */
+int acquire_fd_from_tcpmain(int conid, int *fd)
+{
+	struct tcp_connection *s_con, *tmp;
+	long msg[2];
+	int n;
+
+	if (unlikely((s_con = tcpconn_get(conid, 0, 0, 0, 0)) == NULL)) {
+		LM_ERR("invalid connection id %d, (must be a TCP connid)\n", conid);
+		return 0;
+	}
+
+	msg[0] = (long)s_con;
+	msg[1] = CONN_GET_FD;
+
+	n = send_all(unix_tcp_sock, msg, sizeof(msg));
+	if (unlikely(n <= 0)){
+		LM_ERR("failed to send fd request: %s (%d)\n", strerror(errno), errno);
+		goto error_release;
+	}
+
+	n = receive_fd(unix_tcp_sock, &tmp, sizeof(tmp), fd, MSG_WAITALL);
+	if (unlikely(n <= 0)){
+		LM_ERR("failed to get fd (receive_fd): %s (%d)\n", strerror(errno), errno);
+		goto error_release;
+	}
+	tcpconn_put(s_con);
+	return 1;
+
+error_release:
+	tcpconn_put(s_con);
+	return 0;
+}
+
+#if !defined(HAVE_SO_KEEPALIVE) || !defined(HAVE_TCP_KEEPIDLE) || !defined(HAVE_TCP_KEEPCNT) || !defined(HAVE_TCP_KEEPINTVL)
+	#warning "TCP keepalive is not fully supported by your platform"
+
+int tcp_keepalive_enable(int fd, int idle, int count, int interval, int closefd)
+{
+	LM_ERR("tcp_keepalive_enable() failed: this module does not support your platform\n");
+	return -1;
+}
+
+int tcp_keepalive_disable(int fd, int closefd)
+{
+	LM_ERR("tcp_keepalive_disable() failed: this module does not support your platform\n");
+	return -1;
+}
+#else
+
+int tcp_keepalive_enable(int fd, int idle, int count, int interval, int closefd)
+{
+	static const int enable = 1;
+	int ret = -1;
+
+	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable,
+					sizeof(enable))<0){
+		LM_ERR("failed to enable SO_KEEPALIVE: %s\n", strerror(errno));
+		return -1;
+	} else {
+
+		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle,
+						sizeof(idle))<0){
+			LM_ERR("failed to set keepalive idle interval: %s\n", strerror(errno));
+		}
+
+		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count,
+						sizeof(count))<0){
+			LM_ERR("failed to set maximum keepalive count: %s\n", strerror(errno));
+		}
+
+		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
+						sizeof(interval))<0){
+			LM_ERR("failed to set keepalive probes interval: %s\n", strerror(errno));
+		}
+
+		ret = 1;
+		LM_DBG("keepalive enabled for fd=%d, idle=%d, cnt=%d, intvl=%d\n", fd, idle, count, interval);
+	}
+
+	if (closefd)
+	{
+		close(fd);
+	}
+	return ret;
+}
+
+int tcp_keepalive_disable(int fd, int closefd)
+{
+	static const int disable = 0;
+	int ret = -1;
+
+	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &disable,
+					sizeof(disable))<0){
+		LM_WARN("failed to disable SO_KEEPALIVE: %s\n", strerror(errno));
+	} else {
+		ret = 1;
+		LM_DBG("keepalive disabled for fd=%d\n", fd);
+	}
+
+	if (closefd)
+	{
+		close(fd);
+	}
+	return ret;
+}
+
+#endif

+ 32 - 0
modules/tcpops/tcpops.h

@@ -0,0 +1,32 @@
+/**
+ * Copyright 2015 (C) Orange
+ * <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef TCP_KEEPALIVE_H_
+#define TCP_KEEPALIVE_H_
+
+int get_current_fd(int conid, int *fd);
+int acquire_fd_from_tcpmain(int conid, int *fd);
+int tcp_keepalive_enable(int fd, int idle, int count, int interval, int closefd);
+int tcp_keepalive_disable(int fd, int closefd);
+
+#endif /* TCP_KEEPALIVE_H_ */

+ 209 - 0
modules/tcpops/tcpops_mod.c

@@ -0,0 +1,209 @@
+/**
+ * Copyright 2015 (C) Orange
+ * <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../globals.h"
+#include "../../sr_module.h"
+#include "../../tcp_options.h"
+#include "../../dprint.h"
+#include "../../mod_fix.h"
+
+#include "tcpops.h"
+
+MODULE_VERSION
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+static int w_tcp_keepalive_enable4(sip_msg_t* msg, char* con, char* idle, char *cnt, char *intvl);
+static int w_tcp_keepalive_enable3(sip_msg_t* msg, char* idle, char *cnt, char *intvl);
+static int w_tcp_keepalive_disable1(sip_msg_t* msg, char* con);
+static int w_tcp_keepalive_disable0(sip_msg_t* msg);
+
+static int fixup_tcp_keepalive_numpv(void** param, int param_no);
+
+
+static cmd_export_t cmds[]={
+	{"tcp_keepalive_enable", (cmd_function)w_tcp_keepalive_enable4, 4, fixup_tcp_keepalive_numpv,
+		0, ANY_ROUTE},
+	{"tcp_keepalive_enable", (cmd_function)w_tcp_keepalive_enable3, 3, fixup_tcp_keepalive_numpv,
+		0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{"tcp_keepalive_disable", (cmd_function)w_tcp_keepalive_disable1, 1, fixup_tcp_keepalive_numpv,
+		0, ANY_ROUTE},
+	{"tcp_keepalive_disable", (cmd_function)w_tcp_keepalive_disable0, 0, 0,
+		0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+
+
+struct module_exports exports = {
+	"tcpops",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,            /* exported functions to config */
+	0,          /* exported parameters to config */
+	0,               /* exported statistics */
+	0,              /* exported MI functions */
+	0,        /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	LM_DBG("TCP keepalive module loaded.\n");
+
+	return 0;
+}
+
+/**
+ * @brief Initialize async module children
+ */
+static int child_init(int rank)
+{
+
+	return 0;
+}
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	LM_DBG("TCP keepalive module unloaded.\n");
+}
+
+#define _IVALUE_ERROR(NAME) LM_ERR("invalid parameter '" #NAME "' (must be a number)\n")
+#define _IVALUE(NAME)\
+int i_##NAME ;\
+if(fixup_get_ivalue(msg, (gparam_t*)NAME, &( i_##NAME))!=0)\
+{ \
+	_IVALUE_ERROR(NAME);\
+	return -1;\
+}
+
+
+/**
+ *
+ */
+static int w_tcp_keepalive_enable4(sip_msg_t* msg, char* con, char* idle, char *cnt, char *intvl)
+{
+	int fd;
+	int closefd = 0;
+
+	_IVALUE (con)
+
+	if (msg != NULL && msg->rcv.proto_reserved1 == i_con) {
+		if (!get_current_fd(msg->rcv.proto_reserved1, &fd)) {
+			return -1;
+		}
+	} else {
+		if (!acquire_fd_from_tcpmain(i_con, &fd)) {
+			return -1;
+		}
+		closefd = 1;
+	}
+
+	_IVALUE (idle)
+	_IVALUE (cnt)
+	_IVALUE (intvl)
+
+	return tcp_keepalive_enable(fd, i_idle, i_cnt, i_intvl, closefd);
+
+}
+
+static int w_tcp_keepalive_enable3(sip_msg_t* msg, char* idle, char *cnt, char *intvl)
+{
+	int fd;
+
+	if (msg == NULL) {
+		return -1;
+	}
+
+	if (!get_current_fd(msg->rcv.proto_reserved1, &fd)) {
+		return -1;
+	}
+
+	_IVALUE (idle)
+	_IVALUE (cnt)
+	_IVALUE (intvl)
+
+	return tcp_keepalive_enable(fd, i_idle, i_cnt, i_intvl, 0);
+}
+
+static int w_tcp_keepalive_disable1(sip_msg_t* msg, char* con)
+{
+	int fd;
+	int closefd = 0;
+
+	_IVALUE (con)
+
+	if (msg != NULL && msg->rcv.proto_reserved1 == i_con) {
+		if (!get_current_fd(msg->rcv.proto_reserved1, &fd)) {
+			return -1;
+		}
+	} else {
+		if (!acquire_fd_from_tcpmain(i_con, &fd)) {
+			return -1;
+		}
+		closefd = 1;
+	}
+
+	return tcp_keepalive_disable(fd, closefd);
+}
+
+static int w_tcp_keepalive_disable0(sip_msg_t* msg)
+{
+	int fd;
+
+	if (msg == NULL)
+		return -1;
+
+	if (!get_current_fd(msg->rcv.proto_reserved1, &fd)) {
+		return -1;
+	}
+
+	return tcp_keepalive_disable(fd, 0);
+}
+
+/**
+ *
+ */
+static int fixup_tcp_keepalive_numpv(void** param, int param_no)
+{
+	return fixup_igp_null(param, 1);
+}
+
+