20c64cc6 |
/*
* $Id$
*
* 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.
*/
/*
* tcp options
*
* History:
* --------
* 2007-11-28 created by andrei
|
22db42e4 |
* 2009-03-05 use cfg framework (andrei)
|
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);
|
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,
|
3dc4f620 |
"maximum connection 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 "},
/* internal and/or "fixed" versions of some vars
(not supposed to be writeable, read will provide only debugging value*/
|
e655392a |
{ "rd_buf_size", CFG_VAR_INT | CFG_ATOMIC, 512, 65536, 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)"},
|
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;
|
2e8c0383 |
#else /*USE_TCP*/
tcp_default_cfg.max_connections=0;
#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;
}
|
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;
|
2e8c0383 |
#else /* USE_TCP */
tcp_default_cfg.max_connections=0;
#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 |
}
|