Annotation of embedaddon/quagga/pimd/pim_upstream.c, revision 1.1.1.1
1.1 misho 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 "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:
48: static void join_timer_start(struct pim_upstream *up);
49: static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
50:
51: void pim_upstream_free(struct pim_upstream *up)
52: {
53: XFREE(MTYPE_PIM_UPSTREAM, up);
54: }
55:
56: static 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:
64: void 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:
80: static 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:
108: static 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:
124: static 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:
144: void pim_upstream_join_timer_restart(struct pim_upstream *up)
145: {
146: THREAD_OFF(up->t_join_timer);
147: join_timer_start(up);
148: }
149:
150: static 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:
170: void 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:
211: void 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:
250: static 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;
259:
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:
266: /* 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:
279: static 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:
307: static 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:
348: static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
349: struct in_addr group_addr)
350: {
351: struct pim_upstream *up;
352: enum pim_rpf_result rpf_result;
353:
354: up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
355: if (!up) {
356: zlog_err("%s: PIM XMALLOC(%zu) failure",
357: __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:
376: rpf_result = pim_rpf_update(up, 0);
377: if (rpf_result == PIM_RPF_FAILURE) {
378: XFREE(MTYPE_PIM_UPSTREAM, up);
379: return NULL;
380: }
381:
382: listnode_add(qpim_upstream_list, up);
383:
384: return up;
385: }
386:
387: struct 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:
405: struct 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:
421: void 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: */
453: int 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: */
488: void 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: */
525: void 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:
566: void 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:
606: void 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:
634: void 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:
662: static 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>