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 89
 #ifdef SHM_MEM
89 90
 	0, /* mem_dump_shm */
90 91
 #endif
92
+	0, /* udp_mtu (disabled by default) */
93
+	0, /* udp_mtu_try_proto -> default disabled */
94
+	0  /* force_rport */ 
91 95
 };
92 96
 
93 97
 void	*core_cfg = &default_core_cfg;
... ...
@@ -177,5 +181,12 @@ cfg_def_t core_cfg_def[] = {
177 181
 	{"mem_dump_shm",	CFG_VAR_INT,	0, 0, mem_dump_shm_fixup, 0,
178 182
 		"dump shared memory status"},
179 183
 #endif
184
+	{"udp_mtu",	CFG_VAR_INT|CFG_ATOMIC,	0, 65535, 0, 0,
185
+		"fallback to a congestion controlled protocol if send size"
186
+			" exceeds udp_mtu"},
187
+	{"udp_mtu_try_proto", CFG_VAR_INT, 1, 4, 0, fix_global_req_flags,
188
+		"if send size > udp_mtu use proto (1 udp, 2 tcp, 3 tls, 4 sctp)"},
189
+	{"force_rport",     CFG_VAR_INT, 0, 1,  0, fix_global_req_flags,
190
+		"force rport for all the received messages" },
180 191
 	{0, 0, 0, 0, 0, 0}
181 192
 };
... ...
@@ -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 138
 #include "resolve.h"
135 139
 #include "ut.h"
136 140
 #include "pt.h"
141
+#include "cfg/cfg.h"
142
+#include "forward.h"
137 143
 
138 144
 
139 145
 #define append_str(_dest,_src,_len) \
... ...
@@ -148,9 +154,35 @@
148 154
 extern char version[];
149 155
 extern int version_len;
150 156
 
157
+/* global flags for build_req_from_sip_req */
158
+static unsigned int global_req_flags=0;
151 159
 
152 160
 
153 161
 
162
+/** per process fixup function for global_req_flags.
163
+  * It should be called from the configuration framework.
164
+  */
165
+void fix_global_req_flags( str* name)
166
+{
167
+	global_req_flags=0;
168
+	switch(cfg_get(core, core_cfg, udp_mtu_try_proto)){
169
+		case PROTO_NONE:
170
+		case PROTO_UDP:
171
+			/* do nothing */
172
+			break;
173
+		case PROTO_TCP:
174
+			global_req_flags|=FL_MTU_TCP_FB;
175
+			break;
176
+		case PROTO_TLS:
177
+			global_req_flags|=FL_MTU_TLS_FB;
178
+			break;
179
+		case PROTO_SCTP:
180
+			global_req_flags|=FL_MTU_SCTP_FB;
181
+			break;
182
+	}
183
+	if (cfg_get(core, core_cfg, force_rport))
184
+		global_req_flags|=FL_FORCE_RPORT;
185
+}
154 186
 
155 187
 
156 188
 
... ...
@@ -1419,20 +1451,54 @@ error:
1419 1451
 
1420 1452
 
1421 1453
 
