20c64cc6 |
/*
* 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.
*/
|
1d0661db |
/*!
* \file |
88693a29 |
* \brief Kamailio core :: tcp options |
1d0661db |
* \ingroup core
* Module: \ref core
*/
|
20c64cc6 |
#include "tcp_options.h"
#include "dprint.h" |
885b9f62 |
#include "globals.h"
#include "timer_ticks.h" |
22db42e4 |
#include "cfg/cfg.h" |
3dc4f620 |
#include "tcp_init.h" /* DEFAULT* */ |
20c64cc6 |
|
22db42e4 |
/* 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;
|
33bfeb9d |
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); |
a38ff36d |
static int fix_max_tls_conns(void* cfg_h, str* gname, str* name, void** val); |
c0c298d5 |
|
22db42e4 |
/* 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 */ |
c0c298d5 |
{ "connect_timeout", CFG_VAR_INT | CFG_ATOMIC, -1,
TICKS_TO_S(MAX_TCP_CON_LIFETIME), fix_connect_to, 0, |
3dc4f620 |
"used only in non-async mode, in seconds"}, |
c0c298d5 |
{ "send_timeout", CFG_VAR_INT | CFG_ATOMIC, -1, |
ffc72fcf |
MAX_TCP_CON_LIFETIME, fix_send_to, 0, |
3dc4f620 |
"in seconds"}, |
c0c298d5 |
{ "connection_lifetime", CFG_VAR_INT | CFG_ATOMIC, -1, |
9a74e06a |
MAX_TCP_CON_LIFETIME, fix_con_lt, 0, |
3dc4f620 |
"connection lifetime (in seconds)"}, |
c0c298d5 |
{ "max_connections", CFG_VAR_INT | CFG_ATOMIC, 0, (1U<<31)-1,
fix_max_conns, 0, |
61f8b970 |
"maximum tcp connections number, soft limit"},
{ "max_tls_connections", CFG_VAR_INT | CFG_ATOMIC, 0, (1U<<31)-1, |
a38ff36d |
fix_max_tls_conns,0, |
61f8b970 |
"maximum tls connections number, soft limit"}, |
de223f01 |
{ "no_connect", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
"if set only accept new connections, never actively open new ones"}, |
3dc4f620 |
{ "fd_cache", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, |
22db42e4 |
"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"}, |
c0c298d5 |
{ "conn_wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 1024*1024, 0, 0, |
22db42e4 |
"maximum bytes queued for write per connection (depends on async)"}, |
c0c298d5 |
{ "wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 1<<30, 0, 0, |
22db42e4 |
"maximum bytes queued for write allowed globally (depends on async)"}, |
ffc72fcf |
/* see also send_timeout above */ |
22db42e4 |
/* tcp socket options */
{ "defer_accept", CFG_VAR_INT | CFG_READONLY, 0, 3600, 0, 0,
"0/1 on linux, seconds on freebsd (see docs)"}, |
c0c298d5 |
{ "delayed_ack", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, |
22db42e4 |
"initial ack will be delayed and sent with the first data segment"}, |
c0c298d5 |
{ "syncnt", CFG_VAR_INT | CFG_ATOMIC, 0, 1024, 0, 0, |
22db42e4 |
"number of syn retransmissions before aborting a connect (0=not set)"}, |
c0c298d5 |
{ "linger2", CFG_VAR_INT | CFG_ATOMIC, 0, 3600, 0, 0, |
22db42e4 |
"lifetime of orphaned sockets in FIN_WAIT2 state in s (0=not set)"}, |
c0c298d5 |
{ "keepalive", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, |
22db42e4 |
"enables/disables keepalives for tcp"}, |
c0c298d5 |
{ "keepidle", CFG_VAR_INT | CFG_ATOMIC, 0, 24*3600, 0, 0, |
22db42e4 |
"time before sending a keepalive if the connection is idle (linux)"}, |
c0c298d5 |
{ "keepintvl", CFG_VAR_INT | CFG_ATOMIC, 0, 24*3600, 0, 0, |
22db42e4 |
"time interval between keepalive probes on failure (linux)"}, |
c0c298d5 |
{ "keepcnt", CFG_VAR_INT | CFG_ATOMIC, 0, 1<<10, 0, 0, |
22db42e4 |
"number of failed keepalives before dropping the connection (linux)"},
/* other options */ |
c0c298d5 |
{ "crlf_ping", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, |
22db42e4 |
"enable responding to CRLF SIP-level keepalives "}, |
c0c298d5 |
{ "accept_aliases", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, |
3dc4f620 |
"turn on/off tcp aliases (see tcp_accept_aliases) "}, |
c0c298d5 |
{ "alias_flags", CFG_VAR_INT | CFG_ATOMIC, 0, 2, 0, 0, |
3dc4f620 |
"flags used for adding new aliases (FORCE_ADD:1 , REPLACE:2) "}, |
c0c298d5 |
{ "new_conn_alias_flags", CFG_VAR_INT | CFG_ATOMIC, 0, 2, 0, 0, |
3dc4f620 |
"flags for the def. aliases for a new conn. (FORCE_ADD:1, REPLACE:2 "}, |
866a3699 |
{ "accept_no_cl", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, |
90cbe54c |
"accept TCP messges without Content-Length "}, |
3dc4f620 |
/* internal and/or "fixed" versions of some vars
(not supposed to be writeable, read will provide only debugging value*/ |
3c9a176b |
{ "rd_buf_size", CFG_VAR_INT | CFG_ATOMIC, 512, 16777216, 0, 0, |
e655392a |
"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)"}, |
22db42e4 |
{0, 0, 0, 0, 0, 0, 0}
};
void* tcp_cfg; /* tcp config handle */ |
20c64cc6 |
/* set defaults */
void init_tcp_options()
{ |
3dc4f620 |
tcp_default_cfg.connect_timeout_s=DEFAULT_TCP_CONNECT_TIMEOUT; |
ffc72fcf |
tcp_default_cfg.send_timeout=S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT); |
9a74e06a |
tcp_default_cfg.con_lifetime=S_TO_TICKS(DEFAULT_TCP_CONNECTION_LIFETIME_S); |
2e8c0383 |
#ifdef USE_TCP |
3dc4f620 |
tcp_default_cfg.max_connections=tcp_max_connections; |
61f8b970 |
tcp_default_cfg.max_tls_connections=tls_max_connections; |
2e8c0383 |
#else /*USE_TCP*/
tcp_default_cfg.max_connections=0; |
a38ff36d |
tcp_default_cfg.max_tls_connections=0; |
2e8c0383 |
#endif /*USE_TCP*/ |
76cb799e |
#ifdef TCP_ASYNC
tcp_default_cfg.async=1; |
22db42e4 |
tcp_default_cfg.tcpconn_wq_max=32*1024; /* 32 k */
tcp_default_cfg.tcp_wq_max=10*1024*1024; /* 10 MB */ |
d22b82a0 |
#ifdef TCP_CONNECT_WAIT |
22db42e4 |
tcp_default_cfg.tcp_connect_wait=1; |
d22b82a0 |
#endif /* TCP_CONNECT_WAIT */ |
76cb799e |
#endif /* TCP_ASYNC */ |
20c64cc6 |
#ifdef TCP_FD_CACHE |
22db42e4 |
tcp_default_cfg.fd_cache=1; |
20c64cc6 |
#endif
#ifdef HAVE_SO_KEEPALIVE |
22db42e4 |
tcp_default_cfg.keepalive=1; |
20c64cc6 |
#endif
/*
#if defined HAVE_TCP_DEFER_ACCEPT || defined HAVE_TCP_ACCEPT_FILTER |
22db42e4 |
tcp_default_cfg.defer_accept=1; |
20c64cc6 |
#endif
*/
#ifdef HAVE_TCP_QUICKACK |
22db42e4 |
tcp_default_cfg.delayed_ack=1; |
20c64cc6 |
#endif |
22db42e4 |
tcp_default_cfg.crlf_ping=1; |
3dc4f620 |
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; |
e655392a |
tcp_default_cfg.rd_buf_size=DEFAULT_TCP_BUF_SIZE;
tcp_default_cfg.wq_blk_size=DEFAULT_TCP_WBUF_SIZE; |
20c64cc6 |
}
#define W_OPT_NC(option) \ |
22db42e4 |
if (tcp_default_cfg.option){\ |
885b9f62 |
WARN("tcp_options: tcp_" #option \ |
a0b4a4b9 |
" cannot be enabled (recompile needed)\n"); \ |
22db42e4 |
tcp_default_cfg.option=0; \ |
20c64cc6 |
}
#define W_OPT_NS(option) \ |
22db42e4 |
if (tcp_default_cfg.option){\ |
885b9f62 |
WARN("tcp_options: tcp_" #option \ |
a0b4a4b9 |
" cannot be enabled (no OS support)\n"); \ |
22db42e4 |
tcp_default_cfg.option=0; \ |
20c64cc6 |
}
|
3dc4f620 |
/* 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;
}
}
|
33bfeb9d |
static int fix_connect_to(void* cfg_h, str* gname, str* name, void** val) |
c0c298d5 |
{
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;
}
|
33bfeb9d |
static int fix_send_to(void* cfg_h, str* gname, str* name, void** val) |
c0c298d5 |
{
int v; |
ffc72fcf |
v=S_TO_TICKS((int)(long)*val);
fix_timeout("tcp_send_timeout", &v, S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT),
MAX_TCP_CON_LIFETIME); |
c0c298d5 |
*val=(void*)(long)v;
return 0;
}
|
33bfeb9d |
static int fix_con_lt(void* cfg_h, str* gname, str* name, void** val) |
c0c298d5 |
{
int v; |
9a74e06a |
v=S_TO_TICKS((int)(long)*val);
fix_timeout("tcp_connection_lifetime", &v,
MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME); |
c0c298d5 |
*val=(void*)(long)v;
return 0;
}
|
33bfeb9d |
static int fix_max_conns(void* cfg_h, str* gname, str* name, void** val) |
c0c298d5 |
{
int v;
v=(int)(long)*val; |
2e8c0383 |
#ifdef USE_TCP |
c0c298d5 |
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;
} |
2e8c0383 |
#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 */ |
c0c298d5 |
*val=(void*)(long)v;
return 0;
}
|
a38ff36d |
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;
}
|
c0c298d5 |
|
e655392a |
/** 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); |
33bfeb9d |
return c->on_change_cb(&tcp_default_cfg, NULL, &s, (void*)val); |
e655392a |
}
}
return 0;
}
}
WARN("tcp config option \"%s\" not found\n", name);
return -1; /* not found */
}
|
20c64cc6 |
/* checks & warns if some tcp_option cannot be enabled */
void tcp_options_check()
{
#ifndef TCP_FD_CACHE
W_OPT_NC(defer_accept);
#endif
|
76cb799e |
#ifndef TCP_ASYNC
W_OPT_NC(async); |
885b9f62 |
W_OPT_NC(tcpconn_wq_max);
W_OPT_NC(tcp_wq_max); |
76cb799e |
#endif /* TCP_ASYNC */ |
d22b82a0 |
#ifndef TCP_CONNECT_WAIT
W_OPT_NC(tcp_connect_wait);
#endif /* TCP_CONNECT_WAIT */
|
76cb799e |
if (tcp_default_cfg.tcp_connect_wait && !tcp_default_cfg.async){ |
22db42e4 |
tcp_default_cfg.tcp_connect_wait=0; |
d22b82a0 |
}
|
20c64cc6 |
#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 |
3dc4f620 |
if (tcp_default_cfg.keepintvl || tcp_default_cfg.keepidle || |
22db42e4 |
tcp_default_cfg.keepcnt){
tcp_default_cfg.keepalive=1; /* force on */ |
20c64cc6 |
}
#ifndef HAVE_SO_KEEPALIVE
W_OPT_NS(keepalive);
#endif
#ifndef HAVE_TCP_QUICKACK
W_OPT_NS(delayed_ack);
#endif |
3dc4f620 |
/* 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)); |
ffc72fcf |
fix_timeout("tcp_send_timeout", &tcp_default_cfg.send_timeout,
S_TO_TICKS(DEFAULT_TCP_SEND_TIMEOUT),
MAX_TCP_CON_LIFETIME); |
9a74e06a |
fix_timeout("tcp_connection_lifetime", &tcp_default_cfg.con_lifetime,
MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME); |
2e8c0383 |
#ifdef USE_TCP |
3dc4f620 |
tcp_default_cfg.max_connections=tcp_max_connections; |
3ab8db54 |
tcp_default_cfg.max_tls_connections=tls_max_connections; |
2e8c0383 |
#else /* USE_TCP */
tcp_default_cfg.max_connections=0; |
3ab8db54 |
tcp_default_cfg.max_tls_connections=0; |
2e8c0383 |
#endif /* USE_TCP */ |
e655392a |
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); |
20c64cc6 |
}
|
22db42e4 |
void tcp_options_get(struct cfg_group_tcp* t) |
20c64cc6 |
{ |
c0c298d5 |
*t=*(struct cfg_group_tcp*)tcp_cfg; |
22db42e4 |
}
/** 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; |
20c64cc6 |
} |