Browse code

core: forward: tcp fallback for big udp packets

- support for tcp, tls or sctp fallback for udp forwarded requests
that end up bigger then udp_mtu (configurable, disabled by
default). For such messages only the Via is changed (for example
the original built for udp Record-Route is kept untouched so
that subsequent messages in the dialog will use udp if smaller
then udp_mtu).
- udp_mtu and udp_mtu_try_proto (fallback proto) can be changed at
runtime via the cfg framework
(e.g. sercmd cfg.set_now_int core udp_mtu 1300;
cfg.set_now_int core udp_mtu_try_proto 2 )
- force_rport can now be set globally using the config framework
(e.g. sercmd cfg.set_now_int core force_rport 1 )


Author: Andrei Pelinescu-Onciul <andrei@iptel.org>

Andrei Pelinescu-Onciul authored on 18/12/2008 16:06:03
Showing 5 changed files
... ...
@@ -41,6 +41,7 @@
41 41
 #if defined PKG_MALLOC || defined SHM_MEM
42 42
 #include "pt.h"
43 43
 #endif
44
+#include "msg_translator.h" /* fix_global_req_flags() */
44 45
 #include "cfg/cfg.h"
45 46
 #include "cfg_core.h"
46 47
 
... ...
@@ -88,6 +89,9 @@ struct cfg_group_core default_core_cfg = {
88 88
 #ifdef SHM_MEM
89 89
 	0, /* mem_dump_shm */
90 90
 #endif
91
+	0, /* udp_mtu (disabled by default) */
92
+	0, /* udp_mtu_try_proto -> default disabled */
93
+	0  /* force_rport */ 
91 94
 };
92 95
 
93 96
 void	*core_cfg = &default_core_cfg;
... ...
@@ -177,5 +181,12 @@ cfg_def_t core_cfg_def[] = {
177 177
 	{"mem_dump_shm",	CFG_VAR_INT,	0, 0, mem_dump_shm_fixup, 0,
178 178
 		"dump shared memory status"},
179 179
 #endif
180
+	{"udp_mtu",	CFG_VAR_INT|CFG_ATOMIC,	0, 65535, 0, 0,
181
+		"fallback to a congestion controlled protocol if send size"
182
+			" exceeds udp_mtu"},
183
+	{"udp_mtu_try_proto", CFG_VAR_INT, 1, 4, 0, fix_global_req_flags,
184
+		"if send size > udp_mtu use proto (1 udp, 2 tcp, 3 tls, 4 sctp)"},
185
+	{"force_rport",     CFG_VAR_INT, 0, 1,  0, fix_global_req_flags,
186
+		"force rport for all the received messages" },
180 187
 	{0, 0, 0, 0, 0, 0}
181 188
 };
... ...
@@ -85,6 +85,9 @@ struct cfg_group_core {
85 85
 #ifdef SHM_MEM
86 86
 	int mem_dump_shm;
87 87
 #endif
88
+	int udp_mtu; /**< maximum send size for udp, if > try another protocol*/
89
+	int udp_mtu_try_proto; /**< if packet> udp_mtu, try proto (e.g. TCP) */
90
+	int force_rport; /**< if set rport will always be forced*/
88 91
 };
89 92
 
90 93
 extern struct cfg_group_core default_core_cfg;
... ...
@@ -61,6 +61,10 @@
61 61
  *              (rfc3486) (andrei)
62 62
  * 2007-08-31  id_builder() and via_builder() are grouped into one function:
63 63
  *             create_via_hf() -- tm module needs them as well (Miklos)
64
+ * 2008-12-17  build_req_from_sip_req() will now fallback to tcp, tls or sctp
65
+ *              if packet size > udp_mtu and fallback is enabled 
66
+ *             build_req_from_sip_req() uses now global_req_flags along
67
+ *               msg->msg_flags  (andrei)
64 68
  *
65 69
  */
