接口状态检测

定时轮询

  • 通过定时轮询,每 POLLING_DELAY 秒刷新一次接口状态(通过 if_ioctl_flags)。
1
thread_add_timer(master, if_linkbeat_refresh_thread, ifp, POLLING_DELAY);

获取接口标志

  • 使用 ioctl SIOCGIFFLAGS 系统调用获取接口标志。
1
2
3
4
5
6
7
8
9
10
void
if_ioctl_flags(interface_t * ifp)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifp->ifname, sizeof(ifr.ifr_name));
ioctl(fd, SIOCGIFFLAGS, &ifr); // 获取接口标志
ifp->flags = ifr.ifr_flags; // 存储到结构体
close(fd);
}

链路检测

1
2
3
4
5
6
7
8
9
int
if_linkbeat(const interface_t * ifp)
{
if (!global_data->linkbeat_use_polling)
return 1;
if (IF_MII_SUPPORTED(ifp) || IF_ETHTOOL_SUPPORTED(ifp))
return IF_LINKBEAT(ifp); // MII 或 Ethtool 链路检测
return 1; // 默认认为链路正常
}

IF_ISUP

1
2
3
#define IF_ISUP(X) (((X)->flags & IFF_UP)      && \
((X)->flags & IFF_RUNNING) && \
if_linkbeat(X))

接口被认为 UP 需要同时满足:

  • IFF_UP - 接口已启用
  • IFF_RUNNING - 接口正在运行(已分配资源)
  • if_linkbeat() - 链路检测通过

VRRP_ISUP

1
2
#define VRRP_ISUP(V)   ((IF_ISUP((V)->ifp) || (V)->dont_track_primary) & \
((!LIST_ISEMPTY((V)->track_ifp)) ? TRACK_ISUP((V)->track_ifp) : 1))
  • 如果配置了 dont_track_primary,则不跟踪主接口状态
  • 还需要考虑跟踪脚本的权重

接口 down

MASTER 状态

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
vrrp_master()

├─ 检测接口状态: if (!VRRP_ISUP(vrrp))
│ └─ 设置: vrrp->wantstate = VRRP_STATE_GOTO_FAULT

└─ vrrp_state_leave_master(vrrp)

├─ 发送停止优先级广告: vrrp_send_adv(vrrp, VRRP_PRIO_STOP = 0)
│ (通知其他路由器立即进行 MASTER 选举)

├─ 删除虚拟路由: vrrp_handle_iproutes(, IPROUTE_DEL)

├─ 删除虚拟 IP: vrrp_handle_ipaddress(, IPADDRESS_DEL)
│ - VIP (vip)
│ - 扩展 VIP (evip)

├─ 更新状态: vrrp->state = VRRP_STATE_FAULT

├─ 执行通知脚本: notify_instance_exec(vrrp, VRRP_STATE_FAULT)
│ - notify_backup 脚本
│ - notify_fault 脚本
│ - 通用 notify 脚本

├─ 发送 SNMP Trap (如果启用)

└─ 设置 down_timer: ms_down_timer = 3 * adver_int + skew

BACKUP 状态

BACKUP 状态下接口 down 不会立即触发 FAULT,而是:

  1. 检测到接口 down 后,在 vrrp_backup() 中会忽略接收到的报文
  2. 如果 wantstate 是 MASTER,会尝试转换为 MASTER 但会被阻止
  3. 主要在 vrrp_leave_master 和 vrrp_goto_master 中处理
keepalived