tcp: config option for the async write block size
[sip-router] / tcp_options.c
1 /* 
2  * $Id$
3  * 
4  * Copyright (C) 2007 iptelorg GmbH
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * tcp options
20  *
21  * History:
22  * --------
23  *  2007-11-28  created by andrei
24  *  2009-03-05  use cfg framework (andrei)
25  */
26
27 #include "tcp_options.h"
28 #include "dprint.h"
29 #include "globals.h"
30 #include "timer_ticks.h"
31 #include "cfg/cfg.h"
32 #include "tcp_init.h" /* DEFAULT* */
33
34
35
36 /* default/initial values for tcp config options
37    NOTE: all the options are initialized in init_tcp_options()
38    depending on compile time defines */
39 struct cfg_group_tcp tcp_default_cfg;
40 #if 0
41 {
42         1, /* fd_cache, default on */
43         /* tcp async options */
44         0, /* async / tcp_async, default off */
45         1, /* tcp_connect_wait - depends on tcp_async */
46         32*1024, /* tcpconn_wq_max - max. write queue len per connection (32k) */
47         10*1024*1024, /* tcp_wq_max - max.  overall queued bytes  (10MB)*/
48         S_TO_TICKS(tcp_send_timeout), /* tcp_wq_timeout - timeout for queued 
49                                                                          writes, depends on tcp_send_timeout */
50         /* tcp socket options */
51         0, /* defer_accept - on/off*/
52         1, /* delayed_ack - delay ack on connect (on/off)*/
53         0, /* syncnt - numbers of SYNs retrs. before giving up (0 = OS default) */
54         0, /* linger2 - lifetime of orphaned FIN_WAIT2 sockets (0 = OS default)*/
55         1, /* keepalive - on/off */
56         0, /* keepidle - idle time (s) before tcp starts sending keepalives */
57         0, /* keepintvl - interval between keep alives (0 = OS default) */
58         0, /* keepcnt - maximum no. of keepalives (0 = OS default)*/
59         
60         /* other options */
61         1 /* crlf_ping - respond to double CRLF ping/keepalive (on/off) */
62         
63 };
64 #endif
65
66
67
68 static int fix_connect_to(void* cfg_h, str* name, void** val);
69 static int fix_send_to(void* cfg_h, str* name, void** val);
70 static int fix_con_lt(void* cfg_h, str* name, void** val);
71 static int fix_max_conns(void* cfg_h, str* name, void** val);
72
73
74
75 /* cfg_group_tcp description (for the config framework)*/
76 static cfg_def_t tcp_cfg_def[] = {
77         /*   name        , type |input type| chg type, min, max, fixup, proc. cbk 
78               description */
79         { "connect_timeout", CFG_VAR_INT | CFG_ATOMIC,  -1,
80                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME),  fix_connect_to,   0,
81                 "used only in non-async mode, in seconds"},
82         { "send_timeout", CFG_VAR_INT | CFG_ATOMIC,   -1,
83                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME),   fix_send_to,     0,
84                 "in seconds"},
85         { "connection_lifetime", CFG_VAR_INT | CFG_ATOMIC,   -1,
86                                                 MAX_TCP_CON_LIFETIME,               fix_con_lt,      0,
87                 "connection lifetime (in seconds)"},
88         { "max_connections", CFG_VAR_INT | CFG_ATOMIC, 0, (1U<<31)-1,
89                                                                                                                fix_max_conns,    0,
90                 "maximum connection number, soft limit"},
91         { "no_connect",   CFG_VAR_INT | CFG_ATOMIC,      0,   1,      0,         0,
92                 "if set only accept new connections, never actively open new ones"},
93         { "fd_cache",     CFG_VAR_INT | CFG_READONLY,    0,   1,      0,         0,
94                 "file descriptor cache for tcp_send"},
95         /* tcp async options */
96         { "async",        CFG_VAR_INT | CFG_READONLY,    0,   1,      0,         0,
97                 "async mode for writes and connects"},
98         { "connect_wait", CFG_VAR_INT | CFG_READONLY,    0,   1,      0,         0,
99                 "parallel simultaneous connects to the same dst. (0) or one connect"},
100         { "conn_wq_max",  CFG_VAR_INT | CFG_ATOMIC,      0, 1024*1024, 0,        0,
101                 "maximum bytes queued for write per connection (depends on async)"},
102         { "wq_max",       CFG_VAR_INT | CFG_ATOMIC,      0,  1<<30,    0,        0,
103                 "maximum bytes queued for write allowed globally (depends on async)"},
104         /* see also wq_timeout below */
105         /* tcp socket options */
106         { "defer_accept", CFG_VAR_INT | CFG_READONLY,    0,   3600,   0,         0,
107                 "0/1 on linux, seconds on freebsd (see docs)"},
108         { "delayed_ack",  CFG_VAR_INT | CFG_ATOMIC,      0,      1,   0,         0,
109                 "initial ack will be delayed and sent with the first data segment"},
110         { "syncnt",       CFG_VAR_INT | CFG_ATOMIC,      0,   1024,   0,         0,
111                 "number of syn retransmissions before aborting a connect (0=not set)"},
112         { "linger2",      CFG_VAR_INT | CFG_ATOMIC,      0,   3600,   0,         0,
113                 "lifetime of orphaned sockets in FIN_WAIT2 state in s (0=not set)"},
114         { "keepalive",    CFG_VAR_INT | CFG_ATOMIC,      0,      1,   0,         0,
115                 "enables/disables keepalives for tcp"},
116         { "keepidle",     CFG_VAR_INT | CFG_ATOMIC,      0, 24*3600,  0,         0,
117                 "time before sending a keepalive if the connection is idle (linux)"},
118         { "keepintvl",    CFG_VAR_INT | CFG_ATOMIC,      0, 24*3600,  0,         0,
119                 "time interval between keepalive probes on failure (linux)"},
120         { "keepcnt",     CFG_VAR_INT | CFG_ATOMIC,       0,    1<<10,  0,        0,
121                 "number of failed keepalives before dropping the connection (linux)"},
122         /* other options */
123         { "crlf_ping",   CFG_VAR_INT | CFG_ATOMIC,      0,        1,  0,         0,
124                 "enable responding to CRLF SIP-level keepalives "},
125         { "accept_aliases", CFG_VAR_INT | CFG_ATOMIC,   0,        1,  0,         0,
126                 "turn on/off tcp aliases (see tcp_accept_aliases) "},
127         { "alias_flags", CFG_VAR_INT | CFG_ATOMIC,      0,        2,  0,         0,
128                 "flags used for adding new aliases (FORCE_ADD:1 , REPLACE:2) "},
129         { "new_conn_alias_flags", CFG_VAR_INT | CFG_ATOMIC, 0,    2,  0,         0,
130                 "flags for the def. aliases for a new conn. (FORCE_ADD:1, REPLACE:2 "},
131         /* internal and/or "fixed" versions of some vars
132            (not supposed to be writeable, read will provide only debugging value*/
133         { "wq_timeout_ticks",   CFG_VAR_INT | CFG_READONLY, 0,
134                                                                         MAX_TCP_CON_LIFETIME,         0,         0,
135                 "internal send_timeout value in ticks, used in async. mode"},
136         { "rd_buf_size", CFG_VAR_INT | CFG_ATOMIC,    512,    65536,  0,         0,
137                 "internal read buffer size (should be > max. expected datagram)"},
138         { "wq_blk_size", CFG_VAR_INT | CFG_ATOMIC,    1,    65535,  0,         0,
139                 "internal async write block size (debugging use only for now)"},
140         {0, 0, 0, 0, 0, 0, 0}
141 };
142
143
144 void* tcp_cfg; /* tcp config handle */
145
146 /* set defaults */
147 void init_tcp_options()
148 {
149         tcp_default_cfg.connect_timeout_s=DEFAULT_TCP_CONNECT_TIMEOUT;
150         tcp_default_cfg.send_timeout_s=DEFAULT_TCP_SEND_TIMEOUT;
151         tcp_default_cfg.con_lifetime=S_TO_TICKS(DEFAULT_TCP_CONNECTION_LIFETIME_S);
152         tcp_default_cfg.max_connections=tcp_max_connections;
153 #ifdef TCP_ASYNC
154         tcp_default_cfg.async=1;
155         tcp_default_cfg.tcpconn_wq_max=32*1024; /* 32 k */
156         tcp_default_cfg.tcp_wq_max=10*1024*1024; /* 10 MB */
157         tcp_default_cfg.tcp_wq_timeout=S_TO_TICKS(tcp_default_cfg.send_timeout_s);
158 #ifdef TCP_CONNECT_WAIT
159         tcp_default_cfg.tcp_connect_wait=1;
160 #endif /* TCP_CONNECT_WAIT */
161 #endif /* TCP_ASYNC */
162 #ifdef TCP_FD_CACHE
163         tcp_default_cfg.fd_cache=1;
164 #endif
165 #ifdef HAVE_SO_KEEPALIVE
166         tcp_default_cfg.keepalive=1;
167 #endif
168 /*
169 #if defined HAVE_TCP_DEFER_ACCEPT || defined HAVE_TCP_ACCEPT_FILTER
170         tcp_default_cfg.defer_accept=1;
171 #endif
172 */
173 #ifdef HAVE_TCP_QUICKACK
174         tcp_default_cfg.delayed_ack=1;
175 #endif
176         tcp_default_cfg.crlf_ping=1;
177         tcp_default_cfg.accept_aliases=0; /* don't accept aliases by default */
178         /* flags used for adding new aliases */
179         tcp_default_cfg.alias_flags=TCP_ALIAS_FORCE_ADD;
180         /* flags used for adding the default aliases of a new tcp connection */
181         tcp_default_cfg.new_conn_alias_flags=TCP_ALIAS_REPLACE;
182         tcp_default_cfg.rd_buf_size=DEFAULT_TCP_BUF_SIZE;
183         tcp_default_cfg.wq_blk_size=DEFAULT_TCP_WBUF_SIZE;
184 }
185
186
187
188 #define W_OPT_NC(option) \
189         if (tcp_default_cfg.option){\
190                 WARN("tcp_options: tcp_" #option \
191                                 " cannot be enabled (recompile needed)\n"); \
192                 tcp_default_cfg.option=0; \
193         }
194
195
196
197 #define W_OPT_NS(option) \
198         if (tcp_default_cfg.option){\
199                 WARN("tcp_options: tcp_" #option \
200                                 " cannot be enabled (no OS support)\n"); \
201                 tcp_default_cfg.option=0; \
202         }
203
204
205
206 /* if *to<0 to=default_val, else if to>max_val to=max_val */
207 static void fix_timeout(char* name, int* to, int default_val, unsigned max_val)
208 {
209         if (*to < 0) *to=default_val;
210         else if ((unsigned)*to > max_val){
211                 WARN("%s: timeout too big (%u), the maximum value is %u\n",
212                                 name, *to, max_val);
213                 *to=max_val;
214         }
215 }
216
217
218
219 static int fix_connect_to(void* cfg_h, str* name, void** val)
220 {
221         int v;
222         v=(int)(long)*val;
223         fix_timeout("tcp_connect_timeout", &v, DEFAULT_TCP_CONNECT_TIMEOUT,
224                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME));
225         *val=(void*)(long)v;
226         return 0;
227 }
228
229
230 static int fix_send_to(void* cfg_h, str* name, void** val)
231 {
232         int v;
233         v=(int)(long)*val;
234         fix_timeout("tcp_send_timeout", &v, DEFAULT_TCP_SEND_TIMEOUT,
235                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME));
236         *val=(void*)(long)v;
237 #ifdef TCP_ASYNC
238         ((struct cfg_group_tcp*)cfg_h)->tcp_wq_timeout=S_TO_TICKS(v);
239 #endif /* TCP_ASYNC */
240         return 0;
241 }
242
243
244 static int fix_con_lt(void* cfg_h, str* name, void** val)
245 {
246         int v;
247         v=S_TO_TICKS((int)(long)*val);
248         fix_timeout("tcp_connection_lifetime", &v, 
249                                         MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME);
250         *val=(void*)(long)v;
251         return 0;
252 }
253
254
255 static int fix_max_conns(void* cfg_h, str* name, void** val)
256 {
257         int v;
258         v=(int)(long)*val;
259         if (v>tcp_max_connections){
260                 INFO("cannot override hard tcp_max_connections limit, please"
261                                 " restart and increase tcp_max_connections in the cfg.\n");
262                 v=tcp_max_connections;
263         }
264         *val=(void*)(long)v;
265         return 0;
266 }
267
268
269
270 /** fix *val according to the cfg entry "name".
271  * (*val must be integer)
272  * 1. check if *val is between name min..max and if not change it to
273  *    the corresp. value
274  * 2. call fixup callback if defined in the cfg
275  * @return 0 on success
276  */
277 static int tcp_cfg_def_fix(char* name, int* val)
278 {
279         cfg_def_t* c;
280         str s;
281         
282         for (c=&tcp_cfg_def[0]; c->name; c++){
283                 if (strcmp(name, c->name)==0){
284                         /* found */
285                         if ((c->type & CFG_VAR_INT)  && (c->min || c->max)){
286                                 if (*val < c->min) *val=c->min;
287                                 else if (*val > c->max) *val=c->max;
288                                 if (c->on_change_cb){
289                                         s.s=c->name;
290                                         s.len=strlen(s.s);
291                                         return c->on_change_cb(&tcp_default_cfg, &s, (void*)val);
292                                 }
293                         }
294                         return 0;
295                 }
296         }
297         WARN("tcp config option \"%s\" not found\n", name);
298         return -1; /* not found */
299 }
300
301
302
303 /* checks & warns if some tcp_option cannot be enabled */
304 void tcp_options_check()
305 {
306 #ifndef TCP_FD_CACHE
307         W_OPT_NC(defer_accept);
308 #endif
309
310 #ifndef TCP_ASYNC
311         W_OPT_NC(async);
312         W_OPT_NC(tcpconn_wq_max);
313         W_OPT_NC(tcp_wq_max);
314         W_OPT_NC(tcp_wq_timeout);
315 #endif /* TCP_ASYNC */
316 #ifndef TCP_CONNECT_WAIT
317         W_OPT_NC(tcp_connect_wait);
318 #endif /* TCP_CONNECT_WAIT */
319         
320         if (tcp_default_cfg.tcp_connect_wait && !tcp_default_cfg.async){
321                 tcp_default_cfg.tcp_connect_wait=0;
322         }
323         
324 #if ! defined HAVE_TCP_DEFER_ACCEPT && ! defined HAVE_TCP_ACCEPT_FILTER
325         W_OPT_NS(defer_accept);
326 #endif
327 #ifndef HAVE_TCP_SYNCNT
328         W_OPT_NS(syncnt);
329 #endif
330 #ifndef HAVE_TCP_LINGER2
331         W_OPT_NS(linger2);
332 #endif
333 #ifndef HAVE_TCP_KEEPINTVL
334         W_OPT_NS(keepintvl);
335 #endif
336 #ifndef HAVE_TCP_KEEPIDLE
337         W_OPT_NS(keepidle);
338 #endif
339 #ifndef HAVE_TCP_KEEPCNT
340         W_OPT_NS(keepcnt);
341 #endif
342         if (tcp_default_cfg.keepintvl || tcp_default_cfg.keepidle ||
343                         tcp_default_cfg.keepcnt){
344                 tcp_default_cfg.keepalive=1; /* force on */
345         }
346 #ifndef HAVE_SO_KEEPALIVE
347         W_OPT_NS(keepalive);
348 #endif
349 #ifndef HAVE_TCP_QUICKACK
350         W_OPT_NS(delayed_ack);
351 #endif
352         /* fix various timeouts */
353         fix_timeout("tcp_connect_timeout", &tcp_default_cfg.connect_timeout_s,
354                                                 DEFAULT_TCP_CONNECT_TIMEOUT,
355                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME));
356         fix_timeout("tcp_send_timeout", &tcp_default_cfg.send_timeout_s,
357                                                 DEFAULT_TCP_SEND_TIMEOUT,
358                                                 TICKS_TO_S(MAX_TCP_CON_LIFETIME));
359         fix_timeout("tcp_connection_lifetime", &tcp_default_cfg.con_lifetime,
360                                                 MAX_TCP_CON_LIFETIME, MAX_TCP_CON_LIFETIME);
361         /* compute timeout in ticks */
362 #ifdef TCP_ASYNC
363         tcp_default_cfg.tcp_wq_timeout=S_TO_TICKS(tcp_default_cfg.send_timeout_s);
364 #endif /* TCP_ASYNC */
365         tcp_default_cfg.max_connections=tcp_max_connections;
366         tcp_cfg_def_fix("rd_buf_size", (int*)&tcp_default_cfg.rd_buf_size);
367         tcp_cfg_def_fix("wq_blk_size", (int*)&tcp_default_cfg.wq_blk_size);
368         
369 }
370
371
372
373 void tcp_options_get(struct cfg_group_tcp* t)
374 {
375         *t=*(struct cfg_group_tcp*)tcp_cfg;
376 }
377
378
379
380 /** register tcp config into the configuration framework.
381  *  @return 0 on succes, -1 on error*/
382 int tcp_register_cfg()
383 {
384         if (cfg_declare("tcp", tcp_cfg_def, &tcp_default_cfg, cfg_sizeof(tcp),
385                                         &tcp_cfg))
386                 return -1;
387         if (tcp_cfg==0){
388                 BUG("null tcp cfg");
389                 return -1;
390         }
391         return 0;
392 }