66 70
 /* Via special params:
... ...
@@ -134,6 +138,8 @@
134 134
 #include "resolve.h"
135 135
 #include "ut.h"
136 136
 #include "pt.h"
137
+#include "cfg/cfg.h"
138
+#include "forward.h"
137 139
 
138 140
 
139 141
 #define append_str(_dest,_src,_len) \
... ...
@@ -148,9 +154,35 @@
148 148
 extern char version[];
149 149
 extern int version_len;
150 150
 
151
+/* global flags for build_req_from_sip_req */
152
+static unsigned int global_req_flags=0;
151 153
 
152 154
 
153 155
 
156
+/** per process fixup function for global_req_flags.
157
+  * It should be called from the configuration framework.
158
+  */
159
+void fix_global_req_flags( str* name)
160
+{
161
+	global_req_flags=0;
162
+	switch(cfg_get(core, core_cfg, udp_mtu_try_proto)){
163
+		case PROTO_NONE:
164
+		case PROTO_UDP:
165
+			/* do nothing */
166
+			break;
167
+		case PROTO_TCP:
168
+			global_req_flags|=FL_MTU_TCP_FB;
169
+			break;
170
+		case PROTO_TLS:
171
+			global_req_flags|=FL_MTU_TLS_FB;
172
+			break;
173
+		case PROTO_SCTP:
174
+			global_req_flags|=FL_MTU_SCTP_FB;
175
+			break;
176
+	}
177
+	if (cfg_get(core, core_cfg, force_rport))
178
+		global_req_flags|=FL_FORCE_RPORT;
179
+}
154 180
 
155 181
 
156 182
 
... ...
@@ -1419,20 +1451,54 @@ error:
1419 1419
 
1420 1420
 
1421 1421
 
1422
+/** builds a request in memory from another sip request.
1423
+  *
1424
+  * Side-effects: - it adds lumps to the msg which are _not_ cleaned.
1425
+  * All the added lumps are HDR_VIA_T.
1426
+  *               - it might change send_info->proto and send_info->send_socket
1427
+  *                 if proto fallback is enabled (see below).
1428
+  *
1429
+  * Uses also global_req_flags ( OR'ed with msg->msg_flags, see send_info
1430
+  * below).
1431
+  *
1432
+  * @param msg  - sip message structure, complete with lumps
1433
+  * @param returned_len - result length (filled in)
1434
+  * @param send_info  - dest_info structure (value/result), contains where the
1435
+  *                     packet will be sent to (it's needed for building a 
1436
+  *                     correct via, fill RR lumps a.s.o.). If MTU based
1437
+  *                     protocol fall-back is enabled (see flags below),
1438
+  *                     send_info->proto might be updated with the new
1439
+  *                     protocol.
1440
+  *                     msg->msg_flags used:
1441
+  *                     - FL_TCP_MTU_FB, FL_TLS_MTU_FB and FL_SCTP_MTU_FB -
1442
+  *                       fallback to the corresp. proto if the built 
1443
+  *                       message > mtu and send_info->proto==PROTO_UDP. 
1444
+  *                       It will also update send_info->proto.
1445
+  *                     - FL_FORCE_RPORT: add rport to via
1446
+  *
1447
+  * @return pointer to the new request (pkg_malloc'ed, needs freeing when
1448
+  *   done) and sets returned_len or 0 on error.
1449
+  */
1422 1450
 char * build_req_buf_from_sip_req( struct sip_msg* msg,
1423 1451
 								unsigned int *returned_len,
1424
-								struct dest_info* send_info)
1452
+								struct dest_info* send_info
1453
+								)
1425 1454
 {
1426
-	unsigned int len, new_len, received_len, rport_len, uri_len, via_len, body_delta;
1455
+	unsigned int len, new_len, received_len, rport_len, uri_len, via_len,
1456
+				 body_delta;
1427 1457
 	char* line_buf;
1428 1458
 	char* received_buf;
1429 1459
 	char* rport_buf;
1430 1460
 	char* new_buf;
1431 1461
 	char* buf;
1432 1462
 	unsigned int offset, s_offset, size;
1433
-	struct lump* anchor;
1463
+	struct lump* via_anchor;
1464
+	struct lump* via_lump;
1434 1465
 	struct lump* via_insert_param;
1435 1466
 	str branch;
1467
+	unsigned int flags;
1468
+	unsigned int udp_mtu;
1469
+	struct socket_info* ss;
1436 1470
 
1437 1471
 	via_insert_param=0;
1438 1472
 	uri_len=0;
... ...
@@ -1445,9 +1511,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1445 1445
 	rport_buf=0;
1446 1446
 	line_buf=0;
1447 1447
 
1448
-	     /* Calculate message body difference and adjust
1449
-	      * Content-Length
1450
-	      */
1448
+	flags=msg->msg_flags|global_req_flags;
1449
+	/* Calculate message body difference and adjust Content-Length */
1451 1450
 	body_delta = lumps_len(msg, msg->body_lumps, send_info);
1452 1451
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
1453 1452
 		LOG(L_ERR, "ERROR: build_req_buf_from_sip_req: Error while adjusting"
... ...
@@ -1455,13 +1520,14 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1455 1455
 		goto error00;
1456 1456
 	}
1457 1457
 
1458
-	/* create a the via header */
1458
+	/* create the via header */
1459 1459
 	branch.s=msg->add_to_branch_s;
1460 1460
 	branch.len=msg->add_to_branch_len;
1461 1461
 
1462 1462
 	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1463 1463
 	if (!line_buf){
1464
-		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
1464
+		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
1465
+					"memory allocation failure\n");
1465 1466
 		goto error00;
1466 1467
 	}
