Browse code

- tcpconn_connect takes a new optional param.: the address to bind the new socket on - tcpconn_new takes a new param: the local socket address - tcp_use_source_ipv[46] cleanup - MAX_TCP_CON_LIFETIME fix

Andrei Pelinescu-Onciul authored on 25/07/2007 19:40:32
Showing 4 changed files
... ...
@@ -108,6 +108,7 @@
108 108
 #include "dset.h"
109 109
 #include "select.h"
110 110
 #include "flags.h"
111
+#include "tcp_init.h"
111 112
 
112 113
 #include "config.h"
113 114
 #ifdef CORE_TLS
... ...
@@ -746,9 +747,8 @@ assign_stm:
746 746
 	| TCP_MAX_CONNECTIONS EQUAL error { yyerror("number expected"); }
747 747
 	| TCP_SOURCE_IPV4 EQUAL ipv4 {
748 748
 		#ifdef USE_TCP
749
-			tcp_use_source_ipv4 = 1;
750
-			tcp_source_ipv4 = (struct sockaddr_in) {.sin_family = AF_INET, .sin_port = 0};
751
-			memcpy(&tcp_source_ipv4.sin_addr, &($3)->u.addr, 4);
749
+			if (tcp_set_src_addr($3)<0)
750
+				warn("tcp_source_ipv4 failed");
752 751
 		#else
753 752
 			warn("tcp support not compiled in");
754 753
 		#endif
... ...
@@ -758,9 +758,8 @@ assign_stm:
758 758
 	| TCP_SOURCE_IPV6 EQUAL ipv6 {
759 759
 		#ifdef USE_TCP
760 760
 			#ifdef USE_IPV6
761
-				tcp_use_source_ipv6 = 1;
762
-				tcp_source_ipv6 = (struct sockaddr_in6) {.sin6_family = AF_INET6, .sin6_port = 0};
763
-				memcpy(&tcp_source_ipv6.sin6_addr, &($3)->u.addr, 16);
761
+				if (tcp_set_src_addr($3)<0)
762
+					warn("tcp_source_ipv6 failed");
764 763
 			#else
765 764
 				warn("IPv6 support not compiled in");
766 765
 			#endif
... ...
@@ -188,6 +188,22 @@ void print_net(struct net* net);
188 188
 int is_mcast(struct ip_addr* ip);
189 189
 #endif /* USE_MCAST */
190 190
 
191
+/* returns 1 if the given ip address is INADDR_ANY or IN6ADDR_ANY,
192
+ * 0 otherwise */
193
+inline static int ip_addr_any(struct ip_addr* ip)
194
+{
195
+	int r;
196
+	int l;
197
+	
198
+	l=ip->len/4;
199
+	for (r=0; r<l; r++)
200
+		if (ip->u.addr32[r]!=0)
201
+			return 0;
202
+	return 1;
203
+}
204
+
205
+
206
+
191 207
 /* returns 1 if ip & net.mask == net.ip ; 0 otherwise & -1 on error 
192 208
 	[ diff. address families ]) */
193 209
 inline static int matchnet(struct ip_addr* ip, struct net* net)
... ...
@@ -46,5 +46,10 @@ void tcp_main_loop();
46 46
 void tcp_receive_loop(int unix_sock);
47 47
 int tcp_fix_child_sockets(int* fd);
48 48
 
49
+/* sets source address used when opening new sockets and no source is specified
50
+ *  (by default the address is choosen by the kernel)
51
+ * Should be used only on init.
52
+ * returns -1 on error */
53
+int tcp_set_src_addr(struct ip_addr* ip);
49 54
 
50 55
 #endif
... ...
@@ -73,6 +73,8 @@
73 73
  *               (andrei)
74 74
  *  2006-11-04  switched to raw ticks (to fix conversion errors which could
75 75
  *               result in inf. lifetime) (andrei)
76
+ *  2007-07-25  tcpconn_connect can now bind the socket on a specified
77
+ *                source addr/port (andrei)
76 78
  */
77 79
 
78 80
 
... ...
@@ -155,7 +157,7 @@
155 155
 #endif
156 156
 
157 157
 /* maximum accepted lifetime (maximum possible is  ~ MAXINT/2) */
158
-#define MAX_TCP_CON_LIFETIME	(1U<<(sizeof(ticks_t)*8-1))
158
+#define MAX_TCP_CON_LIFETIME	((1U<<(sizeof(ticks_t)*8-1))-1)
159 159
 /* minimum interval tcpconn_timeout() is allowed to run, in ticks */
160 160
 #define TCPCONN_TIMEOUT_MIN_RUN S_TO_TICKS(1)  /* once per s */
161 161
 
... ...
@@ -172,11 +174,11 @@ enum poll_types tcp_poll_method=0; /* by default choose the best method */
172 172
 int tcp_max_connections=DEFAULT_TCP_MAX_CONNECTIONS;
173 173
 int tcp_main_max_fd_no=0;
174 174
 
175
-int tcp_use_source_ipv4 = 0;
176
-struct sockaddr_in tcp_source_ipv4;
175
+static union sockaddr_union tcp_source_ipv4_addr; /* saved bind/srv v4 addr. */
176
+static union sockaddr_union* tcp_source_ipv4=0;
177 177
 #ifdef USE_IPV6
178
-int tcp_use_source_ipv6 = 0;
179
-struct sockaddr_in6 tcp_source_ipv6;
178
+static union sockaddr_union tcp_source_ipv6_addr; /* saved bind/src v6 addr. */
179
+static union sockaddr_union* tcp_source_ipv6=0;
180 180
 #endif
181 181
 
182 182
 static int* tcp_connections_no=0; /* current open connections */
... ...
@@ -200,6 +202,31 @@ static io_wait_h io_h;
200 200
 
201 201
 
202 202
 
203
+/* sets source address used when opening new sockets and no source is specified
204
+ *  (by default the address is choosen by the kernel)
205
+ * Should be used only on init.
206
+ * returns -1 on error */
207
+int tcp_set_src_addr(struct ip_addr* ip)
208
+{
209
+	switch (ip->af){
210
+		case AF_INET:
211
+			ip_addr2su(&tcp_source_ipv4_addr, ip, 0);
212
+			tcp_source_ipv4=&tcp_source_ipv4_addr;
213
+			break;
214
+		#ifdef USE_IPV6
215
+		case AF_INET6:
216
+			ip_addr2su(&tcp_source_ipv6_addr, ip, 0);
217
+			tcp_source_ipv6=&tcp_source_ipv6_addr;
218
+			break;
219
+		#endif
220
+		default:
221
+			return -1;
222
+	}
223
+	return 0;
224
+}
225
+
226
+
227
+
203 228
 /* set all socket/fd options:  disable nagle, tos lowdelay, non-blocking
204 229
  * return -1 on error */
205 230
 static int init_sock_opt(int s)
... ...
@@ -415,6 +442,7 @@ end:
415 415
 
416 416
 
417 417
 struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
418
+									union sockaddr_union* local_addr,
418 419
 									struct socket_info* ba, int type, 
419 420
 									int state)
420 421
 {
... ...
@@ -439,7 +467,10 @@ struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
439 439
 	su2ip_addr(&c->rcv.src_ip, su);
440 440
 	c->rcv.src_port=su_getport(su);
441 441
 	c->rcv.bind_address=ba;
442
-	if (ba){
442
+	if (likely(local_addr)){
443
+		su2ip_addr(&c->rcv.dst_ip, local_addr);
444
+		c->rcv.dst_port=su_getport(local_addr);
445
+	}else if (ba){
443 446
 		c->rcv.dst_ip=ba->address;
444 447
 		c->rcv.dst_port=ba->port_no;
445 448
 	}
... ...
@@ -472,7 +503,9 @@ error:
472 472
 
473 473
 
474 474
 
475
-struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
475
+struct tcp_connection* tcpconn_connect( union sockaddr_union* server, 
476
+										union sockaddr_union* from,
477
+										int type)
476 478
 {
477 479
 	int s;
478 480
 	struct socket_info* si;
... ...
@@ -480,8 +513,6 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
480 480
 	socklen_t my_name_len;
481 481
 	struct tcp_connection* con;
482 482
 	struct ip_addr ip;
483
-	int do_bind = 0;
484
-	struct sockaddr *bind_addr;
485 483
 
486 484
 	s=-1;
487 485
 	
... ...
@@ -501,61 +532,47 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
501 501
 		LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n");
502 502
 		goto error;
503 503
 	}
504
-
505
-	switch (server->s.sa_family) {
506
-		case AF_INET: {
507
-			if (tcp_use_source_ipv4) {
508
-				my_name_len = sizeof(tcp_source_ipv4);
509
-				bind_addr = (struct sockaddr *) &tcp_source_ipv4;
510
-				do_bind = 1;
511
-			}
512
-			break;
513
-		}
514
-#ifdef USE_IPV6
515
-		case AF_INET6: {
516
-			if (tcp_use_source_ipv6) {
517
-				my_name_len = sizeof(tcp_source_ipv6);
518
-				bind_addr = (struct sockaddr *) &tcp_source_ipv6;
519
-				do_bind = 1;
520
-			}
521
-			break;
522
-		}
523
-#endif
524
-		default: {
525
-			/* do nothing special */
526
-			break;
527
-		}
528
-	}
529
-	if (do_bind && bind(s, bind_addr, my_name_len) != 0)
530
-		LOG(L_WARN, "WARNING: tcpconn_connect: binding to source address failed: %s\n", strerror(errno));
504
+	
505
+	if (from && bind(s, &from->s, sockaddru_len(*from)) != 0)
506
+		LOG(L_WARN, "WARNING: tcpconn_connect: binding to source address"
507
+					" failed: %s [%d]\n", strerror(errno), errno);
531 508
 
532 509
 	if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
533 510
 		LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n");
534 511
 		goto error;
535 512
 	}
513
+	if (from){
514
+		su2ip_addr(&ip, from);
515
+		if (!ip_addr_any(&ip))
516
+			/* we already know the source ip, skip the sys. call */
517
+			goto find_socket;
518
+	}
536 519
 	my_name_len=sizeof(my_name);
537 520
 	if (getsockname(s, &my_name.s, &my_name_len)!=0){
538 521
 		LOG(L_ERR, "ERROR: tcp_connect: getsockname failed: %s(%d)\n",
539 522
 				strerror(errno), errno);
540 523
 		si=0; /* try to go on */
524
+		goto skip;
541 525
 	}
526
+	from=&my_name; /* update from with the real "from" address */
542 527
 	su2ip_addr(&ip, &my_name);
528
+find_socket:
543 529
 #ifdef USE_TLS
544 530
 	if (type==PROTO_TLS)
545 531
 		si=find_si(&ip, 0, PROTO_TLS);
546 532
 	else
547 533
 #endif
548 534
 		si=find_si(&ip, 0, PROTO_TCP);
549
-
535
+skip:
550 536
 	if (si==0){
551
-		LOG(L_ERR, "ERROR: tcp_connect: could not find corresponding"
537
+		LOG(L_WARN, "WARNING: tcp_connect: could not find corresponding"
552 538
 				" listening socket, using default...\n");
553 539
 		if (server->s.sa_family==AF_INET) si=sendipv4_tcp;
554 540
 #ifdef USE_IPV6
555 541
 		else si=sendipv6_tcp;
556 542
 #endif
557 543
 	}
558
-	con=tcpconn_new(s, server, si, type, S_CONN_CONNECT);
544
+	con=tcpconn_new(s, server, from, si,  type, S_CONN_CONNECT);
559 545
 	if (con==0){
560 546
 		LOG(L_ERR, "ERROR: tcp_connect: tcpconn_new failed, closing the "
561 547
 				 " socket\n");
... ...
@@ -766,6 +783,7 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
766 766
 	int fd;
767 767
 	long response[2];
768 768
 	int n;
769
+	union sockaddr_union* from;
769 770
 	
770 771
 	port=su_getport(&dst->to);
771 772
 	if (port){
... ...
@@ -795,7 +813,22 @@ no_id:
795 795
 		if (c==0){
796 796
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
797 797
 			/* create tcp connection */
798
-			if ((c=tcpconn_connect(&dst->to, dst->proto))==0){
798
+				from=0;
799
+				/* check to see if we have to use a specific source addr. */
800
+				switch (dst->to.s.sa_family) {
801
+					case AF_INET:
802
+							from = tcp_source_ipv4;
803
+						break;
804
+#ifdef USE_IPV6
805
+					case AF_INET6:
806
+							from = tcp_source_ipv6;
807
+						break;
808
+#endif
809
+					default:
810
+						/* error, bad af, ignore ... */
811
+						break;
812
+				}
813
+			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
799 814
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
800 815
 				return -1;
801 816
 			}
... ...
@@ -1503,7 +1536,7 @@ static inline int handle_new_connect(struct socket_info* si)
1503 1503
 	(*tcp_connections_no)++;
1504 1504
 	
1505 1505
 	/* add socket to list */
1506
-	tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
1506
+	tcpconn=tcpconn_new(new_sock, &su, &si->su, si, si->proto, S_CONN_ACCEPT);
1507 1507
 	if (tcpconn){
1508 1508
 #ifdef TCP_PASS_NEW_CONNECTION_ON_DATA
1509 1509
 		io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn);