⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip_vs_ctl.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * IPVS         An implementation of the IP virtual server support for the *              LINUX operating system.  IPVS is now implemented as a module *              over the NetFilter framework. IPVS can be used to build a *              high-performance and highly available server based on a *              cluster of servers. * * Version:     $Id: ip_vs_ctl.c,v 1.36 2003/06/08 09:31:19 wensong Exp $ * * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org> *              Peter Kese <peter.kese@ijs.si> *              Julian Anastasov <ja@ssi.bg> * *              This program is free software; you can redistribute it and/or *              modify it under the terms of the GNU General Public License *              as published by the Free Software Foundation; either version *              2 of the License, or (at your option) any later version. * * Changes: * */#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/workqueue.h>#include <linux/swap.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4.h>#include <net/ip.h>#include <net/sock.h>#include <asm/uaccess.h>#include <net/ip_vs.h>/* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */static DECLARE_MUTEX(__ip_vs_mutex);/* lock for service table */static DEFINE_RWLOCK(__ip_vs_svc_lock);/* lock for table with the real services */static DEFINE_RWLOCK(__ip_vs_rs_lock);/* lock for state and timeout tables */static DEFINE_RWLOCK(__ip_vs_securetcp_lock);/* lock for drop entry handling */static DEFINE_SPINLOCK(__ip_vs_dropentry_lock);/* lock for drop packet handling */static DEFINE_SPINLOCK(__ip_vs_droppacket_lock);/* 1/rate drop and drop-entry variables */int ip_vs_drop_rate = 0;int ip_vs_drop_counter = 0;static atomic_t ip_vs_dropentry = ATOMIC_INIT(0);/* number of virtual services */static int ip_vs_num_services = 0;/* sysctl variables */static int sysctl_ip_vs_drop_entry = 0;static int sysctl_ip_vs_drop_packet = 0;static int sysctl_ip_vs_secure_tcp = 0;static int sysctl_ip_vs_amemthresh = 1024;static int sysctl_ip_vs_am_droprate = 10;int sysctl_ip_vs_cache_bypass = 0;int sysctl_ip_vs_expire_nodest_conn = 0;int sysctl_ip_vs_expire_quiescent_template = 0;int sysctl_ip_vs_sync_threshold[2] = { 3, 50 };int sysctl_ip_vs_nat_icmp_send = 0;#ifdef CONFIG_IP_VS_DEBUGstatic int sysctl_ip_vs_debug_level = 0;int ip_vs_get_debug_level(void){	return sysctl_ip_vs_debug_level;}#endif/* *	update_defense_level is called from keventd and from sysctl, *	so it needs to protect itself from softirqs */static void update_defense_level(void){	struct sysinfo i;	static int old_secure_tcp = 0;	int availmem;	int nomem;	int to_change = -1;	/* we only count free and buffered memory (in pages) */	si_meminfo(&i);	availmem = i.freeram + i.bufferram;	/* however in linux 2.5 the i.bufferram is total page cache size,	   we need adjust it */	/* si_swapinfo(&i); */	/* availmem = availmem - (i.totalswap - i.freeswap); */	nomem = (availmem < sysctl_ip_vs_amemthresh);	local_bh_disable();	/* drop_entry */	spin_lock(&__ip_vs_dropentry_lock);	switch (sysctl_ip_vs_drop_entry) {	case 0:		atomic_set(&ip_vs_dropentry, 0);		break;	case 1:		if (nomem) {			atomic_set(&ip_vs_dropentry, 1);			sysctl_ip_vs_drop_entry = 2;		} else {			atomic_set(&ip_vs_dropentry, 0);		}		break;	case 2:		if (nomem) {			atomic_set(&ip_vs_dropentry, 1);		} else {			atomic_set(&ip_vs_dropentry, 0);			sysctl_ip_vs_drop_entry = 1;		};		break;	case 3:		atomic_set(&ip_vs_dropentry, 1);		break;	}	spin_unlock(&__ip_vs_dropentry_lock);	/* drop_packet */	spin_lock(&__ip_vs_droppacket_lock);	switch (sysctl_ip_vs_drop_packet) {	case 0:		ip_vs_drop_rate = 0;		break;	case 1:		if (nomem) {			ip_vs_drop_rate = ip_vs_drop_counter				= sysctl_ip_vs_amemthresh /				(sysctl_ip_vs_amemthresh-availmem);			sysctl_ip_vs_drop_packet = 2;		} else {			ip_vs_drop_rate = 0;		}		break;	case 2:		if (nomem) {			ip_vs_drop_rate = ip_vs_drop_counter				= sysctl_ip_vs_amemthresh /				(sysctl_ip_vs_amemthresh-availmem);		} else {			ip_vs_drop_rate = 0;			sysctl_ip_vs_drop_packet = 1;		}		break;	case 3:		ip_vs_drop_rate = sysctl_ip_vs_am_droprate;		break;	}	spin_unlock(&__ip_vs_droppacket_lock);	/* secure_tcp */	write_lock(&__ip_vs_securetcp_lock);	switch (sysctl_ip_vs_secure_tcp) {	case 0:		if (old_secure_tcp >= 2)			to_change = 0;		break;	case 1:		if (nomem) {			if (old_secure_tcp < 2)				to_change = 1;			sysctl_ip_vs_secure_tcp = 2;		} else {			if (old_secure_tcp >= 2)				to_change = 0;		}		break;	case 2:		if (nomem) {			if (old_secure_tcp < 2)				to_change = 1;		} else {			if (old_secure_tcp >= 2)				to_change = 0;			sysctl_ip_vs_secure_tcp = 1;		}		break;	case 3:		if (old_secure_tcp < 2)			to_change = 1;		break;	}	old_secure_tcp = sysctl_ip_vs_secure_tcp;	if (to_change >= 0)		ip_vs_protocol_timeout_change(sysctl_ip_vs_secure_tcp>1);	write_unlock(&__ip_vs_securetcp_lock);	local_bh_enable();}/* *	Timer for checking the defense */#define DEFENSE_TIMER_PERIOD	1*HZstatic void defense_work_handler(void *data);static DECLARE_WORK(defense_work, defense_work_handler, NULL);static void defense_work_handler(void *data){	update_defense_level();	if (atomic_read(&ip_vs_dropentry))		ip_vs_random_dropentry();	schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD);}intip_vs_use_count_inc(void){	return try_module_get(THIS_MODULE);}voidip_vs_use_count_dec(void){	module_put(THIS_MODULE);}/* *	Hash table: for virtual service lookups */#define IP_VS_SVC_TAB_BITS 8#define IP_VS_SVC_TAB_SIZE (1 << IP_VS_SVC_TAB_BITS)#define IP_VS_SVC_TAB_MASK (IP_VS_SVC_TAB_SIZE - 1)/* the service table hashed by <protocol, addr, port> */static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE];/* the service table hashed by fwmark */static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE];/* *	Hash table: for real service lookups */#define IP_VS_RTAB_BITS 4#define IP_VS_RTAB_SIZE (1 << IP_VS_RTAB_BITS)#define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1)static struct list_head ip_vs_rtable[IP_VS_RTAB_SIZE];/* *	Trash for destinations */static LIST_HEAD(ip_vs_dest_trash);/* *	FTP & NULL virtual service counters */static atomic_t ip_vs_ftpsvc_counter = ATOMIC_INIT(0);static atomic_t ip_vs_nullsvc_counter = ATOMIC_INIT(0);/* *	Returns hash value for virtual service */static __inline__ unsignedip_vs_svc_hashkey(unsigned proto, __u32 addr, __u16 port){	register unsigned porth = ntohs(port);	return (proto^ntohl(addr)^(porth>>IP_VS_SVC_TAB_BITS)^porth)		& IP_VS_SVC_TAB_MASK;}/* *	Returns hash value of fwmark for virtual service lookup */static __inline__ unsigned ip_vs_svc_fwm_hashkey(__u32 fwmark){	return fwmark & IP_VS_SVC_TAB_MASK;}/* *	Hashes a service in the ip_vs_svc_table by <proto,addr,port> *	or in the ip_vs_svc_fwm_table by fwmark. *	Should be called with locked tables. */static int ip_vs_svc_hash(struct ip_vs_service *svc){	unsigned hash;	if (svc->flags & IP_VS_SVC_F_HASHED) {		IP_VS_ERR("ip_vs_svc_hash(): request for already hashed, "			  "called from %p\n", __builtin_return_address(0));		return 0;	}	if (svc->fwmark == 0) {		/*		 *  Hash it by <protocol,addr,port> in ip_vs_svc_table		 */		hash = ip_vs_svc_hashkey(svc->protocol, svc->addr, svc->port);		list_add(&svc->s_list, &ip_vs_svc_table[hash]);	} else {		/*		 *  Hash it by fwmark in ip_vs_svc_fwm_table		 */		hash = ip_vs_svc_fwm_hashkey(svc->fwmark);		list_add(&svc->f_list, &ip_vs_svc_fwm_table[hash]);	}	svc->flags |= IP_VS_SVC_F_HASHED;	/* increase its refcnt because it is referenced by the svc table */	atomic_inc(&svc->refcnt);	return 1;}/* *	Unhashes a service from ip_vs_svc_table/ip_vs_svc_fwm_table. *	Should be called with locked tables. */static int ip_vs_svc_unhash(struct ip_vs_service *svc){	if (!(svc->flags & IP_VS_SVC_F_HASHED)) {		IP_VS_ERR("ip_vs_svc_unhash(): request for unhash flagged, "			  "called from %p\n", __builtin_return_address(0));		return 0;	}	if (svc->fwmark == 0) {		/* Remove it from the ip_vs_svc_table table */		list_del(&svc->s_list);	} else {		/* Remove it from the ip_vs_svc_fwm_table table */		list_del(&svc->f_list);	}	svc->flags &= ~IP_VS_SVC_F_HASHED;	atomic_dec(&svc->refcnt);	return 1;}/* *	Get service by {proto,addr,port} in the service table. */static __inline__ struct ip_vs_service *__ip_vs_service_get(__u16 protocol, __u32 vaddr, __u16 vport){	unsigned hash;	struct ip_vs_service *svc;	/* Check for "full" addressed entries */	hash = ip_vs_svc_hashkey(protocol, vaddr, vport);	list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){		if ((svc->addr == vaddr)		    && (svc->port == vport)		    && (svc->protocol == protocol)) {			/* HIT */			atomic_inc(&svc->usecnt);			return svc;		}	}	return NULL;}/* *	Get service by {fwmark} in the service table. */static __inline__ struct ip_vs_service *__ip_vs_svc_fwm_get(__u32 fwmark){	unsigned hash;	struct ip_vs_service *svc;	/* Check for fwmark addressed entries */	hash = ip_vs_svc_fwm_hashkey(fwmark);	list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) {		if (svc->fwmark == fwmark) {			/* HIT */			atomic_inc(&svc->usecnt);			return svc;		}	}	return NULL;}struct ip_vs_service *ip_vs_service_get(__u32 fwmark, __u16 protocol, __u32 vaddr, __u16 vport){	struct ip_vs_service *svc;	read_lock(&__ip_vs_svc_lock);	/*	 *	Check the table hashed by fwmark first	 */	if (fwmark && (svc = __ip_vs_svc_fwm_get(fwmark)))		goto out;	/*	 *	Check the table hashed by <protocol,addr,port>	 *	for "full" addressed entries	 */	svc = __ip_vs_service_get(protocol, vaddr, vport);	if (svc == NULL	    && protocol == IPPROTO_TCP	    && atomic_read(&ip_vs_ftpsvc_counter)	    && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {		/*		 * Check if ftp service entry exists, the packet		 * might belong to FTP data connections.		 */		svc = __ip_vs_service_get(protocol, vaddr, FTPPORT);	}	if (svc == NULL	    && atomic_read(&ip_vs_nullsvc_counter)) {		/*		 * Check if the catch-all port (port zero) exists		 */		svc = __ip_vs_service_get(protocol, vaddr, 0);	}  out:	read_unlock(&__ip_vs_svc_lock);	IP_VS_DBG(6, "lookup service: fwm %u %s %u.%u.%u.%u:%u %s\n",		  fwmark, ip_vs_proto_name(protocol),		  NIPQUAD(vaddr), ntohs(vport),		  svc?"hit":"not hit");	return svc;}static inline void__ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc){	atomic_inc(&svc->refcnt);	dest->svc = svc;}static inline void__ip_vs_unbind_svc(struct ip_vs_dest *dest){	struct ip_vs_service *svc = dest->svc;	dest->svc = NULL;	if (atomic_dec_and_test(&svc->refcnt))		kfree(svc);}/* *	Returns hash value for real service */static __inline__ unsigned ip_vs_rs_hashkey(__u32 addr, __u16 port){	register unsigned porth = ntohs(port);	return (ntohl(addr)^(porth>>IP_VS_RTAB_BITS)^porth)		& IP_VS_RTAB_MASK;}/* *	Hashes ip_vs_dest in ip_vs_rtable by <proto,addr,port>. *	should be called with locked tables. */static int ip_vs_rs_hash(struct ip_vs_dest *dest){	unsigned hash;	if (!list_empty(&dest->d_list)) {		return 0;	}	/*	 *	Hash by proto,addr,port,	 *	which are the parameters of the real service.	 */	hash = ip_vs_rs_hashkey(dest->addr, dest->port);	list_add(&dest->d_list, &ip_vs_rtable[hash]);	return 1;}/* *	UNhashes ip_vs_dest from ip_vs_rtable. *	should be called with locked tables. */static int ip_vs_rs_unhash(struct ip_vs_dest *dest){	/*	 * Remove it from the ip_vs_rtable table.	 */	if (!list_empty(&dest->d_list)) {		list_del(&dest->d_list);		INIT_LIST_HEAD(&dest->d_list);	}	return 1;}/* *	Lookup real service by <proto,addr,port> in the real service table. */struct ip_vs_dest *ip_vs_lookup_real_service(__u16 protocol, __u32 daddr, __u16 dport){	unsigned hash;	struct ip_vs_dest *dest;	/*	 *	Check for "full" addressed entries	 *	Return the first found entry	 */	hash = ip_vs_rs_hashkey(daddr, dport);	read_lock(&__ip_vs_rs_lock);	list_for_each_entry(dest, &ip_vs_rtable[hash], d_list) {		if ((dest->addr == daddr)		    && (dest->port == dport)		    && ((dest->protocol == protocol) ||			dest->vfwmark)) {			/* HIT */			read_unlock(&__ip_vs_rs_lock);			return dest;		}	}	read_unlock(&__ip_vs_rs_lock);	return NULL;}/* *	Lookup destination by {addr,port} in the given service */static struct ip_vs_dest *ip_vs_lookup_dest(struct ip_vs_service *svc, __u32 daddr, __u16 dport){	struct ip_vs_dest *dest;	/*	 * Find the destination for the given service	 */	list_for_each_entry(dest, &svc->destinations, n_list) {		if ((dest->addr == daddr) && (dest->port == dport)) {			/* HIT */			return dest;		}	}	return NULL;}/* *  Lookup dest by {svc,addr,port} in the destination trash. *  The destination trash is used to hold the destinations that are removed *  from the service table but are still referenced by some conn entries. *  The reason to add the destination trash is when the dest is temporary *  down (either by administrator or by monitor program), the dest can be *  picked back from the trash, the remaining connections to the dest can *  continue, and the counting information of the dest is also useful for *  scheduling. */static struct ip_vs_dest *ip_vs_trash_get_dest(struct ip_vs_service *svc, __u32 daddr, __u16 dport){	struct ip_vs_dest *dest, *nxt;	/*	 * Find the destination in trash	 */	list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) {		IP_VS_DBG(3, "Destination %u/%u.%u.%u.%u:%u still in trash, "			  "refcnt=%d\n",

⌨️ 快捷键说明

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