十分感謝yfydz老大發布ip_vs實現分析系列文章,這使我能盡快理解ipvs的工作原理與源碼組成。
不過yfydz的文章過於長,不便於后續檢索。我計划逐步整理,發到blog上。
1、ipvs分為三種負載均衡模式
NAT、tunnel、direct routing(DR)
NAT:所有交互數據必須通過均衡器
tunnel:半連接處理方式,進行了IP封裝
DR:修改MAC地址,需要同一網段。
2、ipvs支持的均衡調度算法
輪叫調度(Round-Robin Scheduling)
加權輪叫調度(Weighted Round-Robin Scheduling)
最小連接調度(Least-Connection Scheduling)
加權最小連接調度(Weighted Least-Connection Scheduling)
基於局部性的最少鏈接(Locality-Based Least Connections Scheduling)
帶復制的基於局部性最少鏈接(Locality-Based Least Connections with Replication Scheduling)
目標地址散列調度(Destination Hashing Scheduling)
源地址散列調度(Source Hashing Scheduling)
3、ipvs代碼記錄
內核為 Linux-kernel 3.3.7
3.1、結構體
ipvs各結構體定義在include\net\ip_vs.h與include\linux\ip_vs.h頭文件中
struct ip_vs_protocol
這個結構用來描述ipvs支持的IP協議。ipvs的IP層協議支持TCP, UDP, AH和ESP這4種IP層協議
struct ip_vs_conn
這個結構用來描述ipvs的鏈接
struct ip_vs_service
這個結構用來描述ipvs對外的虛擬服務器信息
struct ip_vs_dest
這個結構用來描述具體的真實服務器信息
struct ip_vs_scheduler
這個結構用來描述ipvs調度算法,目前調度方法包括rr,wrr,lc, wlc, lblc, lblcr, dh, sh等
struct ip_vs_app
這個結構用來描述ipvs的應用模塊對象
struct ip_vs_service_user
這個結構用來描述ipvs用戶空間的虛擬服務信息
struct ip_vs_dest_user
這個結構用來描述ipvs用戶空間的真實服務器信息
struct ip_vs_stats_user
這個結構用來描述ipvs用戶空間的統計信息
struct ip_vs_getinfo
這個結構用來描述ipvs用戶空間的獲取信息
struct ip_vs_service_entry
這個結構用來描述ipvs用戶空間的服務規則項信息
struct ip_vs_dest_entry
這個結構用來描述ipvs用戶空間的真實服務器規則項信息
struct ip_vs_get_dests
這個結構用來描述ipvs用戶空間的獲取真實服務器項信息
struct ip_vs_get_services
這個結構用來描述ipvs用戶空間的獲取虛擬服務項信息
struct ip_vs_timeout_user
這個結構用來描述ipvs用戶空間的超時信息
struct ip_vs_daemon_user
這個結構用來描述ipvs的內核守護進程信息
3.2、模塊初始化
net\netfilter\ipvs\ip_vs_core.c文件
static int __init ip_vs_init(void)
ipvs服務初始化
net\netfilter\ipvs\ip_vs_ctl.c文件
int __init ip_vs_control_init(void)
ioctl初始化
net\netfilter\ipvs\ip_vs_proto.c文件
int __init ip_vs_protocol_init(void)
協議初始化
net\netfilter\ipvs\ip_vs_conn.c文件
int __init ip_vs_conn_init(void)
連接初始化
net\netfilter\ipvs\ip_vs_core.c文件
static struct nf_hook_ops ip_vs_ops[]
ret = nf_register_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops));
netfilter掛接點數組,具體的數據包處理見數組中對應.hook的函數
3.3、調度算法具體實現
各算法與ip_vs_scheduler結構體對應
rr算法在net\netfilter\ipvs\ip_vs_rr.c文件中實現,以此類推。
static struct ip_vs_scheduler ip_vs_rr_scheduler = {
.name = "rr", /* name */
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
.init_service = ip_vs_rr_init_svc,
.update_service = ip_vs_rr_update_svc,
.schedule = ip_vs_rr_schedule,
};
init_service
算法初始化,在虛擬服務ip_vs_service和調度器綁定時調用(ip_vs_bind_scheduler()函數)
update_service()
函數在目的服務器變化時調用(如ip_vs_add_dest(), ip_vs_edit_dest()等函數)
而算法核心函數schedule()則是在ip_vs_schedule()函數中在新建IPVS連接前調用,找到真正的服務器提供服務,建立IPVS連接。
具體的算法實現看源代碼+yfydz老大的ipvs實現分析。
3.4、連接管理
net\netfilter\ipvs\ip_vs_conn.c文件
struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
進入方向
struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
發出方向
struct ip_vs_conn *
ip_vs_conn_new(const struct ip_vs_conn_param *p,
const union nf_inet_addr *daddr, __be16 dport, unsigned flags,
struct ip_vs_dest *dest, __u32 fwmark)
建立連接
static inline void
ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
綁定真實服務器
int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp)
綁定應用協議
static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp)
綁定發送方法
static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
將連接結構添加到連接hash表
static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
從連接hash表中斷開
static void ip_vs_conn_expire(unsigned long data)
連接超時
static inline void ip_vs_control_del(struct ip_vs_conn *cp)
從主連接中斷開
void ip_vs_unbind_app(struct ip_vs_conn *cp)
解除與應用的綁定
static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
接觸與真實服務器的綁定
static void ip_vs_conn_flush(struct net *net)
釋放所有連接
void ip_vs_random_dropentry(struct net *net)
定時隨即刪除連接
static inline int todrop_entry(struct ip_vs_conn *cp)
判斷是否要刪除連接
3.5、協議管理
net\netfilter\ipvs\ip_vs_proto.c文件
static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp)
注冊一個ipvs協議
static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp)
注銷一個ipvs協議
struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto)
查找服務,返回服務結構指針
void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags)
修改協議超時標記
int *ip_vs_create_timeout_table(int *table, int size)
創建狀態超時表
Int ip_vs_set_state_timeout(int *table, int num, const char *const *names,
const char *name, int to)
修改狀態超時表
const char * ip_vs_state_name(__u16 proto, int state)
返回協議狀態名稱
下面以TCP協議的實現來詳細說明,相關代碼文件為net\netfilter\ipvs\ip_vs_proto_tcp.c
struct ip_vs_protocol ip_vs_protocol_tcp = {
.name = "TCP",
.protocol = IPPROTO_TCP,
.num_states = IP_VS_TCP_S_LAST,
.dont_defrag = 0,
.init = NULL,
.exit = NULL,
.init_netns = __ip_vs_tcp_init,
.exit_netns = __ip_vs_tcp_exit,
.register_app = tcp_register_app,
.unregister_app = tcp_unregister_app,
.conn_schedule = tcp_conn_schedule,
.conn_in_get = ip_vs_conn_in_get_proto,
.conn_out_get = ip_vs_conn_out_get_proto,
.snat_handler = tcp_snat_handler,
.dnat_handler = tcp_dnat_handler,
.csum_check = tcp_csum_check,
.state_name = tcp_state_name,
.state_transition = tcp_state_transition,
.app_conn_bind = tcp_app_conn_bind,
.debug_packet = ip_vs_tcpudp_debug_packet,
.timeout_change = tcp_timeout_change,
};
static void __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd)
tcp初始化函數
static void __ip_vs_tcp_exit(struct net *net, struct ip_vs_proto_data *pd)
tcp退出函數
static int tcp_register_app(struct net *net, struct ip_vs_app *inc)
注冊tcp應用協議
static voidtcp_unregister_app(struct net *net, struct ip_vs_app *inc)
注銷tcp應用協議
static int
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
int *verdict, struct ip_vs_conn **cpp)
tcp連接調度,該函數在ip_vs_in()函數中調用。
struct ip_vs_conn *
ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph,
unsigned int proto_off, int inverse)
進入方向連接查找
struct ip_vs_conn *
ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph,
unsigned int proto_off, int inverse)
發出方向連接查找
static int
tcp_snat_handler(struct sk_buff *skb,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
該函數完成對協議部分數據進行源NAT操作,對TCP來說,NAT部分的數據就是源端口
static inline void
tcp_fast_csum_update(int af, struct tcphdr *tcph,
const union nf_inet_addr *oldip,
const union nf_inet_addr *newip,
__be16 oldport, __be16 newport)
TCP校驗和快速計算法,因為只修改了端口一個參數,可根據RFC1141方法快速計算
static int
tcp_dnat_handler(struct sk_buff *skb,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
該函數完成對協議部分數據進行目的NAT操作,對TCP來說,NAT部分的數據就是目的端口
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
計算IP協議中的校驗和,對於TCP,UDP頭中都有校驗和參數,TCP中的校驗和是必須的,而UDP的校驗和可以不用計算。
該函數用的都是linux內核提供標准的校驗和計算函數
static const char * tcp_state_name(int state)
該函數返回協議狀態名稱字符串
static const char *const tcp_state_name_table[IP_VS_TCP_S_LAST+1] = {
[IP_VS_TCP_S_NONE] = "NONE",
[IP_VS_TCP_S_ESTABLISHED] = "ESTABLISHED",
[IP_VS_TCP_S_SYN_SENT] = "SYN_SENT",
[IP_VS_TCP_S_SYN_RECV] = "SYN_RECV",
[IP_VS_TCP_S_FIN_WAIT] = "FIN_WAIT",
[IP_VS_TCP_S_TIME_WAIT] = "TIME_WAIT",
[IP_VS_TCP_S_CLOSE] = "CLOSE",
[IP_VS_TCP_S_CLOSE_WAIT] = "CLOSE_WAIT",
[IP_VS_TCP_S_LAST_ACK] = "LAST_ACK",
[IP_VS_TCP_S_LISTEN] = "LISTEN",
[IP_VS_TCP_S_SYNACK] = "SYNACK",
[IP_VS_TCP_S_LAST] = "BUG!",
};
TCP協議狀態名稱定義
static void
tcp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
struct ip_vs_proto_data *pd)
tcp狀態轉換
static inline void
set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
int direction, struct tcphdr *th)
設置tcp連接狀態
static struct tcp_states_t tcp_states []
tcp狀態轉換表
static void tcp_timeout_change(struct ip_vs_proto_data *pd, int flags)
超時變化
static int
tcp_app_conn_bind(struct ip_vs_conn *cp)
本函數實現將多連接應用協議處理模塊和IPVS連接進行綁定
未完待續