规则描述
定义
iif 组播数据包进入路由器的接口;
oiflist 将组播数据包发送出路由器的接口列表;
上游 RPT/SPT上朝向 RP/Source的一侧
下游 RPT/SPT上朝向 G组成员的一侧
转发流程
1) 检查报文,如果组播源在路由器直连的子网上,重置(或启动)Keepalive 计时器;
2) 检查是否可以置位STPbit。如果此时在RPT到SPT的待切换状态,此步可以判断¹SPT是否建立完成并切换到SPT;如果此时已经在SPT状态,此步会判断SPT的条件是否仍然满足;如果置位STPbit后续会基于(S,G)状态转发报文,否则基于(*,*,RP)/(*,G)状态转发;
3) RPF检查。检查数据包到达的接口iif,是否是根据TIB 状态计算出的应该到达的接口;
4) 判断如果数据包应该根据(S,G)状态转发,那么使oiflist==(S,G)状态计算出的下游接口,检查oiflist是否为空,不为空的话,重置(S,G)状态的Keepalive 计时器;
5) 判断如果数据包应该根据(*,*,RP)或(*,G)状态转发,使oiflist==(*,*,RP)或(*,G)状态计算出的下游接口,并且检查是否需要切换到SPT;
6) 最后,将iff从oifist 中移除,如果此时oiflist不为空的话,将数据包从oiflist 中发送出去
注:(S,G) SPTbit主要用于判断是基于(*,*,RP)/(*,G)状态转发还是基于 (S,G)状态转发
伪代码
On receipt of data from S to G on interface iif:
if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
set KeepaliveTimer(S,G) to Keepalive_Period
/* Note: a register state transition or UpstreamJPState(S,G)
transition may happen as a result of restarting
KeepaliveTimer, and must be dealt with here. */
}
if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
inherited_olist(S,G) != NULL ) {
set KeepaliveTimer(S,G) to Keepalive_Period
}
// 前两个if主要用来重置计时器
Update_SPTbit(S,G,iif) //判断是否可以置位SPTbit
oiflist = NULL //oiflist 置零
if( iif == RPF_interface(S) AND SPTbit(S,G) == TRUE ) {
oiflist = inherited_olist(S,G)
// 基于(S,G)state 下转发
} else if( iif == RPF_interface(RP(G)) AND SPTbit(S,G) == FALSE) {
oiflist = inherited_olist(S,G,rpt)
CheckSwitchToSpt(S,G)
//基于(*,*,RP)或(*,G)状态转发,并判断是否开始SPT切换
} else {
/* Note: RPF check failed
A transition in an Assert FSM may cause an Assert(S,G)
or Assert(*,G) message to be sent out interface iif.
此时说明RPF 检查失败 */
if ( SPTbit(S,G) == TRUE AND iif is in inherited_olist(S,G) ) {
send Assert(S,G) on iif
/* 当接口(S,G) Assert state 处于“No Info”或“I am Assert Winner”时,
如果从 inherited_olist(S,G)收到(S,G)报文,会再次发起Assert 流程,
发送Assert(S,G),重置定时器;
(S,G)处于“I am Assert Loser”的接口不会出现在inherited_olist(S,G)中 */
} else if ( SPTbit(S,G) == FALSE AND
iif is in inherited_olist(S,G,rpt) {
send Assert(*,G) on iif //同上
}
}
oiflist = oiflist (-) iif //将iif从oiflist 中移除,如果在的话
forward packet on all interfaces in oiflist
函数定义
DirectlyConnected(S) 如果组播源S 是路由器直连的某个子网,或者组播数据发自路由器本身,DirectlyConnected(S) 返回true
inherited_olist(S,G)是在(S,G) 状态下转发组播包的出接口,同时将(*,*,RP)状态,(*,G)状态,asserts 等考虑在内。详见下文描述;
inherited_olist(S,G,rpt) 是在(*,*,RP) 或 (*,G) 状态下转发组播包的出接口,同时将(S,G,rpt) prune 状态,asserts等考虑在内。详见下文描述;
Update_SPTbit 函数
Update_SPTbit(S,G,iif) {
if ( iif == RPF_interface(S) //从SPT的上游邻居收到组播报文
AND JoinDesired(S,G) == TRUE
AND ( DirectlyConnected(S) == TRUE
OR RPF_interface(S) != RPF_interface(RP(G)) //RPT与SPT的上游接口不同
OR inherited_olist(S,G,rpt) == NULL //RPT上没有任何组成员
OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
( RPF'(S,G) != NULL ) ) /*如果RPT与SPT重合,立即根据SPT转发,但不立即Prune RPT */
OR ( I_Am_Assert_Loser(S,G,iif) ) {
Set SPTbit(S,G) to TRUE
}
}
当路由器发现收到的组播包是从SPT上发过来的,即可确定SPT已经建立完成,应基于(S,G)状态转发。注意:当收到组播报文时才会执行Update_SPTbit(S,G,iif)
RPF' 指RPT或SPT上的上游邻居,除非PIM Assert 已经推翻正常的邻居选择
neighbor RPF’(S,G) {
if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
return AssertWinner(S, G, RPF_interface(S) )
} else {
return NBR( RPF_interface(S), MRIB.next_hop( S ) )
}
inherited_olist(S,G,rpt)& inherited_olist(S,G)
inherited_olist(S,G,rpt) =
( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
(+) ( pim_include(*,G) (-) pim_exclude(S,G))
(-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
inherited_olist(S,G) =
inherited_olist(S,G,rpt) (+)
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
joins(S,G)指那些收到(S,G)join的接口集;
prunes(S,G,rpt)指那些收到(*,G)join和(S,G,rpt)prunes的接口集;
pim_include(*,G)和 pim_include(S,G)可以指出那些有希望收到组播流的本地成员的下游接口集。通常情况下只有DR会关心本地成员,但是当assert 发生时,assert winner 会接替转发流量的责任,将组播流或特定源组播流发送到那些有本地成员的接口;
lost_assert(S,G,rpt)指那些收到了(*,G)joins, 但是输掉(S,G)assert的接口集;
pim_include(*,G) =
{ all interfaces I such that:
( ( I_am_DR( I ) AND lost_assert(*,G,I) == FALSE )
OR AssertWinner(*,G,I) == me )
AND local_receiver_include(*,G,I) }
pim_include(S,G) =
{ all interfaces I such that:
( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
OR AssertWinner(S,G,I) == me )
AND local_receiver_include(S,G,I) }
pim_exclude(S,G) =
{ all interfaces I such that:
( (I_am_DR( I ) AND lost_assert(*,G,I) == FALSE )
OR AssertWinner(*,G,I) == me )
AND local_receiver_exclude(S,G,I) }
local_receiver_include(*,G,I),当IGMP/MLD或其他机制任务接口I下的本地成员希望收到发往G的所有组播流,local_receiver_include(*,G,I)返回true;
local_receiver_include(S,G,I),当IGMP/MLD或其他机制认为接口 I 下的本地成员希望收到从 S 到 G 的组播流时, local_receiver_include(S,G,I)返回true;
local_receiver_exclude(S,G,I),当local_receiver_include(*,G,I)返回true,但没有本地成员希望收到来自源S的组播流时,local_receiver_exclude(S,G,I)返回true;
CheckSwitchToSpt(S,G)
CheckSwitchToSpt(S,G)函数决定是否可以开始从RPT切换到SPT:
void
CheckSwitchToSpt(S,G) {
if ( ( pim_include(*,G) (-) pim_exclude(S,G)
(+) pim_include(S,G) != NULL )
AND SwitchToSptDesired(S,G) ) {
# Note: Restarting the KAT will result in the SPT switch
set KeepaliveTimer(S,G) to Keepalive_Period
}
}
SwitchToSptDesired(S,G)可以有不同的实现。比方说,可以设置“永不切换”策略,此时SwitchToSptDesired(S,G)永远返回false ,或者设置收到第一个组播包即返回true,也可以设定一个流量阀值,当组播流量超过阀值时返回true.