1454
+/** builds a request in memory from another sip request.
1455
+  *
1456
+  * Side-effects: - it adds lumps to the msg which are _not_ cleaned.
1457
+  * All the added lumps are HDR_VIA_T.
1458
+  *               - it might change send_info->proto and send_info->send_socket
1459
+  *                 if proto fallback is enabled (see below).
1460
+  *
1461
+  * Uses also global_req_flags ( OR'ed with msg->msg_flags, see send_info
1462
+  * below).
1463
+  *
1464
+  * @param msg  - sip message structure, complete with lumps
1465
+  * @param returned_len - result length (filled in)
1466
+  * @param send_info  - dest_info structure (value/result), contains where the
1467
+  *                     packet will be sent to (it's needed for building a 
1468
+  *                     correct via, fill RR lumps a.s.o.). If MTU based
1469
+  *                     protocol fall-back is enabled (see flags below),
1470
+  *                     send_info->proto might be updated with the new
1471
+  *                     protocol.
1472
+  *                     msg->msg_flags used:
1473
+  *                     - FL_TCP_MTU_FB, FL_TLS_MTU_FB and FL_SCTP_MTU_FB -
1474
+  *                       fallback to the corresp. proto if the built 
1475
+  *                       message > mtu and send_info->proto==PROTO_UDP. 
1476
+  *                       It will also update send_info->proto.
1477
+  *                     - FL_FORCE_RPORT: add rport to via
1478
+  *
1479
+  * @return pointer to the new request (pkg_malloc'ed, needs freeing when
1480
+  *   done) and sets returned_len or 0 on error.
1481
+  */
1422 1482
 char * build_req_buf_from_sip_req( struct sip_msg* msg,
1423 1483
 								unsigned int *returned_len,
1424
-								struct dest_info* send_info)
1484
+								struct dest_info* send_info
1485
+								)
1425 1486
 {
1426
-	unsigned int len, new_len, received_len, rport_len, uri_len, via_len, body_delta;
1487
+	unsigned int len, new_len, received_len, rport_len, uri_len, via_len,
1488
+				 body_delta;
1427 1489
 	char* line_buf;
1428 1490
 	char* received_buf;
1429 1491
 	char* rport_buf;
1430 1492
 	char* new_buf;
1431 1493
 	char* buf;
1432 1494
 	unsigned int offset, s_offset, size;
1433
-	struct lump* anchor;
1495
+	struct lump* via_anchor;
1496
+	struct lump* via_lump;
1434 1497
 	struct lump* via_insert_param;
1435 1498
 	str branch;
1499
+	unsigned int flags;
1500
+	unsigned int udp_mtu;
1501
+	struct socket_info* ss;
1436 1502
 
1437 1503
 	via_insert_param=0;
1438 1504
 	uri_len=0;
... ...
@@ -1445,9 +1511,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1445 1511
 	rport_buf=0;
1446 1512
 	line_buf=0;
1447 1513
 
1448
-	     /* Calculate message body difference and adjust
1449
-	      * Content-Length
1450
-	      */
1514
+	flags=msg->msg_flags|global_req_flags;
1515
+	/* Calculate message body difference and adjust Content-Length */
1451 1516
 	body_delta = lumps_len(msg, msg->body_lumps, send_info);
1452 1517
 	if (adjust_clen(msg, body_delta, send_info->proto) < 0) {
1453 1518
 		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 1520
 		goto error00;
1456 1521
 	}
1457 1522
 
1458
-	/* create a the via header */
1523
+	/* create the via header */
1459 1524
 	branch.s=msg->add_to_branch_s;
1460 1525
 	branch.len=msg->add_to_branch_len;
1461 1526
 
1462 1527
 	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1463 1528
 	if (!line_buf){
1464
-		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
1529
+		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
1530
+					"memory allocation failure\n");
1465 1531
 		goto error00;
1466 1532
 	}
1467 1533
 	/* check if received needs to be added */
... ...
@@ -1478,7 +1544,7 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1478 1544
 	 *  - if via already contains an rport add it and overwrite the previous
1479 1545
 	 *  rport value if present (if you don't want to overwrite the previous
1480 1546
 	 *  version remove the comments) */
1481
-	if ((msg->msg_flags&FL_FORCE_RPORT)||
1547
+	if ((flags&FL_FORCE_RPORT)||
1482 1548
 			(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
1483 1549
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
1484 1550
 			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 1553
 		}
1488 1554
 	}
1489 1555
 
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 1556
 	/* find out where the offset of the first parameter that should be added
1498 1557
 	 * (after host:port), needed by add receive & maybe rport */
1499 1558
 	if (msg->via1->params.s){
... ...
@@ -1546,10 +1605,57 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1546 1605
 	}
1547 1606
 
1548 1607
 	/* compute new msg len and fix overlapping zones*/
1549
-	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info);
1608
+	new_len=len+body_delta+lumps_len(msg, msg->add_rm, send_info)+via_len;
1550 1609
 #ifdef XL_DEBUG
1551 1610
 	LOG(L_ERR, "DEBUG: new_len(%d)=len(%d)+lumps_len\n", new_len, len);
1552 1611
 #endif
1612
+	udp_mtu=cfg_get(core, core_cfg, udp_mtu);
1613
+	if (unlikely((send_info->proto==PROTO_UDP) && udp_mtu && 
1614
+					(flags & FL_MTU_FB_MASK) && (new_len>udp_mtu))){
1615
+		ss=0;
1616
+#ifdef USE_TCP
1617
+		if (!tcp_disable && (flags & FL_MTU_TCP_FB) &&
1618
+				(ss=get_send_socket(msg, &send_info->to, PROTO_TCP))){
1619
+			send_info->proto=PROTO_TCP;
1620
+		}
1621
+	#ifdef USE_TLS
1622
+		else if (!tls_disable && (flags & FL_MTU_TLS_FB) &&
1623
+				(ss=get_send_socket(msg, &send_info->to, PROTO_TLS))){
1624
+			send_info->proto=PROTO_TLS;
1625
+		}
1626
+	#endif /* USE_TLS */
1627
+#endif /* USE_TCP */
1628
+#ifdef USE_SCTP
1629
+	#ifdef USE_TCP
1630
+		else
1631
+	#endif /* USE_TCP */
1632
+		 if (!sctp_disable && (flags & FL_MTU_SCTP_FB) &&
1633
+				(ss=get_send_socket(msg, &send_info->to, PROTO_SCTP))){
1634
+			send_info->proto=PROTO_SCTP;
1635
+		 }
1636
+#endif /* USE_SCTP */
1637
+		
1638
+		if (ss){
1639
+			send_info->send_sock=ss;
1640
+			new_len-=via_len;
1641
+			pkg_free(line_buf);
1642
+			line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1643
+			if (!line_buf){
1644
+				LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: "
1645
+							"memory allocation failure!\n");
1646
+				goto error00;
1647
+			}
1648
+			new_len+=via_len;
1649
+		}
1650
+	}
1651
+	/* add via header to the list */
1652
+	/* try to add it before msg. 1st via */
1653
+	/* add first via, as an anchor for second via*/
1654
+	via_anchor=anchor_lump(msg, msg->via1->hdr.s-buf, 0, HDR_VIA_T);
1655
+	if (via_anchor==0) goto error04;
1656
+	if ((via_lump=insert_new_lump_before(via_anchor, line_buf, via_len,
1657
+											HDR_VIA_T))==0)
1658
+		goto error04;
1553 1659
 
1554 1660
 	if (msg->new_uri.s){
1555 1661
 		uri_len=msg->new_uri.len;
... ...
@@ -1593,11 +1699,12 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1593 1699
 	return new_buf;
1594 1700
 
1595 1701
 error01:
1596
-	if (line_buf) pkg_free(line_buf);
1597 1702
 error02:
1598 1703
 	if (received_buf) pkg_free(received_buf);
1599 1704
 error03:
1600 1705
 	if (rport_buf) pkg_free(rport_buf);
1706
+error04:
1707
+	if (line_buf) pkg_free(line_buf);
1601 1708
 error00:
1602 1709
 	*returned_len=0;
1603 1710
 	return 0;
... ...
@@ -1729,7 +1836,7 @@ char * build_res_buf_from_sip_req( unsigned int code, char *text ,str *new_tag,
1729 1836
 		}
1730 1837
 	}
1731 1838
 	/* check if rport needs to be updated */
1732
-	if ( (msg->msg_flags&FL_FORCE_RPORT)||
1839
+	if ( ((msg->msg_flags|global_req_flags)&FL_FORCE_RPORT)||
1733 1840
 		(msg->via1->rport /*&& msg->via1->rport->value.s==0*/)){
1734 1841
 		if ((rport_buf=rport_builder(msg, &rport_len))==0){
1735 1842
 			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)) &&                  \