浏览代码

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 <[email protected]>
Closes SER-277

Hendrik Scholz 18 年之前
父节点
当前提交
761bd3097e
共有 5 个文件被更改,包括 83 次插入0 次删除
  1. 6 0
      NEWS
  2. 6 0
      cfg.lex
  3. 28 0
      cfg.y
  4. 6 0
      globals.h
  5. 37 0
      tcp_main.c

+ 6 - 0
NEWS

@@ -26,6 +26,12 @@ modules:
                            received bogus CANCEL will create a new transaction 
                            that will live by default 30s).
 
+new config variables:
+  tcp_source_ipv4 = IPv4 address
+  tcp_source_ipv6 = IPv6 address
+    Set the given source IP for all outbound TCP connections.
+    If setting the IP fails the TCP connection will use the default.
+
 
 2.0.0 changes
 

+ 6 - 0
cfg.lex

@@ -272,6 +272,8 @@ TCP_CONNECT_TIMEOUT	"tcp_connect_timeout"
 TCP_CON_LIFETIME	"tcp_connection_lifetime"
 TCP_POLL_METHOD		"tcp_poll_method"
 TCP_MAX_CONNECTIONS	"tcp_max_connections"
+TCP_SOURCE_IPV4		"tcp_source_ipv4"
+TCP_SOURCE_IPV6		"tcp_source_ipv6"
 DISABLE_TLS		"disable_tls"|"tls_disable"
 ENABLE_TLS		"enable_tls"|"tls_enable"
 TLSLOG			"tlslog"|"tls_log"
@@ -501,6 +503,10 @@ EAT_ABLE	[\ \t\b\r]
 									return TCP_POLL_METHOD; }
 <INITIAL>{TCP_MAX_CONNECTIONS}	{ count(); yylval.strval=yytext;
 									return TCP_MAX_CONNECTIONS; }
+<INITIAL>{TCP_SOURCE_IPV4}		{ count(); yylval.strval=yytext;
+									return TCP_SOURCE_IPV4; }
+<INITIAL>{TCP_SOURCE_IPV6}		{ count(); yylval.strval=yytext;
+									return TCP_SOURCE_IPV6; }
 <INITIAL>{DISABLE_TLS}	{ count(); yylval.strval=yytext; return DISABLE_TLS; }
 <INITIAL>{ENABLE_TLS}	{ count(); yylval.strval=yytext; return ENABLE_TLS; }
 <INITIAL>{TLSLOG}		{ count(); yylval.strval=yytext; return TLS_PORT_NO; }

+ 28 - 0
cfg.y

@@ -306,6 +306,8 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token TCP_CON_LIFETIME
 %token TCP_POLL_METHOD
 %token TCP_MAX_CONNECTIONS
+%token TCP_SOURCE_IPV4
+%token TCP_SOURCE_IPV6
 %token DISABLE_TLS
 %token ENABLE_TLS
 %token TLSLOG
@@ -708,6 +710,32 @@ assign_stm:
 		#endif
 	}
 	| TCP_MAX_CONNECTIONS EQUAL error { yyerror("number expected"); }
+	| TCP_SOURCE_IPV4 EQUAL ipv4 {
+		#ifdef USE_TCP
+			tcp_use_source_ipv4 = 1;
+			tcp_source_ipv4 = (struct sockaddr_in) {.sin_family = AF_INET, .sin_port = 0};
+			memcpy(&tcp_source_ipv4.sin_addr, &($3)->u.addr, 4);
+		#else
+			warn("tcp support not compiled in");
+		#endif
+		pkg_free($3);
+	}
+	| TCP_SOURCE_IPV4 EQUAL error { yyerror("IPv4 address expected"); }
+	| TCP_SOURCE_IPV6 EQUAL ipv6 {
+		#ifdef USE_TCP
+			#ifdef USE_IPV6
+				tcp_use_source_ipv6 = 1;
+				tcp_source_ipv6 = (struct sockaddr_in6) {.sin6_family = AF_INET6, .sin6_port = 0};
+				memcpy(&tcp_source_ipv6.sin6_addr, &($3)->u.addr, 16);
+			#else
+				warn("IPv6 support not compiled in");
+			#endif
+		#else
+			warn("tcp support not compiled in");
+		#endif
+		pkg_free($3);
+	}
+	| TCP_SOURCE_IPV6 EQUAL error { yyerror("IPv6 address expected"); }
 	| DISABLE_TLS EQUAL NUMBER {
 		#ifdef USE_TLS
 			tls_disable=$3;

+ 6 - 0
globals.h

@@ -86,6 +86,12 @@ extern int tcp_con_lifetime; /* connection lifetime */
 extern enum poll_types tcp_poll_method;
 extern int tcp_max_fd_no;
 extern int tcp_max_connections;
+extern int tcp_use_source_ipv4;
+extern struct sockaddr_in tcp_source_ipv4;
+#ifdef USE_IPV6
+extern int tcp_use_source_ipv6;
+extern struct sockaddr_in6 tcp_source_ipv6;
+#endif
 #endif
 #ifdef USE_TLS
 extern int tls_disable;

+ 37 - 0
tcp_main.c

@@ -172,6 +172,13 @@ enum poll_types tcp_poll_method=0; /* by default choose the best method */
 int tcp_max_connections=DEFAULT_TCP_MAX_CONNECTIONS;
 int tcp_max_fd_no=0;
 
+int tcp_use_source_ipv4 = 0;
+struct sockaddr_in tcp_source_ipv4;
+#ifdef USE_IPV6
+int tcp_use_source_ipv6 = 0;
+struct sockaddr_in6 tcp_source_ipv6;
+#endif
+
 static int* tcp_connections_no=0; /* current open connections */
 
 /* connection hash table (after ip&port) , includes also aliases */
@@ -473,6 +480,8 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
 	socklen_t my_name_len;
 	struct tcp_connection* con;
 	struct ip_addr ip;
+	int do_bind = 0;
+	struct sockaddr *bind_addr;
 
 	s=-1;
 	
@@ -492,6 +501,34 @@ struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
 		LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n");
 		goto error;
 	}
+
+	switch (server->s.sa_family) {
+		case AF_INET: {
+			if (tcp_use_source_ipv4) {
+				my_name_len = sizeof(tcp_source_ipv4);
+				bind_addr = (struct sockaddr *) &tcp_source_ipv4;
+				do_bind = 1;
+			}
+			break;
+		}
+#ifdef USE_IPV6
+		case AF_INET6: {
+			if (tcp_use_source_ipv6) {
+				my_name_len = sizeof(tcp_source_ipv6);
+				bind_addr = (struct sockaddr *) &tcp_source_ipv6;
+				do_bind = 1;
+			}
+			break;
+		}
+#endif
+		default: {
+			/* do nothing special */
+			break;
+		}
+	}
+	if (do_bind && bind(s, bind_addr, my_name_len) != 0)
+		LOG(L_WARN, "WARNING: tcpconn_connect: binding to source address failed: %s\n", strerror(errno));
+
 	if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
 		LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n");
 		goto error;