keepalived源碼解析 —— vrrp_state_master_rx()

功能:
1、分析收到的 vrrp 包。若包分析返回結果:VRRP_PACKET_OK,則直接返回 false;
2、若滿足以下任一條件:
1)master 收到一個 priority = 0 的通告;
2)配置文件指定了:higher_prio_send_advert(若 master 收到一個更高優先級的 vrrp,則自身在變成 backup 之前發送一個通告)
且 對端 vrrp 的優先級 > 本端 vrrp 的優先級,或者 兩者的優先級相同但ip相同;
則:
發送 vrrp 通告;
3、若對端與本端 vrrp 優先級均爲 VRRP_PRIO_OWNER(255),則本端 vrrp 優先級-1;
4、若滿足以下任一條件:
1)對端 vrrp 的優先級 < 本端 vrrp 的優先級;
2)兩端優先級相同,所使用的的 ip 不同;
則:
1)發送 vrrp 通告(若配置項未指定 lower_prio_no_advert,即:如果收到低優先級的通告,不發送任何通告);
2)發送 ARP 數據包,更新遠端 ARP 緩衝區;
5、若對端 vrrp 優先級 > 本端 vrrp 優先級 或者兩者優先級相同但地址不同,
則:本端 vrrp 切換爲 master

返回值 True:離開 master 狀態,false:保留 master 狀態

