Subject: [pim] some questions/comments to PIM-SM draft-10

From: "edrt"<[email-masked]>
Date: Fri, 3 Sep 2004 23:15:46 +0800

Dear All,

I have some questions/comments to PIM-SM draft-10. Hope someone 
could help to clarify them. 

Thanks
Yi Zhao

-----------
1. 
L1719
	"
	if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) )
	"

Why not change it to 
	if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) AND I_am_DR( iif ))

At non-DR router, keep KAT(S,G) running can only provide informative
information about what multicast sources exist on the directly connected network,
is that what the spec wants to achieve?

-----------
2.
L1817
	"
       if ( ( pim_include(*,G) (-) pim_exclude(S,G)
              (+) pim_include(S,G) != NULL )
            AND SwitchToSptDesired(S,G) ) {
    "
    
Why not change it to 
       if ( ( pim_include(*,G) (-) pim_exclude(S,G) != NULL )
            AND SwitchToSptDesired(S,G) ) {

It seems that this piece of pseudo-code's intention is to trigger LHR
to do SPT switchover, so "(+) pim_include(S,G)" could be ommited.
Because if we have local SSM receiver, we already join SPT. Am I missing
something?

-----------
3.
L1734
	"
	  oiflist = inherited_olist(S,G)
	  if( oiflist != NULL ) {
	       set KeepaliveTimer(S,G) to Keepalive_Period
	  }
	"

Why not change it to 
	oiflist = inherited_olist(S,G)
	if( inherited_olist(S,G,rpt) != NULL ) {
	    set KeepaliveTimer(S,G) to Keepalive_Period
	}

In other words, what's the purpose to keep KAT(S,G) running on SPT
branch nodes which don't have downstream RPT state?

-----------
4. 
L5219
	"
	...send a Prune(S,G,rpt) to RPF'(S,G,rpt)...
	"

Why triggered P(S,G,rpt) is sent to RPF'(S,G,rpt) instead of
RPF'(*,G)? 

If we examine the PruneDesired(S,G,rpt) macro, we can determine
that there are two cases that trigger the router to send P(S,G,rpt) 

* the downstream ASM receivers don't need (S,G) traffic 
   any more. In this case, the desired behaviour is to send
   P(S,G,rpt) to RPF'(S,G,rpt)

* the downstream ASM receivers do need (S,G) traffic, 
   but the (S,G) traffic can be obtained from another
   upstream SPT branch. In this case, the desired behaviour 
   is to send P(S,G,rpt) to RPF'(*,G) (instead of RPF'(S,G,rpt))

Is it make sense if we differentiate the two scenarioes when
sending P(S,G,rpt)?

-----------
5.
L1731
	"
	 if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined ) { 
	"

Why not make sure the packet really coming from SPT, in other words
why not change the pseudo-code to

	if( iif == RPF_interface(S) 
	    AND UpstreamJPState(S,G) == Joined 
	    AND SPTbit(S,G) == TRUE) {

-----------
6.
L1736
	"
	 } else if( iif == RPF_interface(RP(G)) AND SPTbit(S,G) == FALSE) {
	   oiflist = inherited_olist(S,G,rpt)
	"

Why not make sure if we really have joined the RPT? 

-----------
7.
L6098
	"
	    OR (local_receiver_include(*,G,I)==TRUE
	        AND (I_am_DR(I) OR AssertWinner(*,G,I) == me))
	"

Interface satisfies 
"local_receiver_include(*,G,I)==TRUE AND AssertWinner(*,G,I) == me" condition 

must also satisfies
"CouldAssert(*,G,I)", isn't it ?

-----------
8.
L1743
	"
	 } else if ( SPTbit(S,G) == FALSE AND
	                iif is in inherited_olist(S,G,rpt) {
	"

"SPTbit(S,G) == FALSE" check could be omitted, due to the condition branch 
context here, isn't it ?

_______________________________________________
pim mailing list
[email-masked]
https://www1.ietf.org/mailman/listinfo/pim

---

From: Mark Doll <[email-masked]>
Date: Tue, 07 Sep 2004 17:19:49 +0200

Hi Yi,

I think you're wrong in no. 1 and 5-8 and you're right in no. 2-4. But 
although your are right sometimes, all your proposals make no difference 
regarding the correct operation of PIM-SM. Nevertheless, at least no. 4 
should be clarified/corrected in the draft. At least if the IETF not 
only claims to produce high quality standards but also wants to act 
accordingly...

See my notes included below.

Regards, Mark.

> is that what the spec wants to achieve?

Adding "AND I_am_DR(iif)" breaks things. Imagine the following setup 
where two routers X and DR and the sender S are connected to the same LAN:

R -- X --+-- DR -- RP
          |
          S

Receiver R joins (*,G), so router X periodically sends join(*,G) towards 
RP. S multicasts packets, which DR register tunnels to RP, which in turn 
decapsulates them and send them down the shared tree back to DR. To 
prevent receiving duplicate data packets, X has to send a periodic 
prune(S,G,rpt). For this to happen, SPTbit must be true, requiring 
JoinDesired(S,G) to be true which in turn requires a running 
KeepaliveTimer(S,G) on X which is not the DR on the LAN.

> Because if we have local SSM receiver, we already join SPT. Am I missing

Omitting "(+) pim_include(S,G)" seems reasonable to me, too. But of 
course it does not hurt to include it.

> branch nodes which don't have downstream RPT state?

This makes no sense to me, too. Since the only place where the 
KeepaliveTimer is evaluated (besides when handling the register tunnel), 
is the JoinDesired(S,G) macro:

   bool JoinDesired(S,G) {
       return( immediate_olist(S,G) != NULL
               OR ( KeepaliveTimer(S,G) is running
                    AND inherited_olist(S,G) != NULL ) )
   }

Only if immediate_olist(S,G) == NULL the KeepaliveTimer is relevant. But
since inherited_olist(S,G) == inherited_olist(S,G,rpt) (+) 
immediate_olist(S,G) == inherited_olist(S,G,rpt) (+) NULL, the 
KeepaliveTimer is also only relevant if inherited_olist(S,G,rpt) is not 
null. So you are right. But of course it won't break anything to do it 
according to the current specification.

> Is it make sense if we differentiate the two scenarioes when

Yes, this seems reasonalbe to me. I think you are talking about the case 
  RPF_interface(S) == RPF_interface(RP(G)), right? But in this case 
regardless if you sent the Prune(S,G,rpt) to the wrong upstream 
neighbor, it makes no difference anyway. The SPT upstream neighbor will 
ignore it since it has no Join(*,G) state on that interface and the RPT 
upstream neighbor will not send (S,G) data anyway since it has lost the 
Assert(S,G) which will happen in this case (cf. also no. 5).

> 	    AND SPTbit(S,G) == TRUE) {

Your proposal would interrupt data forwarding during the transition from 
RPT to SPT.

First of all: If you add "AND SPTbit(S,G) == TRUE", then "AND 
UpstreamJPState(S,G) == Joined" can be omitted, since SPTbit depends on 
JoinDesired. And when JoinDesired becomes true the UpstreamJPstate 
transitions to Joined.

So let's compare the draft's version

if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined )

with your (simplified) proposal

if( iif == RPF_interface(S) AND SPTbit(S,G) == TRUE ).

In most cases they are identical, since Update_SPTbit(S,G,iif) is 
executed before this if clause, thus SPTbit will be set if 
UpstreamJPState(S,G) == Joined. However there is one exception: When 
RPF_interface(S) == RPF_interface(*,G) and RPF'(S,G) != RPF'(*,G))
Update_SBTbit(S,G,iif) depends on an upstream Assert(S,G) to occur.

The problem with your proposal lies exactly herein. Until the 
Assert(S,G) occurs, all data packets from S to G are only forwarded on 
downstream interfaces in inherited_olist(S,G,rpt). But there might be 
downstream interfaces with source-specific state only. Those interfaces 
are only included in immediate_olist(S,G) (included in 
inherited_olist(S,G)). So this is the correct list that should be used 
for forwarding purposes.

> Why not make sure if we really have joined the RPT? 

See my explanation to no. 5.

> "CouldAssert(*,G,I)", isn't it ?

No, local_receiver_include does not consider joins (included in
CouldAssert).

> "SPTbit(S,G) == FALSE" check could be omitted, due to the condition branch 

No, the condition two lines above might fail because of iif not beeing 
in inherited_olist(S,G) and not because of SPTbit(S,G) beeing false. 
Thus SPTbit(S,G) might be TRUE which must be checked.

---

From: "edrt" <[email-masked]>
Date: Wed, 8 Sep 2004 10:46:57 +0800

Hi Mark,

Really appreciate your analysis of all these comments.

Yes, most of them are trivial, I just want to make sure
I don't overlook something (and the spec don't overlook
something :)

I agree with you on comment no. 1-4. I really overlooked
some point on comment no. 1. Modification according to 
comment no. 2-4 will make the spec looks more accurate and
straightforward on those definitions. 

See my notes on comment no. 5-8.

Thanks
Yi Zhao

>inherited_olist(S,G)). So this is the correct list that should be used 

I agree on the analysis of the scenario you described above, I have
also thought of this. But wouldn't it be resonable if we DO let the
router wait until the assert happens and then forwarding data also to 
source-specific state only downstreams?

Because problems may happen if we forwarding data coming from RPT to 
source-specific state only downstreams when upstream SPT branch is 
actually not setup yet, right? If so, when RPF_interface(RP(G)) == RPF_interface(S),
this scenario may happen.

>See my explanation to no. 5.

If we have not joined the RPT, we will also compute the olist, 
but the olist will be null. So I guess we not checking upstream
RPT join state in this piece pseudo-code only for simplification reason.
Am I missing something?

>No, local_receiver_include does not consider joins (included in

What I mean is "OR AssertWinner(*,G,I) == me" check could be 
removed in the above pseudo-code, because this interface set 
will be included by  
	"
	    CouldAssert(*,G,I)
	"
Right?

>Thus SPTbit(S,G) might be TRUE which must be checked.

If iif is not in inherited_olist(S,G), it will also not in 
inherited_olist(S,G,rpt), so we don't need to check on SPTbit(S,G)
then. Anyway, it is trivial problem, I just want to make sure I don't
overlook some point :)