1467 1468
 	/* check if received needs to be added */
... ...
@@ -1478,7 +1544,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1478 1478
 	 *  - if via already contains an rport add it and overwrite the previous
1479 1479
 	 *  rport value if present (if you don't want to overwrite the previous
1480 1480
 	 *  version remove the comments) */
1481
-	if ((msg->msg_flags&FL_FORCE_RPORT)||
1481
+	if ((flags&FL_FORCE_RPORT)||
1482 1482
 			(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
1483 1483
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
1484 1484
 			LOG(L_ERR, "ERROR: build_req_buf_from_sip_req:"
... ...
@@ -1487,13 +1553,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1487 1487
 		}
1488 1488
 	}
1489 1489
 
1490
-	/* add via header to the list */
1491
-	/* try to add it before msg. 1st via */
1492
-	/* add first via, as an anchor for second via*/
1493
-	anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
1494
-	if (anchor==0) goto error01;
1495
-	if (insert_new_lump_before(anchor, line_buf, via_len, HDR_VIA_T)==0)
1496
-		goto error01;
1497 1490
 	/* find out where the offset of the first parameter that should be added
1498 1491
 	 * (after host:port), needed by add receive & maybe rport */
1499 1492
 	if (msg->via1->params.s){
... ...
@@ -1546,10 +1605,57 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1546 1546
 	}
1547 1547
 
1548 1548
 	/* compute new msg len and fix overlapping zones*/
1549
-	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info);
1549
+	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info)+via_len;
1550 1550
 #ifdef XL_DEBUG
1551 1551
 	LOG(L_ERR, "DEBUG: new_len(%d)=len(%d)+lumps_len\n", new_len, len);
1552 1552
 #endif
