功能:
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;
}