代码版本:

Socket Pool

套接字池(Socket Pool)为虚拟路由冗余协议(VRRP)实例高效管理网络通信,可在合适的情况下允许多个实例共享套接字。

vrrp_dispatcher_init

创建和申请一个 socket pool。

假设有 n 个网卡。每个网卡最多有 2 个 fds(1个用于 VRRP,另一个用于 IPSEC_AH)。所有的 VRRP 实例都通过这些文件描述符(fds)实现多路复用。因此,我们的设计可支持 2×n 个多路复用节点。

1
2
3
4
   fd1  fd2    fd3  fd4          fdi  fdi+1
-----\__/--------\__/---........---\__/---
| ETH0 | | ETH1 | | ETHn |
+------+ +------+ +------+

多个 VRRP 实例不单独占用套接字,而是共享一组 fds 资源,通过操作系统的 I/O 多路复用技术(如 Linux 的epoll、BSD 的kqueue)实现对多个实例通信需求的并发处理,最终达成 “用少量 fds 支持大量实例” 的高效设计目标。

vrrp_create_sockpool

为 VRRP instances 创建 sockets 结构

1
2
3
4
5
vrrp_create_sockpool(vrrp_data->vrrp_socket_pool)
for(e : vrrp_data->vrrp_socket_pool) {
alloc_sock
MALLOC
}

vrrp_open_sockpool

开启物理 sockets

1
2
3
4
5
6
7
8
vrrp_open_sockpool(vrrp_data->vrrp_socket_pool)
for(e : vrrp_data->vrrp_socket_pool) {
sock_obj = ELEMENT_DATA(e);
sock_obj->fd_in = open_vrrp_socket();
socket(AF_INET, SOCK_RAW, proto);
sock_obj->fd_out = open_vrrp_send_socket();
socket(AF_INET, SOCK_RAW, proto);
}

vrrp_set_fds

为 VRRP instances 分配合适的 sockets

1
2
3
4
5
vrrp_set_fds(vrrp_data->vrrp_socket_pool)
for(e : vrrp_data->vrrp_socket_pool) {
vrrp->fd_in = sock_obj->fd_in;
vrrp->fd_out = sock_obj->fd_out;
}

通告处理

VRRP 调度器处理传入的 VRRP 通告,并根据实例的当前状态对其进行处理:

代码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
vrrp_read_dispatcher_thread
vrrp_dispatcher_read
/* read & affect received buffer */
read(fd, vrrp_buffer, VRRP_PACKET_TEMP_LEN);

/* Searching for matching instance */
vrrp = vrrp_index_lookup(hd->vrid, fd);

/* Run the FSM handler */
prev_state = vrrp->state;
VRRP_FSM_READ(vrrp, vrrp_buffer, len);
/* MASTER */
vrrp_leave_master
vrrp_state_master_rx
/* Process the incoming packet */
ret = vrrp_check_packet(vrrp, buf, buflen);

if (Lower priority) {
vrrp_send_adv(vrrp, vrrp->priority);
vrrp_send_gratuitous_arp(vrrp);
return 0;
}
else {
vrrp->ms_down_timer =
3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp);
vrrp->wantstate = VRRP_STATE_BACK;
vrrp->state = VRRP_STATE_BACK;
return 1;
}
/* BACKUP */
vrrp_backup
vrrp_state_backup
if(Higher priority)
vrrp->ms_down_timer =
3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp);
else
vrrp->wantstate = VRRP_STATE_GOTO_MASTER;
vrrp_send_adv(vrrp, vrrp->priority);

vrrp_send_adv

  1. VRRP 协议报文封装在 IP 报文中,发送到分配给 VRRP 的 IP 组播地址
  2. 在IP 报文头中
  • 源地址为发送报文接口的主 IP 地址
  • 目的地址为 224.0.0.18
  • TTL 必须是 255。(VRRP 路由器会丢弃 TTL 不等于 255 的 VRRP 协议报文)
  • 协议号是 112
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* send VRRP advertissement */
vrrp_send_adv
/* build VRRP packet */
vrrp_build_pkt(vrrp, prio)
/* build IP header */
vrrp_build_ip
ip->ttl = VRRP_IP_TTL;
ip->protocol =
(vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP;
ip->saddr = VRRP_PKT_SADDR(vrrp);
ip->daddr = htonl(INADDR_VRRP_GROUP);
/* checksum must be done last */
ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0);
/* build the vrrp header */
vrrp_build_vrrp(vrrp, prio, vrrp->send_buffer, vrrp->send_buffer_size)
keepalived