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 44
 #include <netinet/in.h>
44 45
 #include <netdb.h>
45 46
 #include "str.h"
47
+#include "compiler_opt.h"
46 48
 
47 49
 
48 50
 #include "dprint.h"
... ...
@@ -204,6 +206,31 @@ inline static int ip_addr_any(struct ip_addr* ip)
204 206
 
205 207
 
206 208
 
209
+/* creates an ANY ip_addr (filled with 0, af and len properly set) */
210
+inline static void ip_addr_mk_any(int af, struct ip_addr* ip)
211
+{
212
+	ip->af=af;
213
+	if (likely(af==AF_INET)){
214
+		ip->len=4;
215
+		ip->u.addr32[0]=0;
216
+	}
217
+#ifdef USE_IPV6
218
+	else{
219
+		ip->len=16;
220
+#if (defined (ULONG_MAX) && ULONG_MAX > 4294967295) || defined LP64
221
+		/* long is 64 bits */
222
+		ip->u.addrl[0]=0;
223
+		ip->u.addrl[1]=0;
224
+#else
225
+		ip->u.addr32[0]=0;
226
+		ip->u.addr32[1]=0;
227
+		ip->u.addr32[2]=0;
228
+		ip->u.addr32[3]=0;
229
+#endif /* ULONG_MAX */
230
+	}
231
+#endif
232
+}
233
+
207 234
 /* returns 1 if ip & net.mask == net.ip ; 0 otherwise & -1 on error 
208 235
 	[ diff. address families ]) */
209 236
 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 45
 #include "atomic_ops.h"
44 46
 #include "timer_ticks.h"
45 47
 
46
-#define TCP_CON_MAX_ALIASES 4 /* maximum number of port aliases */
48
+/* maximum number of port aliases x search wildcard possibilities */
49
+#define TCP_CON_MAX_ALIASES (4*3) 
47 50
 
48 51
 #define TCP_BUF_SIZE	4096 
49 52
 #define DEFAULT_TCP_CONNECTION_LIFETIME 120 /* in  seconds */
... ...
@@ -180,25 +183,41 @@ struct tcp_connection{
180 183
 #define TCPCONN_LOCK lock_get(tcpconn_lock);
181 184
 #define TCPCONN_UNLOCK lock_release(tcpconn_lock);
182 185
 
183
-#define TCP_ALIAS_HASH_SIZE 1024
186
+#define TCP_ALIAS_HASH_SIZE 4096
184 187
 #define TCP_ID_HASH_SIZE 1024
185 188
 
186
-static inline unsigned tcp_addr_hash(struct ip_addr* ip, unsigned short port)
189
+/* hash (dst_ip, dst_port, local_ip, local_port) */
190
+static inline unsigned tcp_addr_hash(	struct ip_addr* ip, 
191
+										unsigned short port,
192
+										struct ip_addr* l_ip,
193
+										unsigned short l_port)
187 194
 {
188
-	if(ip->len==4) return (ip->u.addr32[0]^port)&(TCP_ALIAS_HASH_SIZE-1);
195
+	unsigned h;
196
+
197
+	if(ip->len==4)
198
+		h=(ip->u.addr32[0]^port)^(l_ip->u.addr32[0]^l_port);
189 199
 	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);
200
+		h= (ip->u.addr32[0]^ip->u.addr32[1]^ip->u.addr32[2]^
201
+				ip->u.addr32[3]^port) ^
202
+			(l_ip->u.addr32[0]^l_ip->u.addr32[1]^l_ip->u.addr32[2]^
203
+				l_ip->u.addr32[3]^l_port);
192 204
 	else{
193 205
 		LOG(L_CRIT, "tcp_addr_hash: BUG: bad len %d for an ip address\n",
194 206
 				ip->len);
195 207
 		return 0;
196 208
 	}
209
+	/* make sure the first bits are influenced by all 32
210
+	 * (the first log2(TCP_ALIAS_HASH_SIZE) bits should be a mix of all
211
+	 *  32)*/
212
+	h ^= h>>17;
213
+	h ^= h>>7;
214
+	return h & (TCP_ALIAS_HASH_SIZE-1);
197 215
 }
198 216
 
199 217
 #define tcp_id_hash(id) (id&(TCP_ID_HASH_SIZE-1))
200 218
 
201 219
 struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
220
+									union sockaddr_union* local_addr,
202 221
 									ticks_t timeout);
203 222
 
204 223
 #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 204
 
203 205
 
204 206
 
207
+inline static int _tcpconn_add_alias_unsafe(struct tcp_connection* c, int port,
208
+										struct ip_addr* l_ip, int l_port);
209
+
210
+
211
+
205 212
 /* sets source address used when opening new sockets and no source is specified
206 213
  *  (by default the address is choosen by the kernel)
207 214
  * Should be used only on init.
... ...
@@ -589,23 +596,36 @@ error:
589 596
 
590 597
 /* adds a tcp connection to the tcpconn hashes
591 598
  * Note: it's called _only_ from the tcp_main process */
592
-struct tcp_connection*  tcpconn_add(struct tcp_connection *c)
599
+inline static struct tcp_connection*  tcpconn_add(struct tcp_connection *c)
593 600
 {
601
+	struct ip_addr zero_ip;
594 602
 
595
-	if (c){
603
+	if (likely(c)){
604
+		ip_addr_mk_any(c->rcv.src_ip.af, &zero_ip);
596 605
 		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);
606
+		c->aliases=0;
598 607
 		TCPCONN_LOCK;
599 608
 		/* add it at the begining of the list*/
600 609
 		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++;
610
+		/* set the aliases */
611
+		/* first alias is for (peer_ip, peer_port, 0 ,0) -- for finding
612
+		 *  any connection to peer_ip, peer_port
613
+		 * the second alias is for (peer_ip, peer_port, local_addr, 0) -- for
614
+		 *  finding any conenction to peer_ip, peer_port from local_addr 
615
+		 * the third alias is for (peer_ip, peer_port, local_addr, local_port) 
616
+		 *   -- for finding if a fully specified connection exists */
617
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &zero_ip, 0);
618
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &c->rcv.dst_ip, 0);
619
+		_tcpconn_add_alias_unsafe(c, c->rcv.src_port, &c->rcv.dst_ip,
620
+														c->rcv.dst_port);
621
+		/* ignore add_alias errors, there are some valid cases when one
622
+		 *  of the add_alias would fail (e.g. first add_alias for 2 connections
623
+		 *   with the same destination but different src. ip*/
607 624
 		TCPCONN_UNLOCK;
608
-		DBG("tcpconn_add: hashes: %d, %d\n", c->con_aliases[0].hash,
625
+		DBG("tcpconn_add: hashes: %d:%d:%d, %d\n",
626
+												c->con_aliases[0].hash,
627
+												c->con_aliases[1].hash,
628
+												c->con_aliases[2].hash,
609 629
 												c->id_hash);
610 630
 		return c;
611 631
 	}else{
... ...
@@ -651,15 +671,20 @@ void tcpconn_rm(struct tcp_connection* c)
651 671
 }
652 672
 
653 673
 
654
-/* finds a connection, if id=0 uses the ip addr & port (host byte order)
674
+/* finds a connection, if id=0 uses the ip addr, port, local_ip and local port
675
+ *  (host byte order) and tries to find the connection that matches all of
676
+ *   them. Wild cards can be used for local_ip and local_port (a 0 filled
677
+ *   ip address and/or a 0 local port).
655 678
  * WARNING: unprotected (locks) use tcpconn_get unless you really
656 679
  * know what you are doing */
657
-struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
680
+struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port,
681
+										struct ip_addr* l_ip, int l_port)
658 682
 {
659 683
 
660 684
 	struct tcp_connection *c;
661 685
 	struct tcp_conn_alias* a;
662 686
 	unsigned hash;
687
+	int is_local_ip_any;
663 688
 	
664 689
 #ifdef EXTRA_DEBUG
665 690
 	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 700
 			if ((id==c->id)&&(c->state!=S_CONN_BAD)) return c;
676 701
 		}
677 702
 	}else if (ip){
678
-		hash=tcp_addr_hash(ip, port);
703
+		hash=tcp_addr_hash(ip, port, l_ip, l_port);
704
+		is_local_ip_any=ip_addr_any(l_ip);
679 705
 		for (a=tcpconn_aliases_hash[hash]; a; a=a->next){
680 706
 #ifdef EXTRA_DEBUG
681 707
 			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 709
 			print_ip("ip=",&a->parent->rcv.src_ip,"\n");
684 710
 #endif
685 711
 			if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) &&
686
-					(ip_addr_cmp(ip, &a->parent->rcv.src_ip)) )
712
+					((l_port==0) || (l_port==a->parent->rcv.dst_port)) &&
713
+					(ip_addr_cmp(ip, &a->parent->rcv.src_ip)) &&
714
+					(is_local_ip_any ||
715
+						ip_addr_cmp(l_ip, &a->parent->rcv.dst_ip))
716
+				)
687 717
 				return a->parent;
688 718
 		}
689 719
 	}
... ...
@@ -692,13 +722,30 @@ struct tcp_connection* _tcpconn_find(int id, struct ip_addr* ip, int port)
692 722
 
693 723
 
694 724
 
695
-/* _tcpconn_find with locks and timeout */
725
+/* _tcpconn_find with locks and timeout
726
+ * local_addr contains the desired local ip:port. If null any local address 
727
+ * will be used.  IN*ADDR_ANY or 0 port are wild cards.
728
+ */
696 729
 struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
730
+									union sockaddr_union* local_addr,
697 731
 									ticks_t timeout)
698 732
 {
699 733
 	struct tcp_connection* c;
734
+	struct ip_addr local_ip;
735
+	int local_port;
736
+	
737
+	local_port=0;
738
+	if (ip){
739
+		if (local_addr){
740
+			su2ip_addr(&local_ip, local_addr);
741
+			local_port=su_getport(local_addr);
742
+		}else{
743
+			ip_addr_mk_any(ip->af, &local_ip);
744
+			local_port=0;
745
+		}
746
+	}
700 747
 	TCPCONN_LOCK;
701
-	c=_tcpconn_find(id, ip, port);
748
+	c=_tcpconn_find(id, ip, port, &local_ip, local_port);
702 749
 	if (c){ 
703 750
 			atomic_inc(&c->refcnt);
704 751
 			c->timeout=get_ticks_raw()+timeout;
... ...
@@ -709,26 +756,29 @@ struct tcp_connection* tcpconn_get(int id, struct ip_addr* ip, int port,
709 756
 
710 757
 
711 758
 
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)
759
+/* add c->dst:port, local_addr as an alias for the "id" connection, 
760
+ * returns 0 on success, <0 on failure ( -1  - null c, -2 too many aliases,
761
+ *  -3 alias already present and pointing to another connection)
762
+ * WARNING: must be called with TCPCONN_LOCK held */
763
+inline static int _tcpconn_add_alias_unsafe(struct tcp_connection* c, int port,
764
+										struct ip_addr* l_ip, int l_port)
715 765
 {
716
-	struct tcp_connection* c;
717 766
 	unsigned hash;
718 767
 	struct tcp_conn_alias* a;
768
+	int is_local_ip_any;
719 769
 	
720 770
 	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);
771
+	is_local_ip_any=ip_addr_any(l_ip);
726 772
 	if (c){
727
-		hash=tcp_addr_hash(&c->rcv.src_ip, port);
773
+		hash=tcp_addr_hash(&c->rcv.src_ip, port, l_ip, l_port);
728 774
 		/* search the aliases for an already existing one */
729 775
 		for (a=tcpconn_aliases_hash[hash]; a; a=a->next){
730 776
 			if ( (a->parent->state!=S_CONN_BAD) && (port==a->port) &&
731
-					(ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) ){
777
+					( (l_port==0) || (l_port==a->parent->rcv.dst_port)) &&
778
+					(ip_addr_cmp(&c->rcv.src_ip, &a->parent->rcv.src_ip)) &&
779
+					( is_local_ip_any || 
780
+					  ip_addr_cmp(&a->parent->rcv.dst_ip, l_ip))
781
+					){
732 782
 				/* found */
733 783
 				if (a->parent!=c) goto error_sec;
734 784
 				else goto ok;
... ...
@@ -743,38 +793,88 @@ int tcpconn_add_alias(int id, int port, int proto)
743 793
 		c->aliases++;
744 794
 	}else goto error_not_found;
745 795
 ok:
746
-	TCPCONN_UNLOCK;
747 796
 #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",
797
+	if (a) DBG("_tcpconn_add_alias_unsafe: alias already present\n");
798
+	else   DBG("_tcpconn_add_alias_unsafe: alias port %d for hash %d, id %d\n",
750 799
 			port, hash, c->id);
751 800
 #endif
752 801
 	return 0;
753 802
 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);
803
+	/* too many aliases */
804
+	return -2;
805
+error_not_found:
806
+	/* null connection */
757 807
 	return -1;
808
+error_sec:
809
+	/* alias already present and pointing to a different connection
810
+	 * (hijack attempt?) */
811
+	return -3;
812
+}
813
+
814
+
815
+
816
+/* add port as an alias for the "id" connection, 
817
+ * returns 0 on success,-1 on failure */
818
+int tcpconn_add_alias(int id, int port, int proto)
819
+{
820
+	struct tcp_connection* c;
821
+	int ret;
822
+	struct ip_addr zero_ip;
823
+	
824
+	/* fix the port */
825
+	port=port?port:((proto==PROTO_TLS)?SIPS_PORT:SIP_PORT);
826
+	TCPCONN_LOCK;
827
+	/* check if alias already exists */
828
+	c=_tcpconn_find(id, 0, 0, 0, 0);
829
+	if (c){
830
+		ip_addr_mk_any(c->rcv.src_ip.af, &zero_ip);
831
+		
832
+		/* alias src_ip:port, 0, 0 */
833
+		ret=_tcpconn_add_alias_unsafe(c, port,  &zero_ip, 0);
834
+		if (ret<0 && ret!=-3) goto error;
835
+		/* alias src_ip:port, local_ip, 0 */
836
+		ret=_tcpconn_add_alias_unsafe(c, port,  &c->rcv.dst_ip, 0);
837
+		if (ret<0 && ret!=-3) goto error;
838
+		/* alias src_ip:port, local_ip, local_port */
839
+		ret=_tcpconn_add_alias_unsafe(c, port,  &c->rcv.dst_ip,
840
+															c->rcv.dst_port);
841
+		if (ret<0) goto error;
842
+	}else goto error_not_found;
843
+	TCPCONN_UNLOCK;
844
+	return 0;
758 845
 error_not_found:
759 846
 	TCPCONN_UNLOCK;
760 847
 	LOG(L_ERR, "ERROR: tcpconn_add_alias: no connection found for id %d\n",id);
761 848
 	return -1;
762
-error_sec:
849
+error:
763 850
 	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);
851
+	switch(ret){
852
+		case -2:
853
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: too many aliases"
854
+					" for connection %p (%d)\n", c, c->id);
855
+			break;
856
+		case -3:
857
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: possible port"
858
+					" hijack attempt\n");
859
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: alias for %d port %d already"
860
+						" present and points to another connection \n",
861
+						c->id, port);
862
+			break;
863
+		default:
864
+			LOG(L_ERR, "ERROR: tcpconn_add_alias: unkown error %d\n", ret);
865
+	}
768 866
 	return -1;
769 867
 }
770 868
 
771 869
 
772 870
 
773 871
 /* finds a tcpconn & sends on it
774
- * uses the dst members to, proto (TCP|TLS) and id
872
+ * uses the dst members to, proto (TCP|TLS) and id and tries to send
873
+ *  from the "from" address (if non null and id==0)
775 874
  * returns: number of bytes written (>=0) on success
776 875
  *          <0 on error */
777
-int tcp_send(struct dest_info* dst, char* buf, unsigned len)
876
+int tcp_send(struct dest_info* dst, union sockaddr_union* from,
877
+					char* buf, unsigned len)
778 878
 {
779 879
 	struct tcp_connection *c;
780 880
 	struct tcp_connection *tmp;
... ...
@@ -783,14 +883,13 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
783 883
 	int fd;
784 884
 	long response[2];
785 885
 	int n;
786
-	union sockaddr_union* from;
787 886
 	
788 887
 	port=su_getport(&dst->to);
789 888
 	if (port){
790 889
 		su2ip_addr(&ip, &dst->to);
791
-		c=tcpconn_get(dst->id, &ip, port, tcp_con_lifetime); 
890
+		c=tcpconn_get(dst->id, &ip, port, from, tcp_con_lifetime); 
792 891
 	}else if (dst->id){
793
-		c=tcpconn_get(dst->id, 0, 0, tcp_con_lifetime);
892
+		c=tcpconn_get(dst->id, 0, 0, 0, tcp_con_lifetime);
794 893
 	}else{
795 894
 		LOG(L_CRIT, "BUG: tcp_send called with null id & to\n");
796 895
 		return -1;
... ...
@@ -800,7 +899,7 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
800 899
 		if (c==0) {
801 900
 			if (port){
802 901
 				/* try again w/o id */
803
-				c=tcpconn_get(0, &ip, port, tcp_con_lifetime);
902
+				c=tcpconn_get(0, &ip, port, from, tcp_con_lifetime);
804 903
 				goto no_id;
805 904
 			}else{
806 905
 				LOG(L_ERR, "ERROR: tcp_send: id %d not found, dropping\n",
... ...
@@ -813,7 +912,7 @@ no_id:
813 912
 		if (c==0){
814 913
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
815 914
 			/* create tcp connection */
816
-				from=0;
915
+			if (from==0){
817 916
 				/* check to see if we have to use a specific source addr. */
818 917
 				switch (dst->to.s.sa_family) {
819 918
 					case AF_INET:
... ...
@@ -828,6 +927,7 @@ no_id:
828 927
 						/* error, bad af, ignore ... */
829 928
 						break;
830 929
 				}
930
+			}
831 931
 			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
832 932
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
833 933
 				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