From 5193ff25446ba6de8ee9c2fcaa82d402e4262075 Mon Sep 17 00:00:00 2001 From: Camille Oudot Date: Wed, 4 Feb 2015 15:59:30 +0100 Subject: [PATCH] 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. --- modules/tcpops/Makefile | 14 +++ modules/tcpops/README | 115 +++++++++++++++++ modules/tcpops/doc/Makefile | 4 + modules/tcpops/doc/functions.xml | 107 ++++++++++++++++ modules/tcpops/doc/params.xml | 13 ++ modules/tcpops/doc/tcpops.xml | 44 +++++++ modules/tcpops/tcpops.c | 171 +++++++++++++++++++++++++ modules/tcpops/tcpops.h | 32 +++++ modules/tcpops/tcpops_mod.c | 209 +++++++++++++++++++++++++++++++ 9 files changed, 709 insertions(+) create mode 100644 modules/tcpops/Makefile create mode 100644 modules/tcpops/README create mode 100644 modules/tcpops/doc/Makefile create mode 100644 modules/tcpops/doc/functions.xml create mode 100644 modules/tcpops/doc/params.xml create mode 100644 modules/tcpops/doc/tcpops.xml create mode 100644 modules/tcpops/tcpops.c create mode 100644 modules/tcpops/tcpops.h create mode 100644 modules/tcpops/tcpops_mod.c diff --git a/modules/tcpops/Makefile b/modules/tcpops/Makefile new file mode 100644 index 00000000000..40fbcf6ed0b --- /dev/null +++ b/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 diff --git a/modules/tcpops/README b/modules/tcpops/README new file mode 100644 index 00000000000..63d5750ce9a --- /dev/null +++ b/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)"); + } + ... +} diff --git a/modules/tcpops/doc/Makefile b/modules/tcpops/doc/Makefile new file mode 100644 index 00000000000..21a618d5932 --- /dev/null +++ b/modules/tcpops/doc/Makefile @@ -0,0 +1,4 @@ +docs = tcpops.xml + +docbook_dir=../../../docbook +include $(docbook_dir)/Makefile.module diff --git a/modules/tcpops/doc/functions.xml b/modules/tcpops/doc/functions.xml new file mode 100644 index 00000000000..e8d125f88ed --- /dev/null +++ b/modules/tcpops/doc/functions.xml @@ -0,0 +1,107 @@ + + + +
+ + + + Functions + +
+ + <function>tcp_keepalive_enable([conid], idle, count, interval)</function> + + + 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. + + <function>tcp_keepalive_enable</function> usage + + +
+ +
+ + <function>tcp_keepalive_disable([conid])</function> + + + 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. + + <function>tcp_keepalive_disable</function> usage + + +
+
diff --git a/modules/tcpops/doc/params.xml b/modules/tcpops/doc/params.xml new file mode 100644 index 00000000000..eb6af5bfc54 --- /dev/null +++ b/modules/tcpops/doc/params.xml @@ -0,0 +1,13 @@ + + + +
+ + + + Parameters + + + +
diff --git a/modules/tcpops/doc/tcpops.xml b/modules/tcpops/doc/tcpops.xml new file mode 100644 index 00000000000..1c3a30542f5 --- /dev/null +++ b/modules/tcpops/doc/tcpops.xml @@ -0,0 +1,44 @@ + + + + + + TCP Ops module + + + Camille + Oudot + Orange +
+ camille.oudot@orange.com +
+
+
+ + 2015 + Orange + +
+ + + + Admin Guide +
+ 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). + +
+ + + +
+
+ diff --git a/modules/tcpops/tcpops.c b/modules/tcpops/tcpops.c new file mode 100644 index 00000000000..49268dcde65 --- /dev/null +++ b/modules/tcpops/tcpops.c @@ -0,0 +1,171 @@ +/** + * Copyright 2015 (C) Orange + * + * + * 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 +#include +#include +#include + +#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 diff --git a/modules/tcpops/tcpops.h b/modules/tcpops/tcpops.h new file mode 100644 index 00000000000..6f608233d48 --- /dev/null +++ b/modules/tcpops/tcpops.h @@ -0,0 +1,32 @@ +/** + * Copyright 2015 (C) Orange + * + * + * 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_ */ diff --git a/modules/tcpops/tcpops_mod.c b/modules/tcpops/tcpops_mod.c new file mode 100644 index 00000000000..08cbc22f47d --- /dev/null +++ b/modules/tcpops/tcpops_mod.c @@ -0,0 +1,209 @@ +/** + * Copyright 2015 (C) Orange + * + * + * 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 +#include +#include +#include + +#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); +} + +