mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
12 KiB
399 lines
12 KiB
/*
|
|
* Copyright (C) 2007 iptelorg GmbH
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Kamailio core :: tcp options
|
|
* \ingroup core
|
|
* Module: \ref core
|
|
*/
|
|
|
|
#include "tcp_options.h"
|
|
#include "dprint.h"
|
|
#include "globals.h"
|
|
#include "timer_ticks.h"
|
|
#include "cfg/cfg.h"
|
|
#include "tcp_init.h" /* DEFAULT* */
|
|
|
|
|
|
|
|
/* default/initial values for tcp config options
|
|
NOTE: all the options are initialized in init_tcp_options()
|
|
depending on compile time defines */
|
|
struct cfg_group_tcp tcp_default_cfg;
|
|
|
|
|
|
|
|
static int fix_connect_to(void* cfg_h, str* gname, str* name, void** val);
|
|
static int fix_send_to(void* cfg_h, str* gname, str* name, void** val);
|
|
static int fix_con_lt(void* cfg_h, str* gname, str* name, void** val);
|
|
static int fix_max_conns(void* cfg_h, str* gname, str* name, void** val);
|
|
static int fix_max_tls_conns(void* cfg_h, str* gname, str* name, void** val);
|
|
|
|
|
|
|
|
/* cfg_group_tcp description (for the config framework)*/
|
|
static cfg_def_t tcp_cfg_def[] = {
|
|
/* name , type |input type| chg type, min, max, fixup, proc. cbk
|
|
description */
|
|
{ "connect_timeout", CFG_VAR_INT | CFG_ATOMIC, -1,
|
|
TICKS_TO_S(MAX_TCP_CON_LIFETIME), fix_connect_to, 0,
|
|
"used only in non-async mode, in seconds"},
|
|
{ "send_timeout", CFG_VAR_INT | CFG_ATOMIC, -1,
|
|
MAX_TCP_CON_LIFETIME, fix_send_to, 0,
|
|
"in seconds"},
|
|
{ "connection_lifetime", CFG_VAR_INT | CFG_ATOMIC, -1,
|
|
MAX_TCP_CON_LIFETIME, fix_con_lt, 0,
|
|
"connection lifetime (in seconds)"},
|
|
{ "max_connections", CFG_VAR_INT | CFG_ATOMIC, 0, (1U<<31)-1,
|
|
fix_max_conns, 0,
|
|
"maximum tcp connections number, soft limit"},
|
|
{ "max_tls_connections", CFG_VAR_INT | CFG_ATOMIC, 0, (1U<<31)-1,
|
|
fix_max_tls_conns,0,
|
|
"maximum tls connections number, soft limit"},
|
|
{ "no_connect", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"if set only accept new connections, never actively open new ones"},
|
|
{ "fd_cache", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0,
|
|
"file descriptor cache for tcp_send"},
|
|
/* tcp async options */
|
|
{ "async", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0,
|
|
"async mode for writes and connects"},
|
|
{ "connect_wait", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0,
|
|
"parallel simultaneous connects to the same dst. (0) or one connect"},
|
|
{ "conn_wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 1024*1024, 0, 0,
|
|
"maximum bytes queued for write per connection (depends on async)"},
|
|
{ "wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 1<<30, 0, 0,
|
|
"maximum bytes queued for write allowed globally (depends on async)"},
|
|
/* see also send_timeout above */
|
|
/* tcp socket options */
|
|
{ "defer_accept", CFG_VAR_INT | CFG_READONLY, 0, 3600, 0, 0,
|
|
"0/1 on linux, seconds on freebsd (see docs)"},
|
|
{ "delayed_ack", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"initial ack will be delayed and sent with the first data segment"},
|
|
{ "syncnt", CFG_VAR_INT | CFG_ATOMIC, 0, 1024, 0, 0,
|
|
"number of syn retransmissions before aborting a connect (0=not set)"},
|
|
{ "linger2", CFG_VAR_INT | CFG_ATOMIC, 0, 3600, 0, 0,
|
|
"lifetime of orphaned sockets in FIN_WAIT2 state in s (0=not set)"},
|
|
{ "keepalive", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"enables/disables keepalives for tcp"},
|
|
{ "keepidle", CFG_VAR_INT | CFG_ATOMIC, 0, 24*3600, 0, 0,
|
|
"time before sending a keepalive if the connection is idle (linux)"},
|
|
{ "keepintvl", CFG_VAR_INT | CFG_ATOMIC, 0, 24*3600, 0, 0,
|
|
"time interval between keepalive probes on failure (linux)"},
|
|
{ "keepcnt", CFG_VAR_INT | CFG_ATOMIC, 0, 1<<10, 0, 0,
|
|
"number of failed keepalives before dropping the connection (linux)"},
|
|
/* other options */
|
|
{ "crlf_ping", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"enable responding to CRLF SIP-level keepalives "},
|
|
{ "accept_aliases", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"turn on/off tcp aliases (see tcp_accept_aliases) "},
|
|
{ "alias_flags", CFG_VAR_INT | CFG_ATOMIC, 0, 2, 0, 0,
|
|
"flags used for adding new aliases (FORCE_ADD:1 , REPLACE:2) "},
|
|
{ "new_conn_alias_flags", CFG_VAR_INT | CFG_ATOMIC, 0, 2, 0, 0,
|
|
"flags for the def. aliases for a new conn. (FORCE_ADD:1, REPLACE:2 "},
|
|
{ "accept_no_cl", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
|
|
"accept TCP messges without Content-Length "},
|
|
/* internal and/or "fixed" versions of some vars
|
|
(not supposed to be writeable, read will provide only debugging value*/
|
|
{ "rd_buf_size", CFG_VAR_INT | CFG_ATOMIC, 512, 16777216, 0, 0,
|
|
"internal read buffer size (should be > max. expected datagram)"},
|
|
{ "wq_blk_size", CFG_VAR_INT | CFG_ATOMIC, 1, 65535, 0, 0,
|
|
"internal async write block size (debugging use only for now)"},
|
|
{0, 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
|
|
void* tcp_cfg; /* tcp config handle */
|
|
|
|
/* set defaults */
|
|
void init_tcp_options()
|
|
{
|
|
tcp_default_cfg.connect_timeout_s=DEFAULT_TCP_CONNECT_TIMEOUT;
|
|
tcp_default_cfg.send_timeout=S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT);
|
|
tcp_default_cfg.con_lifetime=S_TO_TICKS(DEFAULT_TCP_CONNECTION_LIFETIME_S);
|
|
#ifdef USE_TCP
|
|
tcp_default_cfg.max_connections=tcp_max_connections;
|
|
tcp_default_cfg.max_tls_connections=tls_max_connections;
|
|
#else /*USE_TCP*/
|
|
tcp_default_cfg.max_connections=0;
|
|
tcp_default_cfg.max_tls_connections=0;
|
|
#endif /*USE_TCP*/
|
|
#ifdef TCP_ASYNC
|
|
tcp_default_cfg.async=1;
|
|
tcp_default_cfg.tcpconn_wq_max=32*1024; /* 32 k */
|
|
tcp_default_cfg.tcp_wq_max=10*1024*1024; /* 10 MB */
|
|
#ifdef TCP_CONNECT_WAIT
|
|
tcp_default_cfg.tcp_connect_wait=1;
|
|
#endif /* TCP_CONNECT_WAIT */
|
|
#endif /* TCP_ASYNC */
|
|
#ifdef TCP_FD_CACHE
|
|
tcp_default_cfg.fd_cache=1;
|
|
#endif
|
|
#ifdef HAVE_SO_KEEPALIVE
|
|
tcp_default_cfg.keepalive=1;
|
|
#endif
|
|
/*
|
|
#if defined HAVE_TCP_DEFER_ACCEPT || defined HAVE_TCP_ACCEPT_FILTER
|
|
tcp_default_cfg.defer_accept=1;
|
|
#endif
|
|
*/
|
|
#ifdef HAVE_TCP_QUICKACK
|
|
tcp_default_cfg.delayed_ack=1;
|
|
#endif
|
|
tcp_default_cfg.crlf_ping=1;
|
|
tcp_default_cfg.accept_aliases=0; /* don't accept aliases by default */
|
|
/* flags used for adding new aliases */
|
|
tcp_default_cfg.alias_flags=TCP_ALIAS_FORCE_ADD;
|
|
/* flags used for adding the default aliases of a new tcp connection */
|
|
tcp_default_cfg.new_conn_alias_flags=TCP_ALIAS_REPLACE;
|
|
tcp_default_cfg.rd_buf_size=DEFAULT_TCP_BUF_SIZE;
|
|
tcp_default_cfg.wq_blk_size=DEFAULT_TCP_WBUF_SIZE;
|
|
}
|
|
|
|
|
|
|
|
#define W_OPT_NC(option) \
|
|
if (tcp_default_cfg.option){\
|
|
WARN("tcp_options: tcp_" #option \
|
|
" cannot be enabled (recompile needed)\n"); \
|
|
tcp_default_cfg.option=0; \
|
|
}
|
|
|
|
|
|
|
|
#define W_OPT_NS(option) \
|
|
if (tcp_default_cfg.option){\
|
|
WARN("tcp_options: tcp_" #option \
|
|
" cannot be enabled (no OS support)\n"); \
|
|
tcp_default_cfg.option=0; \
|
|
}
|
|
|
|
|
|
|
|
/* if *to<0 to=default_val, else if to>max_val to=max_val */
|
|
static void fix_timeout(char* name, int* to, int default_val, unsigned max_val)
|
|
{
|
|
if (*to < 0) *to=default_val;
|
|
else if ((unsigned)*to > max_val){
|
|
WARN("%s: timeout too big (%u), the maximum value is %u\n",
|
|
name, *to, max_val);
|
|
*to=max_val;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int fix_connect_to(void* cfg_h, str* gname, str* name, void** val)
|
|
{
|
|
int v;
|
|
v=(int)(long)*val;
|
|
fix_timeout("tcp_connect_timeout", &v, DEFAULT_TCP_CONNECT_TIMEOUT,
|
|
TICKS_TO_S(MAX_TCP_CON_LIFETIME));
|
|
*val=(void*)(long)v;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int fix_send_to(void* cfg_h, str* gname, str* name, void** val)
|
|
{
|
|
int v;
|
|
v=S_TO_TICKS((int)(long)*val);
|
|
fix_timeout("tcp_send_timeout", &v, S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT),
|
|
MAX_TCP_CON_LIFETIME);
|
|
*val=(void*)(long)v;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int fix_con_lt(void* cfg_h, str* gname, str* name, void** val)
|
|
{
|
|
int v;
|
|
v=S_TO_TICKS((int)(long)*val);
|
|
fix_timeout("tcp_connection_lifetime", &v,
|
|
MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME);
|
|
*val=(void*)(long)v;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int fix_max_conns(void* cfg_h, str* gname, str* name, void** val)
|
|
{
|
|
int v;
|
|
v=(int)(long)*val;
|
|
#ifdef USE_TCP
|
|
if (v>tcp_max_connections){
|
|
INFO("cannot override hard tcp_max_connections limit, please"
|
|
" restart and increase tcp_max_connections in the cfg.\n");
|
|
v=tcp_max_connections;
|
|
}
|
|
#else /* USE_TCP */
|
|
if (v){
|
|
ERR("TCP support disabled at compile-time, tcp_max_connection is"
|
|
" hardwired to 0.\n");
|
|
v=0;
|
|
}
|
|
#endif /*USE_TCP */
|
|
*val=(void*)(long)v;
|
|
return 0;
|
|
}
|
|
|
|
static int fix_max_tls_conns(void* cfg_h, str* gname, str* name, void** val)
|
|
{
|
|
int v;
|
|
v=(int)(long)*val;
|
|
#ifdef USE_TLS
|
|
if (v>tls_max_connections){
|
|
INFO("cannot override hard tls_max_connections limit, please"
|
|
" restart and increase tls_max_connections in the cfg.\n");
|
|
v=tls_max_connections;
|
|
}
|
|
#else /* USE_TLS */
|
|
if (v){
|
|
ERR("TLS support disabled at compile-time, tls_max_connection is"
|
|
" hardwired to 0.\n");
|
|
v=0;
|
|
}
|
|
#endif /*USE_TLS */
|
|
*val=(void*)(long)v;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/** fix *val according to the cfg entry "name".
|
|
* (*val must be integer)
|
|
* 1. check if *val is between name min..max and if not change it to
|
|
* the corresp. value
|
|
* 2. call fixup callback if defined in the cfg
|
|
* @return 0 on success
|
|
*/
|
|
static int tcp_cfg_def_fix(char* name, int* val)
|
|
{
|
|
cfg_def_t* c;
|
|
str s;
|
|
|
|
for (c=&tcp_cfg_def[0]; c->name; c++){
|
|
if (strcmp(name, c->name)==0){
|
|
/* found */
|
|
if ((c->type & CFG_VAR_INT) && (c->min || c->max)){
|
|
if (*val < c->min) *val=c->min;
|
|
else if (*val > c->max) *val=c->max;
|
|
if (c->on_change_cb){
|
|
s.s=c->name;
|
|
s.len=strlen(s.s);
|
|
return c->on_change_cb(&tcp_default_cfg, NULL, &s, (void*)val);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
WARN("tcp config option \"%s\" not found\n", name);
|
|
return -1; /* not found */
|
|
}
|
|
|
|
|
|
|
|
/* checks & warns if some tcp_option cannot be enabled */
|
|
void tcp_options_check()
|
|
{
|
|
#ifndef TCP_FD_CACHE
|
|
W_OPT_NC(defer_accept);
|
|
#endif
|
|
|
|
#ifndef TCP_ASYNC
|
|
W_OPT_NC(async);
|
|
W_OPT_NC(tcpconn_wq_max);
|
|
W_OPT_NC(tcp_wq_max);
|
|
#endif /* TCP_ASYNC */
|
|
#ifndef TCP_CONNECT_WAIT
|
|
W_OPT_NC(tcp_connect_wait);
|
|
#endif /* TCP_CONNECT_WAIT */
|
|
|
|
if (tcp_default_cfg.tcp_connect_wait && !tcp_default_cfg.async){
|
|
tcp_default_cfg.tcp_connect_wait=0;
|
|
}
|
|
|
|
#if ! defined HAVE_TCP_DEFER_ACCEPT && ! defined HAVE_TCP_ACCEPT_FILTER
|
|
W_OPT_NS(defer_accept);
|
|
#endif
|
|
#ifndef HAVE_TCP_SYNCNT
|
|
W_OPT_NS(syncnt);
|
|
#endif
|
|
#ifndef HAVE_TCP_LINGER2
|
|
W_OPT_NS(linger2);
|
|
#endif
|
|
#ifndef HAVE_TCP_KEEPINTVL
|
|
W_OPT_NS(keepintvl);
|
|
#endif
|
|
#ifndef HAVE_TCP_KEEPIDLE
|
|
W_OPT_NS(keepidle);
|
|
#endif
|
|
#ifndef HAVE_TCP_KEEPCNT
|
|
W_OPT_NS(keepcnt);
|
|
#endif
|
|
if (tcp_default_cfg.keepintvl || tcp_default_cfg.keepidle ||
|
|
tcp_default_cfg.keepcnt){
|
|
tcp_default_cfg.keepalive=1; /* force on */
|
|
}
|
|
#ifndef HAVE_SO_KEEPALIVE
|
|
W_OPT_NS(keepalive);
|
|
#endif
|
|
#ifndef HAVE_TCP_QUICKACK
|
|
W_OPT_NS(delayed_ack);
|
|
#endif
|
|
/* fix various timeouts */
|
|
fix_timeout("tcp_connect_timeout", &tcp_default_cfg.connect_timeout_s,
|
|
DEFAULT_TCP_CONNECT_TIMEOUT,
|
|
TICKS_TO_S(MAX_TCP_CON_LIFETIME));
|
|
fix_timeout("tcp_send_timeout", &tcp_default_cfg.send_timeout,
|
|
S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT),
|
|
MAX_TCP_CON_LIFETIME);
|
|
fix_timeout("tcp_connection_lifetime", &tcp_default_cfg.con_lifetime,
|
|
MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME);
|
|
#ifdef USE_TCP
|
|
tcp_default_cfg.max_connections=tcp_max_connections;
|
|
tcp_default_cfg.max_tls_connections=tls_max_connections;
|
|
#else /* USE_TCP */
|
|
tcp_default_cfg.max_connections=0;
|
|
tcp_default_cfg.max_tls_connections=0;
|
|
#endif /* USE_TCP */
|
|
tcp_cfg_def_fix("rd_buf_size", (int*)&tcp_default_cfg.rd_buf_size);
|
|
tcp_cfg_def_fix("wq_blk_size", (int*)&tcp_default_cfg.wq_blk_size);
|
|
}
|
|
|
|
|
|
|
|
void tcp_options_get(struct cfg_group_tcp* t)
|
|
{
|
|
*t=*(struct cfg_group_tcp*)tcp_cfg;
|
|
}
|
|
|
|
|
|
|
|
/** register tcp config into the configuration framework.
|
|
* @return 0 on succes, -1 on error*/
|
|
int tcp_register_cfg()
|
|
{
|
|
if (cfg_declare("tcp", tcp_cfg_def, &tcp_default_cfg, cfg_sizeof(tcp),
|
|
&tcp_cfg))
|
|
return -1;
|
|
if (tcp_cfg==0){
|
|
BUG("null tcp cfg");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|