vs/nat原理分析
内核版本:2.6.12
1.vs/nat原理简介
vs/nat全称visual server /net address translation。通过vs/nat的体系结构如下:
在一组服务器前有一个调度器(Load Balancer),它们是通过Switch/HUB相连接的。这些服务器提供相同的网络服务、相同的内容,即不管请求被发送到哪一台服务器,执行结果是一样的。服务器Load Balancer被称为虚拟服务器(visual server),对于客户,它的ip地址被称为虚拟ip地址(visual IP address)。下面详细说明一下服务的流程。
1.客户根据虚拟服务器的虚拟ip地址来发送自己的请求,就是说第一步请求会被发往虚拟服务器;
2.请求到达虚拟服务器之后,服务器根据调度算法从一组真实的服务器中选出一台服务器,将请求报文的目标地址改成真实服务器的地址。同时虚拟服务器在connection HASH中记录这个连接(连接记录会记录 客户端地址信息、虚拟服务器地址信息以及选定服务器地址信息)。当这个属于这个连接的下一个报文到达时,直接从连接记录中获得选定服务器的地址信息,然后进行同样的改写操作,将数据包发出去。值得注意的是真实服务器的ip地址是属于保留地址172.16.0.0/255.128.0.0的(LVS设定的)。连接记录也是有状态的,当它无效或者超时的时候就从HASH表中将它删除。明显,这一步骤需要一个DNAT操作来把数据包重定位到真实服务器;
3.真实服务器接到请求后,做相应处理。服务器对客户的回复数据包会发往虚拟服务器;
4.虚拟服务器收到真实服务器的回复数据包,查找到HASH表中关联的连接记录,然后对数据包做SNAT操作,将数据包的目标地址改写为客户地址;
5.虚拟服务器将改写好的数据包发往客户;
对于复杂协议需要额外的处理模块,这和netfilter中是类似的,这里不做额外说明了。
下面举一个实际的例子来说明一下过程:
VS/NAT的配置如下表所示,所有到IP地址为202.103.106.5和端口为80的流量都被负载均衡地调度的真实服务器172.16.0.2:80和172.16.0.3:8000上。目标地址为202.103.106.5:21的报文被转移到172.16.0.3:21上。而到其他端口的报文将被拒绝。
Protocol |
Virtual IP Address |
Port |
Real IP Address |
Port |
Weight |
TCP |
202.103.106.5 |
80 |
172.16.0.2 |
80 |
1 |
172.16.0.3 |
8000 |
2 |
|||
TCP |
202.103.106.5 |
21 |
172.16.0.3 |
21 |
1 |
访问Web服务的报文可能有以下的源地址和目标地址:
SOURCE |
202.100.1.2:3456 |
DEST |
202.103.106.5:80 |
调度器从调度列表中选出一台服务器,例如是172.16.0.3:8000。该报文会被改写为如下地址,并将它发送给选出的服务器。
SOURCE |
202.100.1.2:3456 |
DEST |
172.16.0.3:8000 |
从服务器返回到调度器的响应报文如下:
SOURCE |
172.16.0.3:8000 |
DEST |
202.100.1.2:3456
|
响应报文的源地址会被改写为虚拟服务的地址,再将报文发送给客户:
SOURCE |
202.103.106.5:80 |
DEST |
202.100.1.2:3456
|
这样,客户认为是从202.103.106.5:80服务得到正确的响应,而不会知道该请求是服务器172.16.0.2还是服务器172.16.0.3处理的。
2.vs/nat 实现细节
2.1 vs/nat在netfilter中的位置
vs/nat是基于netfilter框架的,其主要工作在LOACL_IN链和FORWARD链上。
2.2 代码分析
vs/nat 没有使用netfilter的连接跟踪机制,而是基于自己的需要,实现了一个自己使用的连接跟踪机制。这个连接跟踪机制和netfilter的连接跟踪机制基本原理是一样的。
数据结构部分:
1. ip_vs_service /*The information about the virtual service offered to the net and the forwarding entries*/
一个虚拟服务器不止通过一种协议来监听一个端口,它可以监听不同协议上的不同端口的客户请求。可知ip_vs_service便是某个协议的某个端口的一种抽象。
1 struct ip_vs_service { 2 struct list_head s_list; /* for normal service table */ 3 struct list_head f_list; /* for fwmark-based service table */ 4 atomic_t refcnt; /* reference counter */ 5 atomic_t usecnt; /* use counter */ 6 7 __u16 protocol; /* which protocol (TCP/UDP) */ 8 __u32 addr; /* IP address for virtual service */ 9 __u16 port; /* port number for the service */ 10 __u32 fwmark; /* firewall mark of the service */ 11 unsigned flags; /* service status flags */ 12 unsigned timeout; /* persistent timeout in ticks */ 13 __u32 netmask; /* grouping granularity */ 14 15 struct list_head destinations; /* real server d-linked list */ 16 __u32 num_dests; /* number of servers */ 17 struct ip_vs_stats stats; /* statistics for the service */ 18 struct ip_vs_app *inc; /* bind conns to this app inc */ 19 20 /* for scheduling */ 21 struct ip_vs_scheduler *scheduler; /* bound scheduler object */ 22 rwlock_t sched_lock; /* lock sched_data */ 23 void *sched_data; /* scheduler application data */ 24 };
ip_vs_service是被存放到两个hash中的,用于不同方向上的快速检索。
2. ip_vs_conn /*IP_VS structure allocated for each dynamically scheduled connection */
这
1 struct ip_vs_conn { 2 struct list_head c_list; /* hashed list heads *///用于组织成HASH 3 4 /* Protocol, addresses and port numbers */ 5 __u32 caddr; /* client address */ 6 __u32 vaddr; /* virtual address */ 7 __u32 daddr; /* destination address */ 8 __u16 cport; //client端口 9 __u16 vport; //virtual 端口 10 __u16 dport; //destination 端口 11 __u16 protocol; /* Which protocol (TCP/UDP) */ 12 13 /* counter and timer */ 14 atomic_t refcnt; /* reference count,引用计数 */ 15 struct timer_list timer; /* Expiration timer */ 16 volatile unsigned long timeout; /* timeout */ 17 18 /* Flags and state transition */ 19 spinlock_t lock; /* lock for state transition */ 20 volatile __u16 flags; /* status flags */ 21 volatile __u16 state; /* state info, 标识连接状态的*/ 22 23 /* Control members */ 24 struct ip_vs_conn *control; /* Master control connection / 25 atomic_t n_control; /* Number of controlled ones */ 26 27 struct ip_vs_dest *dest; /* real server */ 28 atomic_t in_pkts; /* incoming packet counter */ 29 30 /* packet transmitter for different forwarding methods. If it 31 mangles the packet, it must return NF_DROP or better NF_STOLEN, 32 otherwise this must be changed to a sk_buff **. 33 */ 34 int (*packet_xmit)(struct sk_buff *skb, struct ip_vs_conn *cp, 35 struct ip_vs_protocol *pp); 36 37 /* Note: we can group the following members into a structure, 38 in order to save more space, and the following members are 39 only used in VS/NAT anyway */ 40 struct ip_vs_app *app; /* bound ip_vs_app object */ 41 void *app_data; /* Application private data */ 42 struct ip_vs_seq in_seq; /* incoming seq. struct */ 43 struct ip_vs_seq out_seq; /* outgoing seq. struct */ 44 };
连接记录是以hash的方式组织的,与netfilter的连接记录相比,这里的更加简洁一些。
3.ip_vs_protocol
这是和协议相关的一个结构体,不同的协议对应它的一个实例,为方便理解,这里直接列举一下tcp协议对应的实例:
1 struct ip_vs_protocol ip_vs_protocol_tcp = { 2 .name = "TCP", 3 .protocol = IPPROTO_TCP, 4 .dont_defrag = 0, 5 .appcnt = ATOMIC_INIT(0), 6 .init = tcp_init, //初始化钩子 7 .exit = tcp_exit, 8 .register_app = tcp_register_app, 9 .unregister_app = tcp_unregister_app, 10 .conn_schedule = tcp_conn_schedule, //函数会根据算法寻找到合适的dest,并新建一个连接记录 11 12 .conn_in_get = tcp_conn_in_get, //数据包进入本地时查找连接记录,使用数据包源端信息hash 13 .conn_out_get = tcp_conn_out_get, //转发数据包时查找连接记录,使用数据包目的端信息hash 14 15 .snat_handler = tcp_snat_handler, //SNAT操作函数 16 .dnat_handler = tcp_dnat_handler, //DNAT操作函数 17 .csum_check = tcp_csum_check, 18 .state_name = tcp_state_name, 19 .state_transition = tcp_state_transition, 20 .app_conn_bind = tcp_app_conn_bind, 21 .debug_packet = ip_vs_tcpudp_debug_packet, 22 .timeout_change = tcp_timeout_change, 23 .set_state_timeout = tcp_set_state_timeout, 24 };
流程模拟 :(客户发送新的数据包给虚拟服务器),函数ip_vs_in()图解:
3.总结
vs/nat也是利用连接记录来跟踪数据包以完成DNAT的操作的,函数ip_vs_out的原理和ip_vs_in是一样的,就不重复说明了。通过分析,也可以发现vs/nat的实现机制是基于netfilter框架的,而且基本原理上和netfilter的nat实现类似,都是基于连接跟踪的。vs/nat根据自己的需要,实现了另外一个连接跟踪,相比而言,要比netfilter的实现要简单一些,不过这也是因为vs/nat的使用场景的复杂度较低的缘故。vs/nat只负责自己配置的特定协议和端口上的连接跟踪,这是最主要的区别。