十分感谢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连接进行绑定
未完待续