keepalived源码解析 —— vrrp_dispatcher_read_timeout

当 vrrp socket 发生 读超时时,会调用函数:vrrp_dispatcher_read_timeout()。

1、依据当前 master/back 状态,进行处理。若 back 发生读超时,则尝试将本端 vrrp 切换到 master;若 master 发生读超时,则直接发送 gratuitous ARP、vrrp 通告等。
/* Handle dispatcher read timeout */
static int
vrrp_dispatcher_read_timeout(sock_t *sock)
{
	vrrp_t *vrrp;
	int prev_state;

	set_time_now();

	rb_for_each_entry_cached(vrrp, &sock->rb_sands, rb_sands) {
		/* 判断是否超时 */
		if (vrrp->sands.tv_sec == TIMER_DISABLED ||
		    timercmp(&vrrp->sands, &time_now, >))
			break;

		prev_state = vrrp->state;

		if (vrrp->state == VRRP_STATE_BACK) {
			if (__test_bit(LOG_DETAIL_BIT, &debug))
				log_message(LOG_INFO, "(%s) Receive advertisement timeout", vrrp->iname);
			/* back 接收 master vrrp 通告超时时,尝试把本端 vrrp 切换成master */
			vrrp_goto_master(vrrp);
		}
		else if (vrrp->state == VRRP_STATE_MAST)
			/* 发送 gratuitous ARP、vrrp 通告等,参考 2.3小节 */
			vrrp_master(vrrp);

		/* handle instance synchronization */
#ifdef _TSM_DEBUG_
		if (do_tsm_debug)
			log_message(LOG_INFO, "Send [%s] TSM transition : [%d,%d] Wantstate = [%d]",
				vrrp->iname, prev_state, vrrp->state, vrrp->wantstate);
#endif
		VRRP_TSM_HANDLE(prev_state, vrrp);
	
		/* 设置 vrrp sands 并调整 rb tree,具体参看 2.2小节 */
		vrrp_init_instance_sands(vrrp);
	}

	return sock->fd_in;
}
2、back 尝试将本端 vrrp 切换到 master:
static void
vrrp_goto_master(vrrp_t * vrrp)
{
	/* handle master state transition */
	vrrp->wantstate = VRRP_STATE_MAST; /* 想要切换到 master */
	vrrp_state_goto_master(vrrp);
}
void
vrrp_state_goto_master(vrrp_t * vrrp)
{
	/* 若开启了同步组,进一步判断是否需要切换到 master */
	if (vrrp->sync && !vrrp_sync_can_goto_master(vrrp))
	{
		vrrp->wantstate = VRRP_STATE_MAST;
		return;
	}

#if defined _WITH_VRRP_AUTH_
	/* If becoming MASTER in IPSEC AH AUTH, we reset the anti-replay */
	if (vrrp->ipsecah_counter.cycle) {
		vrrp->ipsecah_counter.cycle = false;
		vrrp->ipsecah_counter.seq_number = 0;
	}
#endif

#ifdef _WITH_SNMP_RFCV3_
	vrrp->stats->master_reason = vrrp->stats->next_master_reason;
#endif

	vrrp->state = VRRP_STATE_MAST;
	
	/* 设置 vrrp sands 并调整 rb tree */
	vrrp_init_instance_sands(vrrp);
	
	/* 发送 gratuitous ARP、vrrp 通告等 */
	vrrp_state_master_tx(vrrp);
}
2.1 判断同步组是否需要切换到 master:

vrrp 同步组(syncchroization group):
假设路由有2个网段,一个内网,一个外网,每个网段都开启一个 vrrp_instance,且 vrrp 配置为检查内网:
1)若不使用同步组,那么当外网出现问题时,vrrp 会认为自己是健康的,则不会发生 master 和 back 的切换,从而导致问题;
2)若使用同步组,那么可以把内外网两个 vrrp_instance 放入同一个 vrrp_rsync_group,那么当 Group 里任何一个 vrrp_instance 出现问题时,另一个 vrrp_instance 也会发生切换(即使这个instance没有发生故障)。

