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 + -
显示快捷键?