1553
+	udp_mtu=cfg_get(core, core_cfg, udp_mtu);
1554
+	if (unlikely((send_info->proto==PROTO_UDP) && udp_mtu && 
1555
+					(flags & FL_MTU_FB_MASK) && (new_len>udp_mtu))){
1556
+		ss=0;
1557
+#ifdef USE_TCP
1558
+		if (!tcp_disable && (flags & FL_MTU_TCP_FB) &&
1559
+				(ss=get_send_socket(msg, &send_info->to, PROTO_TCP))){
1560
+			send_info->proto=PROTO_TCP;
1561
+		}
1562
+	#ifdef USE_TLS
1563
+		else if (!tls_disable && (flags & FL_MTU_TLS_FB) &&
1564
+				(ss=get_send_socket(msg, &send_info->to, PROTO_TLS))){
1565
+			send_info->proto=PROTO_TLS;
1566
+		}
1567
+	#endif /* USE_TLS */
1568
+#endif /* USE_TCP */
1569
+#ifdef USE_SCTP
1570
+	#ifdef USE_TCP
1571
+		else
1572
+	#endif /* USE_TCP */
1573
+		 if (!sctp_disable && (flags & FL_MTU_SCTP_FB) &&
1574
+				(ss=get_send_socket(msg, &send_info->to, PROTO_SCTP))){
1575
+			send_info->proto=PROTO_SCTP;
1576
+		 }
1577
+#endif /* USE_SCTP */
1578
+		
1579
+		if (ss){
1580
+			send_info->send_sock=ss;
1581
+			new_len-=via_len;
1582
+			pkg_free(line_buf);
1583
+			line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1584
+			if (!line_buf){
1585
+				LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
1586
+							"memory allocation failure!\n");
1587
+				goto error00;
1588
+			}
1589
+			new_len+=via_len;
1590
+		}
1591
+	}
1592
+	/* add via header to the list */
1593
+	/* try to add it before msg. 1st via */
1594
+	/* add first via, as an anchor for second via*/
1595
+	via_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
1596
+	if (via_anchor==0) goto error04;
1597
+	if ((via_lump=insert_new_lump_before(via_anchor, line_buf, via_len,
1598
+											HDR_VIA_T))==0)
1599
+		goto error04;
1553 1600
 
1554 1601
 	if (msg->new_uri.s){
1555 1602
 		uri_len=msg->new_uri.len;
... ...
@@ -1593,11 +1699,12 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1593 1593
 	return new_buf;
1594 1594
 
1595 1595
 error01:
1596
-	if (line_buf) pkg_free(line_buf);
1597 1596
 error02:
1598 1597
 	if (received_buf) pkg_free(received_buf);
1599 1598
 error03:
1600 1599
 	if (rport_buf) pkg_free(rport_buf);
1600
+error04:
1601
+	if (line_buf) pkg_free(line_buf);
1601 1602
 error00:
1602 1603
 	*returned_len=0;
1603 1604
 	return 0;
... ...
@@ -1729,7 +1836,7 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
1729 1729
 		}
1730 1730
 	}
1731 1731
 	/* check if rport needs to be updated */
1732
-	if ( (msg->msg_flags&FL_FORCE_RPORT)||
1732
+	if ( ((msg->msg_flags|global_req_flags)&FL_FORCE_RPORT)||
1733 1733
 		(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
1734 1734
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
1735 1735
 			LOG(L_ERR, "ERROR: build_res_buf_from_sip_req:"
... ...
@@ -149,5 +149,7 @@ char * build_all( struct sip_msg* msg, int adjust_clen,
149 149
 			int *error,
150 150
 			struct dest_info* send_info);
151 151
 
152
+/** cfg framework fixup */
153
+void fix_global_req_flags( str* name);
152 154
 
153 155
 #endif
... ...
@@ -92,6 +92,11 @@ enum request_method { METHOD_UNDEF=0, METHOD_INVITE=1, METHOD_CANCEL=2, METHOD_A
92 92
                                 (for failure route use) */
93 93
 #define FL_HASH_INDEX  128 /* msg->hash_index contains a valid value (tm use)*/
94 94
 
95
+#define FL_MTU_TCP_FB   256
96
+#define FL_MTU_TLS_FB   512
97
+#define FL_MTU_SCTP_FB 1024
98
+#define FL_MTU_FB_MASK  (FL_MTU_TCP_FB|FL_MTU_TLS_FB|FL_MTU_SCTP_FB)
99
+
95 100
 
96 101
 #define IFISMETHOD(methodname,firstchar)                                  \
97 102
 if (  (*tmp==(firstchar) || *tmp==((firstchar) | 32)) &&                  \