Index: /branches/rel_apv_10_7/usr/src/sys/click/netinet/click_ftp.c
===================================================================
--- /branches/rel_apv_10_7/usr/src/sys/click/netinet/click_ftp.c	(revision 39136)
+++ /branches/rel_apv_10_7/usr/src/sys/click/netinet/click_ftp.c	(working copy)
@@ -19,6 +19,8 @@
 #include <sys/types.h>
 #include <sys/proc.h>
 #include <sys/pcpu.h>
+#include <sys/queue.h>
+#include <sys/time.h>
 
 #include <net/if.h>
 #include <net/route.h>
@@ -96,6 +98,37 @@
 uint16_t ftp_portrange_start;
 uint16_t ftp_portrange_end;
 
+/*
+ * TWSD-621
+ * The management of firewall rules for the FTPS data channel(encrypted mode).
+ * 1. Connect:
+ *    Add the new "bound_pcb" to the "recod_bound_pcb_tailhead" list.
+ * 2. Disconnect:
+ *    Add the "bound_pcb" with a timestamp to the "next_del_bound_pcb_tailhead" list
+ *    from the "recod_bound_pcb_tailhead" list.
+ * 3. Other event:
+ *    Check each "bound_pcb" in "next_del_bound_pcb_tailhead"
+ *    and delete those that have exceeded 0.05 seconds.
+ */
+struct ftps_bound_pcb_node {
+    struct timeval mark_time;
+    uint32_t server_addr;
+    uint16_t server_port;
+    clickpcb_t *bound_pcb;
+    TAILQ_ENTRY(ftps_bound_pcb_node) entries;
+};
+TAILQ_HEAD(ftps_bound_pcb_tailhead, ftps_bound_pcb_node);
+
+#define  DELETE_BOUND_PCB_THRESHOLD 50000  // 50ms
+__thread int tailhead_initialized = 0;
+__thread struct ftps_bound_pcb_tailhead recod_bound_pcb_tailhead;
+__thread struct ftps_bound_pcb_tailhead next_del_bound_pcb_tailhead;
+void init_ftps_bound_pcb_tailhead();
+void delete_ftps_bound_pcb();
+void delete_ftps_bound_pcb_by_ptr(clickpcb_t *bound_pcb);
+void mark_ftps_bound_pcb(clickpcb_t *pcb);
+void recod_ftps_bound_pcb(clickpcb_t *server_pcb, clickpcb_t *bound_pcb);
+
 static int  ftp_new(clickpcb_t *pcb, int app_t);
 static void ftp_predelete(clickpcb_t *pcb);
 static void ftp_freeboundpcb(clickpcb_t *pcb);
@@ -186,9 +219,12 @@
 			 ((x) < '0' || (x) > '9'))
 
 static __inline void 
-ftps_delete_listen_pcb(clickpcb_t *pcb)
+ftps_delete_listen_pcb(clickpcb_t *pcb, int check_bound_pcb_list)
 {
 	if (pcb != NULL) {
+		if (check_bound_pcb_list) {
+			delete_ftps_bound_pcb_by_ptr(pcb);
+		}
 		if(CLICKPCB_IS_IPV6(pcb)) {
 			l7popen_cleanup6(&pcb->cp_remoteip6,
 				&pcb->cp_localip6,
@@ -216,7 +252,7 @@
            clickpcb_t *active_open_pcb, click_ftpdata_t *ftpdata)
 {
 	clickpcb_t *ippcb;
-	ftps_delete_listen_pcb(ftpdata->bound_data_pcb);
+	ftps_delete_listen_pcb(ftpdata->bound_data_pcb, 1);
 	listen_pcb->cp_app_head = ((clickpcb_pipe_t *)active_open_pcb)->target->ippcb.cp_app_head;
 	listen_pcb->cp_udata = passive_open_pcb;
 	
@@ -618,7 +654,7 @@
 		if (pcb->cp_udata != NULL) {
 			ftpdata = (click_ftpdata_t *)pcb->cp_udata;
 			if(ftpdata != NULL) {
-				ftps_delete_listen_pcb(ftpdata->bound_data_pcb);
+				ftps_delete_listen_pcb(ftpdata->bound_data_pcb, 1);
 				if (ftpdata->data_pcb != NULL) {
 					if (ftpdata->data_pcb->cp_app != ftps_datachannel_plaintext_delete) {
 						/*ftps ciphertext*/
@@ -649,6 +685,7 @@
 		} else {
 			SPLICE_TARGET_PCB(pcb) = NULL;
 		}
+		delete_ftps_bound_pcb();
 		return 0;
 	}
            	 
@@ -698,7 +735,7 @@
 		if (!FTP_IS_CLIENT(pcb)) {
 			ftpdata = (click_ftpdata_t *)pcb->cp_udata;
 			if(ftpdata != NULL) {
-				ftps_delete_listen_pcb(ftpdata->bound_data_pcb);
+				ftps_delete_listen_pcb(ftpdata->bound_data_pcb, 1);
 				if(ftpdata->data_pcb != NULL) {
 					if (ftpdata->data_pcb->cp_app != ftps_datachannel_plaintext_delete) {
 						/*
@@ -2104,6 +2141,8 @@
 		}
 	}
 
+	delete_ftps_bound_pcb();
+
 	if((vs&&vs->ftp_portrange_start != 0) || ftp_portrange_start != 0) {
 		listen_pcb = clicktcp_bind_ftp(vs, ippcb->cp_localip.s_addr,
 			ippcb->cp_remoteip.s_addr, bind_port, IPPROTO_TCP, 1,
@@ -2147,7 +2186,17 @@
 		&& (!(((click_ftpdata_t *)clnt_pcb->cp_udata)->flags & FTPS_DATA_PLAINTEXT_FLAG))) {
 		listen_pcb->cp_extflags |= CLICKPCB_FTPS_DATA;
 	}
-	
+
+	/* bug 17757, qiuzj, 20071023 */
+	/* give listen_pcb the VS infomation from clnt_pcb */
+	ftp_data_pcb_set_slb_info(listen_pcb, clnt_pcb);
+
+	if (clnt_pcb->cp_type == PCB_PIPE) {/* FTPS */
+		ftps_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
+	} else {
+		ftp_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
+	}
+
 	if(CLICKPCB_IS_IPV6(listen_pcb)) {
 		l7popen_insearch6(&listen_pcb->cp_remoteip6,
 		    &listen_pcb->cp_localip6,
@@ -2161,16 +2210,6 @@
 		    listen_pcb->cp_localport,
 		    listen_pcb->cp_ip_p);
 	}
-	
-	/* bug 17757, qiuzj, 20071023 */
-	/* give listen_pcb the VS infomation from clnt_pcb */
-	ftp_data_pcb_set_slb_info(listen_pcb, clnt_pcb);
-	
-	if (clnt_pcb->cp_type == PCB_PIPE) {/* FTPS */
-		ftps_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
-	} else {
-		ftp_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
-	}
 
 	if ((pcb->cp_flags & CLICKPCB_SLB_CONN) && (!SLB_IS_VS_IPV6(vs) && (vs->ftp_extip != 0))) {
 		myip = ntohl(vs->ftp_extip); /*configured external data connection ip */
@@ -2650,7 +2689,9 @@
 			return;
 		}
 	}
-	
+
+	delete_ftps_bound_pcb();
+
 	if((vs&&vs->ftp_portrange_start != 0) || ftp_portrange_start != 0) {
 		if (CLICKPCB_IS_IPV6 (ippcb)) {
 			listen_pcb = clicktcp6_bind_ftp(vs, &ippcb->cp_localip6,
@@ -2690,7 +2731,17 @@
     
 	return;
     }
-	
+
+	/* bug 17757, qiuzj, 20071023 */
+	/* give listen_pcb the VS infomation from clnt_pcb */
+	ftp_data_pcb_set_slb_info(listen_pcb, clnt_pcb);
+
+	if(clnt_pcb->cp_type == PCB_PIPE) {/* FTPS */
+		ftps_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
+	} else {
+		ftp_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
+	}
+
 	if(CLICKPCB_IS_IPV6(listen_pcb)) {
 		l7popen_insearch6(&listen_pcb->cp_remoteip6,
 		    &listen_pcb->cp_localip6,
@@ -2704,16 +2755,7 @@
 		    listen_pcb->cp_localport,
 		    listen_pcb->cp_ip_p);
 	}
-    /* bug 17757, qiuzj, 20071023 */
-    /* give listen_pcb the VS infomation from clnt_pcb */
-    ftp_data_pcb_set_slb_info(listen_pcb, clnt_pcb);
 
-	if(clnt_pcb->cp_type == PCB_PIPE) {/* FTPS */
-		ftps_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
-	} else {
-		ftp_setupdataconn(listen_pcb, pcb, clnt_pcb, ftpdata);
-	}
-	
 	if ((pcb->cp_flags & CLICKPCB_SLB_CONN) && (!SLB_IS_VS_IPV6(vs) && (vs->ftp_extip != 0))) {
 		myip = ntohl(vs->ftp_extip); /*configured external data connection ip */
 	} else {
@@ -3044,10 +3086,13 @@
 		}
 		pcb->cp_app = NULL;
 		pcb->cp_flags &= ~CLICKPCB_PRE_DELETE;
+		delete_ftps_bound_pcb();
+		mark_ftps_bound_pcb(pcb);
 		return 0;
 	}
 
-	if (pcb_is_new(pcb)) {		
+	if (pcb_is_new(pcb)) {
+		delete_ftps_bound_pcb();
 		sslp = (struct ssl_data *)(pcb->cp_udata);	
 		if (sslp != NULL && sslp->proxy_data.pcb != NULL) {
 			bound_pcb = sslp->proxy_data.pcb;
@@ -3090,7 +3135,7 @@
 		if (pcb->cp_type == PCB_PIPE && 
 		    ((sslp->session_cache && sslp->session_cache->proxy_data.error_code != 0) ||
 		    (!sslp->session_cache && sslp->proxy_data.error_code != 0))) {
-			ftps_delete_listen_pcb(bound_pcb);
+			ftps_delete_listen_pcb(bound_pcb, 1);
 			if(control_data != NULL) {
 				control_data->bound_data_pcb = NULL;
 			}
@@ -3159,7 +3204,7 @@
 			control_data->bound_data_pcb = NULL;
 			pcb->cp_app = NULL;
 			pcb_terminate(pcb, RST_ID_FTPS_12);
-			ftps_delete_listen_pcb(bound_pcb);
+			ftps_delete_listen_pcb(bound_pcb, 1);
 			return 1;
 		}
 		
@@ -3186,7 +3231,7 @@
 		}
 
 		control_data->bound_data_pcb = NULL;
-		ftps_delete_listen_pcb(bound_pcb);
+		recod_ftps_bound_pcb(server_pcb, bound_pcb);
 #if 0
 		if(!CLICKPCB_IS_IPV6(ippcb)) {
 			eroute_rule = find_eroute_without_mbuf(ippcb->cp_remoteip.s_addr, ippcb->cp_localip.s_addr,
@@ -4126,3 +4171,89 @@
 	}
 	return 0;
 }
+
+/*
+ * Initialize the management list for FTPS bound_pcb
+ */
+void init_ftps_bound_pcb_tailhead() {
+	if (tailhead_initialized == 0) {
+		TAILQ_INIT(&recod_bound_pcb_tailhead);
+		TAILQ_INIT(&next_del_bound_pcb_tailhead);
+		tailhead_initialized = 1;
+	}
+}
+
+/*
+ * Delete bound_pcb that have been disconnected for more than X seconds.
+ */
+void delete_ftps_bound_pcb() {
+	clickpcb_t *bound_pcb = NULL;
+	struct timeval now;
+	struct ftps_bound_pcb_node *node, *tmp_node;
+
+	init_ftps_bound_pcb_tailhead();
+	microuptime(&now);
+	TAILQ_FOREACH_SAFE(node, &next_del_bound_pcb_tailhead, entries, tmp_node) {
+		long long elapsed_usec = (now.tv_sec - node->mark_time.tv_sec) * 1000000LL +
+									(now.tv_usec - node->mark_time.tv_usec);
+		if (elapsed_usec >= DELETE_BOUND_PCB_THRESHOLD) {
+			bound_pcb = node->bound_pcb;
+			ftps_delete_listen_pcb(bound_pcb, 0);
+			TAILQ_REMOVE(&next_del_bound_pcb_tailhead, node, entries);
+			FREE(node, M_TEMP);
+		} else {
+			break;
+		}
+	}
+}
+
+/*
+ * Delete a specific bound_pcb from the FTPS management list.
+ */
+void delete_ftps_bound_pcb_by_ptr(clickpcb_t *bound_pcb) {
+	struct ftps_bound_pcb_node *node, *tmp_node;
+
+	init_ftps_bound_pcb_tailhead();
+	TAILQ_FOREACH_SAFE(node, &recod_bound_pcb_tailhead, entries, tmp_node) {
+		if (node->bound_pcb == bound_pcb) {
+			TAILQ_REMOVE(&recod_bound_pcb_tailhead, node, entries);
+			FREE(node, M_TEMP);
+		}
+	}
+	TAILQ_FOREACH_SAFE(node, &next_del_bound_pcb_tailhead, entries, tmp_node) {
+		if (node->bound_pcb == bound_pcb) {
+			TAILQ_REMOVE(&next_del_bound_pcb_tailhead, node, entries);
+			FREE(node, M_TEMP);
+		}
+	}
+}
+
+/*
+ * Add a timestamp to bound_pcb when the connection is closed.
+ */
+void mark_ftps_bound_pcb(clickpcb_t *pcb) {
+	struct ftps_bound_pcb_node *node, *tmp_node;
+
+	init_ftps_bound_pcb_tailhead();
+	TAILQ_FOREACH_SAFE(node, &recod_bound_pcb_tailhead, entries, tmp_node) {
+		if (node->server_addr == pcb->cp_remoteip.s_addr && node->server_port == pcb->cp_remoteport) {
+			TAILQ_REMOVE(&recod_bound_pcb_tailhead, node, entries);
+			TAILQ_INSERT_TAIL(&next_del_bound_pcb_tailhead, node, entries);
+			microuptime(&node->mark_time);
+		}
+	}
+}
+
+/*
+ * Record a new bound_pcb.
+ */
+void recod_ftps_bound_pcb(clickpcb_t *server_pcb, clickpcb_t *bound_pcb) {
+	struct ftps_bound_pcb_node *node;
+
+	init_ftps_bound_pcb_tailhead();
+	MALLOC(node, struct ftps_bound_pcb_node *, sizeof(struct ftps_bound_pcb_node), M_TEMP, M_NOWAIT);
+	node->server_addr = server_pcb->cp_remoteip.s_addr;
+	node->server_port = server_pcb->cp_remoteport;
+	node->bound_pcb = bound_pcb;
+	TAILQ_INSERT_TAIL(&recod_bound_pcb_tailhead, node, entries);
+}
\ No newline at end of file
