📄 ip_vs_ctl.c
字号:
/* * 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 + -