bool
vrrp_state_master_rx(vrrp_t * vrrp, const vrrphdr_t *hd, const char *buf, ssize_t buflen)
{
	ssize_t ret;
#ifdef _WITH_VRRP_AUTH_
	const ipsec_ah_t *ah;
#endif
	unsigned master_adver_int;
	int addr_cmp;
	vrrp_t *gvrrp;
	element e;

// TODO - could we get here with wantstate == FAULT and STATE != FAULT?
	/* return on link failure */
// TODO - not needed???
	/* vrrp 出現故障 */
	if (vrrp->wantstate == VRRP_STATE_FAULT) {
		vrrp->master_adver_int = vrrp->adver_int;
		vrrp->ms_down_timer = 3 * vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp);
		vrrp->state = VRRP_STATE_FAULT;
		send_instance_notifies(vrrp);
		vrrp->last_transition = timer_now();
		return true;
	}

	/* 分析收到的數據包 */
	ret = vrrp_check_packet(vrrp, hd, buf, buflen, true);
	
	/* 包無效,直接返回 */
	if (ret != VRRP_PACKET_OK)
		return false;
	
	/* 比較數據包地址 與 vrrp 地址*/
	addr_cmp = vrrp_saddr_cmp(&vrrp->pkt_saddr, vrrp);
	
	/*
	若滿足以下任一條件:
	1、master 收到一個 priority = 0 的通告;
	2、配置文件指定了:higher_prio_send_advert(若 master 收到一個更高優先級的 vrrp,則自身在變成 backup 之前發送一個通告)
	且 對端 vrrp 的優先級 > 本端 vrrp 的優先級,或者 兩者的優先級相同但ip相同;
	則:
	發送 vrrp 通告	
	*/
	if (hd->priority == 0 ||
	    (vrrp->higher_prio_send_advert &&
	     (hd->priority > vrrp->effective_priority ||
	      (hd->priority == vrrp->effective_priority && addr_cmp > 0)))) {
		log_message(LOG_INFO, "(%s) Master received priority 0 or lower priority advert", vrrp->iname);
		vrrp_send_adv(vrrp, vrrp->effective_priority);/* 發送 vrrp 通告 */

		if (hd->priority == 0)
			return false;
	}
	
	/* 若對端與本端 vrrp 優先級均爲 VRRP_PRIO_OWNER(255),則本端 vrrp 優先級-1 */
	if (hd->priority == vrrp->effective_priority) {
		if (addr_cmp == 0)
			/* 對端使用與本端相同的ip發送優先級一樣的 vrrp */
			log_message(LOG_INFO, "(%s) WARNING - equal priority advert received from remote host with our IP address.", vrrp->iname);
		else if (vrrp->effective_priority == VRRP_PRIO_OWNER) {
			/* If we are configured as the address owner (priority == 255), and we receive an advertisement
			 * from another system indicating it is also the address owner, then there is a clear conflict.
			 * Report a configuration error, and drop our priority as a workaround. */
			log_message(LOG_INFO, "(%s) CONFIGURATION ERROR: local instance and a remote instance are both configured as address owner, please fix - reducing local priority", vrrp->iname);
			vrrp->effective_priority = VRRP_PRIO_OWNER - 1;
			vrrp->base_priority = VRRP_PRIO_OWNER - 1;
		}
	}
	
	/*
	若滿足以下任一條件:
	1、對端 vrrp 的優先級 < 本端 vrrp 的優先級;
	2、兩端優先級相同,所使用的的 ip 不同;
	則:
	1)發送 vrrp 通告(若配置項未指定 lower_prio_no_advert,即:如果收到低優先級的通告,不發送任何通告);
	2)發送 ARP 數據包,更新遠端 ARP 緩衝區;
	*/
	if (hd->priority < vrrp->effective_priority ||
	    (hd->priority == vrrp->effective_priority &&
	     addr_cmp < 0)) {
		/* We receive a lower prio adv we just refresh remote ARP cache */
		log_message(LOG_INFO, "(%s) Received advert from %s with lower priority %d, ours %d%s",
					vrrp->iname,
					inet_sockaddrtos(&vrrp->pkt_saddr),
					hd->priority,
					vrrp->effective_priority,
					!vrrp->lower_prio_no_advert ? ", forcing new election" : "");
#ifdef _WITH_VRRP_AUTH_
		if (vrrp->auth_type == VRRP_AUTH_AH) {
			ah = (const ipsec_ah_t *) (buf + sizeof(struct iphdr));
			log_message(LOG_INFO, "(%s) IPSEC-AH : Syncing seq_num"
					      " - Increment seq"
					    , vrrp->iname);
// TODO - why is seq_number taken from lower priority advert?
			vrrp->ipsecah_counter.seq_number = ntohl(ah->seq_number) + 1;
			vrrp->ipsecah_counter.cycle = false;
		}
#endif
		/*
		若配置項未指定 lower_prio_no_advert(如果收到低優先級的通告,不發送任何通告),
		則發送 vrrp 通告
		*/
		if (!vrrp->lower_prio_no_advert)
			vrrp_send_adv(vrrp, vrrp->effective_priority);
		
		/*
		若指定了 garp_lower_prio_rep(當 master 接收到一個較低優先級的 vrrp 後,一次發送 gratuitous apr 的數量組),
		則發送 ARP 數據包,更新遠端 ARP 緩衝區
		*/
		if (vrrp->garp_lower_prio_rep) {
			vrrp_send_link_update(vrrp, vrrp->garp_lower_prio_rep);
			if (vrrp->garp_lower_prio_delay)
				thread_add_timer(master, vrrp_lower_prio_gratuitous_arp_thread,
						 vrrp, vrrp->garp_lower_prio_delay);

			/* If we are a member of a sync group, send GARP messages
			 * for any other member of the group that has
			 * garp_lower_prio_rep set */
			if (vrrp->sync) {
				LIST_FOREACH(vrrp->sync->vrrp_instances, gvrrp, e) {
					if (gvrrp == vrrp)
						continue;
					if (!gvrrp->garp_lower_prio_rep)
						continue;

					vrrp_send_link_update(gvrrp, gvrrp->garp_lower_prio_rep);
					if (gvrrp->garp_lower_prio_delay)
						thread_add_timer(master, vrrp_lower_prio_gratuitous_arp_thread,
								 gvrrp, gvrrp->garp_lower_prio_delay);
				}
			}
		}

		/* If a lower priority router has transitioned to master, there has presumably
		 * been an intermittent communications break between the master and backup. It
		 * appears that servers in an Amazon AWS environment can experience this.
		 * The problem then occurs if a notify_master script is executed on the backup
		 * that has just transitioned to master and the script executes something like
		 * a `aws ec2 assign-private-ip-addresses` command, thereby removing the address
		 * from the 'proper' master. Executing notify_master_rx_lower_pri notification
		 * allows the 'proper' master to recover the secondary addresses. */
		send_event_notify(vrrp, VRRP_EVENT_MASTER_RX_LOWER_PRI);

		return false;
	}
	
	/*
	若對端 vrrp 優先級 > 本端 vrrp 優先級 或者兩者優先級相同但地址不同,
	則:本端 vrrp 切換爲 master
	*/
	if (hd->priority > vrrp->effective_priority ||
	    (hd->priority == vrrp->effective_priority && addr_cmp > 0)) {
		if (hd->priority > vrrp->effective_priority)
			log_message(LOG_INFO, "(%s) Master received advert from %s with higher priority %d, ours %d",
						vrrp->iname,
						inet_sockaddrtos(&vrrp->pkt_saddr),
						hd->priority,
						vrrp->effective_priority);
		else
			log_message(LOG_INFO, "(%s) Master received advert from %s with same priority %d but higher IP address than ours",
						vrrp->iname,
						inet_sockaddrtos(&vrrp->pkt_saddr),
						hd->priority);
#ifdef _WITH_VRRP_AUTH_
		if (vrrp->auth_type == VRRP_AUTH_AH)
			vrrp->ipsecah_counter.cycle = false;
#endif

		if (vrrp->version == VRRP_VERSION_3) {
			master_adver_int = (ntohs(hd->v3.adver_int) & 0x0FFF) * TIMER_CENTI_HZ;
			/* As per RFC5798, set Master_Adver_Interval to Adver Interval contained
			 * in the ADVERTISEMENT
			 */
			if (vrrp->master_adver_int != master_adver_int) {
				log_message(LOG_INFO, "(%s) advertisement interval updated from %u to %u milli-sec from higher priority master",
						vrrp->iname, vrrp->master_adver_int / (TIMER_HZ / 1000), master_adver_int / (TIMER_HZ / 1000));
				vrrp->master_adver_int = master_adver_int;
			}
		}
		vrrp->ms_down_timer = 3 * vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp);
		vrrp->master_priority = hd->priority;
		vrrp->wantstate = VRRP_STATE_BACK;
		vrrp->state = VRRP_STATE_BACK;
		return true;
	}

	return false;
}
發佈了69 篇原創文章 · 獲贊 20 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章