New upstream release and new maintainer
[quagga-debian.git] / pimd / pim_ifchannel.c
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
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
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    
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
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
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
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 */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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  */
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
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 }