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 747
 	| TCP_MAX_CONNECTIONS EQUAL error { yyerror("number expected"); }
747 748
 	| TCP_SOURCE_IPV4 EQUAL ipv4 {
748 749
 		#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);
750
+			if (tcp_set_src_addr($3)<0)
751
+				warn("tcp_source_ipv4 failed");
752 752
 		#else
753 753
 			warn("tcp support not compiled in");
754 754
 		#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 157
 #endif
156 158
 
157 159
 /* maximum accepted lifetime (maximum possible is  ~ MAXINT/2) */
158
-#define MAX_TCP_CON_LIFETIME	(1U<<(sizeof(ticks_t)*8-1))
160
+#define MAX_TCP_CON_LIFETIME	((1U<<(sizeof(ticks_t)*8-1))-1)
159 161
 /* minimum interval tcpconn_timeout() is allowed to run, in ticks */
160 162
 #define TCPCONN_TIMEOUT_MIN_RUN S_TO_TICKS(1)  /* once per s */
161 163
 
... ...
@@ -172,11 +174,11 @@ enum poll_types tcp_poll_method=0; /* by default choose the best method */
172 174
 int tcp_max_connections=DEFAULT_TCP_MAX_CONNECTIONS;
173 175
 int tcp_main_max_fd_no=0;
174 176
 
175
-int tcp_use_source_ipv4 = 0;
176
-struct sockaddr_in tcp_source_ipv4;
177
+static union sockaddr_union tcp_source_ipv4_addr; /* saved bind/srv v4 addr. */
178
+static union sockaddr_union* tcp_source_ipv4=0;
177 179
 #ifdef USE_IPV6
178
-int tcp_use_source_ipv6 = 0;
179
-struct sockaddr_in6 tcp_source_ipv6;
180
+static union sockaddr_union tcp_source_ipv6_addr; /* saved bind/src v6 addr. */
181
+static union sockaddr_union* tcp_source_ipv6=0;
180 182
 #endif
181 183
 
182 184
 static int* tcp_connections_no=0; /* current open connections */
... ...
@@ -200,6 +202,31 @@ static io_wait_h io_h;
200 202
 
201 203
 
202 204
 
205
+/* sets source address used when opening new sockets and no source is specified
206
+ *  (by default the address is choosen by the kernel)
207
+ * Should be used only on init.
208
+ * returns -1 on error */
209
+int tcp_set_src_addr(struct ip_addr* ip)
210
+{
211
+	switch (ip->af){
212
+		case AF_INET:
213
+			ip_addr2su(&tcp_source_ipv4_addr, ip, 0);
214
+			tcp_source_ipv4=&tcp_source_ipv4_addr;
215
+			break;
216
+		#ifdef USE_IPV6
217
+		case AF_INET6:
218
+			ip_addr2su(&tcp_source_ipv6_addr, ip, 0);
219
+			tcp_source_ipv6=&tcp_source_ipv6_addr;
220
+			break;
221
+		#endif
222
+		default:
223
+			return -1;
224
+	}
225
+	return 0;
226
+}
227
+
228
+
229
+
203 230
 /* set all socket/fd options:  disable nagle, tos lowdelay, non-blocking
204 231
  * return -1 on error */
205 232
 static int init_sock_opt(int s)
... ...
@@ -415,6 +442,7 @@ end:
415 442
 
416 443
 
417 444
 struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
445
+									union sockaddr_union* local_addr,
418 446
 									struct socket_info* ba, int type, 
419 447
 									int state)
420 448
 {
... ...
@@ -439,7 +467,10 @@ struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
439 467
 	su2ip_addr(&c->rcv.src_ip, su);
440 468
 	c->rcv.src_port=su_getport(su);
441 469
 	c->rcv.bind_address=ba;
442
-	if (ba){
470
+	if (likely(local_addr)){
471
+		su2ip_addr(&c->rcv.dst_ip, local_addr);
472
+		c->rcv.dst_port=su_getport(local_addr);
473
+	}else if (ba){
443 474
 		c->rcv.dst_ip=ba->address;
444 475
 		c->rcv.dst_port=ba->port_no;
445 476
 	}
... ...
@@ -472,7 +503,9 @@ error:
472 503
 
473 504
 
474 505
 
475
-struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
506
+struct tcp_connection* tcpconn_connect( union sockaddr_union* server, 
507
+										union sockaddr_union* from,
508
+										int type)
476 509
 {
477 510
 	int s;
478 511
 	struct socket_info* si;
... ...
@@ -480,8 +513,6 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
480 513
 	socklen_t my_name_len;
481 514
 	struct tcp_connection* con;
482 515
 	struct ip_addr ip;
483
-	int do_bind = 0;
484
-	struct sockaddr *bind_addr;
485 516
 
486 517
 	s=-1;
487 518
 	
... ...
@@ -501,61 +532,47 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
501 532
 		LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n");
502 533
 		goto error;
503 534
 	}
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));
535
+	
536
+	if (from && bind(s, &from->s, sockaddru_len(*from)) != 0)
537
+		LOG(L_WARN, "WARNING: tcpconn_connect: binding to source address"
538
+					" failed: %s [%d]\n", strerror(errno), errno);
531 539
 
532 540
 	if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
533 541
 		LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n");
534 542
 		goto error;
535 543
 	}
544
+	if (from){
545
+		su2ip_addr(&ip, from);
546
+		if (!ip_addr_any(&ip))
547
+			/* we already know the source ip, skip the sys. call */
548
+			goto find_socket;
549
+	}
536 550
 	my_name_len=sizeof(my_name);
537 551
 	if (getsockname(s, &my_name.s, &my_name_len)!=0){
538 552
 		LOG(L_ERR, "ERROR: tcp_connect: getsockname failed: %s(%d)\n",
539 553
 				strerror(errno), errno);
540 554
 		si=0; /* try to go on */
555
+		goto skip;
541 556
 	}
557
+	from=&my_name; /* update from with the real "from" address */
542 558
 	su2ip_addr(&ip, &my_name);
559
+find_socket:
543 560
 #ifdef USE_TLS
544 561
 	if (type==PROTO_TLS)
545 562
 		si=find_si(&ip, 0, PROTO_TLS);
546 563
 	else
547 564
 #endif
548 565
 		si=find_si(&ip, 0, PROTO_TCP);
549
-
566
+skip:
550 567
 	if (si==0){
551
-		LOG(L_ERR, "ERROR: tcp_connect: could not find corresponding"
568
+		LOG(L_WARN, "WARNING: tcp_connect: could not find corresponding"
552 569
 				" listening socket, using default...\n");
553 570
 		if (server->s.sa_family==AF_INET) si=sendipv4_tcp;
554 571
 #ifdef USE_IPV6
555 572
 		else si=sendipv6_tcp;
556 573
 #endif
557 574
 	}
558
-	con=tcpconn_new(s, server, si, type, S_CONN_CONNECT);
575
+	con=tcpconn_new(s, server, from, si,  type, S_CONN_CONNECT);
559 576
 	if (con==0){
560 577
 		LOG(L_ERR, "ERROR: tcp_connect: tcpconn_new failed, closing the "
561 578
 				 " socket\n");
... ...
@@ -766,6 +783,7 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
766 783
 	int fd;
767 784
 	long response[2];
768 785
 	int n;
786
+	union sockaddr_union* from;
769 787
 	
770 788
 	port=su_getport(&dst->to);
771 789
 	if (port){
... ...
@@ -795,7 +813,22 @@ no_id:
795 813
 		if (c==0){
796 814
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
797 815
 			/* create tcp connection */
798
-			if ((c=tcpconn_connect(&dst->to, dst->proto))==0){
816
+				from=0;
817
+				/* check to see if we have to use a specific source addr. */
818
+				switch (dst->to.s.sa_family) {
819
+					case AF_INET:
820
+							from = tcp_source_ipv4;
821
+						break;
822
+#ifdef USE_IPV6
823
+					case AF_INET6:
824
+							from = tcp_source_ipv6;
825
+						break;
826
+#endif
827
+					default:
828
+						/* error, bad af, ignore ... */
829
+						break;
830
+				}
831
+			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
799 832
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
800 833
 				return -1;
801 834
 			}
... ...
@@ -1503,7 +1536,7 @@ static inline int handle_new_connect(struct socket_info* si)
1503 1536
 	(*tcp_connections_no)++;
1504 1537
 	
1505 1538
 	/* add socket to list */
1506
-	tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
1539
+	tcpconn=tcpconn_new(new_sock, &su, &si->su, si, si->proto, S_CONN_ACCEPT);
1507 1540
 	if (tcpconn){
1508 1541
 #ifdef TCP_PASS_NEW_CONNECTION_ON_DATA
1509 1542
 		io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn);