浏览代码

tcp: internal macro-hooks for stats & events

- added macros for tcp statistics (for now empty, keeping
  more statistics will only involve redefining them):

  TCP_STATS_ESTABLISHED(state) - new connection established
  TCP_STATS_CONNECT_FAILED() - new outgoing connection failed
  TCP_STATS_LOCAL_REJECT() - new incoming connection rejected
                            (max. no exceeded)
  TCP_STATS_CON_TIMEOUT() - connection closed for being idle too
                            long
  TCP_STATS_SEND_TIMEOUT() - send fails due to a timeout
  TCP_STATS_SENDQ_FULL()   - send fails because of the buffering
                             capacity being exceed (async mode
                             only)

- added macros for various tcp events (for now some of them are
  used for logging):

  TCP_EV_CONNECT_RST(...) - connect attempt received RST
  TCP_EV_CONNECT_UNREACHABLE(...)
  TCP_EV_CONNECT_TIMEOUT(...)
  TCP_EV_CONNECT_NO_MORE_PORTS(...)
  TCP_EV_CONNECT_ERR(...)  - other connect error
  TCP_EV_SEND_TIMEOUT(...)
  TCP_EV_SENDQ_FULL(...)
  TCP_EV_IDLE_CONN_CLOSED(...)
Andrei Pelinescu-Onciul 16 年之前
父节点
当前提交
7bb2b4ca37
共有 4 个文件被更改,包括 475 次插入75 次删除
  1. 97 0
      tcp_ev.h
  2. 262 66
      tcp_main.c
  3. 44 9
      tcp_read.c
  4. 72 0
      tcp_stats.h

+ 97 - 0
tcp_ev.h

@@ -0,0 +1,97 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2009 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * tcp_ev.h - tcp events
+ */
+/*
+ * History:
+ * --------
+ *  2009-04-09  initial version (andrei)
+*/
+
+#ifndef __tcp_ev_h
+#define __tcp_ev_h
+
+#include <errno.h>
+#include <string.h>
+
+#include "ip_addr.h"
+
+
+/** a connect attempt got a RST from the peer
+ * Note: the RST might be for the connect() itself (SYN), for the first
+ *  send() attempt on the connection (unlikely) or received immediately after
+ * the connect() succeeded (unlikely, the remote host would have a very small
+ *  window after accepting a connection to send a RST before it receives
+ * any data).
+ *
+ * @param err - if 0 it should be ignored (no corresp. libc error), if non-0
+ *                it will contain the errno.
+ * @param lip   - pointer to an ip_addr containing the local ip
+ *                   or 0 if dynamic (WARNING can be 0).
+ * @param lport - pointer to an ip_addr containing the local port or 0
+ *                   if unknown/dynamic.
+ * @param dst   - pointer to a sockaddr_union containing the destination.
+ * @param proto - protocol used
+ */
+#define TCP_EV_CONNECT_RST(err, lip, lport, dst, proto) \
+	LOG(L_ERR, "connect %s failed (RST) %s\n", \
+			su2a(dst, sizeof(*(dst))), (err)?strerror(err):"")
+
+/** a connect failed because the remote host/network is unreachable. */
+#define TCP_EV_CONNECT_UNREACHABLE(err, lip, lport, dst, proto) \
+	LOG(L_ERR, "connect %s failed (unreachable) %s\n", \
+			su2a(dst, sizeof(*(dst))), (err)?strerror(err):"")
+
+/** a connect attempt did timeout. */
+#define TCP_EV_CONNECT_TIMEOUT(err, lip, lport, dst, proto) \
+	LOG(L_ERR, "connect %s failed (timeout) %s\n", \
+			su2a(dst, sizeof(*(dst))), (err)?strerror(err):"")
+
+/** a connect attempt failed because the local ports are exhausted. */
+#define TCP_EV_CONNECT_NO_MORE_PORTS(err, lip, lport, dst, proto) \
+	LOG(L_ERR, "connect %s failed (no more ports) %s\n", \
+			su2a(dst, sizeof(*(dst))), (err)?strerror(err):"")
+
+/** a connect attempt failed for some unknown reason.  */
+#define TCP_EV_CONNECT_ERR(err, lip, lport, dst, proto) \
+	LOG(L_ERR, "connect %s failed %s\n", \
+			su2a(dst, sizeof(*(dst))), (err)?strerror(err):"")
+
+
+/** send failed due to timeout.
+ * @param err   - if 0 it should be ignored (no corresp. libc error), if non-0
+ *                it will contain the errno.
+ * @param rcv   - pointer to rcv_info structure
+ * 
+ */
+#define TCP_EV_SEND_TIMEOUT(err, rcv)
+
+/** send failed due to buffering capacity being exceeded.
+  * (only in async mode) */
+#define TCP_EV_SENDQ_FULL(err, rcv)
+
+/** established connection closed for being idle too long. */
+#define TCP_EV_IDLE_CONN_CLOSED(err, rcv)
+
+
+
+
+#endif /*__tcp_ev_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 262 - 66
tcp_main.c

@@ -100,6 +100,7 @@
  *  2009-02-26  direct blacklist support (andrei)
  *  2009-03-20  s/wq_timeout/send_timeout ; send_timeout is now in ticks
  *              (andrei)
+ * 2009-04-09  tcp ev and tcp stats macros added (andrei)
  */
 
 
@@ -155,6 +156,8 @@
 #include "sr_module.h"
 #include "tcp_server.h"
 #include "tcp_init.h"
+#include "tcp_stats.h"
+#include "tcp_ev.h"
 #include "tsend.h"
 #include "timer_ticks.h"
 #include "local_timer.h"
@@ -503,19 +506,45 @@ again:
 			else goto error_timeout;
 		}
 		if (errno!=EINPROGRESS && errno!=EALREADY){
+			switch(errno){
+				case ENETUNREACH:
+				case EHOSTUNREACH:
 #ifdef USE_DST_BLACKLIST
-			if (cfg_get(core, core_cfg, use_dst_blacklist))
-				switch(errno){
-					case ECONNREFUSED:
-					case ENETUNREACH:
-					case ETIMEDOUT:
-					case ECONNRESET:
-					case EHOSTUNREACH:
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su(BLST_ERR_CONNECT, type,
+									 (union sockaddr_union*)servaddr, 0);
+#endif /* USE_DST_BLACKLIST */
+					TCP_EV_CONNECT_UNREACHABLE(errno, 0, 0,
+									(union sockaddr_union*)servaddr, type);
+					break;
+				case ETIMEDOUT:
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
 						dst_blacklist_su(BLST_ERR_CONNECT, type,
 										 (union sockaddr_union*)servaddr, 0);
-						break;
-				}
 #endif /* USE_DST_BLACKLIST */
+					TCP_EV_CONNECT_TIMEOUT(errno, 0, 0,
+									(union sockaddr_union*)servaddr, type);
+					break;
+				case ECONNREFUSED:
+				case ECONNRESET:
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su(BLST_ERR_CONNECT, type,
+										 (union sockaddr_union*)servaddr, 0);
+#endif /* USE_DST_BLACKLIST */
+					TCP_EV_CONNECT_RST(errno, 0, 0,
+									(union sockaddr_union*)servaddr, type);
+					break;
+				case EAGAIN: /* not posix, but supported on linux and bsd */
+					TCP_EV_CONNECT_NO_MORE_PORTS(errno, 0, 0,
+									(union sockaddr_union*)servaddr, type);
+					break;
+				default:
+					TCP_EV_CONNECT_ERR(errno, 0, 0,
+										(union sockaddr_union*)servaddr, type);
+			}
+			TCP_STATS_CONNECT_FAILED();
 			LOG(L_ERR, "ERROR: tcp_blocking_connect %s: (%d) %s\n",
 					su2a((union sockaddr_union*)servaddr, addrlen),
 					errno, strerror(errno));
@@ -584,6 +613,8 @@ error_timeout:
 		dst_blacklist_su(BLST_ERR_CONNECT, type,
 							(union sockaddr_union*)servaddr, 0);
 #endif /* USE_DST_BLACKLIST */
+	TCP_EV_CONNECT_TIMEOUT(0, 0, 0, (union sockaddr_union*)servaddr, type);
+	TCP_STATS_CONNECT_FAILED();
 	LOG(L_ERR, "ERROR: tcp_blocking_connect %s: timeout %d s elapsed "
 				"from %d s\n", su2a((union sockaddr_union*)servaddr, addrlen),
 				elapsed, cfg_get(tcp, tcp_cfg, connect_timeout_s));
@@ -630,15 +661,34 @@ inline static int _wbufq_add(struct  tcp_connection* c, char* data,
 					size, q->queued, *tcp_total_wq,
 					TICKS_TO_S(t-q->wr_timeout-
 						cfg_get(tcp, tcp_cfg, send_timeout)));
+		if (q->first && TICKS_LT(q->wr_timeout, t)){
+			if (unlikely(c->state==S_CONN_CONNECT)){
 #ifdef USE_DST_BLACKLIST
-		if (q->first && TICKS_LT(q->wr_timeout, t) &&
-				cfg_get(core, core_cfg, use_dst_blacklist)){
-			ERR("blacklisting, state=%d\n", c->state);
-			dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
-									BLST_ERR_SEND,
-								c->rcv.proto, &c->rcv.src_su, 0);
-		}
+				if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
+					DBG("blacklisting, state=%d\n", c->state);
+					dst_blacklist_su( BLST_ERR_CONNECT, c->rcv.proto,
+										&c->rcv.src_su, 0);
+				}
+#endif /* USE_DST_BLACKLIST */
+				TCP_EV_CONNECT_TIMEOUT(0, TCP_LADDR(c), TCP_LPORT(c),
+											TCP_PSU(c), TCP_PROTO(c));
+				TCP_STATS_CONNECT_FAILED();
+			}else{
+#ifdef USE_DST_BLACKLIST
+				if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
+					DBG("blacklisting, state=%d\n", c->state);
+					dst_blacklist_su( BLST_ERR_SEND, c->rcv.proto,
+										&c->rcv.src_su, 0);
+				}
 #endif /* USE_DST_BLACKLIST */
+				TCP_EV_SEND_TIMEOUT(0, &c->rcv);
+				TCP_STATS_SEND_TIMEOUT();
+			}
+		}else{
+			/* if it's not a timeout => queue full */
+			TCP_EV_SENDQ_FULL(0, &c->rcv);
+			TCP_STATS_SENDQ_FULL();
+		}
 		goto error;
 	}
 	
@@ -808,20 +858,55 @@ inline static int wbufq_run(int fd, struct tcp_connection* c, int* empty)
 			if (n<0){
 				/* EINTR is handled inside _tcpconn_write_nb */
 				if (!(errno==EAGAIN || errno==EWOULDBLOCK)){
-#ifdef USE_DST_BLACKLIST
-					if (cfg_get(core, core_cfg, use_dst_blacklist))
+					if (unlikely(c->state==S_CONN_CONNECT)){
 						switch(errno){
 							case ENETUNREACH:
+							case EHOSTUNREACH: /* not posix for send() */
+#ifdef USE_DST_BLACKLIST
+								if (cfg_get(core, core_cfg, use_dst_blacklist))
+									dst_blacklist_su(BLST_ERR_CONNECT,
+															c->rcv.proto,
+															&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+								TCP_EV_CONNECT_UNREACHABLE(errno, TCP_LADDR(c),
+													TCP_LPORT(c), TCP_PSU(c),
+													TCP_PROTO(c));
+								break;
+							case ECONNREFUSED:
 							case ECONNRESET:
-							/*case EHOSTUNREACH: -- not posix */
-								dst_blacklist_su((c->state==S_CONN_CONNECT)?
-														BLST_ERR_CONNECT:
-														BLST_ERR_SEND,
+#ifdef USE_DST_BLACKLIST
+								if (cfg_get(core, core_cfg, use_dst_blacklist))
+									dst_blacklist_su(BLST_ERR_CONNECT,
+															c->rcv.proto,
+															&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+								TCP_EV_CONNECT_RST(0, TCP_LADDR(c),
+													TCP_LPORT(c), TCP_PSU(c),
+													TCP_PROTO(c));
+								break;
+							default:
+								TCP_EV_CONNECT_ERR(errno, TCP_LADDR(c),
+													TCP_LPORT(c), TCP_PSU(c),
+													TCP_PROTO(c));
+						}
+						TCP_STATS_CONNECT_FAILED();
+					}else{
+						switch(errno){
+							case ECONNREFUSED:
+							case ECONNRESET:
+								TCP_STATS_CON_RESET();
+								/* no break */
+							case ENETUNREACH:
+							case EHOSTUNREACH: /* not posix for send() */
+#ifdef USE_DST_BLACKLIST
+								if (cfg_get(core, core_cfg, use_dst_blacklist))
+									dst_blacklist_su(BLST_ERR_SEND,
 														c->rcv.proto,
 														&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
 								break;
 						}
-#endif /* USE_DST_BLACKLIST */
+					}
 					ret=-1;
 					LOG(L_ERR, "ERROR: wbuf_runq: %s [%d]\n",
 						strerror(errno), errno);
@@ -839,8 +924,10 @@ inline static int wbufq_run(int fd, struct tcp_connection* c, int* empty)
 	lock_release(&c->write_lock);
 	if (likely(ret>0)){
 		q->wr_timeout=get_ticks_raw()+cfg_get(tcp, tcp_cfg, send_timeout);
-		if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT))
+		if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT)){
+			TCP_STATS_ESTABLISHED(c->state);
 			c->state=S_CONN_OK;
+		}
 	}
 	return ret;
 }
@@ -1032,19 +1119,37 @@ again:
 				*state=S_CONN_CONNECT;
 			else if (errno==EINTR) goto again;
 			else if (errno!=EALREADY){
+				switch(errno){
+					case ENETUNREACH:
+					case EHOSTUNREACH:
 #ifdef USE_DST_BLACKLIST
-				if (cfg_get(core, core_cfg, use_dst_blacklist))
-					switch(errno){
-						case ECONNREFUSED:
-						case ENETUNREACH:
-						case ETIMEDOUT:
-						case ECONNRESET:
-						case EHOSTUNREACH:
-							dst_blacklist_su(BLST_ERR_CONNECT, type, server,
-												0);
-							break;
-				}
+						if (cfg_get(core, core_cfg, use_dst_blacklist))
+							dst_blacklist_su(BLST_ERR_CONNECT, type, server,0);
+#endif /* USE_DST_BLACKLIST */
+						TCP_EV_CONNECT_UNREACHABLE(errno, 0, 0, server, type);
+						break;
+					case ETIMEDOUT:
+#ifdef USE_DST_BLACKLIST
+						if (cfg_get(core, core_cfg, use_dst_blacklist))
+							dst_blacklist_su(BLST_ERR_CONNECT, type, server,0);
 #endif /* USE_DST_BLACKLIST */
+						TCP_EV_CONNECT_TIMEOUT(errno, 0, 0, server, type);
+						break;
+					case ECONNREFUSED:
+					case ECONNRESET:
+#ifdef USE_DST_BLACKLIST
+						if (cfg_get(core, core_cfg, use_dst_blacklist))
+							dst_blacklist_su(BLST_ERR_CONNECT, type, server,0);
+#endif /* USE_DST_BLACKLIST */
+						TCP_EV_CONNECT_RST(errno, 0, 0, server, type);
+						break;
+					case EAGAIN:/* not posix, but supported on linux and bsd */
+						TCP_EV_CONNECT_NO_MORE_PORTS(errno, 0, 0, server,type);
+						break;
+					default:
+						TCP_EV_CONNECT_ERR(errno, 0, 0, server, type);
+				}
+				TCP_STATS_CONNECT_FAILED();
 				LOG(L_ERR, "ERROR: tcp_do_connect: connect %s: (%d) %s\n",
 							su2a(server, sizeof(*server)),
 							errno, strerror(errno));
@@ -1744,9 +1849,11 @@ no_id:
 						DBG("tcp_send: pending write on new connection %p "
 								" (%d/%d bytes written)\n", c, n, len);
 						if (n<0) n=0;
-						else 
+						else{
+							TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
 							c->state=S_CONN_OK; /* partial write => connect()
 													ended */
+						}
 						/* add to the write queue */
 						lock_get(&c->write_lock);
 							if (unlikely(_wbufq_insert(c, buf+n, len-n)<0)){
@@ -1774,19 +1881,33 @@ no_id:
 						n=len;
 						goto end;
 					}
+					/* if first write failed it's most likely a
+					   connect error */
+					switch(errno){
+						case ENETUNREACH:
+						case EHOSTUNREACH:  /* not posix for send() */
 #ifdef USE_DST_BLACKLIST
-					if (cfg_get(core, core_cfg, use_dst_blacklist))
-						switch(errno){
-							case ENETUNREACH:
-							case ECONNRESET:
-							/*case EHOSTUNREACH: -- not posix */
-								/* if first write failed it's most likely a
-								   connect error */
+							if (cfg_get(core, core_cfg, use_dst_blacklist))
 								dst_blacklist_add( BLST_ERR_CONNECT, dst, 0);
-								break;
-						}
 #endif /* USE_DST_BLACKLIST */
+							TCP_EV_CONNECT_UNREACHABLE(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
+							break;
+						case ECONNREFUSED:
+						case ECONNRESET:
+#ifdef USE_DST_BLACKLIST
+							if (cfg_get(core, core_cfg, use_dst_blacklist))
+								dst_blacklist_add( BLST_ERR_CONNECT, dst, 0);
+#endif /* USE_DST_BLACKLIST */
+							TCP_EV_CONNECT_RST(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
+							break;
+						default:
+							TCP_EV_CONNECT_ERR(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
+					}
 					/* error: destroy it directly */
+					TCP_STATS_CONNECT_FAILED();
 					LOG(L_ERR, "ERROR: tcp_send %s: connect & send "
 										" for %p failed:" " %s (%d)\n",
 										su2a(&dst->to, sizeof(dst->to)),
@@ -1794,6 +1915,7 @@ no_id:
 					goto conn_wait_error;
 				}
 				LOG(L_INFO, "tcp_send: quick connect for %p\n", c);
+				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
 				c->state=S_CONN_OK;
 				/* send to tcp_main */
 				response[0]=(long)c;
@@ -1814,6 +1936,8 @@ no_id:
 								su2a(&dst->to, sizeof(dst->to)));
 				return -1;
 			}
+			if (likely(c->state==S_CONN_OK))
+				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
 			atomic_set(&c->refcnt, 2); /* ref. from here and it will also
 			                              be added in the tcp_main hash */
 			fd=c->s;
@@ -1965,8 +2089,10 @@ send_it:
 			enable_write_watch=_wbufq_empty(c);
 			if (n<0) n=0;
 			else if (unlikely(c->state==S_CONN_CONNECT ||
-						c->state==S_CONN_ACCEPT))
+						c->state==S_CONN_ACCEPT)){
+				TCP_STATS_ESTABLISHED(c->state);
 				c->state=S_CONN_OK; /* something was written */
+			}
 			if (unlikely(_wbufq_add(c, buf+n, len-n)<0)){
 				lock_release(&c->write_lock);
 				n=-1;
@@ -1989,20 +2115,49 @@ send_it:
 			lock_release(&c->write_lock);
 		}
 #endif /* TCP_ASYNC */
-#ifdef USE_DST_BLACKLIST
-		if (cfg_get(core, core_cfg, use_dst_blacklist))
+		if (unlikely(c->state==S_CONN_CONNECT)){
 			switch(errno){
 				case ENETUNREACH:
+				case EHOSTUNREACH: /* not posix for send() */
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su(BLST_ERR_CONNECT, c->rcv.proto,
+											&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+					TCP_EV_CONNECT_UNREACHABLE(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
+					break;
+				case ECONNREFUSED:
+				case ECONNRESET:
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su(BLST_ERR_CONNECT, c->rcv.proto,
+											&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+					TCP_EV_CONNECT_RST(errno, TCP_LADDR(c), TCP_LPORT(c),
+										TCP_PSU(c), TCP_PROTO(c));
+					break;
+				default:
+					TCP_EV_CONNECT_ERR(errno, TCP_LADDR(c), TCP_LPORT(c),
+										TCP_PSU(c), TCP_PROTO(c));
+				}
+			TCP_STATS_CONNECT_FAILED();
+		}else{
+			switch(errno){
+				case ECONNREFUSED:
 				case ECONNRESET:
+					TCP_STATS_CON_RESET();
+					/* no break */
+				case ENETUNREACH:
 				/*case EHOSTUNREACH: -- not posix */
-					dst_blacklist_su((c->state==S_CONN_CONNECT)?
-											BLST_ERR_CONNECT:
-											BLST_ERR_SEND,
-										c->rcv.proto,
-										&c->rcv.src_su, 0);
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su(BLST_ERR_SEND, c->rcv.proto,
+												&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
 					break;
 			}
-#endif /* USE_DST_BLACKLIST */
+		}
 		LOG(L_ERR, "ERROR: tcp_send: failed to send on %p (%s:%d->%s): %s (%d)"
 					"\n", c, ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port,
 					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
@@ -2042,8 +2197,10 @@ error:
 	lock_release(&c->write_lock);
 #endif /* TCP_ASYNC */
 	/* in non-async mode here we're either in S_CONN_OK or S_CONN_ACCEPT*/
-	if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT))
+	if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT)){
+			TCP_STATS_ESTABLISHED(c->state);
 			c->state=S_CONN_OK;
+	}
 end:
 #ifdef TCP_FD_CACHE
 	if (unlikely((fd_cache_e==0) && use_fd_cache)){
@@ -2682,14 +2839,27 @@ inline static int handle_tcp_child(struct tcp_child* tcp_c, int fd_i)
 							"refcnt= %d\n", tcpconn,
 							atomic_get(&tcpconn->refcnt));
 					/* timeout */
+					if (unlikely(tcpconn->state==S_CONN_CONNECT)){
 #ifdef USE_DST_BLACKLIST
-					if (cfg_get(core, core_cfg, use_dst_blacklist))
-						dst_blacklist_su((tcpconn->state==S_CONN_CONNECT)?
-													BLST_ERR_CONNECT:
-													BLST_ERR_SEND,
+						if (cfg_get(core, core_cfg, use_dst_blacklist))
+							dst_blacklist_su( BLST_ERR_CONNECT,
+													tcpconn->rcv.proto,
+													&tcpconn->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+						TCP_EV_CONNECT_TIMEOUT(0, TCP_LADDR(tcpconn),
+										TCP_LPORT(tcpconn), TCP_PSU(tcpconn),
+										TCP_PROTO(tcpconn));
+						TCP_STATS_CONNECT_FAILED();
+					}else{
+#ifdef USE_DST_BLACKLIST
+						if (cfg_get(core, core_cfg, use_dst_blacklist))
+							dst_blacklist_su( BLST_ERR_SEND,
 													tcpconn->rcv.proto,
 													&tcpconn->rcv.src_su, 0);
 #endif /* USE_DST_BLACKLIST */
+						TCP_EV_SEND_TIMEOUT(0, &tcpconn->rcv);
+						TCP_STATS_SEND_TIMEOUT();
+					}
 					if (unlikely(tcpconn->flags & F_CONN_WRITE_W)){
 						io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);
 						tcpconn->flags&=~F_CONN_WRITE_W;
@@ -3182,6 +3352,7 @@ static inline int handle_new_connect(struct socket_info* si)
 					*tcp_connections_no,
 					cfg_get(tcp, tcp_cfg, max_connections));
 		close(new_sock);
+		TCP_STATS_LOCAL_REJECT();
 		return 1; /* success, because the accept was succesfull */
 	}
 	if (unlikely(init_sock_opt_accept(new_sock)<0)){
@@ -3190,6 +3361,7 @@ static inline int handle_new_connect(struct socket_info* si)
 		return 1; /* success, because the accept was succesfull */
 	}
 	(*tcp_connections_no)++;
+	TCP_STATS_ESTABLISHED(S_CONN_ACCEPT);
 	
 	dst_su=&si->su;
 	if (unlikely(si->flags & SI_IS_ANY)){
@@ -3323,6 +3495,16 @@ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, short ev,
 				LOG(L_CRIT, "BUG: tcpconn_ev: unhashed connection %p\n",
 							tcpconn);
 			}
+			if (unlikely(ev & POLLERR)){
+				if (unlikely(tcpconn->state=S_CONN_CONNECT)){
+					TCP_EV_CONNECT_ERR(0, TCP_LADDR(tcpconn),
+										TCP_LPORT(tcpconn), TCP_PSU(tcpconn),
+										TCP_PROTO(tcpconn));
+					TCP_STATS_CONNECT_FAILED();
+				}else{
+					TCP_STATS_CON_RESET(); /* FIXME: it could != RST */
+				}
+			}
 			tcpconn_put_destroy(tcpconn);
 			goto error;
 		}
@@ -3485,15 +3667,27 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln* tl, void* data)
 		else
 			return (ticks_t)(c->timeout - t);
 	}