---

From: Mark Doll <[email-masked]>
Date: Wed, 08 Sep 2004 17:11:47 +0200

Hi Yi!

> I don't overlook something (and the spec don't overlook

Whish it would be so clear to me as is seems to be for you. :-)

> straightforward on those definitions. 

And this IMHO would be very helpful for people to understand the spec.
At least it would be for me. PIM-SM is by far the most complex spec that
I've studied so far, so it would really help if redundant pseudo code is
eliminated as far as possible.

I wish one could use PIM-SM for SSM only and BIDIR-PIM for ASM.
BIDIR-PIM is so elegant and admirably simple compared to PIM-SM...  ;-)

> See my notes on comment no. 5-8.

Yes you are right here, too, but see my comments inline.

- Mark.

> this scenario may happen.

I was wrong. In what I had in mind yesterday there would be _no_
interruption in data packet forwarding at all, because that
source-specific only downstream interface never had  (*,G) state before,
so it never had received any packet from the shared tree before.

But there would be an interruption, if it had (*,G) before. So asume a
receiver who is in (*,G) but then decides, that he is only interested in
one of the senders. The router's interface the sender is on, transitions
from beeing in pim_include(*,G) not any more and to beeing in
pim_include(S,G) at almost the same. In this case, data traffic would be
interrupted until the SPT tree has been completely build up. Of course a
workaround would be to keep the interface in pim_include(*,G) for a
while until the SPT has been build up, but that's a hack.

So the question is: Is this interruption really the behavoir a receiver
expects? Yes, if we interpret a receiver joining (S,G) as "I want to
receive traffic from S to G _only_ via a SPT". No, if we interpret it as
"I don't care how traffic travels through the internet, just make sure
that I only receive traffic to G sent by S". I think the latter
interpretation fits better.

> RPT join state in this piece pseudo-code only for simplification reason.

I think, too, this is the reason for omitting the "have I really joined
RPT" check. Would be cumbersome to check this here.

> removed in the above pseudo-code, because this interface set 

Yes. Although there's a syntactic difference, since

local_receiver_include(*,G,I) == TRUE AND AssertWinner(*,G,I) == me

misses to check RPF_Interface(RP(G)) != I which is included in
CouldAssert(*,G,I). But since we can't win an assert if
RPF_Interface(RP(G)) == I, your simplification is valid.

> then. Anyway, it is trivial problem, I just want to make sure I don't

Yes, of course, simply overlooked this.

---

From: "edrt"<[email-masked]>
Date: Thu, 9 Sep 2004 23:12:04 +0800

Hi Mark,

I think we reach to agreement on most of the comments,
but I want to talk more on comment 5.

See my notes below.

Thanks
Yi Zhao

>interpretation fits better.

Yes, from the user's point of view, we don't care how data is travelling 
in the internet. so long as the data are delivered to us as soon as 
possible and as fast as possible.

But from protocol design's point of view, borrow data from upstream RPT
branch and forward them to downstream SPT branch, while upstream SPT branch
is actually not setup yet, can casue problem. 

Below is a example illustrates how it will interrupt data being delivered to
downstream RPT branch. (P.S. the example below is borrowed from a discussion
with Pavlin sometime ago)

	S
	|
	Xn
	|
   ...
	|
	|
	X3  RP
	 \  |
	  \ |
	   \|
	    R1
	    | \
	    R2 \
	    |   X2
	    R3 /
	    | /
	    R4
	   /|
	  / R5
	 X1 |
	 |  |
	rx1 rx2

If
* RPT path to rx2 is RP->R1->R2->R3->R4->R5->rx2
* SPT path to rx1 is Xn->...X3->R1->X2->R4->X1->rx1

Say, the below event sequence happens 
* rx2 joins(*,G)
* RPT path to rx2 is setup
* traffic are forwarded from RP to rx2

and then
* rx1 joins(S,G)
* join(S,G) travels through R1 and on its way to Xn
* R1 receives traffic from RP meanwhile, then forward them to R2, and X2!
* R4 receive traffic from X2, and send Prune(S,G,rpt) to RP!

If
* RP receives Prune(S,G,rpt) at t1
* Xn receives Join(S,G) at t2
* t1 < t2
During the time window (t1->t2), traffic to rx2 will be interrupted until
the SPT branch to rx2 is setup.

So, borrow data from upstream RPT branch and forward them to downstream
SPT branch, while upstream SPT branch is actually not setup yet, really 
do harm. That why I ask if the fowarding pseudo-code could be changed 
from 
	 if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined ) { 
	 
to something like
     if( iif == RPF_interface(S) AND SPTbit(S,G) == TRUE ) { 

Am I missing something?

---

From: Mark Doll <[email-masked]>
Date: Fri, 17 Sep 2004 18:24:50 +0200

Hi Yi!

Sorry, that it take so long for me to answer this.

>      if( iif == RPF_interface(S) AND SPTbit(S,G) == TRUE ) { 

Yes, I also prefer waiting until the SPT has been completely set up. I 
simply hasitated to agree with you, because changing something that's 
not broken is unnecessary. Thanks that you showed us that the current 
spec indeed seems to be broken!

One last comment on my argument against your proposal. I my last mail I 
wrote, when a receiver joins (S,G) and leaves (*,G) simultanously, data 
traffic might be interrupted in your proposed modification and I 
claimed, that this might not be the behavior a receivers expects.

If SPT and RPT are completely disjoint (also requiring that the router 
responsible for (S,G) local receivers differs from the router that's 
responsible for (*,G) receivers), this join/leave combination will cause 
  an interruption in any case, also with the current spec. So this 
argument should not be a showstopper at all.

Regards,

- Mark.

P.S.: Just to sum up our discussion here and to make shure that I 
haven't misunderstood you, you propose something like the following as 
the new data forwarding rule:

if ( DirectlyConnected(S) == TRUE )
         set KeepaliveTimer(S,G) to Keepalive_Period
}
Update_SPTbit(S,G,iif)
oiflist = inherited_olist(S,G,rpt)
if ( SPTbit(S,G) == TRUE ) {
         # Note: start KAT only on nodes that have downstream RPT state
         if ( oiflist != NULL ) {
                 set KeepaliveTimer(S,G) to KeepalivePeriod
         }
         # Note: set oiflist to inherited_olist(S,G)
         oiflist = oiflist (+) immediate_olist(S,G)
         if ( iif == RPF_interface(S) ) {
                 forward packet to oiflist (-) iif
         } else if ( iif in oiflist ) {
                 send Assert(S,G) on iif
         }
} else {
         if ( iif == RPF_interface(RP(G) ) {
                 # Note: Checking KAT is optional
                 if ( KeepaliveTimer is not running ) {
                         CheckSwitchToSpt(S,G)
                 }
                 forward packet to oiflist (-) iif
         } else if ( iif in oiflist ) {
                 send Assert(*,G) on iif
         }
}

void
Update_SPTbit(S,G,iif) {
         # unchanged
}

void
CheckSwitchToSpt(S,G) {
         if ( pim_include(*,G) (-) pim_exclude(S,G) != NULL
              AND SwitchToSptDesired(S,G) == TRUE ) {
                 set KeepaliveTimer(S,G) to KeepalivePeriod
}

---

From: Mark Doll <[email-masked]>
Date: Sat, 18 Sep 2004 14:59:33 +0200

>>        set KeepaliveTimer(S,G) to Keepalive_Period
[...]
> 
> 
> Yes Mark, except line 1-3 should remain as
> "
>  if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {        

ACK. Sorry for my inattention.

- Mark.