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:80172.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/natnetfilter中的位置

         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只负责自己配置的特定协议和端口上的连接跟踪,这是最主要的区别。


 

posted @ 2013-02-26 15:12  刘少东的博客  阅读(1546)  评论(0编辑  收藏  举报