/* Check transition to master state */
bool
vrrp_sync_can_goto_master(vrrp_t * vrrp)
{
	vrrp_t *isync;
	vrrp_sgroup_t *vgroup = vrrp->sync;
	element e;
	
	/* 同步组的状态为 master */
	if (GROUP_STATE(vgroup) == VRRP_STATE_MAST)
		return true;
	
	/* Only sync to master if everyone wants to
	 * i.e. prefer backup state to avoid thrashing */
	 /* 判断同步组下的其他 vrrp_instance 是否想要切换到 master */
	LIST_FOREACH(vgroup->vrrp_instances, isync, e) {
		if (isync != vrrp && isync->wantstate != VRRP_STATE_MAST) {
			/* Make sure we give time for other instances to be
			 * ready to become master. The timer here doesn't
			 * really matter, since we are waiting for other
			 * instances to be ready. */
			vrrp->ms_down_timer = 3 * vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp);
			vrrp_init_instance_sands(vrrp);
			return false;
		}
	}
	return true;
}
2.2 设置 vrrp sands 并调整 rb tree:
/* Compute the new instance sands */
void
vrrp_init_instance_sands(vrrp_t * vrrp)
{
	set_time_now();
	
	if (vrrp->state == VRRP_STATE_MAST) {
		if (vrrp->reload_master)
			/* 设置为当前时间 */
			vrrp->sands = time_now;
		else
			/* 当前时间 + vrrp 发送通告报文的时间间隔 */
			vrrp->sands = timer_add_long(time_now, vrrp->adver_int);
	}
	else if (vrrp->state == VRRP_STATE_BACK) {
		/*
		 * When in the BACKUP state the expiry timer should be updated to
		 * time_now plus the Master Down Timer, when a non-preemptable packet is
		 * received.
		 */
		/*
		当前时间 + master 超时时间
		master 超时时间:vrrp->ms_down_timer = 3 * vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp),
		若此计时器超时,则 back 就会宣布 master 死亡。
		
		master_adver_int:master 发送 vrrp 通告报文的时间间隔
		*/
		vrrp->sands = timer_add_long(time_now, vrrp->ms_down_timer);
	}
	else if (vrrp->state == VRRP_STATE_FAULT || vrrp->state == VRRP_STATE_INIT)
		vrrp->sands.tv_sec = TIMER_DISABLED; /* TIMER_DISABLED = LONG_MIN */
	
	/* Move node to new position in tree */
	rb_move_cached(&vrrp->sockets->rb_sands, vrrp, rb_sands, vrrp_timer_cmp);
}
2.3 发送 gratuitous ARP、vrrp 通告:
/* MASTER state processing */
void
vrrp_state_master_tx(vrrp_t * vrrp)
{
	/* 若未设置 vip */
	if (!VRRP_VIP_ISSET(vrrp)) {
		log_message(LOG_INFO, "(%s) Entering MASTER STATE"
				    , vrrp->iname);
		
		/* 添加 Virtual IP addresses / virtual routes / virtual rules */
		vrrp_state_become_master(vrrp);
		/*
		 * If we catch the master transition
		 * register a gratuitous arp thread delayed to garp_delay secs.
		 */
		
		/*
		vrrp_gratuitous_arp_thread() :延迟广播 gratuitous ARP
		
		garp_delay:发送 gratuitous ARP/NA 的时间间隔。
		
		gratuitous ARP 也称为免费 ARP,无故 ARP。
		gratuitous ARP 不同于一般的 ARP 请求,它并非期待得到 ip 对应的 mac 地址,
		而是当主机启动时,将发送一个 gratuitous arp 请求,即请求自己的 ip 地址的 mac 地址。	
		*/
		if (vrrp->garp_delay)
			thread_add_timer(master, vrrp_gratuitous_arp_thread,
					 vrrp, vrrp->garp_delay);
	} else if (timerisset(&vrrp->garp_refresh) &&
		   timercmp(&time_now, &vrrp->garp_refresh_timer, >)) {
		/* 直接广播 gratuitous ARP */
		vrrp_send_link_update(vrrp, vrrp->garp_refresh_rep);
		vrrp->garp_refresh_timer = timer_add_now(vrrp->garp_refresh);
	}
	
	/* 向 ipv4 组播、ipv6 组播或点播地址发送 vrrp 通告 */
	vrrp_send_adv(vrrp, vrrp->effective_priority);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值