1 /*
2   PIM for Quagga
3   Copyright (C) 2008  Everton da Silva Marques
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; see the file COPYING; if not, write to the
17   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18   MA 02110-1301 USA
19 
20   $QuaggaId: $Format:%an, %ai, %h$ $
21 */
22 
23 #include <zebra.h>
24 
25 #include "linklist.h"
26 #include "thread.h"
27 #include "memory.h"
28 
29 #include "pimd.h"
30 #include "pim_str.h"
31 #include "pim_iface.h"
32 #include "pim_ifchannel.h"
33 #include "pim_zebra.h"
34 #include "pim_time.h"
35 #include "pim_msg.h"
36 #include "pim_pim.h"
37 #include "pim_join.h"
38 #include "pim_rpf.h"
39 #include "pim_macro.h"
40 
pim_ifchannel_free(struct pim_ifchannel * ch)41 void pim_ifchannel_free(struct pim_ifchannel *ch)
42 {
43   zassert(!ch->t_ifjoin_expiry_timer);
44   zassert(!ch->t_ifjoin_prune_pending_timer);
45   zassert(!ch->t_ifassert_timer);
46 
47   XFREE(MTYPE_PIM_IFCHANNEL, ch);
48 }
49 
pim_ifchannel_delete(struct pim_ifchannel * ch)50 void pim_ifchannel_delete(struct pim_ifchannel *ch)
51 {
52   struct pim_interface *pim_ifp;
53 
54   pim_ifp = ch->interface->info;
55   zassert(pim_ifp);
56 
57   if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
58     pim_upstream_update_join_desired(ch->upstream);
59   }
60 
61   pim_upstream_del(ch->upstream);
62 
63   THREAD_OFF(ch->t_ifjoin_expiry_timer);
64   THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
65   THREAD_OFF(ch->t_ifassert_timer);
66 
67   /*
68     notice that listnode_delete() can't be moved
69     into pim_ifchannel_free() because the later is
70     called by list_delete_all_node()
71   */
72   listnode_delete(pim_ifp->pim_ifchannel_list, ch);
73 
74   pim_ifchannel_free(ch);
75 }
76 
77 #define IFCHANNEL_NOINFO(ch)					\
78   (								\
79    ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)	\
80    &&								\
81    ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO)			\
82    &&								\
83    ((ch)->ifassert_state == PIM_IFASSERT_NOINFO)		\
84    )
85 
delete_on_noinfo(struct pim_ifchannel * ch)86 static void delete_on_noinfo(struct pim_ifchannel *ch)
87 {
88   if (IFCHANNEL_NOINFO(ch)) {
89 
90     /* In NOINFO state, timers should have been cleared */
91     zassert(!ch->t_ifjoin_expiry_timer);
92     zassert(!ch->t_ifjoin_prune_pending_timer);
93     zassert(!ch->t_ifassert_timer);
94 
95     pim_ifchannel_delete(ch);
96   }
97 }
98 
pim_ifchannel_ifjoin_switch(const char * caller,struct pim_ifchannel * ch,enum pim_ifjoin_state new_state)99 void pim_ifchannel_ifjoin_switch(const char *caller,
100 				 struct pim_ifchannel *ch,
101 				 enum pim_ifjoin_state new_state)
102 {
103   enum pim_ifjoin_state old_state = ch->ifjoin_state;
104 
105   if (old_state == new_state) {
106     if (PIM_DEBUG_PIM_EVENTS) {
107       zlog_debug("%s calledby %s: non-transition on state %d (%s)",
108 		 __PRETTY_FUNCTION__, caller, new_state,
109 		 pim_ifchannel_ifjoin_name(new_state));
110     }
111     return;
112   }
113 
114   zassert(old_state != new_state);
115 
116   ch->ifjoin_state = new_state;
117 
118   /* Transition to/from NOINFO ? */
119   if (
120       (old_state == PIM_IFJOIN_NOINFO)
121       ||
122       (new_state == PIM_IFJOIN_NOINFO)
123       ) {
124 
125     if (PIM_DEBUG_PIM_EVENTS) {
126       char src_str[100];
127       char grp_str[100];
128       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
129       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
130       zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
131 		 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
132 		 src_str, grp_str, ch->interface->name);
133     }
134 
135     /*
136       Record uptime of state transition to/from NOINFO
137     */
138     ch->ifjoin_creation = pim_time_monotonic_sec();
139 
140     pim_upstream_update_join_desired(ch->upstream);
141     pim_ifchannel_update_could_assert(ch);
142     pim_ifchannel_update_assert_tracking_desired(ch);
143   }
144 }
145 
pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)146 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
147 {
148   switch (ifjoin_state) {
149   case PIM_IFJOIN_NOINFO:        return "NOINFO";
150   case PIM_IFJOIN_JOIN:          return "JOIN";
151   case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
152   }
153 
154   return "ifjoin_bad_state";
155 }
156 
pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)157 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
158 {
159   switch (ifassert_state) {
160   case PIM_IFASSERT_NOINFO:      return "NOINFO";
161   case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
162   case PIM_IFASSERT_I_AM_LOSER:  return "LOSER";
163   }
164 
165   return "ifassert_bad_state";
166 }
167 
168 /*
169   RFC 4601: 4.6.5.  Assert State Macros
170 
171   AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
172   defaults to Infinity when in the NoInfo state.
173 */
reset_ifassert_state(struct pim_ifchannel * ch)174 void reset_ifassert_state(struct pim_ifchannel *ch)
175 {
176   THREAD_OFF(ch->t_ifassert_timer);
177 
178   pim_ifassert_winner_set(ch,
179 			  PIM_IFASSERT_NOINFO,
180 			  qpim_inaddr_any,
181 			  qpim_infinite_assert_metric);
182 }
183 
pim_ifchannel_new(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)184 static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
185 					       struct in_addr source_addr,
186 					       struct in_addr group_addr)
187 {
188   struct pim_ifchannel *ch;
189   struct pim_interface *pim_ifp;
190   struct pim_upstream  *up;
191 
192   pim_ifp = ifp->info;
193   zassert(pim_ifp);
194 
195   up = pim_upstream_add(source_addr, group_addr);
196   if (!up) {
197     char src_str[100];
198     char grp_str[100];
199     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
200     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
201     zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
202 	     __PRETTY_FUNCTION__,
203 	     src_str, grp_str, ifp->name);
204     return 0;
205   }
206 
207   ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
208   if (!ch) {
209     zlog_err("%s: PIM XMALLOC(%zu) failure",
210 	     __PRETTY_FUNCTION__, sizeof(*ch));
211     return 0;
212   }
213 
214   ch->flags                        = 0;
215   ch->upstream                     = up;
216   ch->interface                    = ifp;
217   ch->source_addr                  = source_addr;
218   ch->group_addr                   = group_addr;
219   ch->local_ifmembership           = PIM_IFMEMBERSHIP_NOINFO;
220 
221   ch->ifjoin_state                 = PIM_IFJOIN_NOINFO;
222   ch->t_ifjoin_expiry_timer        = 0;
223   ch->t_ifjoin_prune_pending_timer = 0;
224   ch->ifjoin_creation              = 0;
225 
226   ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
227   ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
228 
229   ch->ifassert_winner.s_addr = 0;
230 
231   /* Assert state */
232   ch->t_ifassert_timer   = 0;
233   reset_ifassert_state(ch);
234   if (pim_macro_ch_could_assert_eval(ch))
235     PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
236   else
237     PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
238 
239   if (pim_macro_assert_tracking_desired_eval(ch))
240     PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
241   else
242     PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
243 
244   /* Attach to list */
245   listnode_add(pim_ifp->pim_ifchannel_list, ch);
246 
247   zassert(IFCHANNEL_NOINFO(ch));
248 
249   return ch;
250 }
251 
pim_ifchannel_find(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)252 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
253 					 struct in_addr source_addr,
254 					 struct in_addr group_addr)
255 {
256   struct pim_interface *pim_ifp;
257   struct listnode      *ch_node;
258   struct pim_ifchannel *ch;
259 
260   zassert(ifp);
261 
262   pim_ifp = ifp->info;
263 
264   if (!pim_ifp) {
265     char src_str[100];
266     char grp_str[100];
267     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
268     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
269     zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
270 	      __PRETTY_FUNCTION__,
271 	      src_str, grp_str,
272 	      ifp->name);
273     return 0;
274   }
275 
276   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
277     if (
278 	(source_addr.s_addr == ch->source_addr.s_addr) &&
279 	(group_addr.s_addr == ch->group_addr.s_addr)
280 	) {
281       return ch;
282     }
283   }
284 
285   return 0;
286 }
287 
ifmembership_set(struct pim_ifchannel * ch,enum pim_ifmembership membership)288 static void ifmembership_set(struct pim_ifchannel *ch,
289 			     enum pim_ifmembership membership)
290 {
291   if (ch->local_ifmembership == membership)
292     return;
293 
294   if (PIM_DEBUG_PIM_EVENTS) {
295     char src_str[100];
296     char grp_str[100];
297     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
298     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
299     zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
300 	       __PRETTY_FUNCTION__,
301 	       src_str, grp_str,
302 	       membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
303 	       ch->interface->name);
304   }
305 
306   ch->local_ifmembership = membership;
307 
308   pim_upstream_update_join_desired(ch->upstream);
309   pim_ifchannel_update_could_assert(ch);
310   pim_ifchannel_update_assert_tracking_desired(ch);
311 }
312 
313 
pim_ifchannel_membership_clear(struct interface * ifp)314 void pim_ifchannel_membership_clear(struct interface *ifp)
315 {
316   struct pim_interface *pim_ifp;
317   struct listnode      *ch_node;
318   struct pim_ifchannel *ch;
319 
320   pim_ifp = ifp->info;
321   zassert(pim_ifp);
322 
323   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
324     ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
325   }
326 }
327 
pim_ifchannel_delete_on_noinfo(struct interface * ifp)328 void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
329 {
330   struct pim_interface *pim_ifp;
331   struct listnode      *node;
332   struct listnode      *next_node;
333   struct pim_ifchannel *ch;
334 
335   pim_ifp = ifp->info;
336   zassert(pim_ifp);
337 
338   for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
339     delete_on_noinfo(ch);
340   }
341 }
342 
pim_ifchannel_add(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)343 struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
344 					struct in_addr source_addr,
345 					struct in_addr group_addr)
346 {
347   struct pim_ifchannel *ch;
348   char src_str[100];
349   char grp_str[100];
350 
351   ch = pim_ifchannel_find(ifp, source_addr, group_addr);
352   if (ch)
353     return ch;
354 
355   ch = pim_ifchannel_new(ifp, source_addr, group_addr);
356   if (ch)
357     return ch;
358 
359   pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
360   pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
361   zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
362 	    __PRETTY_FUNCTION__,
363 	    src_str, grp_str, ifp->name);
364 
365   return 0;
366 }
367 
ifjoin_to_noinfo(struct pim_ifchannel * ch)368 static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
369 {
370   pim_forward_stop(ch);
371   pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
372   delete_on_noinfo(ch);
373 }
374 
on_ifjoin_expiry_timer(struct thread * t)375 static int on_ifjoin_expiry_timer(struct thread *t)
376 {
377   struct pim_ifchannel *ch;
378 
379   zassert(t);
380   ch = THREAD_ARG(t);
381   zassert(ch);
382 
383   ch->t_ifjoin_expiry_timer = 0;
384 
385   zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
386 
387   ifjoin_to_noinfo(ch);
388   /* ch may have been deleted */
389 
390   return 0;
391 }
392 
prune_echo(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)393 static void prune_echo(struct interface *ifp,
394 		       struct in_addr source_addr,
395 		       struct in_addr group_addr)
396 {
397   struct pim_interface *pim_ifp;
398   struct in_addr neigh_dst_addr;
399 
400   pim_ifp = ifp->info;
401   zassert(pim_ifp);
402 
403   neigh_dst_addr = pim_ifp->primary_address;
404 
405   if (PIM_DEBUG_PIM_EVENTS) {
406     char source_str[100];
407     char group_str[100];
408     char neigh_dst_str[100];
409     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
410     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
411     pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
412     zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
413 	       __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
414   }
415 
416   pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
417 		     0 /* boolean: send_join=false (prune) */);
418 }
419 
on_ifjoin_prune_pending_timer(struct thread * t)420 static int on_ifjoin_prune_pending_timer(struct thread *t)
421 {
422   struct pim_ifchannel *ch;
423   int send_prune_echo; /* boolean */
424   struct interface *ifp;
425   struct pim_interface *pim_ifp;
426   struct in_addr ch_source;
427   struct in_addr ch_group;
428 
429   zassert(t);
430   ch = THREAD_ARG(t);
431   zassert(ch);
432 
433   ch->t_ifjoin_prune_pending_timer = 0;
434 
435   zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
436 
437   /* Send PruneEcho(S,G) ? */
438   ifp = ch->interface;
439   pim_ifp = ifp->info;
440   send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
441 
442   /* Save (S,G) */
443   ch_source = ch->source_addr;
444   ch_group = ch->group_addr;
445 
446   ifjoin_to_noinfo(ch);
447   /* from here ch may have been deleted */
448 
449   if (send_prune_echo)
450     prune_echo(ifp, ch_source, ch_group);
451 
452   return 0;
453 }
454 
check_recv_upstream(int is_join,struct interface * recv_ifp,struct in_addr upstream,struct in_addr source_addr,struct in_addr group_addr,uint8_t source_flags,int holdtime)455 static void check_recv_upstream(int is_join,
456 				struct interface *recv_ifp,
457 				struct in_addr upstream,
458 				struct in_addr source_addr,
459 				struct in_addr group_addr,
460 				uint8_t source_flags,
461 				int holdtime)
462 {
463   struct pim_upstream *up;
464 
465   /* Upstream (S,G) in Joined state ? */
466   up = pim_upstream_find(source_addr, group_addr);
467   if (!up)
468     return;
469   if (up->join_state != PIM_UPSTREAM_JOINED)
470     return;
471 
472   /* Upstream (S,G) in Joined state */
473 
474   if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
475     /* RPF'(S,G) not found */
476     char src_str[100];
477     char grp_str[100];
478     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
479     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
480     zlog_warn("%s %s: RPF'(%s,%s) not found",
481 	      __FILE__, __PRETTY_FUNCTION__,
482 	      src_str, grp_str);
483     return;
484   }
485 
486   /* upstream directed to RPF'(S,G) ? */
487   if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
488     char src_str[100];
489     char grp_str[100];
490     char up_str[100];
491     char rpf_str[100];
492     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
493     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
494     pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
495     pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
496     zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
497 	      __FILE__, __PRETTY_FUNCTION__,
498 	      src_str, grp_str,
499 	      up_str, rpf_str, recv_ifp->name);
500     return;
501   }
502   /* upstream directed to RPF'(S,G) */
503 
504   if (is_join) {
505     /* Join(S,G) to RPF'(S,G) */
506     pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
507     return;
508   }
509 
510   /* Prune to RPF'(S,G) */
511 
512   if (source_flags & PIM_RPT_BIT_MASK) {
513     if (source_flags & PIM_WILDCARD_BIT_MASK) {
514       /* Prune(*,G) to RPF'(S,G) */
515       pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
516 						     up, up->rpf.rpf_addr);
517       return;
518     }
519 
520     /* Prune(S,G,rpt) to RPF'(S,G) */
521     pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
522 						   up, up->rpf.rpf_addr);
523     return;
524   }
525 
526   /* Prune(S,G) to RPF'(S,G) */
527   pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
528 						 up->rpf.rpf_addr);
529 }
530 
nonlocal_upstream(int is_join,struct interface * recv_ifp,struct in_addr upstream,struct in_addr source_addr,struct in_addr group_addr,uint8_t source_flags,uint16_t holdtime)531 static int nonlocal_upstream(int is_join,
532 			     struct interface *recv_ifp,
533 			     struct in_addr upstream,
534 			     struct in_addr source_addr,
535 			     struct in_addr group_addr,
536 			     uint8_t source_flags,
537 			     uint16_t holdtime)
538 {
539   struct pim_interface *recv_pim_ifp;
540   int is_local; /* boolean */
541 
542   recv_pim_ifp = recv_ifp->info;
543   zassert(recv_pim_ifp);
544 
545   is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
546 
547   if (PIM_DEBUG_PIM_TRACE) {
548     char up_str[100];
549     char src_str[100];
550     char grp_str[100];
551     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
552     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
553     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
554     zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
555 	      __PRETTY_FUNCTION__,
556 	      is_join ? "join" : "prune",
557 	      src_str, grp_str,
558 	      is_local ? "local" : "non-local",
559 	      up_str, recv_ifp->name);
560   }
561 
562   if (is_local)
563     return 0;
564 
565   /*
566     Since recv upstream addr was not directed to our primary
567     address, check if we should react to it in any way.
568   */
569   check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
570 		      source_flags, holdtime);
571 
572   return 1; /* non-local */
573 }
574 
pim_ifchannel_join_add(struct interface * ifp,struct in_addr neigh_addr,struct in_addr upstream,struct in_addr source_addr,struct in_addr group_addr,uint8_t source_flags,uint16_t holdtime)575 void pim_ifchannel_join_add(struct interface *ifp,
576 			    struct in_addr neigh_addr,
577 			    struct in_addr upstream,
578 			    struct in_addr source_addr,
579 			    struct in_addr group_addr,
580 			    uint8_t source_flags,
581 			    uint16_t holdtime)
582 {
583   struct pim_interface *pim_ifp;
584   struct pim_ifchannel *ch;
585 
586   if (nonlocal_upstream(1 /* join */, ifp, upstream,
587 			source_addr, group_addr, source_flags, holdtime)) {
588     return;
589   }
590 
591   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
592   if (!ch)
593     return;
594 
595   /*
596     RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
597 
598     Transitions from "I am Assert Loser" State
599 
600     Receive Join(S,G) on Interface I
601 
602     We receive a Join(S,G) that has the Upstream Neighbor Address
603     field set to my primary IP address on interface I.  The action is
604     to transition to NoInfo state, delete this (S,G) assert state
605     (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
606     to operate.
607 
608     Notice: The nonlocal_upstream() test above ensures the upstream
609     address of the join message is our primary address.
610    */
611   if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
612     char src_str[100];
613     char grp_str[100];
614     char neigh_str[100];
615     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
616     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
617     pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
618     zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
619 	      __PRETTY_FUNCTION__,
620 	      src_str, grp_str, neigh_str, ifp->name);
621 
622     assert_action_a5(ch);
623   }
624 
625   pim_ifp = ifp->info;
626   zassert(pim_ifp);
627 
628   switch (ch->ifjoin_state) {
629   case PIM_IFJOIN_NOINFO:
630     pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
631     if (pim_macro_chisin_oiflist(ch)) {
632       pim_forward_start(ch);
633     }
634     break;
635   case PIM_IFJOIN_JOIN:
636     zassert(!ch->t_ifjoin_prune_pending_timer);
637 
638     /*
639       In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
640       previously received join message with holdtime=0xFFFF.
641      */
642     if (ch->t_ifjoin_expiry_timer) {
643       unsigned long remain =
644 	thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
645       if (remain > holdtime) {
646 	/*
647 	  RFC 4601: 4.5.3.  Receiving (S,G) Join/Prune Messages
648 
649 	  Transitions from Join State
650 
651           The (S,G) downstream state machine on interface I remains in
652           Join state, and the Expiry Timer (ET) is restarted, set to
653           maximum of its current value and the HoldTime from the
654           triggering Join/Prune message.
655 
656 	  Conclusion: Do not change the ET if the current value is
657 	  higher than the received join holdtime.
658 	 */
659 	return;
660       }
661     }
662     THREAD_OFF(ch->t_ifjoin_expiry_timer);
663     break;
664   case PIM_IFJOIN_PRUNE_PENDING:
665     zassert(!ch->t_ifjoin_expiry_timer);
666     zassert(ch->t_ifjoin_prune_pending_timer);
667     THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
668     pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
669     break;
670   }
671 
672   zassert(!IFCHANNEL_NOINFO(ch));
673 
674   if (holdtime != 0xFFFF) {
675     THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
676 		    on_ifjoin_expiry_timer,
677 		    ch, holdtime);
678   }
679 }
680 
pim_ifchannel_prune(struct interface * ifp,struct in_addr upstream,struct in_addr source_addr,struct in_addr group_addr,uint8_t source_flags,uint16_t holdtime)681 void pim_ifchannel_prune(struct interface *ifp,
682 			 struct in_addr upstream,
683 			 struct in_addr source_addr,
684 			 struct in_addr group_addr,
685 			 uint8_t source_flags,
686 			 uint16_t holdtime)
687 {
688   struct pim_ifchannel *ch;
689   int jp_override_interval_msec;
690 
691   if (nonlocal_upstream(0 /* prune */, ifp, upstream,
692 			source_addr, group_addr, source_flags, holdtime)) {
693     return;
694   }
695 
696   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
697   if (!ch)
698     return;
699 
700   switch (ch->ifjoin_state) {
701   case PIM_IFJOIN_NOINFO:
702   case PIM_IFJOIN_PRUNE_PENDING:
703     /* nothing to do */
704     break;
705   case PIM_IFJOIN_JOIN:
706     {
707       struct pim_interface *pim_ifp;
708 
709       pim_ifp = ifp->info;
710 
711       zassert(ch->t_ifjoin_expiry_timer);
712       zassert(!ch->t_ifjoin_prune_pending_timer);
713 
714       THREAD_OFF(ch->t_ifjoin_expiry_timer);
715 
716       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
717 
718       if (listcount(pim_ifp->pim_neighbor_list) > 1) {
719 	jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
720       }
721       else {
722 	jp_override_interval_msec = 0; /* schedule to expire immediately */
723 	/* If we called ifjoin_prune() directly instead, care should
724 	   be taken not to use "ch" afterwards since it would be
725 	   deleted. */
726       }
727 
728       THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
729 			   on_ifjoin_prune_pending_timer,
730 			   ch, jp_override_interval_msec);
731 
732       zassert(!ch->t_ifjoin_expiry_timer);
733       zassert(ch->t_ifjoin_prune_pending_timer);
734     }
735     break;
736   }
737 
738 }
739 
pim_ifchannel_local_membership_add(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)740 void pim_ifchannel_local_membership_add(struct interface *ifp,
741 					struct in_addr source_addr,
742 					struct in_addr group_addr)
743 {
744   struct pim_ifchannel *ch;
745   struct pim_interface *pim_ifp;
746 
747   /* PIM enabled on interface? */
748   pim_ifp = ifp->info;
749   if (!pim_ifp)
750     return;
751   if (!PIM_IF_TEST_PIM(pim_ifp->options))
752     return;
753 
754   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
755   if (!ch) {
756     return;
757   }
758 
759   ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
760 
761   zassert(!IFCHANNEL_NOINFO(ch));
762 }
763 
pim_ifchannel_local_membership_del(struct interface * ifp,struct in_addr source_addr,struct in_addr group_addr)764 void pim_ifchannel_local_membership_del(struct interface *ifp,
765 					struct in_addr source_addr,
766 					struct in_addr group_addr)
767 {
768   struct pim_ifchannel *ch;
769   struct pim_interface *pim_ifp;
770 
771   /* PIM enabled on interface? */
772   pim_ifp = ifp->info;
773   if (!pim_ifp)
774     return;
775   if (!PIM_IF_TEST_PIM(pim_ifp->options))
776     return;
777 
778   ch = pim_ifchannel_find(ifp, source_addr, group_addr);
779   if (!ch)
780     return;
781 
782   ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
783 
784   delete_on_noinfo(ch);
785 }
786 
pim_ifchannel_update_could_assert(struct pim_ifchannel * ch)787 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
788 {
789   int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
790   int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
791 
792   if (new_couldassert == old_couldassert)
793     return;
794 
795   if (PIM_DEBUG_PIM_EVENTS) {
796     char src_str[100];
797     char grp_str[100];
798     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
799     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
800     zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
801 	       __PRETTY_FUNCTION__,
802 	       src_str, grp_str, ch->interface->name,
803 	       old_couldassert, new_couldassert);
804   }
805 
806   if (new_couldassert) {
807     /* CouldAssert(S,G,I) switched from FALSE to TRUE */
808     PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
809   }
810   else {
811     /* CouldAssert(S,G,I) switched from TRUE to FALSE */
812     PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
813 
814     if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
815       assert_action_a4(ch);
816     }
817   }
818 
819   pim_ifchannel_update_my_assert_metric(ch);
820 }
821 
822 /*
823   my_assert_metric may be affected by:
824 
825   CouldAssert(S,G)
826   pim_ifp->primary_address
827   rpf->source_nexthop.mrib_metric_preference;
828   rpf->source_nexthop.mrib_route_metric;
829  */
pim_ifchannel_update_my_assert_metric(struct pim_ifchannel * ch)830 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
831 {
832   struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
833 
834   if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
835       return;
836 
837   if (PIM_DEBUG_PIM_EVENTS) {
838     char src_str[100];
839     char grp_str[100];
840     char old_addr_str[100];
841     char new_addr_str[100];
842     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
843     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
844     pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
845     pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
846     zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
847 	       __PRETTY_FUNCTION__,
848 	       src_str, grp_str, ch->interface->name,
849 	       ch->ifassert_my_metric.rpt_bit_flag,
850 	       ch->ifassert_my_metric.metric_preference,
851 	       ch->ifassert_my_metric.route_metric,
852 	       old_addr_str,
853 	       my_metric_new.rpt_bit_flag,
854 	       my_metric_new.metric_preference,
855 	       my_metric_new.route_metric,
856 	       new_addr_str);
857   }
858 
859   ch->ifassert_my_metric = my_metric_new;
860 
861   if (pim_assert_metric_better(&ch->ifassert_my_metric,
862 			       &ch->ifassert_winner_metric)) {
863     assert_action_a5(ch);
864   }
865 }
866 
pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel * ch)867 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
868 {
869   int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
870   int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
871 
872   if (new_atd == old_atd)
873     return;
874 
875   if (PIM_DEBUG_PIM_EVENTS) {
876     char src_str[100];
877     char grp_str[100];
878     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
879     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
880     zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
881 	       __PRETTY_FUNCTION__,
882 	       src_str, grp_str, ch->interface->name,
883 	       old_atd, new_atd);
884   }
885 
886   if (new_atd) {
887     /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
888     PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
889   }
890   else {
891     /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
892     PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
893 
894     if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
895       assert_action_a5(ch);
896     }
897   }
898 }
899