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