Bläddra i källkod

- 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 18 år sedan
förälder
incheckning
205fcb02c0
4 ändrade filer med 100 tillägg och 47 borttagningar
  1. 5 6
      cfg.y
  2. 16 0
      ip_addr.h
  3. 5 0
      tcp_init.h
  4. 74 41
      tcp_main.c

+ 5 - 6
cfg.y

@@ -108,6 +108,7 @@
 #include "dset.h"
 #include "select.h"
 #include "flags.h"
+#include "tcp_init.h"
 
 #include "config.h"
 #ifdef CORE_TLS
@@ -746,9 +747,8 @@ assign_stm:
 	| 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);
+			if (tcp_set_src_addr($3)<0)
+				warn("tcp_source_ipv4 failed");
 		#else
 			warn("tcp support not compiled in");
 		#endif
@@ -758,9 +758,8 @@ assign_stm:
 	| 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);
+				if (tcp_set_src_addr($3)<0)
+					warn("tcp_source_ipv6 failed");
 			#else
 				warn("IPv6 support not compiled in");
 			#endif

+ 16 - 0
ip_addr.h

@@ -188,6 +188,22 @@ void print_net(struct net* net);
 int is_mcast(struct ip_addr* ip);
 #endif /* USE_MCAST */
 
+/* returns 1 if the given ip address is INADDR_ANY or IN6ADDR_ANY,
+ * 0 otherwise */
+inline static int ip_addr_any(struct ip_addr* ip)
+{
+	int r;
+	int l;
+	
+	l=ip->len/4;
+	for (r=0; r<l; r++)
+		if (ip->u.addr32[r]!=0)
+			return 0;
+	return 1;
+}
+
+
+
 /* returns 1 if ip & net.mask == net.ip ; 0 otherwise & -1 on error 
 	[ diff. address families ]) */
 inline static int matchnet(struct ip_addr* ip, struct net* net)

+ 5 - 0
tcp_init.h

@@ -46,5 +46,10 @@ void tcp_main_loop();
 void tcp_receive_loop(int unix_sock);
 int tcp_fix_child_sockets(int* fd);
 
+/* sets source address used when opening new sockets and no source is specified
+ *  (by default the address is choosen by the kernel)
+ * Should be used only on init.
+ * returns -1 on error */
+int tcp_set_src_addr(struct ip_addr* ip);
 
 #endif

+ 74 - 41
tcp_main.c

@@ -73,6 +73,8 @@
  *               (andrei)
  *  2006-11-04  switched to raw ticks (to fix conversion errors which could
  *               result in inf. lifetime) (andrei)
+ *  2007-07-25  tcpconn_connect can now bind the socket on a specified
+ *                source addr/port (andrei)
  */
 
 
@@ -155,7 +157,7 @@
 #endif
 
 /* maximum accepted lifetime (maximum possible is  ~ MAXINT/2) */
-#define MAX_TCP_CON_LIFETIME	(1U<<(sizeof(ticks_t)*8-1))
+#define MAX_TCP_CON_LIFETIME	((1U<<(sizeof(ticks_t)*8-1))-1)
 /* minimum interval tcpconn_timeout() is allowed to run, in ticks */
 #define TCPCONN_TIMEOUT_MIN_RUN S_TO_TICKS(1)  /* once per s */
 
@@ -172,11 +174,11 @@ enum poll_types tcp_poll_method=0; /* by default choose the best method */
 int tcp_max_connections=DEFAULT_TCP_MAX_CONNECTIONS;
 int tcp_main_max_fd_no=0;
 
-int tcp_use_source_ipv4 = 0;
-struct sockaddr_in tcp_source_ipv4;
+static union sockaddr_union tcp_source_ipv4_addr; /* saved bind/srv v4 addr. */
+static union sockaddr_union* tcp_source_ipv4=0;
 #ifdef USE_IPV6
-int tcp_use_source_ipv6 = 0;
-struct sockaddr_in6 tcp_source_ipv6;
+static union sockaddr_union tcp_source_ipv6_addr; /* saved bind/src v6 addr. */
+static union sockaddr_union* tcp_source_ipv6=0;
 #endif
 
 static int* tcp_connections_no=0; /* current open connections */
@@ -200,6 +202,31 @@ static io_wait_h io_h;
 
 
 
+/* sets source address used when opening new sockets and no source is specified
+ *  (by default the address is choosen by the kernel)
+ * Should be used only on init.
+ * returns -1 on error */
+int tcp_set_src_addr(struct ip_addr* ip)
+{
+	switch (ip->af){
+		case AF_INET:
+			ip_addr2su(&tcp_source_ipv4_addr, ip, 0);
+			tcp_source_ipv4=&tcp_source_ipv4_addr;
+			break;
+		#ifdef USE_IPV6
+		case AF_INET6:
+			ip_addr2su(&tcp_source_ipv6_addr, ip, 0);
+			tcp_source_ipv6=&tcp_source_ipv6_addr;
+			break;
+		#endif
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+
+
 /* set all socket/fd options:  disable nagle, tos lowdelay, non-blocking
  * return -1 on error */
 static int init_sock_opt(int s)
@@ -415,6 +442,7 @@ end:
 
 
 struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
+									union sockaddr_union* local_addr,
 									struct socket_info* ba, int type, 
 									int state)
 {
@@ -439,7 +467,10 @@ struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
 	su2ip_addr(&c->rcv.src_ip, su);
 	c->rcv.src_port=su_getport(su);
 	c->rcv.bind_address=ba;
-	if (ba){
+	if (likely(local_addr)){
+		su2ip_addr(&c->rcv.dst_ip, local_addr);
+		c->rcv.dst_port=su_getport(local_addr);
+	}else if (ba){
 		c->rcv.dst_ip=ba->address;
 		c->rcv.dst_port=ba->port_no;
 	}
@@ -472,7 +503,9 @@ error:
 
 
 
-struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type)
+struct tcp_connection* tcpconn_connect( union sockaddr_union* server, 
+										union sockaddr_union* from,
+										int type)
 {
 	int s;
 	struct socket_info* si;
@@ -480,8 +513,6 @@ 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;
 	
@@ -501,61 +532,47 @@ 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 (from && bind(s, &from->s, sockaddru_len(*from)) != 0)
+		LOG(L_WARN, "WARNING: tcpconn_connect: binding to source address"
+					" failed: %s [%d]\n", strerror(errno), 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;
 	}
+	if (from){
+		su2ip_addr(&ip, from);
+		if (!ip_addr_any(&ip))
+			/* we already know the source ip, skip the sys. call */
+			goto find_socket;
+	}
 	my_name_len=sizeof(my_name);
 	if (getsockname(s, &my_name.s, &my_name_len)!=0){
 		LOG(L_ERR, "ERROR: tcp_connect: getsockname failed: %s(%d)\n",
 				strerror(errno), errno);
 		si=0; /* try to go on */
+		goto skip;
 	}
+	from=&my_name; /* update from with the real "from" address */
 	su2ip_addr(&ip, &my_name);
+find_socket:
 #ifdef USE_TLS
 	if (type==PROTO_TLS)
 		si=find_si(&ip, 0, PROTO_TLS);
 	else
 #endif
 		si=find_si(&ip, 0, PROTO_TCP);
-
+skip:
 	if (si==0){
-		LOG(L_ERR, "ERROR: tcp_connect: could not find corresponding"
+		LOG(L_WARN, "WARNING: tcp_connect: could not find corresponding"
 				" listening socket, using default...\n");
 		if (server->s.sa_family==AF_INET) si=sendipv4_tcp;
 #ifdef USE_IPV6
 		else si=sendipv6_tcp;
 #endif
 	}
-	con=tcpconn_new(s, server, si, type, S_CONN_CONNECT);
+	con=tcpconn_new(s, server, from, si,  type, S_CONN_CONNECT);
 	if (con==0){
 		LOG(L_ERR, "ERROR: tcp_connect: tcpconn_new failed, closing the "
 				 " socket\n");
@@ -766,6 +783,7 @@ int tcp_send(struct dest_info* dst, char* buf, unsigned len)
 	int fd;
 	long response[2];
 	int n;
+	union sockaddr_union* from;
 	
 	port=su_getport(&dst->to);
 	if (port){
@@ -795,7 +813,22 @@ no_id:
 		if (c==0){
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
 			/* create tcp connection */
-			if ((c=tcpconn_connect(&dst->to, dst->proto))==0){
+				from=0;
+				/* check to see if we have to use a specific source addr. */
+				switch (dst->to.s.sa_family) {
+					case AF_INET:
+							from = tcp_source_ipv4;
+						break;
+#ifdef USE_IPV6
+					case AF_INET6:
+							from = tcp_source_ipv6;
+						break;
+#endif
+					default:
+						/* error, bad af, ignore ... */
+						break;
+				}
+			if ((c=tcpconn_connect(&dst->to, from, dst->proto))==0){
 				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
 				return -1;
 			}
@@ -1503,7 +1536,7 @@ static inline int handle_new_connect(struct socket_info* si)
 	(*tcp_connections_no)++;
 	
 	/* add socket to list */
-	tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
+	tcpconn=tcpconn_new(new_sock, &su, &si->su, si, si->proto, S_CONN_ACCEPT);
 	if (tcpconn){
 #ifdef TCP_PASS_NEW_CONNECTION_ON_DATA
 		io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn);