Browse code

Add two new config variables to set source IP on outbound TCP connections. As Jan stated this is useful for HA setups with virtual IPs.

Patch provided by Jan Andres <jan.andres@freenet-ag.de>
Closes SER-277

Hendrik Scholz authored on 30/05/2007 12:48:24
Showing 5 changed files
... ...
@@ -26,6 +26,12 @@ modules:
26 26
                            received bogus CANCEL will create a new transaction 
27 27
                            that will live by default 30s).
28 28
 
29
+new config variables:
30
+  tcp_source_ipv4 = IPv4 address
31
+  tcp_source_ipv6 = IPv6 address
32
+    Set the given source IP for all outbound TCP connections.
33
+    If setting the IP fails the TCP connection will use the default.
34
+
29 35
 
30 36
 2.0.0 changes
31 37
 
... ...
@@ -272,6 +272,8 @@ TCP_CONNECT_TIMEOUT	"tcp_connect_timeout"
272 272
 TCP_CON_LIFETIME	"tcp_connection_lifetime"
273 273
 TCP_POLL_METHOD		"tcp_poll_method"
274 274
 TCP_MAX_CONNECTIONS	"tcp_max_connections"
275
+TCP_SOURCE_IPV4		"tcp_source_ipv4"
276
+TCP_SOURCE_IPV6		"tcp_source_ipv6"
275 277
 DISABLE_TLS		"disable_tls"|"tls_disable"
276 278
 ENABLE_TLS		"enable_tls"|"tls_enable"
277 279
 TLSLOG			"tlslog"|"tls_log"
... ...
@@ -501,6 +503,10 @@ EAT_ABLE	[\ \t\b\r]
501 503
 									return TCP_POLL_METHOD; }
502 504
 <INITIAL>{TCP_MAX_CONNECTIONS}	{ count(); yylval.strval=yytext;
503 505
 									return TCP_MAX_CONNECTIONS; }
506
+<INITIAL>{TCP_SOURCE_IPV4}		{ count(); yylval.strval=yytext;
507
+									return TCP_SOURCE_IPV4; }
508
+<INITIAL>{TCP_SOURCE_IPV6}		{ count(); yylval.strval=yytext;
509
+									return TCP_SOURCE_IPV6; }
504 510
 <INITIAL>{DISABLE_TLS}	{ count(); yylval.strval=yytext; return DISABLE_TLS; }
505 511
 <INITIAL>{ENABLE_TLS}	{ count(); yylval.strval=yytext; return ENABLE_TLS; }
506 512
 <INITIAL>{TLSLOG}		{ count(); yylval.strval=yytext; return TLS_PORT_NO; }
... ...
@@ -306,6 +306,8 @@ static struct socket_id* mk_listen_id(char*, int, int);
306 306
 %token TCP_CON_LIFETIME
307 307
 %token TCP_POLL_METHOD
308 308
 %token TCP_MAX_CONNECTIONS
309
+%token TCP_SOURCE_IPV4
310
+%token TCP_SOURCE_IPV6
309 311
 %token DISABLE_TLS
310 312
 %token ENABLE_TLS
311 313
 %token TLSLOG
... ...
@@ -708,6 +710,32 @@ assign_stm:
708 710
 		#endif
709 711
 	}
710 712
 	| TCP_MAX_CONNECTIONS EQUAL error { yyerror("number expected"); }
713
+	| TCP_SOURCE_IPV4 EQUAL ipv4 {
714
+		#ifdef USE_TCP
715
+			tcp_use_source_ipv4 = 1;
716
+			tcp_source_ipv4 = (struct sockaddr_in) {.sin_family = AF_INET, .sin_port = 0};
717
+			memcpy(&tcp_source_ipv4.sin_addr, &($3)->u.addr, 4);
718
+		#else
719
+			warn("tcp support not compiled in");
720
+		#endif
721
+		pkg_free($3);
722
+	}
723
+	| TCP_SOURCE_IPV4 EQUAL error { yyerror("IPv4 address expected"); }
724
+	| TCP_SOURCE_IPV6 EQUAL ipv6 {
725
+		#ifdef USE_TCP
726
+			#ifdef USE_IPV6
727
+				tcp_use_source_ipv6 = 1;
728
+				tcp_source_ipv6 = (struct sockaddr_in6) {.sin6_family = AF_INET6, .sin6_port = 0};
729
+				memcpy(&tcp_source_ipv6.sin6_addr, &($3)->u.addr, 16);
730
+			#else
731
+				warn("IPv6 support not compiled in");
732
+			#endif
733
+		#else
734
+			warn("tcp support not compiled in");
735
+		#endif
736
+		pkg_free($3);
737
+	}
738
+	| TCP_SOURCE_IPV6 EQUAL error { yyerror("IPv6 address expected"); }
711 739
 	| DISABLE_TLS EQUAL NUMBER {
712 740
 		#ifdef USE_TLS
713 741
 			tls_disable=$3;
... ...
@@ -86,6 +86,12 @@ extern int tcp_con_lifetime; /* connection lifetime */
86 86
 extern enum poll_types tcp_poll_method;
87 87
 extern int tcp_max_fd_no;
88 88
 extern int tcp_max_connections;
89
+extern int tcp_use_source_ipv4;
90
+extern struct sockaddr_in tcp_source_ipv4;
91
+#ifdef USE_IPV6
92
+extern int tcp_use_source_ipv6;
93
+extern struct sockaddr_in6 tcp_source_ipv6;
94
+#endif
89 95
 #endif
90 96
 #ifdef USE_TLS
91 97
 extern int tls_disable;
... ...
@@ -172,6 +172,13 @@ 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_max_fd_no=0;
174 174
 
175
+int tcp_use_source_ipv4 = 0;
176
+struct sockaddr_in tcp_source_ipv4;
177
+#ifdef USE_IPV6
178
+int tcp_use_source_ipv6 = 0;
179
+struct sockaddr_in6 tcp_source_ipv6;
180
+#endif
181
+
175 182
 static int* tcp_connections_no=0; /* current open connections */
176 183
 
177 184
 /* connection hash table (after ip&port) , includes also aliases */
... ...
@@ -473,6 +480,8 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
473 480
 	socklen_t my_name_len;
474 481
 	struct tcp_connection* con;
475 482
 	struct ip_addr ip;
483
+	int do_bind = 0;
484
+	struct sockaddr *bind_addr;
476 485
 
477 486
 	s=-1;
478 487
 	
... ...
@@ -492,6 +501,34 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
492 501
 		LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n");
493 502
 		goto error;
494 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));
531
+
495 532
 	if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
496 533
 		LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n");
497 534
 		goto error;