Browse code

tcp: blacklist at tcp level if possible

- automatically blacklist destinations if connecting to them fails
(BLST_ERR_CONNECT) or send fails (BLST_ERR_SEND), either due to a
protocol error (RST, protocol level timeout a.s.o), or because
of a ser level send/connect timeout.
Note: in this cases the sip_msg parameter of the blacklist will
be null (since in general the message triggering the error is not
known), so if you register a blacklist callback you should make
sure it works with null sip_msgs too.

- if a connection is in a connect pending state (S_CONN_CONNECT)
and something is read on it, move it into established state
(S_CONN_OK). This can happen only in tcp async mode.

- fix transition directly to S_CONN_OK from S_CONN_PENDING (should go
through S_CONN_CONNECT first)

Andrei Pelinescu-Onciul authored on 26/02/2009 23:13:22
Showing 2 changed files
... ...
@@ -97,6 +97,7 @@
97 97
  *               POLLHUP (andrei)
98 98
  *              on write error check if there's still data in the socket 
99 99
  *               read buffer and process it first (andrei)
100
+ *  2009-02-26  direct blacklist support (andrei)
100 101
  */
101 102
 
102 103
 
... ...
@@ -161,7 +162,10 @@
161 161
 #else
162 162
 #include "tls_hooks_init.h"
163 163
 #include "tls_hooks.h"
164
-#endif
164
+#endif /* CORE_TLS*/
165
+#ifdef USE_DST_BLACKLIST
166
+#include "dst_blacklist.h"
167
+#endif /* USE_DST_BLACKLIST */
165 168
 
166 169
 #include "tcp_info.h"
167 170
 #include "tcp_options.h"
... ...
@@ -481,7 +485,8 @@ error:
481 481
  * if BLOCKING_USE_SELECT and HAVE_SELECT are defined it will internally
482 482
  * use select() instead of poll (bad if fd > FD_SET_SIZE, poll is preferred)
483 483
  */
484
-static int tcp_blocking_connect(int fd, const struct sockaddr *servaddr,
484
+static int tcp_blocking_connect(int fd, int type,
485
+								const struct sockaddr *servaddr,
485 486
 								socklen_t addrlen)
486 487
 {
487 488
 	int n;
... ...
@@ -511,6 +516,19 @@ again:
511 511
 			else goto error_timeout;
512 512
 		}
513 513
 		if (errno!=EINPROGRESS && errno!=EALREADY){
514
+#ifdef USE_DST_BLACKLIST
515
+			if (cfg_get(core, core_cfg, use_dst_blacklist))
516
+				switch(errno){
517
+					case ECONNREFUSED:
518
+					case ENETUNREACH:
519
+					case ETIMEDOUT:
520
+					case ECONNRESET:
521
+					case EHOSTUNREACH:
522
+						dst_blacklist_su(BLST_ERR_CONNECT, type,
523
+										 (union sockaddr_union*)servaddr, 0);
524
+						break;
525
+				}
526
+#endif /* USE_DST_BLACKLIST */
514 527
 			LOG(L_ERR, "ERROR: tcp_blocking_connect %s: (%d) %s\n",
515 528
 					su2a((union sockaddr_union*)servaddr, addrlen),
516 529
 					errno, strerror(errno));
... ...
@@ -574,6 +592,11 @@ again:
574 574
 	}
575 575
 error_timeout:
576 576
 	/* timeout */
577
+#ifdef USE_DST_BLACKLIST
578
+	if (cfg_get(core, core_cfg, use_dst_blacklist))
579
+		dst_blacklist_su(BLST_ERR_CONNECT, type,
580
+							(union sockaddr_union*)servaddr, 0);
581
+#endif /* USE_DST_BLACKLIST */
577 582
 	LOG(L_ERR, "ERROR: tcp_blocking_connect %s: timeout %d s elapsed "
578 583
 				"from %d s\n", su2a((union sockaddr_union*)servaddr, addrlen),
579 584
 				elapsed, tcp_connect_timeout);
... ...
@@ -619,6 +642,15 @@ inline static int _wbufq_add(struct  tcp_connection* c, char* data,
619 619
 					" (%d, total %d, last write %d s ago)\n",
620 620
 					size, q->queued, *tcp_total_wq,
621 621
 					TICKS_TO_S(t-q->wr_timeout-tcp_options.tcp_wq_timeout));
622
+#ifdef USE_DST_BLACKLIST
623
+		if (q->first && TICKS_LT(q->wr_timeout, t) &&
624
+				cfg_get(core, core_cfg, use_dst_blacklist)){
625
+			ERR("blacklisting, state=%d\n", c->state);
626
+			dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
627
+									BLST_ERR_SEND,
628
+								c->rcv.proto, &c->rcv.src_su, 0);
629
+		}
630
+#endif /* USE_DST_BLACKLIST */
622 631
 		goto error;
623 632
 	}
624 633
 	
... ...
@@ -682,7 +714,7 @@ inline static int _wbufq_insert(struct  tcp_connection* c, char* data,
682 682
 		return _wbufq_add(c, data, size);
683 683
 	
684 684
 	if (unlikely((*tcp_total_wq+size)>tcp_options.tcp_wq_max)){
685
-		LOG(L_ERR, "ERROR: wbufq_insert(%d bytes): write queue full or timeout"
685
+		LOG(L_ERR, "ERROR: wbufq_insert(%d bytes): write queue full"
686 686
 					" (%d, total %d, last write %d s ago)\n",
687 687
 					size, q->queued, *tcp_total_wq,
688 688
 					TICKS_TO_S(get_ticks_raw()-q->wr_timeout-
... ...
@@ -784,11 +816,24 @@ inline static int wbufq_run(int fd, struct tcp_connection* c, int* empty)
784 784
 				break;
785 785
 			}
786 786
 			q->wr_timeout=t+tcp_options.tcp_wq_timeout;
787
-			c->state=S_CONN_OK;
788 787
 		}else{
789 788
 			if (n<0){
790 789
 				/* EINTR is handled inside _tcpconn_write_nb */
791 790
 				if (!(errno==EAGAIN || errno==EWOULDBLOCK)){
791
+#ifdef USE_DST_BLACKLIST
792
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
793
+						switch(errno){
794
+							case ENETUNREACH:
795
+							case ECONNRESET:
796
+							/*case EHOSTUNREACH: -- not posix */
797
+								dst_blacklist_su((c->state==S_CONN_CONNECT)?
798
+														BLST_ERR_CONNECT:
799
+														BLST_ERR_SEND,
800
+														c->rcv.proto,
801
+														&c->rcv.src_su, 0);
802
+								break;
803
+						}
804
+#endif /* USE_DST_BLACKLIST */
792 805
 					ret=-1;
793 806
 					LOG(L_ERR, "ERROR: wbuf_runq: %s [%d]\n",
794 807
 						strerror(errno), errno);
... ...
@@ -994,6 +1039,19 @@ again:
994 994
 			if (likely(errno==EINPROGRESS))
995 995
 				*state=S_CONN_CONNECT;
996 996
 			else if (errno!=EALREADY){
997
+#ifdef USE_DST_BLACKLIST
998
+				if (cfg_get(core, core_cfg, use_dst_blacklist))
999
+					switch(errno){
1000
+						case ECONNREFUSED:
1001
+						case ENETUNREACH:
1002
+						case ETIMEDOUT:
1003
+						case ECONNRESET:
1004
+						case EHOSTUNREACH:
1005
+							dst_blacklist_su(BLST_ERR_CONNECT, type, server,
1006
+												0);
1007
+							break;
1008
+				}
1009
+#endif /* USE_DST_BLACKLIST */
997 1010
 				LOG(L_ERR, "ERROR: tcp_do_connect: connect %s: (%d) %s\n",
998 1011
 							su2a(server, sizeof(*server)),
999 1012
 							errno, strerror(errno));
... ...
@@ -1002,7 +1060,8 @@ again:
1002 1002
 		}
1003 1003
 	}else{
1004 1004
 #endif /* TCP_BUF_WRITE */
1005
-		if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
1005
+		if (tcp_blocking_connect(s, type, &server->s,
1006
+									sockaddru_len(*server))<0){
1006 1007
 			LOG(L_ERR, "ERROR: tcp_do_connect: tcp_blocking_connect %s"
1007 1008
 						" failed\n", su2a(server, sizeof(*server)));
1008 1009
 			goto error;
... ...
@@ -1656,6 +1715,8 @@ no_id:
1656 1656
 				/* do connect and if src ip or port changed, update the 
1657 1657
 				 * aliases */
1658 1658
 				if (unlikely((fd=tcpconn_finish_connect(c, from))<0)){
1659
+					/* tcpconn_finish_connect will automatically blacklist
1660
+					   on error => no need to do it here */
1659 1661
 					LOG(L_ERR, "ERROR: tcp_send %s: tcpconn_finish_connect(%p)"
1660 1662
 							" failed\n", su2a(&dst->to, sizeof(dst->to)),
1661 1663
 								c);
... ...
@@ -1703,6 +1764,18 @@ no_id:
1703 1703
 						n=len;
1704 1704
 						goto end;
1705 1705
 					}
1706
+#ifdef USE_DST_BLACKLIST
1707
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
1708
+						switch(errno){
1709
+							case ENETUNREACH:
1710
+							case ECONNRESET:
1711
+							/*case EHOSTUNREACH: -- not posix */
1712
+								/* if first write failed it's most likely a
1713
+								   connect error */
1714
+								dst_blacklist_add( BLST_ERR_CONNECT, dst, 0);
1715
+								break;
1716
+						}
1717
+#endif /* USE_DST_BLACKLIST */
1706 1718
 					/* error: destroy it directly */
1707 1719
 					LOG(L_ERR, "ERROR: tcp_send %s: connect & send "
1708 1720
 										" for %p failed:" " %s (%d)\n",
... ...
@@ -1899,6 +1972,20 @@ send_it:
1899 1899
 			lock_release(&c->write_lock);
1900 1900
 		}
1901 1901
 #endif /* TCP_BUF_WRITE */
1902
+#ifdef USE_DST_BLACKLIST
1903
+		if (cfg_get(core, core_cfg, use_dst_blacklist))
1904
+			switch(errno){
1905
+				case ENETUNREACH:
1906
+				case ECONNRESET:
1907
+				/*case EHOSTUNREACH: -- not posix */
1908
+					dst_blacklist_su((c->state==S_CONN_CONNECT)?
1909
+											BLST_ERR_CONNECT:
1910
+											BLST_ERR_SEND,
1911
+										c->rcv.proto,
1912
+										&c->rcv.src_su, 0);
1913
+					break;
1914
+			}
1915
+#endif /* USE_DST_BLACKLIST */
1902 1916
 		LOG(L_ERR, "ERROR: tcp_send: failed to send on %p (%s:%d->%s): %s (%d)"
1903 1917
 					"\n", c, ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port,
1904 1918
 					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
... ...
@@ -2579,6 +2666,14 @@ inline static int handle_tcp_child(struct tcp_child* tcp_c, int fd_i)
2579 2579
 							"refcnt= %d\n", tcpconn,
2580 2580
 							atomic_get(&tcpconn->refcnt));
2581 2581
 					/* timeout */
2582
+#ifdef USE_DST_BLACKLIST
2583
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
2584
+						dst_blacklist_su((tcpconn->state==S_CONN_CONNECT)?
2585
+													BLST_ERR_CONNECT:
2586
+													BLST_ERR_SEND,
2587
+													tcpconn->rcv.proto,
2588
+													&tcpconn->rcv.src_su, 0);
2589
+#endif /* USE_DST_BLACKLIST */
2582 2590
 					if (unlikely(tcpconn->flags & F_CONN_WRITE_W)){
2583 2591
 						io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);
2584 2592
 						tcpconn->flags&=~F_CONN_WRITE_W;
... ...
@@ -2893,7 +2988,6 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
2893 2893
 				tcpconn_put_destroy(tcpconn);
2894 2894
 				break;
2895 2895
 			}
2896
-			tcpconn->state=S_CONN_OK;
2897 2896
 			(*tcp_connections_no)++;
2898 2897
 			tcpconn->s=fd;
2899 2898
 			/* update the timeout*/
... ...
@@ -2905,6 +2999,7 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
2905 2905
 								tcp_con_lifetime, t);
2906 2906
 			tcpconn->flags|=F_CONN_MAIN_TIMER|F_CONN_READ_W|F_CONN_WANTS_RD;
2907 2907
 			if (unlikely(cmd==CONN_NEW_COMPLETE)){
2908
+				tcpconn->state=S_CONN_OK;
2908 2909
 				/* check if needs to be watched for write */
2909 2910
 				lock_get(&tcpconn->write_lock);
2910 2911
 					/* if queue non empty watch it for write */
... ...
@@ -2914,6 +3009,10 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
2914 2914
 									(F_CONN_WRITE_W|F_CONN_WANTS_WR);
2915 2915
 			}else{
2916 2916
 				/* CONN_NEW_PENDING_WRITE */
2917
+				/* we don't know if we successfully sent anything, but
2918
+				   for sure we haven't sent all what we wanted, so consider
2919
+				   the connection in "connecting" state */
2920
+				tcpconn->state=S_CONN_CONNECT;
2917 2921
 				/* no need to check, we have something queued for write */
2918 2922
 				flags=POLLOUT;
2919 2923
 				tcpconn->flags|=(F_CONN_WRITE_W|F_CONN_WANTS_WR);
... ...
@@ -3351,6 +3450,15 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln* tl, void* data)
3351 3351
 		else
3352 3352
 			return (ticks_t)(c->timeout - t);
3353 3353
 	}
3354
+#ifdef USE_DST_BLACKLIST
3355
+	/* if time out due to write, add it to the blacklist */
3356
+	if (tcp_options.tcp_buf_write && _wbufq_non_empty(c) &&
3357
+			TICKS_GE(t, c->wbuf_q.wr_timeout) &&
3358
+			cfg_get(core, core_cfg, use_dst_blacklist))
3359
+		dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
3360
+										BLST_ERR_SEND,
3361
+								c->rcv.proto, &c->rcv.src_su, 0);
3362
+#endif /* USE_DST_BLACKLIST */
3354 3363
 #else /* ! TCP_BUF_WRITE */
3355 3364
 	if (TICKS_LT(t, c->timeout)){
3356 3365
 		/* timeout extended, exit */
... ...
@@ -40,6 +40,7 @@
40 40
  * 2007-11-26  improved tcp timers: switched to local_timer (andrei)
41 41
  * 2008-02-04  optimizations: handle POLLRDHUP (if supported), detect short
42 42
  *              reads (sock. buffer empty) (andrei)
43
+ * 2009-02-26  direct blacklist support (andrei)
43 44
  */
44 45
 
45 46
 #ifdef USE_TCP
... ...
@@ -72,7 +73,10 @@
72 72
 #include "tls/tls_server.h"
73 73
 #else
74 74
 #include "tls_hooks.h"
75
-#endif
75
+#endif /* CORE_TLS */
76
+#ifdef USE_DST_BLACKLIST
77
+#include "dst_blacklist.h"
78
+#endif /* USE_DST_BLACKLIST */
76 79
 
77 80
 #define HANDLE_IO_INLINE
78 81
 #include "io_wait.h"
... ...
@@ -142,6 +146,19 @@ again:
142 142
 				bytes_read=0; /* nothing has been read */
143 143
 			}else if (errno == EINTR) goto again;
144 144
 			else{
145
+#ifdef USE_DST_BLACKLIST
146
+				if (cfg_get(core, core_cfg, use_dst_blacklist))
147
+					switch(errno){
148
+						case ECONNRESET:
149
+						case ETIMEDOUT:
150
+							dst_blacklist_su((c->state==S_CONN_CONNECT)?
151
+													BLST_ERR_CONNECT:
152
+													BLST_ERR_SEND,
153
+													c->rcv.proto,
154
+													&c->rcv.src_su, 0);
155
+							break;
156
+					}
157
+#endif /* USE_DST_BLACKLIST */
145 158
 				LOG(L_ERR, "ERROR: tcp_read: error reading: %s (%d)\n",
146 159
 							strerror(errno), errno);
147 160
 				r->error=TCP_READ_ERROR;
... ...
@@ -152,10 +169,16 @@ again:
152 152
 			c->state=S_CONN_EOF;
153 153
 			*flags|=RD_CONN_EOF;
154 154
 			DBG("tcp_read: EOF on %p, FD %d\n", c, fd);
155
+		}else{
156
+			if (unlikely(c->state==S_CONN_CONNECT))
157
+				c->state=S_CONN_OK;
155 158
 		}
156 159
 		/* short read */
157 160
 		*flags|=RD_CONN_SHORT_READ;
158
-	} /* else normal full read */
161
+	}else{ /* else normal full read */
162
+		if (unlikely(c->state==S_CONN_CONNECT))
163
+			c->state=S_CONN_OK;
164
+	}
159 165
 #ifdef EXTRA_DEBUG
160 166
 	DBG("tcp_read: read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos);
161 167
 #endif