Browse code

- support for setting the source address in tcp_send() and tcpconn_get() (should allow for a better tcp force_send_socket() in the future) - add multiple aliases for each connection, to cover all the search possiblities: (dst_ip, dst_port), (local_ip, dst_ip, dst_port), (local_ip, local_port, dst_ip, dst_port). - improved connection hash function

Andrei Pelinescu-Onciul authored on 01/08/2007 00:05:40
Showing 8 changed files
... ...
@@ -272,7 +272,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
272 272
 						/*tcp*/
273 273
 						dst.proto=PROTO_TCP;
274 274
 						dst.id=0;
275
-						ret=tcp_send(&dst, tmp, len);
275
+						ret=tcp_send(&dst, 0, tmp, len);
276 276
 				}
277 277
 #endif
278 278
 			}else{
... ...
@@ -281,8 +281,8 @@ found:
281 281
  *               be !=0 
282 282
  *   port      - used only if dst!=0 (else the port in send_info->to is used)
283 283
  *   send_info - filled dest_info structure:
284
- *               if the send_socket memeber is null, a send_socket will be 
285
- *               choosen automatically
284
+ *               if the send_socket member is null, a send_socket will be 
285
+ *               chosen automatically
286 286
  * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
287 287
  * random id along with proto==PROTO_TCP can have bad consequences, same for
288 288
  *   a bogus send_socket value)
... ...
@@ -112,7 +112,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
112 112
 					" support is disabled\n");
113 113
 			goto error;
114 114
 		}else{
115
-			if (tcp_send(dst, buf, len)<0){
115
+			if (tcp_send(dst, 0, buf, len)<0){
116 116
 				STATS_TX_DROPS;
117 117
 				LOG(L_ERR, "msg_send: ERROR: tcp_send failed\n");
118 118
 				goto error;
... ...
@@ -127,7 +127,7 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
127 127
 					" support is disabled\n");
128 128
 			goto error;
129 129
 		}else{
130
-			if (tcp_send(dst, buf, len)<0){
130
+			if (tcp_send(dst, 0, buf, len)<0){
131 131
 				STATS_TX_DROPS;
132 132
 				LOG(L_ERR, "msg_send: ERROR: tcp_send failed\n");
133 133
 				goto error;
... ...
@@ -32,6 +32,7 @@
32 32
  *  2003-04-06  all ports are stored/passed in host byte order now (andrei)
33 33
  *  2006-04-20  comp support in recv_info and dest_info (andrei)
34 34
  *  2006-04-21  added init_dst_from_rcv (andrei)
35
+ *  2007-06-26  added ip_addr_mk_any() (andrei)
35 36
  */
36 37
 
37 38
 #ifndef ip_addr_h
... ...
@@ -43,6 +44,7 @@
43 43
 #include <netinet/in.h>
44 44
 #include <netdb.h>
45 45
 #include "str.h"
46
+#include "compiler_opt.h"
46 47
 
47 48
 
48 49
 #include "dprint.h"
... ...
@@ -204,6 +206,31 @@ inline static int ip_addr_any(struct ip_addr* ip)
204 204
 
205 205
 
206 206
 
207
+/* creates an ANY ip_addr (filled with 0, af and len properly set) */
208
+inline static void ip_addr_mk_any(int af, struct ip_addr* ip)
209
+{
210
+	ip->af=af;
211
+	if (likely(af==AF_INET)){
212
+		ip->len=4;
213
+		ip->u.addr32[0]=0;
214
+	}
215
+#ifdef USE_IPV6
216
+	else{
217
+		ip->len=16;
218
+#if (defined (ULONG_MAX) && ULONG_MAX > 4294967295) || defined LP64
219
+		/* long is 64 bits */
220
+		ip->u.addrl[0]=0;
221
+		ip->u.addrl[1]=0;
222
+#else
223
+		ip->u.addr32[0]=0;
224
+		ip->u.addr32[1]=0;
225
+		ip->u.addr32[2]=0;
226
+		ip->u.addr32[3]=0;
227
+#endif /* ULONG_MAX */
228
+	}
229
+#endif
230
+}
231
+
207 232
 /* returns 1 if ip & net.mask == net.ip ; 0 otherwise & -1 on error 
208 233
 	[ diff. address families ]) */
209 234
 inline static int matchnet(struct ip_addr* ip, struct net* net)
... ...
@@ -196,7 +196,7 @@ static int check_via_address(struct ip_addr* ip, str *name,
196 196
 	if (resolver&DO_DNS){
197 197
 		DBG("check_via_address: doing dns lookup\n");
198 198
 		/* try all names ips */
199
-		he=sip_resolvehost(name, &port, 0); /* FIXME proto? */
199
+		he=sip_resolvehost(name, &port, 0); /* don't use naptr */
200 200
 		if (he && ip->af==he->h_addrtype){
201 201
 			for(i=0;he && he->h_addr_list[i];i++){
202 202
 				if ( memcmp(&he->h_addr_list[i], ip->u.addr, ip->len)==0)
... ...
@@ -31,6 +31,8 @@
31 31
  *  2003-06-30  added tcp_connection flags & state (andrei) 
32 32
  *  2003-10-27  tcp port aliases support added (andrei)
33 33
  *  2006-10-13  added tcp_req_states for STUN (vlada)
34
+ *  2007-07-26  improved tcp connection hash function; increased aliases
35
+ *               hash size (andrei)
34 36
  */
35 37
 
36 38
 
... ...
@@ -43,7 +45,8 @@
43 43
 #include "atomic_ops.h"
44 44
 #include "timer_ticks.h"
45 45
 
46
-#define TCP_CON_MAX_ALIASES 4 /* maximum number of port aliases */
46
+/* maximum number of port aliases x search wildcard possibilities */
47
+#define TCP_CON_MAX_ALIASES (4*3) 
47 48
 
48 49
 #define TCP_BUF_SIZE	4096 
49 50
 #define DEFAULT_TCP_CONNECTION_LIFETIME 120 /* in  seconds */
... ...
@@ -180,25 +183,41 @@ struct tcp_connection{
180 180
 #define TCPCONN_LOCK lock_get(tcpconn_lock);
181 181
 #define TCPCONN_UNLOCK lock_release(tcpconn_lock);
182 182
 
183
-#define TCP_ALIAS_HASH_SIZE 1024
183
+#define TCP_ALIAS_HASH_SIZE 4096
184 184
 #define TCP_ID_HASH_SIZE 1024
185 185
 
186
-static inline unsigned tcp_addr_hash(struct ip_addr* ip, unsigned short port)
186
+/* hash (dst_ip, dst_port, local_ip, local_port) */
187
+static inline unsigned tcp_addr_hash(	struct ip_addr* ip, 
188
+										unsigned short port,
189
+										struct ip_addr* l_ip,
190
+										unsigned short l_port)
187 191
 {
188
-	if(ip->len==4) return (ip->u.addr32[0]^port)&(TCP_ALIAS_HASH_SIZE-1);
192
+	unsigned h;
193
+
194
+	if(ip->len==4)
195
+		h=(ip->u.addr32[0]^port)^(l_ip->u.addr32[0]^l_port);
189 196
 	else if (ip->len==16) 
190
-			return (ip->u.addr32[0]^ip->u.addr32[1]^ip->u.addr32[2]^
191
-					ip->u.addr32[3]^port) & (TCP_ALIAS_HASH_SIZE-1);
197
+		h= (ip->u.addr32[0]^ip->u.addr32[1]^ip->u.addr32[2]^
198
+				ip->u.addr32[3]^port) ^
199
+			(l_ip->u.addr32[0]^l_ip->u.addr32[1]^l_ip->u.addr32[2]^
200
+				l_ip->u.addr32[3]^l_port);
192 201
 	else{
193 202
 		LOG(L_CRIT, "tcp_addr_hash: BUG: bad len %d for an ip address\n",
194 203
 				ip->len);
195 204
 		return 0;
196 205
 	}
206
+	/* make sure the first bits are influenced by all 32
207
+	 * (the first log2(TCP_ALIAS_HASH_SIZE) bits should be a mix of all
208
+	 *  32)*/
209
+	h ^= h>>17;
210
+	h ^= h>>7;
211
+	return h & (TCP_ALIAS_HASH_SIZE-1);
197 212
 }
198 213
 
199 214
 #define tcp_id_hash(id) (id&(TCP_ID_HASH_SIZE-1))
200 215
 
201 216
 struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
217
+									union sockaddr_union* local_addr,
202 218
 									ticks_t timeout);
203 219
 
204 220
 #endif
... ...
@@ -75,6 +75,8 @@
75 75
  *               result in inf. lifetime) (andrei)
76 76
  *  2007-07-25  tcpconn_connect can now bind the socket on a specified
77 77
  *                source addr/port (andrei)
78
+ *  2007-07-26   tcp_send() and tcpconn_get() can now use a specified source
79
+ *                addr./port (andrei)
78 80
  */
79 81
 
80 82
 
... ...
@@ -202,6 +204,11 @@ static io_wait_h io_h;
202 202
 
203 203
 
204 204
 
205
+inline static int _tcpconn_add_alias_unsafe(struct tcp_connection* c, int port,
206
+										struct ip_addr* l_ip, int l_port);
207
+
208
+
209
+
205 210
 /* sets source address used when opening new sockets and no source is specified
206 211
  *  (by default the address is choosen by the kernel)
207 212
  * Should be used only on init.
... ...
@@ -589,23 +596,36 @@ error:
589 589
 
590 590
 /* adds a tcp connection to the tcpconn hashes
591 591
  * Note: it's called _only_ from the tcp_main process */
592
-struct tcp_connection*  tcpconn_add(struct tcp_connection *c)
592
+inline static struct tcp_connection*  tcpconn_add(struct tcp_connection *c)
593 593
 {
594
+	struct ip_addr zero_ip;
594 595
 
595
-	if (c){
596
+	if (likely(c)){
597
+		ip_addr_mk_any(c->rcv.src_ip.af, &zero_ip);
596 598
 		c->id_hash=tcp_id_hash(c->id);
597
-		c->con_aliases[0].hash=tcp_addr_hash(&c->rcv.src_ip, c->rcv.src_port);
599
+		c->aliases=0;
598 600
 		TCPCONN_LOCK;
599 601
 		/* add it at the begining of the list*/
600 602
 		tcpconn_listadd(tcpconn_id_hash[c->id_hash], c, id_next, id_prev);
601
-		/* set the first alias */
602
-		c->con_aliases[0].port=c->rcv.src_port;
603
-		c->con_aliases[0].parent=c;
604
-		tcpconn_listadd(tcpconn_aliases_hash[c->con_aliases[0].hash],
605
-							&c->con_aliases[0], next, prev);
606
-		c->aliases++;
603
+		/* set the aliases */
604
+		/* first alias is for (peer_ip, peer_port, 0 ,0) -- for finding
605
+		 *  any connection to peer_ip, peer_port
606
+		 * the second alias is for (peer_ip, peer_port, local_addr, 0) -- for
607
+		 *  finding any conenction to peer_ip, peer_port from local_addr 
608
+		 * the third alias is for (peer_ip, peer_port, local_addr, local_port) 
609
+		 *   -- for finding if a fully specified connection exists */
610
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &zero_ip, 0);
611
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &c->rcv.dst_ip, 0);
612
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &c->rcv.dst_ip,
613
+														c->rcv.dst_port);
614
+		/* ignore add_alias errors, there are some valid cases when one
615
+		 *  of the add_alias would fail (e.g. first add_alias for 2 connections
616
+		 *   with the same destination but different src. ip*/
607 617
 		TCPCONN_UNLOCK;
608
-		DBG("tcpconn_add: hashes: %d, %d\n", c->con_aliases[0].hash,
618
+		DBG("tcpconn_add: hashes: %d:%d:%d, %d\n",
619
+												c->con_aliases[0].hash,
620
+												c->con_aliases[1].hash,
621
+												c->con_aliases[2].hash,
609 622
 												c->id_hash);
610 623
 		return c;
611 624
 	}else{
... ...
@@ -651,15 +671,20 @@ void tcpconn_rm(struct tcp_connection* c)
651 651
 }
652 652
 
653 653
 
654
-/* finds a connection, if id=0 uses the ip addr & port (host byte order)
654
+/* finds a connection, if id=0 uses the ip addr, port, local_ip and local port
655
+ *  (host byte order) and tries to find the connection that matches all of
656
+ *   them. Wild cards can be used for local_ip and local_port (a 0 filled
657
+ *   ip address and/or a 0 local port).
655 658
  * WARNING: unprotected (locks) use tcpconn_get unless you really
656 659
  * know what you are doing */
657
-struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
660
+struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port,
661
+										struct ip_addr* l_ip, int l_port)
658 662
 {
659 663
 
660 664
 	struct tcp_connection *c;
661 665
 	struct tcp_conn_alias* a;
662 666
 	unsigned hash;
667
+	int is_local_ip_any;
663 668
 	
664 669
 #ifdef EXTRA_DEBUG
665 670
 	DBG("tcpconn_find: %d  port %d\n",id, port);
... ...
@@ -675,7 +700,8 @@ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
675 675
 			if ((id==c->id)&&(c->state!=S_CONN_BAD)) return c;
676 676
 		}
677 677
 	}else if (ip){
678
-		hash=tcp_addr_hash(ip, port);
678
+		hash=tcp_addr_hash(ip, port, l_ip, l_port);
679
+		is_local_ip_any=ip_addr_any(l_ip);
679 680
 		for (a=tcpconn_aliases_hash[hash]; a; a=a->next){
680 681
 #ifdef EXTRA_DEBUG
681 682
 			DBG("a=%p, c=%p, c->id=%d, alias port= %d port=%d\n", a, a->parent,
... ...
@@ -683,7 +709,11 @@ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
683 683
 			print_ip("ip=",&a->parent->rcv.src_ip,"\n");
684 684
 #endif
685 685
 			if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) &&
686
-					(ip_addr_cmp(ip, &a->parent->rcv.src_ip)) )
686
+					((l_port==0) || (l_port==a->parent->rcv.dst_port)) &&
687
+					(ip_addr_cmp(ip, &a->parent->rcv.src_ip)) &&
688
+					(is_local_ip_any ||
689
+						ip_addr_cmp(l_ip, &a->parent->rcv.dst_ip))
690
+				)
687 691
 				return a->parent;
688 692
 		}
689 693
 	}
... ...
@@ -692,13 +722,30 @@ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
692 692
 
693 693
 
694 694
 
695
-/* _tcpconn_find with locks and timeout */
695
+/* _tcpconn_find with locks and timeout
696
+ * local_addr contains the desired local ip:port. If null any local address 
697
+ * will be used.  IN*ADDR_ANY or 0 port are wild cards.
698
+ */
696 699
 struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
700
+									union sockaddr_union* local_addr,
697 701
 									ticks_t timeout)
698 702
 {
699 703
 	struct tcp_connection* c;
704
+	struct ip_addr local_ip;
705
+	int local_port;
706
+	
707
+	local_port=0;
708
+	if (ip){
709
+		if (local_addr){
710
+			su2ip_addr(&local_ip, local_addr);
711
+			local_port=su_getport(local_addr);
712
+		}else{
713
+			ip_addr_mk_any(ip->af, &local_ip);
714
+			local_port=0;
715
+		}
716
+	}
700 717
 	TCPCONN_LOCK;
701
-	c=_tcpconn_find(id, ip, port);
718
+	c=_tcpconn_find(id, ip, port, &local_ip, local_port);
702 719
 	if (c){ 
703 720
 			atomic_inc(&c->refcnt);
704 721
 			c->timeout=get_ticks_raw()+timeout;
... ...
@@ -709,26 +756,29 @@ struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
709 709
 
710 710
 
711 711
 
712
-/* add port as an alias for the "id" connection
713
- * returns 0 on success,-1 on failure */
714
-int tcpconn_add_alias(int id, int port, int proto)
712
+/* add c->dst:port, local_addr as an alias for the "id" connection, 
713
+ * returns 0 on success, <0 on failure ( -1  - null c, -2 too many aliases,
714
+ *  -3 alias already present and pointing to another connection)
715
+ * WARNING: must be called with TCPCONN_LOCK held */
716
+inline static int _tcpconn_add_alias_unsafe(struct tcp_connection* c, int port,
717
+										struct ip_addr* l_ip, int l_port)
715 718
 {
716
-	struct tcp_connection* c;
717 719
 	unsigned hash;
718 720
 	struct tcp_conn_alias* a;
721
+	int is_local_ip_any;
719 722
 	
720 723
 	a=0;
721
-	/* fix the port */
722
-	port=port?port:((proto==PROTO_TLS)?SIPS_PORT:SIP_PORT);
723
-	TCPCONN_LOCK;
724
-	/* check if alias already exists */
725
-	c=_tcpconn_find(id, 0, 0);
724
+	is_local_ip_any=ip_addr_any(l_ip);
726 725
 	if (c){
727
-		hash=tcp_addr_hash(&c->rcv.src_ip, port);
726
+		hash=tcp_addr_hash(&c->rcv.src_ip, port, l_ip, l_port);
728 727
 		/* search the aliases for an already existing one */
729 728
 		for (a=tcpconn_aliases_hash[hash]; a; a=a->next){
730 729
 			if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) &&
731
-					(ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) ){
730
+					( (l_port==0) || (l_port==a->parent->rcv.dst_port)) &&
731
+					(ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) &&
732
+					( is_local_ip_any || 
733
+					  ip_addr_cmp(&a->parent->rcv.dst_ip, l_ip))
734
+					){
732 735
 				/* found */
733 736
 				if (a->parent!=c) goto error_sec;
734 737
 				else goto ok;
... ...
@@ -743,38 +793,88 @@ int tcpconn_add_alias(int id, int port, int proto)
743 743
 		c->aliases++;
744 744
 	}else goto error_not_found;
745 745
 ok:
746
-	TCPCONN_UNLOCK;
747 746
 #ifdef EXTRA_DEBUG
748
-	if (a) DBG("tcpconn_add_alias: alias already present\n");
749
-	else   DBG("tcpconn_add_alias: alias port %d for hash %d, id %d\n",
747
+	if (a) DBG("_tcpconn_add_alias_unsafe: alias already present\n");
748
+	else   DBG("_tcpconn_add_alias_unsafe: alias port %d for hash %d, id %d\n",
750 749
 			port, hash, c->id);
751 750
 #endif
752 751
 	return 0;
753 752
 error_aliases:
754
-	TCPCONN_UNLOCK;
755
-	LOG(L_ERR, "ERROR: tcpconn_add_alias: too many aliases for connection %p"
756
-				" (%d)\n", c, c->id);
753
+	/* too many aliases */
754
+	return -2;
755
+error_not_found:
756
+	/* null connection */
757 757
 	return -1;
758
+error_sec:
759
+	/* alias already present and pointing to a different connection
760
+	 * (hijack attempt?) */
761
+	return -3;
762
+}
763
+
764
+
765
+
766
+/* add port as an alias for the "id" connection, 
767
+ * returns 0 on success,-1 on failure */
768
+int tcpconn_add_alias(int id, int port, int proto)
769
+{
770
+	struct tcp_connection* c;
771
+	int ret;
772
+	struct ip_addr zero_ip;
773
+	
774
+	/* fix the port */
775
+	port=port?port:((proto==PROTO_TLS)?SIPS_PORT:SIP_PORT);
776
+	TCPCONN_LOCK;
777
+	/* check if alias already exists */
778
+	c=_tcpconn_find(id, 0, 0, 0, 0);
779
+	if (c){
780
+		ip_addr_mk_any(c->rcv.src_ip.af, &zero_ip);
781
+		
782
+		/* alias src_ip:port, 0, 0 */
783
+		ret=_tcpconn_add_alias_unsafe(c, port,  &zero_ip, 0);
784
+		if (ret<0 && ret!=-3) goto error;
785
+		/* alias src_ip:port, local_ip, 0 */
786
+		ret=_tcpconn_add_alias_unsafe(c, port,  &c->rcv.dst_ip, 0);
787
+		if (ret<0 && ret!=-3) goto error;
788
+		/* alias src_ip:port, local_ip, local_port */
789
+		ret=_tcpconn_add_alias_unsafe(c, port,  &c->rcv.dst_ip,
790
+															c->rcv.dst_port);
791
+		if (ret<0) goto error;
792
+	}else goto error_not_found;
793
+	TCPCONN_UNLOCK;
794
+	return 0;
758 795
 error_not_found:
759 796
 	TCPCONN_UNLOCK;
760 797
 	LOG(L_ERR, "ERROR: tcpconn_add_alias: no connection found for id %d\n",id);
761 798
 	return -1;
762
-error_sec:
799
+error:
763 800
 	TCPCONN_UNLOCK;
764
-	LOG(L_ERR, "ERROR: tcpconn_add_alias: possible port hijack attempt\n");
765
-	LOG(L_ERR, "ERROR: tcpconn_add_alias: alias already present and points"
766
-			" to another connection (%d : %d and %d : %d)\n",
767
-			a->parent->id,  port, c->id, port);
801
+	switch(ret){
802
+		case -2:
803
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: too many aliases"
804
+					" for connection %p (%d)\n", c, c->id);
805
+			break;
806
+		case -3:
807
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: possible port"
808
+					" hijack attempt\n");
809
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: alias for %d port %d already"
810
+						" present and points to another connection \n",
811
+						c->id, port);
812
+			break;
813
+		default:
814
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: unkown error %d\n", ret);
815
+	}
768 816
 	return -1;
769 817
 }
770 818
 
771 819
 
772 820
 
773 821
 /* finds a tcpconn & sends on it
774
- * uses the dst members to, proto (TCP|TLS) and id
822
+ * uses the dst members to, proto (TCP|TLS) and id and tries to send
823
+ *  from the "from" address (if non null and id==0)
775 824
  * returns: number of bytes written (>=0) on success
776 825
  *          <0 on error */
777
-int tcp_send(struct dest_info* dst, char* buf, unsigned len)
826
+int tcp_send(struct dest_info* dst, union sockaddr_union* from,
827
+					char* buf, unsigned len)
778 828
 {
779 829
 	struct tcp_connection *c;
780 830
 	struct tcp_connection *tmp;
... ...
@@ -783,14 +883,13 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
783 783
 	int fd;
784 784
 	long response[2];
785 785
 	int n;
786
-	union sockaddr_union* from;
787 786
 	
788 787
 	port=su_getport(&dst->to);
789 788
 	if (port){
790 789
 		su2ip_addr(&ip, &dst->to);
791
-		c=tcpconn_get(dst->id, &ip, port, tcp_con_lifetime); 
790
+		c=tcpconn_get(dst->id, &ip, port, from, tcp_con_lifetime); 
792 791
 	}else if (dst->id){
793
-		c=tcpconn_get(dst->id, 0, 0, tcp_con_lifetime);
792
+		c=tcpconn_get(dst->id, 0, 0, 0, tcp_con_lifetime);
794 793
 	}else{
795 794
 		LOG(L_CRIT, "BUG: tcp_send called with null id & to\n");
796 795
 		return -1;
... ...
@@ -800,7 +899,7 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
800 800
 		if (c==0) {
801 801
 			if (port){
802 802
 				/* try again w/o id */
803
-				c=tcpconn_get(0, &ip, port, tcp_con_lifetime);
803
+				c=tcpconn_get(0, &ip, port, from, tcp_con_lifetime);
804 804
 				goto no_id;
805 805
 			}else{
806 806
 				LOG(L_ERR, "ERROR: tcp_send: id %d not found, dropping\n",
... ...
@@ -813,7 +912,7 @@ no_id:
813 813
 		if (c==0){
814 814
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
815 815
 			/* create tcp connection */
816
-				from=0;
816
+			if (from==0){
817 817
 				/* check to see if we have to use a specific source addr. */
818 818
 				switch (dst->to.s.sa_family) {
819 819
 					case AF_INET:
... ...
@@ -828,6 +927,7 @@ no_id:
828 828
 						/* error, bad af, ignore ... */
829 829
 						break;
830 830
 				}
831
+			}
831 832
 			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
832 833
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
833 834
 				return -1;
... ...
@@ -34,7 +34,8 @@
34 34
 
35 35
 /* "public" functions*/
36 36
 
37
-int tcp_send(struct dest_info* dst, char* buf, unsigned len);
37
+int tcp_send(struct dest_info* dst, union sockaddr_union* from,
38
+				char* buf, unsigned len);
38 39
 
39 40
 int tcpconn_add_alias(int id, int port, int proto);
40 41