blob: a4d274ab54d22f5a03ea9cb879811495bc12fa71 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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 "zebra/rib.h"
26
27#include "log.h"
28#include "zclient.h"
29#include "memory.h"
30#include "thread.h"
31#include "linklist.h"
32
33#include "pimd.h"
34#include "pim_pim.h"
35#include "pim_str.h"
36#include "pim_time.h"
37#include "pim_iface.h"
38#include "pim_join.h"
39#include "pim_zlookup.h"
40#include "pim_upstream.h"
41#include "pim_ifchannel.h"
42#include "pim_neighbor.h"
43#include "pim_rpf.h"
44#include "pim_zebra.h"
45#include "pim_oil.h"
46#include "pim_macro.h"
47
48static void join_timer_start(struct pim_upstream *up);
49static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
50
51void pim_upstream_free(struct pim_upstream *up)
52{
53 XFREE(MTYPE_PIM_UPSTREAM, up);
54}
55
56static void upstream_channel_oil_detach(struct pim_upstream *up)
57{
58 if (up->channel_oil) {
59 pim_channel_oil_del(up->channel_oil);
60 up->channel_oil = 0;
61 }
62}
63
64void pim_upstream_delete(struct pim_upstream *up)
65{
66 THREAD_OFF(up->t_join_timer);
67
68 upstream_channel_oil_detach(up);
69
70 /*
71 notice that listnode_delete() can't be moved
72 into pim_upstream_free() because the later is
73 called by list_delete_all_node()
74 */
75 listnode_delete(qpim_upstream_list, up);
76
77 pim_upstream_free(up);
78}
79
80static void send_join(struct pim_upstream *up)
81{
82 zassert(up->join_state == PIM_UPSTREAM_JOINED);
83
84
85 if (PIM_DEBUG_PIM_TRACE) {
86 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
87 char src_str[100];
88 char grp_str[100];
89 char rpf_str[100];
90 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
91 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
92 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
93 zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
94 __PRETTY_FUNCTION__,
95 src_str, grp_str, rpf_str);
96 /* warning only */
97 }
98 }
99
100 /* send Join(S,G) to the current upstream neighbor */
101 pim_joinprune_send(up->rpf.source_nexthop.interface,
102 up->rpf.rpf_addr,
103 up->source_addr,
104 up->group_addr,
105 1 /* join */);
106}
107
108static int on_join_timer(struct thread *t)
109{
110 struct pim_upstream *up;
111
112 zassert(t);
113 up = THREAD_ARG(t);
114 zassert(up);
115
116 send_join(up);
117
118 up->t_join_timer = 0;
119 join_timer_start(up);
120
121 return 0;
122}
123
124static void join_timer_start(struct pim_upstream *up)
125{
126 if (PIM_DEBUG_PIM_EVENTS) {
127 char src_str[100];
128 char grp_str[100];
129 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
130 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
131 zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
132 __PRETTY_FUNCTION__,
133 qpim_t_periodic,
134 src_str, grp_str);
135 }
136
137 zassert(!up->t_join_timer);
138
139 THREAD_TIMER_ON(master, up->t_join_timer,
140 on_join_timer,
141 up, qpim_t_periodic);
142}
143
144void pim_upstream_join_timer_restart(struct pim_upstream *up)
145{
146 THREAD_OFF(up->t_join_timer);
147 join_timer_start(up);
148}
149
150static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
151 int interval_msec)
152{
153 if (PIM_DEBUG_PIM_EVENTS) {
154 char src_str[100];
155 char grp_str[100];
156 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
157 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
158 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
159 __PRETTY_FUNCTION__,
160 interval_msec,
161 src_str, grp_str);
162 }
163
164 THREAD_OFF(up->t_join_timer);
165 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
166 on_join_timer,
167 up, interval_msec);
168}
169
170void pim_upstream_join_suppress(struct pim_upstream *up,
171 struct in_addr rpf_addr,
172 int holdtime)
173{
174 long t_joinsuppress_msec;
175 long join_timer_remain_msec;
176
177 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
178 1000 * holdtime);
179
180 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
181
182 if (PIM_DEBUG_PIM_TRACE) {
183 char src_str[100];
184 char grp_str[100];
185 char rpf_str[100];
186 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
187 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
188 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
189 zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
190 __FILE__, __PRETTY_FUNCTION__,
191 src_str, grp_str,
192 rpf_str,
193 join_timer_remain_msec, t_joinsuppress_msec);
194 }
195
196 if (join_timer_remain_msec < t_joinsuppress_msec) {
197 if (PIM_DEBUG_PIM_TRACE) {
198 char src_str[100];
199 char grp_str[100];
200 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
201 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
202 zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
203 __FILE__, __PRETTY_FUNCTION__,
204 src_str, grp_str, t_joinsuppress_msec);
205 }
206
207 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
208 }
209}
210
211void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
212 struct pim_upstream *up,
213 struct in_addr rpf_addr)
214{
215 long join_timer_remain_msec;
216 int t_override_msec;
217
218 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
219 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
220
221 if (PIM_DEBUG_PIM_TRACE) {
222 char src_str[100];
223 char grp_str[100];
224 char rpf_str[100];
225 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
226 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
227 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
228 zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
229 debug_label,
230 src_str, grp_str, rpf_str,
231 join_timer_remain_msec, t_override_msec);
232 }
233
234 if (join_timer_remain_msec > t_override_msec) {
235 if (PIM_DEBUG_PIM_TRACE) {
236 char src_str[100];
237 char grp_str[100];
238 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
239 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
240 zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
241 debug_label,
242 src_str, grp_str,
243 t_override_msec);
244 }
245
246 pim_upstream_join_timer_restart_msec(up, t_override_msec);
247 }
248}
249
250static void forward_on(struct pim_upstream *up)
251{
252 struct listnode *ifnode;
253 struct listnode *ifnextnode;
254 struct listnode *chnode;
255 struct listnode *chnextnode;
256 struct interface *ifp;
257 struct pim_interface *pim_ifp;
258 struct pim_ifchannel *ch;
Everton Marques871dbcf2009-08-11 15:43:05 -0300259
260 /* scan all interfaces */
261 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
262 pim_ifp = ifp->info;
263 if (!pim_ifp)
264 continue;
265
Everton Marques871dbcf2009-08-11 15:43:05 -0300266 /* scan per-interface (S,G) state */
267 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
268
269 if (ch->upstream != up)
270 continue;
271
272 if (pim_macro_chisin_oiflist(ch))
273 pim_forward_start(ch);
274
275 } /* scan iface channel list */
276 } /* scan iflist */
277}
278
279static void forward_off(struct pim_upstream *up)
280{
281 struct listnode *ifnode;
282 struct listnode *ifnextnode;
283 struct listnode *chnode;
284 struct listnode *chnextnode;
285 struct interface *ifp;
286 struct pim_interface *pim_ifp;
287 struct pim_ifchannel *ch;
288
289 /* scan all interfaces */
290 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
291 pim_ifp = ifp->info;
292 if (!pim_ifp)
293 continue;
294
295 /* scan per-interface (S,G) state */
296 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
297
298 if (ch->upstream != up)
299 continue;
300
301 pim_forward_stop(ch);
302
303 } /* scan iface channel list */
304 } /* scan iflist */
305}
306
307static void pim_upstream_switch(struct pim_upstream *up,
308 enum pim_upstream_state new_state)
309{
310 enum pim_upstream_state old_state = up->join_state;
311
312 zassert(old_state != new_state);
313
314 up->join_state = new_state;
315 up->state_transition = pim_time_monotonic_sec();
316
317 if (PIM_DEBUG_PIM_EVENTS) {
318 char src_str[100];
319 char grp_str[100];
320 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
321 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
322 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
323 __PRETTY_FUNCTION__,
324 ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
325 src_str, grp_str);
326 }
327
328 pim_upstream_update_assert_tracking_desired(up);
329
330 if (new_state == PIM_UPSTREAM_JOINED) {
331 forward_on(up);
332 send_join(up);
333 join_timer_start(up);
334 }
335 else {
336 forward_off(up);
337 pim_joinprune_send(up->rpf.source_nexthop.interface,
338 up->rpf.rpf_addr,
339 up->source_addr,
340 up->group_addr,
341 0 /* prune */);
342 zassert(up->t_join_timer);
343 THREAD_OFF(up->t_join_timer);
344 }
345
346}
347
348static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
349 struct in_addr group_addr)
350{
351 struct pim_upstream *up;
Donald Sharp7f567432015-06-12 17:47:26 -0700352 enum pim_rpf_result rpf_result;
Everton Marques871dbcf2009-08-11 15:43:05 -0300353
354 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
355 if (!up) {
David Lamparter5c697982012-02-16 04:47:56 +0100356 zlog_err("%s: PIM XMALLOC(%zu) failure",
Everton Marques871dbcf2009-08-11 15:43:05 -0300357 __PRETTY_FUNCTION__, sizeof(*up));
358 return 0;
359 }
360
361 up->source_addr = source_addr;
362 up->group_addr = group_addr;
363 up->flags = 0;
364 up->ref_count = 1;
365 up->t_join_timer = 0;
366 up->join_state = 0;
367 up->state_transition = pim_time_monotonic_sec();
368 up->channel_oil = 0;
369
370 up->rpf.source_nexthop.interface = 0;
371 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
372 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
373 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
374 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
375
Donald Sharp7f567432015-06-12 17:47:26 -0700376 rpf_result = pim_rpf_update(up, 0);
377 if (rpf_result == PIM_RPF_FAILURE) {
378 XFREE(MTYPE_PIM_UPSTREAM, up);
379 return NULL;
380 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300381
382 listnode_add(qpim_upstream_list, up);
383
384 return up;
385}
386
387struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
388 struct in_addr group_addr)
389{
390 struct listnode *up_node;
391 struct pim_upstream *up;
392
393 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
394 if (
395 (source_addr.s_addr == up->source_addr.s_addr) &&
396 (group_addr.s_addr == up->group_addr.s_addr)
397 ) {
398 return up;
399 }
400 }
401
402 return 0;
403}
404
405struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
406 struct in_addr group_addr)
407{
408 struct pim_upstream *up;
409
410 up = pim_upstream_find(source_addr, group_addr);
411 if (up) {
412 ++up->ref_count;
413 }
414 else {
415 up = pim_upstream_new(source_addr, group_addr);
416 }
417
418 return up;
419}
420
421void pim_upstream_del(struct pim_upstream *up)
422{
423 --up->ref_count;
424
425 if (up->ref_count < 1) {
426 pim_upstream_delete(up);
427 }
428}
429
430/*
431 Evaluate JoinDesired(S,G):
432
433 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
434 in the set:
435
436 inherited_olist(S,G) =
437 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
438
439 JoinDesired(S,G) may be affected by changes in the following:
440
441 pim_ifp->primary_address
442 pim_ifp->pim_dr_addr
443 ch->ifassert_winner_metric
444 ch->ifassert_winner
445 ch->local_ifmembership
446 ch->ifjoin_state
447 ch->upstream->rpf.source_nexthop.mrib_metric_preference
448 ch->upstream->rpf.source_nexthop.mrib_route_metric
449 ch->upstream->rpf.source_nexthop.interface
450
451 See also pim_upstream_update_join_desired() below.
452 */
453int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
454{
455 struct listnode *ifnode;
456 struct listnode *ifnextnode;
457 struct listnode *chnode;
458 struct listnode *chnextnode;
459 struct interface *ifp;
460 struct pim_interface *pim_ifp;
461 struct pim_ifchannel *ch;
462
463 /* scan all interfaces */
464 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
465 pim_ifp = ifp->info;
466 if (!pim_ifp)
467 continue;
468
469 /* scan per-interface (S,G) state */
470 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
471 if (ch->upstream != up)
472 continue;
473
474 if (pim_macro_ch_lost_assert(ch))
475 continue; /* keep searching */
476
477 if (pim_macro_chisin_joins_or_include(ch))
478 return 1; /* true */
479 } /* scan iface channel list */
480 } /* scan iflist */
481
482 return 0; /* false */
483}
484
485/*
486 See also pim_upstream_evaluate_join_desired() above.
487*/
488void pim_upstream_update_join_desired(struct pim_upstream *up)
489{
490 int was_join_desired; /* boolean */
491 int is_join_desired; /* boolean */
492
493 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
494
495 is_join_desired = pim_upstream_evaluate_join_desired(up);
496 if (is_join_desired)
497 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
498 else
499 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
500
501 /* switched from false to true */
502 if (is_join_desired && !was_join_desired) {
503 zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
504 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
505 return;
506 }
507
508 /* switched from true to false */
509 if (!is_join_desired && was_join_desired) {
510 zassert(up->join_state == PIM_UPSTREAM_JOINED);
511 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
512 return;
513 }
514}
515
516/*
517 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
518 Transitions from Joined State
519 RPF'(S,G) GenID changes
520
521 The upstream (S,G) state machine remains in Joined state. If the
522 Join Timer is set to expire in more than t_override seconds, reset
523 it so that it expires after t_override seconds.
524*/
525void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
526{
527 struct listnode *up_node;
528 struct listnode *up_nextnode;
529 struct pim_upstream *up;
530
531 /*
532 Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
533 */
534 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
535
536 if (PIM_DEBUG_PIM_TRACE) {
537 char neigh_str[100];
538 char src_str[100];
539 char grp_str[100];
540 char rpf_addr_str[100];
541 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
542 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
543 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
544 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
545 zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
546 __PRETTY_FUNCTION__,
547 neigh_str, src_str, grp_str,
548 up->join_state == PIM_UPSTREAM_JOINED,
549 rpf_addr_str);
550 }
551
552 /* consider only (S,G) upstream in Joined state */
553 if (up->join_state != PIM_UPSTREAM_JOINED)
554 continue;
555
556 /* match RPF'(S,G)=neigh_addr */
557 if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
558 continue;
559
560 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
561 up, neigh_addr);
562 }
563}
564
565
566void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
567 struct interface *old_rpf_ifp)
568{
569 struct listnode *ifnode;
570 struct listnode *ifnextnode;
571 struct interface *ifp;
572
573 /* scan all interfaces */
574 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
575 struct listnode *chnode;
576 struct listnode *chnextnode;
577 struct pim_ifchannel *ch;
578 struct pim_interface *pim_ifp;
579
580 pim_ifp = ifp->info;
581 if (!pim_ifp)
582 continue;
583
584 /* search all ifchannels */
585 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
586 if (ch->upstream != up)
587 continue;
588
589 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
590 if (
591 /* RPF_interface(S) was NOT I */
592 (old_rpf_ifp == ch->interface)
593 &&
594 /* RPF_interface(S) stopped being I */
595 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
596 ) {
597 assert_action_a5(ch);
598 }
599 } /* PIM_IFASSERT_I_AM_LOSER */
600
601 pim_ifchannel_update_assert_tracking_desired(ch);
602 }
603 }
604}
605
606void pim_upstream_update_could_assert(struct pim_upstream *up)
607{
608 struct listnode *ifnode;
609 struct listnode *ifnextnode;
610 struct listnode *chnode;
611 struct listnode *chnextnode;
612 struct interface *ifp;
613 struct pim_interface *pim_ifp;
614 struct pim_ifchannel *ch;
615
616 /* scan all interfaces */
617 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
618 pim_ifp = ifp->info;
619 if (!pim_ifp)
620 continue;
621
622 /* scan per-interface (S,G) state */
623 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
624
625 if (ch->upstream != up)
626 continue;
627
628 pim_ifchannel_update_could_assert(ch);
629
630 } /* scan iface channel list */
631 } /* scan iflist */
632}
633
634void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
635{
636 struct listnode *ifnode;
637 struct listnode *ifnextnode;
638 struct listnode *chnode;
639 struct listnode *chnextnode;
640 struct interface *ifp;
641 struct pim_interface *pim_ifp;
642 struct pim_ifchannel *ch;
643
644 /* scan all interfaces */
645 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
646 pim_ifp = ifp->info;
647 if (!pim_ifp)
648 continue;
649
650 /* scan per-interface (S,G) state */
651 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
652
653 if (ch->upstream != up)
654 continue;
655
656 pim_ifchannel_update_my_assert_metric(ch);
657
658 } /* scan iface channel list */
659 } /* scan iflist */
660}
661
662static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
663{
664 struct listnode *ifnode;
665 struct listnode *ifnextnode;
666 struct listnode *chnode;
667 struct listnode *chnextnode;
668 struct interface *ifp;
669 struct pim_interface *pim_ifp;
670 struct pim_ifchannel *ch;
671
672 /* scan all interfaces */
673 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
674 pim_ifp = ifp->info;
675 if (!pim_ifp)
676 continue;
677
678 /* scan per-interface (S,G) state */
679 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
680
681 if (ch->upstream != up)
682 continue;
683
684 pim_ifchannel_update_assert_tracking_desired(ch);
685
686 } /* scan iface channel list */
687 } /* scan iflist */
688}