]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
pimd: Limit search to relevant ifchannels in some cases
[mirror_frr.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
21 #include <zebra.h>
22
23 #include "linklist.h"
24 #include "thread.h"
25 #include "memory.h"
26 #include "if.h"
27 #include "vrf.h"
28 #include "hash.h"
29 #include "jhash.h"
30 #include "prefix.h"
31
32 #include "pimd.h"
33 #include "pim_str.h"
34 #include "pim_iface.h"
35 #include "pim_ifchannel.h"
36 #include "pim_zebra.h"
37 #include "pim_time.h"
38 #include "pim_msg.h"
39 #include "pim_pim.h"
40 #include "pim_join.h"
41 #include "pim_rpf.h"
42 #include "pim_macro.h"
43 #include "pim_oil.h"
44 #include "pim_upstream.h"
45 #include "pim_ssm.h"
46
47 int
48 pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2)
49 {
50 struct pim_interface *pim_ifp1;
51 struct pim_interface *pim_ifp2;
52
53 if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr))
54 return -1;
55
56 if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr))
57 return 1;
58
59 if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr))
60 return -1;
61
62 if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr))
63 return 1;
64
65 pim_ifp1 = ch1->interface->info;
66 pim_ifp2 = ch2->interface->info;
67 if (ntohl(pim_ifp1->primary_address.s_addr) < ntohl(pim_ifp2->primary_address.s_addr))
68 return -1;
69
70 if (ntohl(pim_ifp1->primary_address.s_addr) > ntohl(pim_ifp2->primary_address.s_addr))
71 return 1;
72
73 if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
74 return -1;
75
76 if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
77 return 1;
78
79 return 0;
80 }
81
82 /*
83 * A (*,G) or a (*,*) is going away
84 * remove the parent pointer from
85 * those pointing at us
86 */
87 static void
88 pim_ifchannel_remove_children (struct pim_ifchannel *ch)
89 {
90 struct pim_ifchannel *child;
91
92 if (!ch->sources)
93 return;
94
95 while (!list_isempty (ch->sources))
96 {
97 child = listnode_head (ch->sources);
98 child->parent = NULL;
99 listnode_delete (ch->sources, child);
100 }
101 }
102
103 /*
104 * A (*,G) or a (*,*) is being created
105 * find all the children that would point
106 * at us.
107 */
108 static void
109 pim_ifchannel_find_new_children (struct pim_ifchannel *ch)
110 {
111 struct pim_interface *pim_ifp = ch->interface->info;
112 struct pim_ifchannel *child;
113 struct listnode *ch_node;
114
115 // Basic Sanity that we are not being silly
116 if ((ch->sg.src.s_addr != INADDR_ANY) &&
117 (ch->sg.grp.s_addr != INADDR_ANY))
118 return;
119
120 if ((ch->sg.src.s_addr == INADDR_ANY) &&
121 (ch->sg.grp.s_addr == INADDR_ANY))
122 return;
123
124 for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
125 {
126 if ((ch->sg.grp.s_addr != INADDR_ANY) &&
127 (child->sg.grp.s_addr == ch->sg.grp.s_addr) &&
128 (child != ch))
129 {
130 child->parent = ch;
131 listnode_add_sort (ch->sources, child);
132 }
133 }
134 }
135
136 void pim_ifchannel_free(struct pim_ifchannel *ch)
137 {
138 XFREE(MTYPE_PIM_IFCHANNEL, ch);
139 }
140
141 void pim_ifchannel_delete(struct pim_ifchannel *ch)
142 {
143 struct pim_interface *pim_ifp;
144
145 pim_ifp = ch->interface->info;
146
147 if (ch->upstream->channel_oil)
148 {
149 uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
150 if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
151 mask = PIM_OIF_FLAG_PROTO_IGMP;
152
153 pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, mask);
154 /*
155 * Do we have any S,G's that are inheriting?
156 * Nuke from on high too.
157 */
158 if (ch->upstream->sources)
159 {
160 struct pim_upstream *child;
161 struct listnode *up_node;
162
163 for (ALL_LIST_ELEMENTS_RO (ch->upstream->sources, up_node, child))
164 pim_channel_del_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
165 }
166 }
167
168 /*
169 * When this channel is removed
170 * we need to find all our children
171 * and make sure our pointers are fixed
172 */
173 pim_ifchannel_remove_children (ch);
174
175 if (ch->sources)
176 list_delete (ch->sources);
177
178 if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
179 pim_upstream_update_join_desired(ch->upstream);
180 }
181
182 pim_upstream_del(ch->upstream, __PRETTY_FUNCTION__);
183 ch->upstream = NULL;
184
185 THREAD_OFF(ch->t_ifjoin_expiry_timer);
186 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
187 THREAD_OFF(ch->t_ifassert_timer);
188
189 if (ch->parent)
190 {
191 listnode_delete (ch->parent->sources, ch);
192 ch->parent = NULL;
193 }
194 /*
195 notice that listnode_delete() can't be moved
196 into pim_ifchannel_free() because the later is
197 called by list_delete_all_node()
198 */
199 listnode_delete(pim_ifp->pim_ifchannel_list, ch);
200 hash_release(pim_ifp->pim_ifchannel_hash, ch);
201 listnode_delete(pim_ifchannel_list, ch);
202
203 pim_ifchannel_free(ch);
204 }
205
206 void
207 pim_ifchannel_delete_all (struct interface *ifp)
208 {
209 struct pim_interface *pim_ifp;
210 struct listnode *ifchannel_node;
211 struct listnode *ifchannel_nextnode;
212 struct pim_ifchannel *ifchannel;
213
214 pim_ifp = ifp->info;
215 if (!pim_ifp)
216 return;
217
218 for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, ifchannel_node,
219 ifchannel_nextnode, ifchannel))
220 {
221 pim_ifchannel_delete (ifchannel);
222 }
223 }
224
225 static void delete_on_noinfo(struct pim_ifchannel *ch)
226 {
227 if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO &&
228 ch->ifjoin_state == PIM_IFJOIN_NOINFO &&
229 ch->t_ifjoin_expiry_timer == NULL)
230 pim_ifchannel_delete(ch);
231
232 }
233
234 void pim_ifchannel_ifjoin_switch(const char *caller,
235 struct pim_ifchannel *ch,
236 enum pim_ifjoin_state new_state)
237 {
238 enum pim_ifjoin_state old_state = ch->ifjoin_state;
239
240 if (PIM_DEBUG_PIM_EVENTS)
241 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
242 ch->interface->name,
243 ch->sg_str,
244 pim_ifchannel_ifjoin_name (ch->ifjoin_state, ch->flags),
245 pim_ifchannel_ifjoin_name (new_state, 0));
246
247
248 if (old_state == new_state) {
249 if (PIM_DEBUG_PIM_EVENTS) {
250 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
251 __PRETTY_FUNCTION__, caller, new_state,
252 pim_ifchannel_ifjoin_name(new_state, 0));
253 }
254 return;
255 }
256
257 ch->ifjoin_state = new_state;
258
259 if (ch->sg.src.s_addr == INADDR_ANY)
260 {
261 struct pim_upstream *up = ch->upstream;
262 struct pim_upstream *child;
263 struct listnode *up_node;
264
265 if (up)
266 {
267 if (ch->ifjoin_state == PIM_IFJOIN_NOINFO)
268 {
269 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
270 {
271 struct channel_oil *c_oil = child->channel_oil;
272 struct pim_interface *pim_ifp = ch->interface->info;
273
274 if (PIM_DEBUG_PIM_TRACE)
275 zlog_debug("%s %s: Prune(S,G)=%s from %s",
276 __FILE__, __PRETTY_FUNCTION__,
277 child->sg_str, up->sg_str);
278 if (!c_oil)
279 continue;
280
281 if (!pim_upstream_evaluate_join_desired (child))
282 {
283 pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
284 pim_upstream_update_join_desired (child);
285 }
286
287 /*
288 * If the S,G has no if channel and the c_oil still
289 * has output here then the *,G was supplying the implied
290 * if channel. So remove it.
291 * I think this is dead code now. is it?
292 */
293 if (!ch && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
294 pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
295 }
296 }
297 if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
298 {
299 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
300 {
301 if (PIM_DEBUG_PIM_TRACE)
302 zlog_debug("%s %s: Join(S,G)=%s from %s",
303 __FILE__, __PRETTY_FUNCTION__,
304 child->sg_str, up->sg_str);
305
306 if (pim_upstream_evaluate_join_desired (child))
307 {
308 pim_channel_add_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
309 pim_upstream_update_join_desired (child);
310 }
311 }
312 }
313 }
314 }
315 /* Transition to/from NOINFO ? */
316 if ((old_state == PIM_IFJOIN_NOINFO) ||
317 (new_state == PIM_IFJOIN_NOINFO)) {
318
319 if (PIM_DEBUG_PIM_EVENTS) {
320 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
321 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
322 ch->sg_str, ch->interface->name);
323 }
324
325 /*
326 Record uptime of state transition to/from NOINFO
327 */
328 ch->ifjoin_creation = pim_time_monotonic_sec();
329
330 pim_upstream_update_join_desired(ch->upstream);
331 pim_ifchannel_update_could_assert(ch);
332 pim_ifchannel_update_assert_tracking_desired(ch);
333 }
334 }
335
336 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
337 int flags)
338 {
339 switch (ifjoin_state) {
340 case PIM_IFJOIN_NOINFO:
341 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
342 return "SGRpt";
343 else
344 return "NOINFO";
345 break;
346 case PIM_IFJOIN_JOIN:
347 return "JOIN";
348 break;
349 case PIM_IFJOIN_PRUNE:
350 return "PRUNE";
351 break;
352 case PIM_IFJOIN_PRUNE_PENDING:
353 return "PRUNEP";
354 break;
355 case PIM_IFJOIN_PRUNE_TMP:
356 return "PRUNET";
357 break;
358 case PIM_IFJOIN_PRUNE_PENDING_TMP:
359 return "PRUNEPT";
360 break;
361 }
362
363 return "ifjoin_bad_state";
364 }
365
366 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
367 {
368 switch (ifassert_state) {
369 case PIM_IFASSERT_NOINFO: return "NOINFO";
370 case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
371 case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
372 }
373
374 return "ifassert_bad_state";
375 }
376
377 /*
378 RFC 4601: 4.6.5. Assert State Macros
379
380 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
381 defaults to Infinity when in the NoInfo state.
382 */
383 void reset_ifassert_state(struct pim_ifchannel *ch)
384 {
385 THREAD_OFF(ch->t_ifassert_timer);
386
387 pim_ifassert_winner_set(ch,
388 PIM_IFASSERT_NOINFO,
389 qpim_inaddr_any,
390 qpim_infinite_assert_metric);
391 }
392
393 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
394 struct prefix_sg *sg)
395 {
396 struct pim_interface *pim_ifp;
397 struct pim_ifchannel *ch;
398 struct pim_ifchannel lookup;
399
400 pim_ifp = ifp->info;
401
402 if (!pim_ifp) {
403 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
404 __PRETTY_FUNCTION__,
405 pim_str_sg_dump (sg),
406 ifp->name);
407 return NULL;
408 }
409
410 lookup.sg = *sg;
411 ch = hash_lookup (pim_ifp->pim_ifchannel_hash, &lookup);
412
413 return ch;
414 }
415
416 static void ifmembership_set(struct pim_ifchannel *ch,
417 enum pim_ifmembership membership)
418 {
419 if (ch->local_ifmembership == membership)
420 return;
421
422 if (PIM_DEBUG_PIM_EVENTS) {
423 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
424 __PRETTY_FUNCTION__,
425 ch->sg_str,
426 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
427 ch->interface->name);
428 }
429
430 ch->local_ifmembership = membership;
431
432 pim_upstream_update_join_desired(ch->upstream);
433 pim_ifchannel_update_could_assert(ch);
434 pim_ifchannel_update_assert_tracking_desired(ch);
435 }
436
437
438 void pim_ifchannel_membership_clear(struct interface *ifp)
439 {
440 struct pim_interface *pim_ifp;
441 struct listnode *ch_node;
442 struct pim_ifchannel *ch;
443
444 pim_ifp = ifp->info;
445 zassert(pim_ifp);
446
447 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
448 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
449 }
450 }
451
452 void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
453 {
454 struct pim_interface *pim_ifp;
455 struct listnode *node;
456 struct listnode *next_node;
457 struct pim_ifchannel *ch;
458
459 pim_ifp = ifp->info;
460 zassert(pim_ifp);
461
462 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
463 delete_on_noinfo(ch);
464 }
465 }
466
467 /*
468 * For a given Interface, if we are given a S,G
469 * Find the *,G (If we have it).
470 * If we are passed a *,G, find the *,* ifchannel
471 * if we have it.
472 */
473 static struct pim_ifchannel *
474 pim_ifchannel_find_parent (struct pim_ifchannel *ch)
475 {
476 struct prefix_sg parent_sg = ch->sg;
477 struct pim_ifchannel *parent = NULL;
478
479 // (S,G)
480 if ((parent_sg.src.s_addr != INADDR_ANY) &&
481 (parent_sg.grp.s_addr != INADDR_ANY))
482 {
483 parent_sg.src.s_addr = INADDR_ANY;
484 parent = pim_ifchannel_find (ch->interface, &parent_sg);
485
486 if (parent)
487 listnode_add (parent->sources, ch);
488 return parent;
489 }
490
491 return NULL;
492 }
493
494 struct pim_ifchannel *
495 pim_ifchannel_add(struct interface *ifp,
496 struct prefix_sg *sg, int flags)
497 {
498 struct pim_interface *pim_ifp;
499 struct pim_ifchannel *ch;
500 struct pim_upstream *up;
501
502 ch = pim_ifchannel_find(ifp, sg);
503 if (ch)
504 return ch;
505
506 pim_ifp = ifp->info;
507
508 up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__);
509 if (!up) {
510 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
511 __PRETTY_FUNCTION__,
512 pim_str_sg_dump (sg), ifp->name);
513 return NULL;
514 }
515
516 ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
517 if (!ch) {
518 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
519 __PRETTY_FUNCTION__,
520 up->sg_str, ifp->name);
521
522 pim_upstream_del (up, __PRETTY_FUNCTION__);
523 return NULL;
524 }
525
526 ch->flags = 0;
527 ch->upstream = up;
528 ch->interface = ifp;
529 ch->sg = *sg;
530 pim_str_sg_set (sg, ch->sg_str);
531 ch->parent = pim_ifchannel_find_parent (ch);
532 if (ch->sg.src.s_addr == INADDR_ANY)
533 {
534 ch->sources = list_new ();
535 ch->sources->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
536 }
537 else
538 ch->sources = NULL;
539
540 pim_ifchannel_find_new_children (ch);
541 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
542
543 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
544 ch->t_ifjoin_expiry_timer = NULL;
545 ch->t_ifjoin_prune_pending_timer = NULL;
546 ch->ifjoin_creation = 0;
547
548 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
549 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
550
551 ch->ifassert_winner.s_addr = 0;
552
553 /* Assert state */
554 ch->t_ifassert_timer = NULL;
555 ch->ifassert_state = PIM_IFASSERT_NOINFO;
556 reset_ifassert_state(ch);
557 if (pim_macro_ch_could_assert_eval(ch))
558 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
559 else
560 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
561
562 if (pim_macro_assert_tracking_desired_eval(ch))
563 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
564 else
565 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
566
567 /* Attach to list */
568 listnode_add_sort(pim_ifp->pim_ifchannel_list, ch);
569 ch = hash_get (pim_ifp->pim_ifchannel_hash, ch, hash_alloc_intern);
570 listnode_add_sort(pim_ifchannel_list, ch);
571
572 return ch;
573 }
574
575 static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
576 {
577 pim_forward_stop(ch);
578 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
579 delete_on_noinfo(ch);
580 }
581
582 static int on_ifjoin_expiry_timer(struct thread *t)
583 {
584 struct pim_ifchannel *ch;
585
586 ch = THREAD_ARG(t);
587
588 ch->t_ifjoin_expiry_timer = NULL;
589
590 ifjoin_to_noinfo(ch);
591 /* ch may have been deleted */
592
593 return 0;
594 }
595
596 static int on_ifjoin_prune_pending_timer(struct thread *t)
597 {
598 struct pim_ifchannel *ch;
599 int send_prune_echo; /* boolean */
600 struct interface *ifp;
601 struct pim_interface *pim_ifp;
602
603 ch = THREAD_ARG(t);
604
605 ch->t_ifjoin_prune_pending_timer = NULL;
606
607 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING)
608 {
609 /* Send PruneEcho(S,G) ? */
610 ifp = ch->interface;
611 pim_ifp = ifp->info;
612 send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
613
614 ifjoin_to_noinfo(ch);
615 /* from here ch may have been deleted */
616
617 if (send_prune_echo)
618 {
619 struct pim_rpf rpf;
620
621 rpf.source_nexthop.interface = ifp;
622 rpf.rpf_addr.u.prefix4 = pim_ifp->primary_address;
623 pim_jp_agg_single_upstream_send(&rpf, ch->upstream, 0);
624 }
625 }
626 else
627 {
628 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
629 __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg),
630 pim_ifchannel_ifjoin_name (ch->ifjoin_state, ch->flags));
631 }
632
633 return 0;
634 }
635
636 static void check_recv_upstream(int is_join,
637 struct interface *recv_ifp,
638 struct in_addr upstream,
639 struct prefix_sg *sg,
640 uint8_t source_flags,
641 int holdtime)
642 {
643 struct pim_upstream *up;
644
645 /* Upstream (S,G) in Joined state ? */
646 up = pim_upstream_find(sg);
647 if (!up)
648 return;
649 if (up->join_state != PIM_UPSTREAM_JOINED)
650 return;
651
652 /* Upstream (S,G) in Joined state */
653
654 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
655 /* RPF'(S,G) not found */
656 zlog_warn("%s %s: RPF'%s not found",
657 __FILE__, __PRETTY_FUNCTION__,
658 up->sg_str);
659 return;
660 }
661
662 /* upstream directed to RPF'(S,G) ? */
663 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
664 char up_str[INET_ADDRSTRLEN];
665 char rpf_str[PREFIX_STRLEN];
666 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
667 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
668 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
669 __FILE__, __PRETTY_FUNCTION__,
670 up->sg_str,
671 up_str, rpf_str, recv_ifp->name);
672 return;
673 }
674 /* upstream directed to RPF'(S,G) */
675
676 if (is_join) {
677 /* Join(S,G) to RPF'(S,G) */
678 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime);
679 return;
680 }
681
682 /* Prune to RPF'(S,G) */
683
684 if (source_flags & PIM_RPT_BIT_MASK) {
685 if (source_flags & PIM_WILDCARD_BIT_MASK) {
686 /* Prune(*,G) to RPF'(S,G) */
687 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up);
688 return;
689 }
690
691 /* Prune(S,G,rpt) to RPF'(S,G) */
692 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up);
693 return;
694 }
695
696 /* Prune(S,G) to RPF'(S,G) */
697 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
698 }
699
700 static int
701 nonlocal_upstream(int is_join,
702 struct interface *recv_ifp,
703 struct in_addr upstream,
704 struct prefix_sg *sg,
705 uint8_t source_flags,
706 uint16_t holdtime)
707 {
708 struct pim_interface *recv_pim_ifp;
709 int is_local; /* boolean */
710
711 recv_pim_ifp = recv_ifp->info;
712 zassert(recv_pim_ifp);
713
714 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
715
716 if (is_local)
717 return 0;
718
719 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
720 char up_str[INET_ADDRSTRLEN];
721 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
722 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
723 __PRETTY_FUNCTION__,
724 is_join ? "join" : "prune",
725 pim_str_sg_dump (sg),
726 up_str, recv_ifp->name);
727 }
728
729 /*
730 * Since recv upstream addr was not directed to our primary
731 * address, check if we should react to it in any way.
732 */
733 check_recv_upstream(is_join, recv_ifp, upstream, sg,
734 source_flags, holdtime);
735
736 return 1; /* non-local */
737 }
738
739 void pim_ifchannel_join_add(struct interface *ifp,
740 struct in_addr neigh_addr,
741 struct in_addr upstream,
742 struct prefix_sg *sg,
743 uint8_t source_flags,
744 uint16_t holdtime)
745 {
746 struct pim_interface *pim_ifp;
747 struct pim_ifchannel *ch;
748
749 if (nonlocal_upstream(1 /* join */, ifp, upstream,
750 sg, source_flags, holdtime)) {
751 return;
752 }
753
754 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
755 if (!ch)
756 return;
757
758 /*
759 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
760
761 Transitions from "I am Assert Loser" State
762
763 Receive Join(S,G) on Interface I
764
765 We receive a Join(S,G) that has the Upstream Neighbor Address
766 field set to my primary IP address on interface I. The action is
767 to transition to NoInfo state, delete this (S,G) assert state
768 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
769 to operate.
770
771 Notice: The nonlocal_upstream() test above ensures the upstream
772 address of the join message is our primary address.
773 */
774 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
775 char neigh_str[INET_ADDRSTRLEN];
776 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
777 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
778 __PRETTY_FUNCTION__,
779 ch->sg_str, neigh_str, ifp->name);
780
781 assert_action_a5(ch);
782 }
783
784 pim_ifp = ifp->info;
785 zassert(pim_ifp);
786
787 switch (ch->ifjoin_state) {
788 case PIM_IFJOIN_NOINFO:
789 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
790 if (pim_macro_chisin_oiflist(ch)) {
791 pim_upstream_inherited_olist (ch->upstream);
792 pim_forward_start(ch);
793 }
794 break;
795 case PIM_IFJOIN_JOIN:
796 zassert(!ch->t_ifjoin_prune_pending_timer);
797
798 /*
799 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
800 previously received join message with holdtime=0xFFFF.
801 */
802 if (ch->t_ifjoin_expiry_timer) {
803 unsigned long remain =
804 thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
805 if (remain > holdtime) {
806 /*
807 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
808
809 Transitions from Join State
810
811 The (S,G) downstream state machine on interface I remains in
812 Join state, and the Expiry Timer (ET) is restarted, set to
813 maximum of its current value and the HoldTime from the
814 triggering Join/Prune message.
815
816 Conclusion: Do not change the ET if the current value is
817 higher than the received join holdtime.
818 */
819 return;
820 }
821 }
822 THREAD_OFF(ch->t_ifjoin_expiry_timer);
823 break;
824 case PIM_IFJOIN_PRUNE:
825 if (source_flags & PIM_ENCODE_RPT_BIT)
826 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
827 break;
828 case PIM_IFJOIN_PRUNE_PENDING:
829 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
830 if (source_flags & PIM_ENCODE_RPT_BIT)
831 {
832 THREAD_OFF(ch->t_ifjoin_expiry_timer);
833 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
834 }
835 else
836 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
837 break;
838 case PIM_IFJOIN_PRUNE_TMP:
839 break;
840 case PIM_IFJOIN_PRUNE_PENDING_TMP:
841 break;
842 }
843
844 if (holdtime != 0xFFFF) {
845 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
846 on_ifjoin_expiry_timer,
847 ch, holdtime);
848 }
849 }
850
851 void pim_ifchannel_prune(struct interface *ifp,
852 struct in_addr upstream,
853 struct prefix_sg *sg,
854 uint8_t source_flags,
855 uint16_t holdtime)
856 {
857 struct pim_ifchannel *ch;
858 struct pim_interface *pim_ifp;
859 int jp_override_interval_msec;
860
861 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
862 sg, source_flags, holdtime)) {
863 return;
864 }
865
866 ch = pim_ifchannel_find (ifp, sg);
867 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT))
868 {
869 if (PIM_DEBUG_TRACE)
870 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
871 __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags);
872 return;
873 }
874
875 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
876 if (!ch)
877 return;
878
879 pim_ifp = ifp->info;
880
881 switch (ch->ifjoin_state) {
882 case PIM_IFJOIN_NOINFO:
883 if (source_flags & PIM_ENCODE_RPT_BIT)
884 {
885 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
886 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
887 if (listcount(pim_ifp->pim_neighbor_list) > 1)
888 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
889 else
890 jp_override_interval_msec = 0; /* schedule to expire immediately */
891 /* If we called ifjoin_prune() directly instead, care should
892 be taken not to use "ch" afterwards since it would be
893 deleted. */
894
895 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
896 THREAD_OFF(ch->t_ifjoin_expiry_timer);
897 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
898 on_ifjoin_prune_pending_timer,
899 ch, jp_override_interval_msec);
900 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
901 on_ifjoin_expiry_timer,
902 ch, holdtime);
903 pim_upstream_update_join_desired(ch->upstream);
904 }
905 break;
906 case PIM_IFJOIN_PRUNE_PENDING:
907 /* nothing to do */
908 break;
909 case PIM_IFJOIN_JOIN:
910 THREAD_OFF(ch->t_ifjoin_expiry_timer);
911
912 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
913
914 if (listcount(pim_ifp->pim_neighbor_list) > 1)
915 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
916 else
917 jp_override_interval_msec = 0; /* schedule to expire immediately */
918 /* If we called ifjoin_prune() directly instead, care should
919 be taken not to use "ch" afterwards since it would be
920 deleted. */
921 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
922 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
923 on_ifjoin_prune_pending_timer,
924 ch, jp_override_interval_msec);
925 break;
926 case PIM_IFJOIN_PRUNE:
927 if (source_flags & PIM_ENCODE_RPT_BIT)
928 {
929 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
930 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
931 on_ifjoin_expiry_timer,
932 ch, holdtime);
933 }
934 break;
935 case PIM_IFJOIN_PRUNE_TMP:
936 if (source_flags & PIM_ENCODE_RPT_BIT)
937 {
938 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
939 THREAD_OFF(ch->t_ifjoin_expiry_timer);
940 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
941 on_ifjoin_expiry_timer,
942 ch, holdtime);
943 }
944 break;
945 case PIM_IFJOIN_PRUNE_PENDING_TMP:
946 if (source_flags & PIM_ENCODE_RPT_BIT)
947 {
948 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
949 THREAD_OFF(ch->t_ifjoin_expiry_timer);
950 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
951 on_ifjoin_expiry_timer,
952 ch, holdtime);
953 }
954 break;
955 }
956 }
957
958 int
959 pim_ifchannel_local_membership_add(struct interface *ifp,
960 struct prefix_sg *sg)
961 {
962 struct pim_ifchannel *ch, *starch;
963 struct pim_interface *pim_ifp;
964
965 /* PIM enabled on interface? */
966 pim_ifp = ifp->info;
967 if (!pim_ifp)
968 return 0;
969 if (!PIM_IF_TEST_PIM(pim_ifp->options))
970 return 0;
971
972 /* skip (*,G) ch creation if G is of type SSM */
973 if (sg->src.s_addr == INADDR_ANY)
974 {
975 if (pim_is_grp_ssm (sg->grp))
976 {
977 if (PIM_DEBUG_PIM_EVENTS)
978 zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM",
979 __PRETTY_FUNCTION__, pim_str_sg_dump (sg));
980 return 1;
981 }
982 }
983
984 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
985 if (!ch) {
986 return 0;
987 }
988
989 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
990
991 if (sg->src.s_addr == INADDR_ANY)
992 {
993 struct pim_upstream *up = pim_upstream_find (sg);
994 struct pim_upstream *child;
995 struct listnode *up_node;
996
997 starch = ch;
998
999 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
1000 {
1001 if (PIM_DEBUG_EVENTS)
1002 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1003 __FILE__, __PRETTY_FUNCTION__,
1004 child->sg_str, ifp->name, up->sg_str);
1005
1006 ch = pim_ifchannel_find (ifp, &child->sg);
1007 if (pim_upstream_evaluate_join_desired_interface (child, ch, starch))
1008 {
1009 pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
1010 pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
1011 }
1012 }
1013
1014 if (pimg->spt.switchover == PIM_SPT_INFINITY)
1015 {
1016 if (pimg->spt.plist)
1017 {
1018 struct prefix_list *plist = prefix_list_lookup (AFI_IP, pimg->spt.plist);
1019 struct prefix g;
1020 g.family = AF_INET;
1021 g.prefixlen = IPV4_MAX_PREFIXLEN;
1022 g.u.prefix4 = up->sg.grp;
1023
1024 if (prefix_list_apply (plist, &g) == PREFIX_DENY)
1025 {
1026 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1027 }
1028 }
1029 }
1030 else
1031 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1032 }
1033
1034 return 1;
1035 }
1036
1037 void pim_ifchannel_local_membership_del(struct interface *ifp,
1038 struct prefix_sg *sg)
1039 {
1040 struct pim_ifchannel *starch, *ch, *orig;
1041 struct pim_interface *pim_ifp;
1042
1043 /* PIM enabled on interface? */
1044 pim_ifp = ifp->info;
1045 if (!pim_ifp)
1046 return;
1047 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1048 return;
1049
1050 orig = ch = pim_ifchannel_find(ifp, sg);
1051 if (!ch)
1052 return;
1053
1054 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1055
1056 if (sg->src.s_addr == INADDR_ANY)
1057 {
1058 struct pim_upstream *up = pim_upstream_find (sg);
1059 struct pim_upstream *child;
1060 struct listnode *up_node, *up_nnode;
1061
1062 starch = ch;
1063
1064 for (ALL_LIST_ELEMENTS (up->sources, up_node, up_nnode, child))
1065 {
1066 struct channel_oil *c_oil = child->channel_oil;
1067 struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg);
1068 struct pim_interface *pim_ifp = ifp->info;
1069
1070 if (PIM_DEBUG_EVENTS)
1071 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1072 __FILE__, __PRETTY_FUNCTION__,
1073 up->sg_str, ifp->name, child->sg_str);
1074
1075 ch = pim_ifchannel_find (ifp, &child->sg);
1076 if (c_oil && !pim_upstream_evaluate_join_desired_interface (child, ch, starch))
1077 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
1078
1079 /*
1080 * If the S,G has no if channel and the c_oil still
1081 * has output here then the *,G was supplying the implied
1082 * if channel. So remove it.
1083 */
1084 if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
1085 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
1086
1087 if (c_oil->oil_size == 0)
1088 pim_upstream_del (child, __PRETTY_FUNCTION__);
1089 }
1090 }
1091 delete_on_noinfo(orig);
1092 }
1093
1094 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1095 {
1096 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1097 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1098
1099 if (new_couldassert == old_couldassert)
1100 return;
1101
1102 if (PIM_DEBUG_PIM_EVENTS) {
1103 char src_str[INET_ADDRSTRLEN];
1104 char grp_str[INET_ADDRSTRLEN];
1105 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1106 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1107 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1108 __PRETTY_FUNCTION__,
1109 src_str, grp_str, ch->interface->name,
1110 old_couldassert, new_couldassert);
1111 }
1112
1113 if (new_couldassert) {
1114 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1115 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1116 }
1117 else {
1118 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1119 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1120
1121 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1122 assert_action_a4(ch);
1123 }
1124 }
1125
1126 pim_ifchannel_update_my_assert_metric(ch);
1127 }
1128
1129 /*
1130 my_assert_metric may be affected by:
1131
1132 CouldAssert(S,G)
1133 pim_ifp->primary_address
1134 rpf->source_nexthop.mrib_metric_preference;
1135 rpf->source_nexthop.mrib_route_metric;
1136 */
1137 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1138 {
1139 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
1140
1141 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1142 return;
1143
1144 if (PIM_DEBUG_PIM_EVENTS) {
1145 char src_str[INET_ADDRSTRLEN];
1146 char grp_str[INET_ADDRSTRLEN];
1147 char old_addr_str[INET_ADDRSTRLEN];
1148 char new_addr_str[INET_ADDRSTRLEN];
1149 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1150 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1151 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
1152 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
1153 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1154 __PRETTY_FUNCTION__,
1155 src_str, grp_str, ch->interface->name,
1156 ch->ifassert_my_metric.rpt_bit_flag,
1157 ch->ifassert_my_metric.metric_preference,
1158 ch->ifassert_my_metric.route_metric,
1159 old_addr_str,
1160 my_metric_new.rpt_bit_flag,
1161 my_metric_new.metric_preference,
1162 my_metric_new.route_metric,
1163 new_addr_str);
1164 }
1165
1166 ch->ifassert_my_metric = my_metric_new;
1167
1168 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1169 &ch->ifassert_winner_metric)) {
1170 assert_action_a5(ch);
1171 }
1172 }
1173
1174 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1175 {
1176 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1177 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1178
1179 if (new_atd == old_atd)
1180 return;
1181
1182 if (PIM_DEBUG_PIM_EVENTS) {
1183 char src_str[INET_ADDRSTRLEN];
1184 char grp_str[INET_ADDRSTRLEN];
1185 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1186 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1187 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1188 __PRETTY_FUNCTION__,
1189 src_str, grp_str, ch->interface->name,
1190 old_atd, new_atd);
1191 }
1192
1193 if (new_atd) {
1194 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1195 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1196 }
1197 else {
1198 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1199 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1200
1201 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1202 assert_action_a5(ch);
1203 }
1204 }
1205 }
1206
1207 /*
1208 * If we have a new pim interface, check to
1209 * see if any of the pre-existing channels have
1210 * their upstream out that way and turn on forwarding
1211 * for that ifchannel then.
1212 */
1213 void
1214 pim_ifchannel_scan_forward_start (struct interface *new_ifp)
1215 {
1216 struct listnode *ifnode;
1217 struct interface *ifp;
1218 struct pim_interface *new_pim_ifp = new_ifp->info;
1219
1220 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
1221 {
1222 struct pim_interface *loop_pim_ifp = ifp->info;
1223 struct listnode *ch_node;
1224 struct pim_ifchannel *ch;
1225
1226 if (!loop_pim_ifp)
1227 continue;
1228
1229 if (new_pim_ifp == loop_pim_ifp)
1230 continue;
1231
1232 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch))
1233 {
1234 if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
1235 {
1236 struct pim_upstream *up = ch->upstream;
1237 if ((!up->channel_oil) &&
1238 (up->rpf.source_nexthop.interface == new_ifp))
1239 pim_forward_start (ch);
1240 }
1241 }
1242 }
1243 }
1244
1245 /*
1246 * Downstream per-interface (S,G,rpt) state machine
1247 * states that we need to move (S,G,rpt) items
1248 * into different states at the start of the
1249 * reception of a *,G join as well, when
1250 * we get End of Message
1251 */
1252 void
1253 pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom)
1254 {
1255 struct pim_ifchannel *child;
1256 struct listnode *ch_node;
1257
1258 if (PIM_DEBUG_PIM_TRACE)
1259 zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__,
1260 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1261 ch->sg_str, eom);
1262 if (!ch->sources)
1263 return;
1264
1265 for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child))
1266 {
1267 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1268 continue;
1269
1270 switch (child->ifjoin_state)
1271 {
1272 case PIM_IFJOIN_NOINFO:
1273 case PIM_IFJOIN_JOIN:
1274 break;
1275 case PIM_IFJOIN_PRUNE:
1276 if (!eom)
1277 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1278 break;
1279 case PIM_IFJOIN_PRUNE_PENDING:
1280 if (!eom)
1281 child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP;
1282 break;
1283 case PIM_IFJOIN_PRUNE_TMP:
1284 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1285 if (eom)
1286 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1287 break;
1288 }
1289 }
1290 }
1291
1292 unsigned int
1293 pim_ifchannel_hash_key (void *arg)
1294 {
1295 struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
1296
1297 return jhash_2words (ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1298 }
1299
1300 int
1301 pim_ifchannel_equal (const void *arg1, const void *arg2)
1302 {
1303 const struct pim_ifchannel *ch1 = (const struct pim_ifchannel *)arg1;
1304 const struct pim_ifchannel *ch2 = (const struct pim_ifchannel *)arg2;
1305
1306 if ((ch1->sg.grp.s_addr == ch2->sg.grp.s_addr) &&
1307 (ch1->sg.src.s_addr == ch2->sg.src.s_addr))
1308 return 1;
1309
1310 return 0;
1311 }