ip_vs_conn.c

来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 1,570 行 · 第 1/3 页

C
1,570
字号
 */static inline voidip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest){	/* if dest is NULL, then return directly */	if (!dest)		return;	/* Increase the refcnt counter of the dest */	atomic_inc(&dest->refcnt);	/* Bind with the destination and its corresponding transmitter */	cp->flags |= atomic_read(&dest->conn_flags);	cp->dest = dest;	IP_VS_DBG(9, "Bind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "		  "d:%u.%u.%u.%u:%d fwd:%c s:%s flg:%X cnt:%d destcnt:%d\n",		  ip_vs_proto_name(cp->protocol),		  NIPQUAD(cp->caddr), ntohs(cp->cport),		  NIPQUAD(cp->vaddr), ntohs(cp->vport),		  NIPQUAD(cp->daddr), ntohs(cp->dport),		  ip_vs_fwd_tag(cp), ip_vs_state_name(cp->state),		  cp->flags, atomic_read(&cp->refcnt),		  atomic_read(&dest->refcnt));}/* *  Unbind a connection entry with its VS destination *  Called by the ip_vs_conn_expire function. */static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp){	struct ip_vs_dest *dest = cp->dest;	/* if dest is NULL, then return directly */	if (!dest)		return;	IP_VS_DBG(9, "Unbind-dest %s c:%u.%u.%u.%u:%d "		  "v:%u.%u.%u.%u:%d d:%u.%u.%u.%u:%d fwd:%c "		  "s:%s flg:%X cnt:%d destcnt:%d",		  ip_vs_proto_name(cp->protocol),		  NIPQUAD(cp->caddr), ntohs(cp->cport),		  NIPQUAD(cp->vaddr), ntohs(cp->vport),		  NIPQUAD(cp->daddr), ntohs(cp->dport),		  ip_vs_fwd_tag(cp), ip_vs_state_name(cp->state),		  cp->flags, atomic_read(&cp->refcnt),		  atomic_read(&dest->refcnt));	/*	 * Decrease the inactconns or activeconns counter	 * if it is not a connection template ((cp->cport!=0)	 *   || (cp->flags & IP_VS_CONN_F_NO_CPORT)).	 */	if (cp->cport || (cp->flags & IP_VS_CONN_F_NO_CPORT)) {		if (cp->flags & IP_VS_CONN_F_INACTIVE) {			atomic_dec(&dest->inactconns);		} else {			atomic_dec(&dest->activeconns);		}	}	/*	 * Simply decrease the refcnt of the dest, because the	 * dest will be either in service's destination list	 * or in the trash.	 */	atomic_dec(&dest->refcnt);}/* *  Checking if the destination of a connection template is available. *  If available, return 1, otherwise invalidate this connection *  template and return 0. */int ip_vs_check_template(struct ip_vs_conn *ct){	struct ip_vs_dest *dest = ct->dest;	/*	 * Checking the dest server status.	 */	if ((dest == NULL) ||	    !(dest->flags & IP_VS_DEST_F_AVAILABLE) || 	    (sysctl_ip_vs_expire_quiescent_template && 	     (atomic_read(&dest->weight) == 0))) {		IP_VS_DBG(9, "check_template: dest not available for "			  "protocol %s s:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "			  "-> d:%u.%u.%u.%u:%d\n",			  ip_vs_proto_name(ct->protocol),			  NIPQUAD(ct->caddr), ntohs(ct->cport),			  NIPQUAD(ct->vaddr), ntohs(ct->vport),			  NIPQUAD(ct->daddr), ntohs(ct->dport));		/*		 * Invalidate the connection template		 */		if (ct->cport) {			if (ip_vs_conn_unhash(ct)) {				ct->dport = 65535;				ct->vport = 65535;				ct->cport = 0;				ip_vs_conn_hash(ct);			}		}		/*		 * Simply decrease the refcnt of the template,		 * don't restart its timer.		 */		atomic_dec(&ct->refcnt);		return 0;	}	return 1;}static inline voidip_vs_timeout_attach(struct ip_vs_conn *cp, struct ip_vs_timeout_table *vstim){	atomic_inc(&vstim->refcnt);	cp->timeout_table = vstim;}static inline void ip_vs_timeout_detach(struct ip_vs_conn *cp){	struct ip_vs_timeout_table *vstim = cp->timeout_table;	if (!vstim)		return;	cp->timeout_table = NULL;	atomic_dec(&vstim->refcnt);}static void ip_vs_conn_expire(unsigned long data){	struct ip_vs_conn *cp = (struct ip_vs_conn *)data;	if (cp->timeout_table)		cp->timeout = cp->timeout_table->timeout[IP_VS_S_TIME_WAIT];	else		cp->timeout = vs_timeout_table.timeout[IP_VS_S_TIME_WAIT];	/*	 *	hey, I'm using it	 */	atomic_inc(&cp->refcnt);	/*	 *	do I control anybody?	 */	if (atomic_read(&cp->n_control))		goto expire_later;	/*	 *	unhash it if it is hashed in the conn table	 */	if (!ip_vs_conn_unhash(cp))		goto expire_later;	/*	 *	refcnt==1 implies I'm the only one referrer	 */	if (likely(atomic_read(&cp->refcnt) == 1)) {		/* make sure that there is no timer on it now */		if (timer_pending(&cp->timer))			del_timer(&cp->timer);		/* does anybody control me? */		if (cp->control)			ip_vs_control_del(cp);		ip_vs_unbind_dest(cp);		ip_vs_unbind_app(cp);		ip_vs_timeout_detach(cp);		if (cp->flags & IP_VS_CONN_F_NO_CPORT)			atomic_dec(&ip_vs_conn_no_cport_cnt);		atomic_dec(&ip_vs_conn_count);		kmem_cache_free(ip_vs_conn_cachep, cp);		return;	}	/* hash it back to the table */	ip_vs_conn_hash(cp);  expire_later:	IP_VS_DBG(7, "delayed: refcnt-1=%d conn.n_control=%d\n",		  atomic_read(&cp->refcnt)-1,		  atomic_read(&cp->n_control));	ip_vs_conn_put(cp);}void ip_vs_conn_expire_now(struct ip_vs_conn *cp){	cp->timeout = 0;	mod_timer(&cp->timer, jiffies);	__ip_vs_conn_put(cp);}/* *  Create a new connection entry and hash it into the ip_vs_conn_tab. */struct ip_vs_conn *ip_vs_conn_new(int proto, __u32 caddr, __u16 cport, __u32 vaddr, __u16 vport,	       __u32 daddr, __u16 dport, unsigned flags,	       struct ip_vs_dest *dest){	struct ip_vs_conn *cp;	cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC);	if (cp == NULL) {		IP_VS_ERR_RL("ip_vs_conn_new: no memory available.\n");		return NULL;	}	memset(cp, 0, sizeof(*cp));	INIT_LIST_HEAD(&cp->c_list);	init_timer(&cp->timer);	cp->timer.data     = (unsigned long)cp;	cp->timer.function = ip_vs_conn_expire;	ip_vs_timeout_attach(cp, ip_vs_timeout_table);	cp->protocol	   = proto;	cp->caddr	   = caddr;	cp->cport	   = cport;	cp->vaddr	   = vaddr;	cp->vport	   = vport;	cp->daddr          = daddr;	cp->dport          = dport;	cp->flags	   = flags;	cp->app_data	   = NULL;	cp->control	   = NULL;	cp->lock           = SPIN_LOCK_UNLOCKED;	atomic_set(&cp->n_control, 0);	atomic_set(&cp->in_pkts, 0);	atomic_inc(&ip_vs_conn_count);	if (flags & IP_VS_CONN_F_NO_CPORT)		atomic_inc(&ip_vs_conn_no_cport_cnt);	/* Bind its application helper (only for VS/NAT) if any */	ip_vs_bind_app(cp);	/* Bind the connection with a destination server */	ip_vs_bind_dest(cp, dest);	/* Set its state and timeout */	vs_set_state_timeout(cp, IP_VS_S_NONE);	/* Bind its packet transmitter */	ip_vs_bind_xmit(cp);	/*	 * Set the entry is referenced by the current thread before hashing	 * it in the table, so that other thread run ip_vs_random_dropentry	 * but cannot drop this entry.	 */	atomic_set(&cp->refcnt, 1);	/* Hash it in the ip_vs_conn_tab finally */	ip_vs_conn_hash(cp);	return cp;}/* *	/proc/net/ip_vs_conn entries */static intip_vs_conn_getinfo(char *buffer, char **start, off_t offset, int length){	off_t pos=0;	int idx, len=0;	char temp[70];	struct ip_vs_conn *cp;	struct list_head *l, *e;	pos = 128;	if (pos > offset) {		len += sprintf(buffer+len, "%-127s\n",			       "Pro FromIP   FPrt ToIP     TPrt DestIP   DPrt State       Expires");	}	for(idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {		/*		 *	Lock is actually only need in next loop		 *	we are called from uspace: must stop bh.		 */		ct_read_lock_bh(idx);		l = &ip_vs_conn_tab[idx];		for (e=l->next; e!=l; e=e->next) {			cp = list_entry(e, struct ip_vs_conn, c_list);			pos += 128;			if (pos <= offset)				continue;			sprintf(temp,				"%-3s %08X %04X %08X %04X %08X %04X %-11s %7lu",				ip_vs_proto_name(cp->protocol),				ntohl(cp->caddr), ntohs(cp->cport),				ntohl(cp->vaddr), ntohs(cp->vport),				ntohl(cp->daddr), ntohs(cp->dport),				ip_vs_state_name(cp->state),				(cp->timer.expires-jiffies)/HZ);			len += sprintf(buffer+len, "%-127s\n", temp);			if (pos >= offset+length) {				ct_read_unlock_bh(idx);				goto done;			}		}		ct_read_unlock_bh(idx);	}  done:	*start = buffer+len-(pos-offset);       /* Start of wanted data */	len = pos-offset;	if (len > length)		len = length;	if (len < 0)		len = 0;	return len;}/* *      Randomly drop connection entries before running out of memory */static inline int todrop_entry(struct ip_vs_conn *cp){	/*	 * The drop rate array needs tuning for real environments.	 * Called from timer bh only => no locking	 */	static char todrop_rate[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};	static char todrop_counter[9] = {0};	int i;	/* if the conn entry hasn't lasted for 60 seconds, don't drop it.	   This will leave enough time for normal connection to get	   through. */	if (cp->timeout+jiffies-cp->timer.expires < 60*HZ)		return 0;	/* Don't drop the entry if its number of incoming packets is not	   located in [0, 8] */	i = atomic_read(&cp->in_pkts);	if (i > 8 || i < 0) return 0;	if (!todrop_rate[i]) return 0;	if (--todrop_counter[i] > 0) return 0;	todrop_counter[i] = todrop_rate[i];	return 1;}void ip_vs_random_dropentry(void){	int idx;	struct ip_vs_conn *cp;	struct list_head *l,*e;	struct ip_vs_conn *ct;	/*	 * Randomly scan 1/32 of the whole table every second	 */	for (idx=0; idx<(IP_VS_CONN_TAB_SIZE>>5); idx++) {		unsigned hash = net_random()&IP_VS_CONN_TAB_MASK;		/*		 *  Lock is actually needed in this loop.		 */		ct_write_lock(hash);		l = &ip_vs_conn_tab[hash];		for (e=l->next; e!=l; e=e->next) {			cp = list_entry(e, struct ip_vs_conn, c_list);			if (!cp->cport && !(cp->flags & IP_VS_CONN_F_NO_CPORT))				/* connection template */				continue;			switch(cp->state) {			case IP_VS_S_SYN_RECV:			case IP_VS_S_SYNACK:				break;			case IP_VS_S_ESTABLISHED:			case IP_VS_S_UDP:				if (todrop_entry(cp))					break;				continue;			default:				continue;			}			/*			 * Drop the entry, and drop its ct if not referenced			 */			atomic_inc(&cp->refcnt);			ct_write_unlock(hash);			if ((ct = cp->control))				atomic_inc(&ct->refcnt);			IP_VS_DBG(4, "del connection\n");			ip_vs_conn_expire_now(cp);			if (ct) {				IP_VS_DBG(4, "del conn template\n");				ip_vs_conn_expire_now(ct);			}			ct_write_lock(hash);		}		ct_write_unlock(hash);	}}/* *      Flush all the connection entries in the ip_vs_conn_tab */static void ip_vs_conn_flush(void){	int idx;	struct ip_vs_conn *cp;	struct list_head *l,*e;	struct ip_vs_conn *ct;  flush_again:	for (idx=0; idx<IP_VS_CONN_TAB_SIZE; idx++) {		/*		 *  Lock is actually needed in this loop.		 */		ct_write_lock_bh(idx);		l = &ip_vs_conn_tab[idx];		for (e=l->next; e!=l; e=e->next) {			cp = list_entry(e, struct ip_vs_conn, c_list);			atomic_inc(&cp->refcnt);			ct_write_unlock(idx);			if ((ct = cp->control))				atomic_inc(&ct->refcnt);			IP_VS_DBG(4, "del connection\n");			ip_vs_conn_expire_now(cp);			if (ct) {				IP_VS_DBG(4, "del conn template\n");				ip_vs_conn_expire_now(ct);			}			ct_write_lock(idx);		}		ct_write_unlock_bh(idx);	}	/* the counter may be not NULL, because maybe some conn entries	   are run by slow timer handler or unhashed but still referred */	if (atomic_read(&ip_vs_conn_count) != 0) {		schedule();		goto flush_again;	}}int ip_vs_conn_init(void){	int idx;	/*	 * Allocate the connection hash table and initialize its list heads	 */	ip_vs_conn_tab = vmalloc(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head));	if (!ip_vs_conn_tab)		return -ENOMEM;	IP_VS_INFO("Connection hash table configured "		   "(size=%d, memory=%ldKbytes)\n",		   IP_VS_CONN_TAB_SIZE,		   (long)(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head))/1024);	IP_VS_DBG(0, "Each connection entry needs %d bytes at least\n",		  sizeof(struct ip_vs_conn));	for (idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {		INIT_LIST_HEAD(&ip_vs_conn_tab[idx]);	}	for (idx = 0; idx < CT_LOCKARRAY_SIZE; idx++)  {		__ip_vs_conntbl_lock_array[idx].l = RW_LOCK_UNLOCKED;	}	/* Allocate ip_vs_conn slab cache */	ip_vs_conn_cachep = kmem_cache_create("ip_vs_conn",					      sizeof(struct ip_vs_conn), 0,					      SLAB_HWCACHE_ALIGN, NULL, NULL);	if (!ip_vs_conn_cachep) {		vfree(ip_vs_conn_tab);		return -ENOMEM;	}	proc_net_create("ip_vs_conn", 0, ip_vs_conn_getinfo);	/* calculate the random value for connection hash */	get_random_bytes(&ip_vs_conn_rnd, sizeof(ip_vs_conn_rnd));	return 0;}void ip_vs_conn_cleanup(void){	/* flush all the connection entries first */	ip_vs_conn_flush();	/* Release the empty cache */	kmem_cache_destroy(ip_vs_conn_cachep);	proc_net_remove("ip_vs_conn");	vfree(ip_vs_conn_tab);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?