-#ifdef USE_DST_BLACKLIST
 	/* if time out due to write, add it to the blacklist */
-	if (tcp_async && _wbufq_non_empty(c) &&
-			TICKS_GE(t, c->wbuf_q.wr_timeout) &&
-			cfg_get(core, core_cfg, use_dst_blacklist))
-		dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
-										BLST_ERR_SEND,
-								c->rcv.proto, &c->rcv.src_su, 0);
+	if (tcp_async && _wbufq_non_empty(c) && TICKS_GE(t, c->wbuf_q.wr_timeout)){
+		if (unlikely(c->state==S_CONN_CONNECT)){
+#ifdef USE_DST_BLACKLIST
+			if (cfg_get(core, core_cfg, use_dst_blacklist))
+				dst_blacklist_su(BLST_ERR_CONNECT, c->rcv.proto,
+									&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+			TCP_EV_CONNECT_TIMEOUT(0, TCP_LADDR(c), TCP_LPORT(c), TCP_PSU(c),
+									TCP_PROTO(c));
+			TCP_STATS_CONNECT_FAILED();
+		}else{
+#ifdef USE_DST_BLACKLIST
+			if (cfg_get(core, core_cfg, use_dst_blacklist))
+				dst_blacklist_su(BLST_ERR_SEND, c->rcv.proto,
+									&c->rcv.src_su, 0);
 #endif /* USE_DST_BLACKLIST */
+			TCP_EV_SEND_TIMEOUT(0, &c->rcv);
+			TCP_STATS_SEND_TIMEOUT();
+		}
+	}
 #else /* ! TCP_ASYNC */
 	if (TICKS_LT(t, c->timeout)){
 		/* timeout extended, exit */
@@ -3520,6 +3714,8 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln* tl, void* data)
 			c->flags&=~(F_CONN_READ_W|F_CONN_WRITE_W);
 		}
 	}
+	TCP_EV_IDLE_CONN_CLOSED(0, &c->rcv);
+	TCP_STATS_CON_TIMEOUT();
 	tcpconn_put_destroy(c);
 	return 0;
 }

+ 44 - 9
tcp_read.c

@@ -41,6 +41,7 @@
  * 2008-02-04  optimizations: handle POLLRDHUP (if supported), detect short
  *              reads (sock. buffer empty) (andrei)
  * 2009-02-26  direct blacklist support (andrei)
+ * 2009-04-09  tcp ev and tcp stats macros added (andrei)
  */
 
 #ifdef USE_TCP
@@ -61,6 +62,8 @@
 
 #include "dprint.h"
 #include "tcp_conn.h"
+#include "tcp_stats.h"
+#include "tcp_ev.h"
 #include "pass_fd.h"
 #include "globals.h"
 #include "receive.h"
@@ -146,19 +149,47 @@ again:
 				bytes_read=0; /* nothing has been read */
 			}else if (errno == EINTR) goto again;
 			else{
-#ifdef USE_DST_BLACKLIST
-				if (cfg_get(core, core_cfg, use_dst_blacklist))
+				if (unlikely(c->state=S_CONN_CONNECT)){
 					switch(errno){
 						case ECONNRESET:
+#ifdef USE_DST_BLACKLIST
+							if (cfg_get(core, core_cfg, use_dst_blacklist))
+								dst_blacklist_su(BLST_ERR_CONNECT,
+														c->rcv.proto,
+														&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+							TCP_EV_CONNECT_RST(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
+							break;
 						case ETIMEDOUT:
-							dst_blacklist_su((c->state==S_CONN_CONNECT)?
-													BLST_ERR_CONNECT:
-													BLST_ERR_SEND,
-													c->rcv.proto,
-													&c->rcv.src_su, 0);
+#ifdef USE_DST_BLACKLIST
+							if (cfg_get(core, core_cfg, use_dst_blacklist))
+								dst_blacklist_su(BLST_ERR_CONNECT,
+														c->rcv.proto,
+														&c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
+							TCP_EV_CONNECT_TIMEOUT(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
 							break;
+						default:
+							TCP_EV_CONNECT_ERR(errno, TCP_LADDR(c),
+									TCP_LPORT(c), TCP_PSU(c), TCP_PROTO(c));
 					}
+					TCP_STATS_CONNECT_FAILED();
+				}else{
+						switch(errno){
+							case ECONNRESET:
+								TCP_STATS_CON_RESET();
+							case ETIMEDOUT:
+#ifdef USE_DST_BLACKLIST
+								if (cfg_get(core, core_cfg, use_dst_blacklist))
+									dst_blacklist_su(BLST_ERR_SEND,
+														c->rcv.proto,
+														&c->rcv.src_su, 0);
 #endif /* USE_DST_BLACKLIST */
+								break;
+						}
+				}
 				LOG(L_ERR, "ERROR: tcp_read: error reading: %s (%d)\n",
 							strerror(errno), errno);
 				r->error=TCP_READ_ERROR;
@@ -170,14 +201,18 @@ again:
 			*flags|=RD_CONN_EOF;
 			DBG("tcp_read: EOF on %p, FD %d\n", c, fd);
 		}else{
-			if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT))
+			if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT)){
+				TCP_STATS_ESTABLISHED(c->state);
 				c->state=S_CONN_OK;
+			}
 		}
 		/* short read */
 		*flags|=RD_CONN_SHORT_READ;
 	}else{ /* else normal full read */
-		if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT))
+		if (unlikely(c->state==S_CONN_CONNECT || c->state==S_CONN_ACCEPT)){
+			TCP_STATS_ESTABLISHED(c->state);
 			c->state=S_CONN_OK;
+		}
 	}
 #ifdef EXTRA_DEBUG
 	DBG("tcp_read: read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos);

+ 72 - 0
tcp_stats.h

@@ -0,0 +1,72 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2009 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * tcp_stats.h - tcp statistics macros
+ */
+/*
+ * History:
+ * --------
+ *  2009-04-08  initial version (andrei)
+*/
+
+#ifndef __tcp_stats_h
+#define __tcp_stats_h
+
+/** called each time a new tcp connection is established.
+ *  @param state - S_CONN_ACCEPT if it was the result of an accept()
+ *               - S_CONN_CONNECT if it was the result of a connect()
+ * Note: in general it will be called when the first packet was received or
+ *   sent on the new connection and not immediately after accept() or 
+ *   connect()
+ */
+#define TCP_STATS_ESTABLISHED(state)
+
+/** called each time a new outgoing connection fails.  */
+#define TCP_STATS_CONNECT_FAILED()
+
+/** called each time a new incoming connection is rejected.
+ * (accept() denied due to maximum number of TCP connections being exceeded)
+ */
+#define TCP_STATS_LOCAL_REJECT()
+
+
+/** called each time a connection lifetime expires.
+  * (the connection is closed for being idle for too long)
+  */
+#define TCP_STATS_CON_TIMEOUT()
+
+
+/** called each time a TCP RST is received on an established connection.  */
+#define TCP_STATS_CON_RESET()
+
+/** called each time a send operation fails due to a timeout.
+  * FIXME: it works only in async mode (in sync. mode a send might timeout
+  *  but the stats won't be increased).
+  */
+#define TCP_STATS_SEND_TIMEOUT()
+
+/** called each time a send fails due to the buffering capacity being exceeded.
+  * (used only in tcp async mode)
+  */
+#define TCP_STATS_SENDQ_FULL()
+
+
+
+#endif /*__tcp_stats_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */